[build] - initial commit
This commit is contained in:
4
.editorconfig
Normal file
4
.editorconfig
Normal file
@@ -0,0 +1,4 @@
|
||||
[*.{kt,kts}]
|
||||
indent_size=2
|
||||
insert_final_newline=false
|
||||
disabled_rules=no-wildcard-imports
|
||||
35
.gitignore
vendored
Normal file
35
.gitignore
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# Gradle files
|
||||
.gradle/
|
||||
|
||||
# IntelliJ files
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
# Build files
|
||||
build/
|
||||
.externalNativeBuild
|
||||
**/.cxx
|
||||
*.so
|
||||
*.apk
|
||||
|
||||
# Local settings
|
||||
local.properties
|
||||
gradle/configuration.gradle
|
||||
|
||||
# Token file
|
||||
MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml
|
||||
|
||||
# Capture files
|
||||
captures/
|
||||
|
||||
# Generated test cases
|
||||
MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/gen/
|
||||
|
||||
# Generated list files from code generation
|
||||
/scripts/generate-style-code.list
|
||||
|
||||
# Input files for running render tests
|
||||
MapboxGLAndroidSDKTestApp/src/main/assets/integration/
|
||||
|
||||
# Local AS configuration file
|
||||
gradle.properties
|
||||
12
.gitmodules
vendored
Normal file
12
.gitmodules
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
[submodule "vendor/mapbox-gl-native"]
|
||||
path = vendor/mapbox-gl-native
|
||||
url = git@github.com:mapbox/mapbox-gl-native.git
|
||||
[submodule "vendor/mapbox-events-android"]
|
||||
path = vendor/mapbox-events-android
|
||||
url = git@github.com:mapbox/mapbox-events-android.git
|
||||
[submodule "vendor/mapbox-java"]
|
||||
path = vendor/mapbox-java
|
||||
url = git@github.com:mapbox/mapbox-java.git
|
||||
[submodule "vendor/mapbox-gestures-android"]
|
||||
path = vendor/mapbox-gestures-android
|
||||
url = git@github.com:mapbox/mapbox-gestures-android.git
|
||||
1767
CHANGELOG.md
Normal file
1767
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
57
DISTRIBUTE.md
Normal file
57
DISTRIBUTE.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Distributing Mapbox GL Native for Android
|
||||
|
||||
Depending on your use case, you may want to support all or just a subset of [Android ABIs](http://developer.android.com/ndk/guides/abis.html). This document covers building an `.aar` file from the Mapbox Maps SDK for Android and building `.so` files for specific ABIs. In normal circumstances an application developer will use [APK splits](https://developer.android.com/studio/build/configure-apk-splits.html) to optimize this at application level.
|
||||
|
||||
##### Build types
|
||||
|
||||
With a `BUILDTYPE` var you can specify the build type for the `.so` and `.aar` files:
|
||||
|
||||
```bash
|
||||
BUILDTYPE=Debug or BUILDTYPE=Release
|
||||
```
|
||||
|
||||
##### Creating an Android Archive file that supports all ABIs
|
||||
|
||||
```sh
|
||||
BUILDTYPE=Release make apackage
|
||||
```
|
||||
|
||||
This will build native libraries to support following ABIs:
|
||||
- armeabi
|
||||
- armeabi-v7a
|
||||
- arm64-v8a
|
||||
- x86
|
||||
- x86_64
|
||||
- mips
|
||||
|
||||
After succesfully finish building the native libraries, gradle will build the MapboxAndroidSDK module and generate an Android Archive file in `MapboxAndroidSDK/build/outputs/aar `. The packaged native libraries can be found in `MapboxAndroidSDK/src/main/jniLibs/<abi>`.
|
||||
|
||||
##### Build native libraries for a specific ABI
|
||||
|
||||
```sh
|
||||
make android-lib-%%
|
||||
```
|
||||
|
||||
In the command above you'll need to replace `%%` with an ABI key listed below:
|
||||
|
||||
| ABI Key | Android ABI |
|
||||
| ------- | ----------- |
|
||||
| arm-v5 | armeabi |
|
||||
| arm-v7 | armeabi-v7a |
|
||||
| arm-v8 | arm64-v8a |
|
||||
| x86 | x86 |
|
||||
| x86-64 | x86_64 |
|
||||
| mips | mips |
|
||||
|
||||
For example, to build the arm64-v8a ABI the Makefile target would be:
|
||||
|
||||
```sh
|
||||
make android-lib-arm-v8
|
||||
```
|
||||
|
||||
It's also possible to build multiple ABI (without having to build them all) by running the Makefile multiple times. For example to build arm64-v8a and x86_64 in the same project the commands would be:
|
||||
|
||||
```sh
|
||||
make android-lib-arm-v8
|
||||
make android-lib-arm-x86-64
|
||||
```
|
||||
227
LICENSE.md
Normal file
227
LICENSE.md
Normal file
@@ -0,0 +1,227 @@
|
||||
<!-- This file was generated. Use `make android-license` to update. -->
|
||||
## Additional Mapbox GL licenses
|
||||
Mapbox GL uses portions of the Android Arch-Common.
|
||||
URL: [https://developer.android.com/topic/libraries/architecture/index.html](https://developer.android.com/topic/libraries/architecture/index.html)
|
||||
License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Arch-Runtime.
|
||||
URL: [https://developer.android.com/topic/libraries/architecture/index.html](https://developer.android.com/topic/libraries/architecture/index.html)
|
||||
License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Lifecycle LiveData.
|
||||
URL: [https://developer.android.com/topic/libraries/architecture/index.html](https://developer.android.com/topic/libraries/architecture/index.html)
|
||||
License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Lifecycle LiveData Core.
|
||||
URL: [https://developer.android.com/topic/libraries/architecture/index.html](https://developer.android.com/topic/libraries/architecture/index.html)
|
||||
License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Lifecycle Runtime.
|
||||
URL: [https://developer.android.com/topic/libraries/architecture/index.html](https://developer.android.com/topic/libraries/architecture/index.html)
|
||||
License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Lifecycle ViewModel.
|
||||
URL: [https://developer.android.com/topic/libraries/architecture/index.html](https://developer.android.com/topic/libraries/architecture/index.html)
|
||||
License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Lifecycle-Common.
|
||||
URL: [https://developer.android.com/topic/libraries/architecture/index.html](https://developer.android.com/topic/libraries/architecture/index.html)
|
||||
License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library Annotations.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library Async Layout Inflater.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library collections.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library compat.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library Coordinator Layout.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library core UI.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library core utils.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library Cursor Adapter.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library Custom View.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library Custom View.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library Document File.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library Drawer Layout.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library fragment.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library Interpolators.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library loader.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library Local Broadcast Manager.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library Print.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library Sliding Pane Layout.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Android Support Library View Pager.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Gson.
|
||||
License: [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Mapbox Accounts SDK for Android.
|
||||
URL: [https://github.com/mapbox/mapbox-accounts-android](https://github.com/mapbox/mapbox-accounts-android)
|
||||
License: [Mapbox Terms of Service](https://www.mapbox.com/tos/)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Mapbox Android Core Library.
|
||||
URL: [https://github.com/mapbox/mapbox-events-android](https://github.com/mapbox/mapbox-events-android)
|
||||
License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Mapbox Android Gestures Library.
|
||||
URL: [https://github.com/mapbox/mapbox-gestures-android](https://github.com/mapbox/mapbox-gestures-android)
|
||||
License: [BSD](https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Mapbox Android Telemetry Library.
|
||||
URL: [https://github.com/mapbox/mapbox-events-android](https://github.com/mapbox/mapbox-events-android)
|
||||
License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Mapbox Java SDK.
|
||||
URL: [https://github.com/mapbox/mapbox-java](https://github.com/mapbox/mapbox-java)
|
||||
License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Mapbox Java SDK.
|
||||
URL: [https://github.com/mapbox/mapbox-java](https://github.com/mapbox/mapbox-java)
|
||||
License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the OkHttp.
|
||||
License: [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Okio.
|
||||
License: [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the SoLoader.
|
||||
URL: [https://github.com/facebook/soloader](https://github.com/facebook/soloader)
|
||||
License: [Apache-2](https://github.com/facebook/soloader/blob/master/LICENSE)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the VersionedParcelable and friends.
|
||||
URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html)
|
||||
License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
Mapbox GL uses portions of the Gradle License Plugin.
|
||||
URL: [https://github.com/jaredsburrows/gradle-license-plugin](https://github.com/jaredsburrows/gradle-license-plugin)
|
||||
License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt)
|
||||
|
||||
===========================================================================
|
||||
|
||||
2
MapboxGLAndroidSDK/.gitignore
vendored
Normal file
2
MapboxGLAndroidSDK/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
dependency-graph-mapbox-libraries.png
|
||||
src/test/resources/mobile-event*
|
||||
105
MapboxGLAndroidSDK/build.gradle
Normal file
105
MapboxGLAndroidSDK/build.gradle
Normal file
@@ -0,0 +1,105 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: "com.jaredsburrows.license"
|
||||
apply plugin: 'kotlin-android'
|
||||
apply from: "${rootDir}/gradle/native-build.gradle"
|
||||
|
||||
dependencies {
|
||||
lintChecks project(":MapboxGLAndroidSDKLint")
|
||||
api dependenciesList.mapboxAndroidTelemetry
|
||||
api dependenciesList.mapboxJavaGeoJSON
|
||||
api dependenciesList.mapboxAndroidGestures
|
||||
api dependenciesList.mapboxAndroidAccounts
|
||||
implementation dependenciesList.mapboxJavaTurf
|
||||
implementation dependenciesList.supportAnnotations
|
||||
implementation dependenciesList.supportFragmentV4
|
||||
implementation dependenciesList.okhttp3
|
||||
implementation dependenciesList.soLoader
|
||||
testImplementation dependenciesList.junit
|
||||
testImplementation dependenciesList.mockito
|
||||
testImplementation dependenciesList.mockk
|
||||
testImplementation dependenciesList.robolectric
|
||||
testImplementation dependenciesList.kotlinLib
|
||||
testImplementation dependenciesList.commonsIO
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion androidVersions.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion androidVersions.minSdkVersion
|
||||
targetSdkVersion androidVersions.targetSdkVersion
|
||||
buildConfigField "String", "GIT_REVISION_SHORT", String.format("\"%s\"", getGitRevision())
|
||||
buildConfigField "String", "MAPBOX_SDK_IDENTIFIER", String.format("\"%s\"", "mapbox-maps-android")
|
||||
buildConfigField "String", "MAPBOX_SDK_VERSION", String.format("\"%s\"", project.VERSION_NAME)
|
||||
buildConfigField "String", "MAPBOX_VERSION_STRING", String.format("\"Mapbox/%s\"", project.VERSION_NAME)
|
||||
buildConfigField "String", "MAPBOX_EVENTS_USER_AGENT", String.format("\"mapbox-maps-android/%s\"", project.VERSION_NAME)
|
||||
consumerProguardFiles 'proguard-rules.pro'
|
||||
}
|
||||
|
||||
// build native libraries
|
||||
List nativeTargets = ["mapbox-gl"]
|
||||
if (project.hasProperty("mapbox.with_test")) {
|
||||
nativeTargets.add("mbgl-test")
|
||||
}
|
||||
if (project.hasProperty("mapbox.with_benchmark")) {
|
||||
nativeTargets.add("mbgl-benchmark")
|
||||
}
|
||||
nativeBuild(nativeTargets)
|
||||
|
||||
// avoid naming conflicts, force usage of prefix
|
||||
resourcePrefix 'mapbox_'
|
||||
|
||||
sourceSets {
|
||||
// limit amount of exposed library resources
|
||||
main.res.srcDirs += 'src/main/res-public'
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable 'MissingTranslation', 'TypographyQuotes', 'ObsoleteLintCustomCheck', 'MissingPermission', 'WrongThreadInterprocedural'
|
||||
checkAllWarnings true
|
||||
warningsAsErrors false
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
returnDefaultValues true
|
||||
|
||||
// Roboelectric 4.0 required config
|
||||
// http://robolectric.org/migrating/#migrating-to-40
|
||||
includeAndroidResources = true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
testCoverageEnabled true
|
||||
jniDebuggable true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
licenseReport {
|
||||
generateHtmlReport = false
|
||||
generateJsonReport = true
|
||||
copyHtmlReportToAssets = false
|
||||
copyJsonReportToAssets = false
|
||||
}
|
||||
|
||||
def static getGitRevision() {
|
||||
def cmd = "git rev-parse --short HEAD"
|
||||
def proc = cmd.execute()
|
||||
def ref = proc.text.trim()
|
||||
return ref
|
||||
}
|
||||
|
||||
configurations {
|
||||
all*.exclude group: 'commons-logging', module: 'commons-logging'
|
||||
all*.exclude group: 'commons-collections', module: 'commons-collections'
|
||||
}
|
||||
apply from: "${rootDir}/gradle/gradle-javadoc.gradle"
|
||||
apply from: "${rootDir}/gradle/gradle-checkstyle.gradle"
|
||||
apply from: "${rootDir}/gradle/gradle-dependencies-graph.gradle"
|
||||
apply from: "${rootDir}/gradle/gradle-update-vendor-modules.gradle"
|
||||
apply from: "${rootDir}/gradle/android-nitpick.gradle"
|
||||
apply from: "${rootDir}/gradle/gradle-bintray.gradle"
|
||||
apply from: "${rootDir}/gradle/jacoco-report.gradle"
|
||||
7
MapboxGLAndroidSDK/lint.xml
Normal file
7
MapboxGLAndroidSDK/lint.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<lint>
|
||||
<!-- Ignore errors caused by OkHTTP -->
|
||||
<issue id="InvalidPackage" severity="ignore">
|
||||
<ignore path="*/okio-1.2.0.jar" />
|
||||
</issue>
|
||||
</lint>
|
||||
41
MapboxGLAndroidSDK/proguard-rules.pro
vendored
Normal file
41
MapboxGLAndroidSDK/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in ../sdk/tools/proguard/proguard-android.txt,
|
||||
# contents of this file will be appended into proguard-android.txt
|
||||
-keepattributes Signature, *Annotation*, EnclosingMethod
|
||||
|
||||
# Reflection on classes from native code
|
||||
-keep class com.google.gson.JsonArray { *; }
|
||||
-keep class com.google.gson.JsonElement { *; }
|
||||
-keep class com.google.gson.JsonObject { *; }
|
||||
-keep class com.google.gson.JsonPrimitive { *; }
|
||||
-dontnote com.google.gson.**
|
||||
|
||||
# dontnote for keeps the entry point x but not the descriptor class y
|
||||
-dontnote com.mapbox.mapboxsdk.maps.MapboxMap$OnFpsChangedListener
|
||||
-dontnote com.mapbox.mapboxsdk.style.layers.PropertyValue
|
||||
-dontnote com.mapbox.mapboxsdk.maps.MapboxMap
|
||||
-dontnote com.mapbox.mapboxsdk.maps.MapboxMapOptions
|
||||
-dontnote com.mapbox.mapboxsdk.log.LoggerDefinition
|
||||
-dontnote com.mapbox.android.core.location.LocationEnginePriority
|
||||
|
||||
# config for okhttp 3.11.0, https://github.com/square/okhttp/pull/3354
|
||||
-dontwarn javax.annotation.**
|
||||
-dontnote okhttp3.internal.**
|
||||
-dontwarn org.codehaus.**
|
||||
|
||||
# config for mapbox-sdk-geojson:3.0.1
|
||||
-keep class com.mapbox.geojson.** { *; }
|
||||
-dontwarn com.google.auto.value.**
|
||||
|
||||
# config for additional notes
|
||||
-dontnote org.robolectric.Robolectric
|
||||
-dontnote libcore.io.Memory
|
||||
-dontnote com.google.protobuf.**
|
||||
-dontnote android.net.**
|
||||
-dontnote org.apache.http.**
|
||||
|
||||
# config for mapbox-sdk-services
|
||||
# while we don't include this dependency directly
|
||||
# a large amount of users combine it with our SDK
|
||||
# we aren't able to provide a proguard config in that project (jar vs aar)
|
||||
-dontwarn com.sun.xml.internal.ws.spi.db.*
|
||||
15
MapboxGLAndroidSDK/src/main/AndroidManifest.xml
Normal file
15
MapboxGLAndroidSDK/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mapbox.mapboxsdk">
|
||||
|
||||
<uses-feature
|
||||
android:glEsVersion="0x00020000"
|
||||
android:required="true"/>
|
||||
<uses-feature
|
||||
android:name="android.hardware.wifi"
|
||||
android:required="false"/> <!-- Implied by ACCESS_WIFI_STATE. -->
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,170 @@
|
||||
package com.mapbox.mapboxsdk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
import com.mapbox.android.accounts.v1.MapboxAccounts;
|
||||
import com.mapbox.mapboxsdk.constants.MapboxConstants;
|
||||
import com.mapbox.mapboxsdk.log.Logger;
|
||||
|
||||
import static com.mapbox.mapboxsdk.constants.MapboxConstants.KEY_PREFERENCE_SKU_TOKEN;
|
||||
|
||||
/**
|
||||
* REMOVAL OR MODIFICATION OF THE FOLLOWING CODE VIOLATES THE MAPBOX TERMS
|
||||
* OF SERVICE
|
||||
* <p>
|
||||
* The following code is used to access Mapbox's Mapping APIs.
|
||||
* <p>
|
||||
* Removal or modification of this code when used with Mapbox's Mapping APIs
|
||||
* can result in termination of your agreement and/or your account with
|
||||
* Mapbox.
|
||||
* <p>
|
||||
* Using this code to access Mapbox Mapping APIs from outside the Mapbox Maps
|
||||
* SDK also violates the Mapbox Terms of Service. On Android, Mapping APIs
|
||||
* should be accessed using the methods documented at
|
||||
* https://www.mapbox.com/android.
|
||||
* <p>
|
||||
* You can access the Mapbox Terms of Service at https://www.mapbox.com/tos/
|
||||
*/
|
||||
class AccountsManager {
|
||||
private static final String TAG = "Mbgl-AccountsManager";
|
||||
private static final String PREFERENCE_USER_ID = "com.mapbox.mapboxsdk.accounts.userid";
|
||||
private static final String PREFERENCE_TIMESTAMP = "com.mapbox.mapboxsdk.accounts.timestamp";
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
private String skuToken;
|
||||
private long timestamp;
|
||||
private boolean isManaged;
|
||||
|
||||
AccountsManager() {
|
||||
isManaged = isSkuTokenManaged();
|
||||
initialize();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
AccountsManager(SharedPreferences sharedPreferences, boolean isManaged) {
|
||||
this.sharedPreferences = sharedPreferences;
|
||||
this.isManaged = isManaged;
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
retrieveSkuTokenAndTimestamp();
|
||||
if (isManaged) {
|
||||
String userId = validateUserId();
|
||||
validateRotation(userId);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSkuTokenManaged() {
|
||||
boolean value = MapboxConstants.DEFAULT_MANAGE_SKU_TOKEN;
|
||||
try {
|
||||
// Try getting a custom value from the app Manifest
|
||||
ApplicationInfo appInfo = retrieveApplicationInfo();
|
||||
if (appInfo.metaData != null) {
|
||||
value = appInfo.metaData.getBoolean(
|
||||
MapboxConstants.KEY_META_DATA_MANAGE_SKU_TOKEN,
|
||||
MapboxConstants.DEFAULT_MANAGE_SKU_TOKEN
|
||||
);
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
Logger.e(TAG, "Failed to read the package metadata: ", exception);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private ApplicationInfo retrieveApplicationInfo() throws PackageManager.NameNotFoundException {
|
||||
return Mapbox.getApplicationContext().getPackageManager().getApplicationInfo(
|
||||
Mapbox.getApplicationContext().getPackageName(),
|
||||
PackageManager.GET_META_DATA);
|
||||
}
|
||||
|
||||
private void retrieveSkuTokenAndTimestamp() {
|
||||
SharedPreferences sharedPreferences = getSharedPreferences();
|
||||
skuToken = sharedPreferences.getString(KEY_PREFERENCE_SKU_TOKEN, "");
|
||||
timestamp = sharedPreferences.getLong(PREFERENCE_TIMESTAMP, 0L);
|
||||
}
|
||||
|
||||
private String validateUserId() {
|
||||
SharedPreferences sharedPreferences = getSharedPreferences();
|
||||
String userId = sharedPreferences.getString(PREFERENCE_USER_ID, "");
|
||||
if (TextUtils.isEmpty(userId)) {
|
||||
userId = generateUserId();
|
||||
SharedPreferences.Editor editor = getSharedPreferences().edit();
|
||||
editor.putString(PREFERENCE_USER_ID, userId);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
return userId;
|
||||
}
|
||||
|
||||
private void validateRotation(String userId) {
|
||||
if (TextUtils.isEmpty(skuToken) || timestamp == 0L) {
|
||||
skuToken = generateSkuToken(userId);
|
||||
timestamp = persistRotation(skuToken);
|
||||
}
|
||||
}
|
||||
|
||||
String getSkuToken() {
|
||||
if (isManaged) {
|
||||
if (isExpired()) {
|
||||
SharedPreferences sharedPreferences = getSharedPreferences();
|
||||
String userId = sharedPreferences.getString(PREFERENCE_USER_ID, "");
|
||||
skuToken = generateSkuToken(userId);
|
||||
timestamp = persistRotation(skuToken);
|
||||
}
|
||||
} else {
|
||||
SharedPreferences sharedPreferences = getSharedPreferences();
|
||||
String notManagedSkuToken = sharedPreferences.getString(KEY_PREFERENCE_SKU_TOKEN, "");
|
||||
skuToken = notManagedSkuToken;
|
||||
}
|
||||
return skuToken;
|
||||
}
|
||||
|
||||
private boolean isExpired() {
|
||||
return isExpired(getNow(), timestamp);
|
||||
}
|
||||
|
||||
static boolean isExpired(long now, long then) {
|
||||
return ((now - then) > DateUtils.HOUR_IN_MILLIS);
|
||||
}
|
||||
|
||||
private long persistRotation(String skuToken) {
|
||||
long now = getNow();
|
||||
SharedPreferences.Editor editor = getSharedPreferences().edit();
|
||||
editor.putLong(PREFERENCE_TIMESTAMP, now);
|
||||
editor.putString(KEY_PREFERENCE_SKU_TOKEN, skuToken);
|
||||
editor.apply();
|
||||
return now;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private SharedPreferences getSharedPreferences() {
|
||||
if (sharedPreferences == null) {
|
||||
sharedPreferences = Mapbox.getApplicationContext()
|
||||
.getSharedPreferences(MapboxConstants.MAPBOX_SHARED_PREFERENCES, Context.MODE_PRIVATE);
|
||||
}
|
||||
return sharedPreferences;
|
||||
}
|
||||
|
||||
static long getNow() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private String generateUserId() {
|
||||
return MapboxAccounts.obtainEndUserId();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private String generateSkuToken(String userId) {
|
||||
return MapboxAccounts.obtainMapsSkuUserToken(userId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.mapbox.mapboxsdk;
|
||||
|
||||
import com.mapbox.mapboxsdk.log.Logger;
|
||||
|
||||
/**
|
||||
* Loads the mapbox-gl shared library
|
||||
* <p>
|
||||
* By default uses the {@link System#loadLibrary(String)},
|
||||
* use {@link #setLibraryLoader(LibraryLoader)} to provide an alternative library loading hook.
|
||||
* </p>
|
||||
*/
|
||||
public abstract class LibraryLoader {
|
||||
|
||||
private static final String TAG = "Mbgl-LibraryLoader";
|
||||
|
||||
private static final LibraryLoader DEFAULT = Mapbox.getModuleProvider()
|
||||
.createLibraryLoaderProvider()
|
||||
.getDefaultLibraryLoader();
|
||||
|
||||
private static volatile LibraryLoader loader = DEFAULT;
|
||||
|
||||
private static boolean loaded;
|
||||
|
||||
/**
|
||||
* Set the library loader that loads the shared library.
|
||||
*
|
||||
* @param libraryLoader the library loader
|
||||
*/
|
||||
public static void setLibraryLoader(LibraryLoader libraryLoader) {
|
||||
loader = libraryLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads "libmapbox-gl.so" native shared library.
|
||||
* <p>
|
||||
* Catches UnsatisfiedLinkErrors and prints a warning to logcat.
|
||||
* </p>
|
||||
*/
|
||||
public static void load() {
|
||||
try {
|
||||
if (!loaded) {
|
||||
loaded = true;
|
||||
loader.load("mapbox-gl");
|
||||
}
|
||||
} catch (UnsatisfiedLinkError error) {
|
||||
loaded = false;
|
||||
String message = "Failed to load native shared library.";
|
||||
Logger.e(TAG, message, error);
|
||||
MapStrictMode.strictModeViolation(message, error);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void load(String name);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.mapbox.mapboxsdk;
|
||||
|
||||
/**
|
||||
* Injects the default library loader.
|
||||
*/
|
||||
public interface LibraryLoaderProvider {
|
||||
|
||||
/**
|
||||
* Creates and returns a the default Library Loader.
|
||||
*
|
||||
* @return the default library loader
|
||||
*/
|
||||
LibraryLoader getDefaultLibraryLoader();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.mapbox.mapboxsdk;
|
||||
|
||||
/**
|
||||
* Using this class you can enable a strict mode that will throw the {@link MapStrictModeException}
|
||||
* whenever the map would fail silently otherwise.
|
||||
*/
|
||||
public class MapStrictMode {
|
||||
private static volatile boolean strictModeEnabled;
|
||||
|
||||
/**
|
||||
* Set the strict mode that will throw the {@link MapStrictModeException}
|
||||
* whenever the map would fail silently otherwise.
|
||||
*
|
||||
* @param strictModeEnabled true to enable the strict mode, false otherwise
|
||||
*/
|
||||
public static synchronized void setStrictModeEnabled(boolean strictModeEnabled) {
|
||||
MapStrictMode.strictModeEnabled = strictModeEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use. Called whenever the strict mode violation occurs.
|
||||
*/
|
||||
public static void strictModeViolation(String message) {
|
||||
if (strictModeEnabled) {
|
||||
throw new MapStrictModeException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use. Called whenever the strict mode violation occurs.
|
||||
*/
|
||||
public static void strictModeViolation(String message, Throwable throwable) {
|
||||
if (strictModeEnabled) {
|
||||
throw new MapStrictModeException(String.format("%s - %s", message, throwable));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use. Called whenever the strict mode violation occurs.
|
||||
*/
|
||||
public static void strictModeViolation(Throwable throwable) {
|
||||
if (strictModeEnabled) {
|
||||
throw new MapStrictModeException(String.format("%s", throwable));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.mapbox.mapboxsdk;
|
||||
|
||||
class MapStrictModeException extends RuntimeException {
|
||||
MapStrictModeException(String message) {
|
||||
super(String.format("Map detected an error that would fail silently otherwise: %s", message));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package com.mapbox.mapboxsdk;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
import com.mapbox.mapboxsdk.constants.MapboxConstants;
|
||||
import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException;
|
||||
import com.mapbox.mapboxsdk.log.Logger;
|
||||
import com.mapbox.mapboxsdk.maps.TelemetryDefinition;
|
||||
import com.mapbox.mapboxsdk.net.ConnectivityReceiver;
|
||||
import com.mapbox.mapboxsdk.storage.FileSource;
|
||||
import com.mapbox.mapboxsdk.utils.ThreadUtils;
|
||||
|
||||
/**
|
||||
* The entry point to initialize the Mapbox Android SDK.
|
||||
* <p>
|
||||
* Obtain a reference by calling {@link #getInstance(Context, String)}. Usually this class is configured in
|
||||
* Application#onCreate() and is responsible for the active access token, application context, and
|
||||
* connectivity state.
|
||||
* </p>
|
||||
*/
|
||||
@UiThread
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
public final class Mapbox {
|
||||
|
||||
private static final String TAG = "Mbgl-Mapbox";
|
||||
private static ModuleProvider moduleProvider;
|
||||
private static Mapbox INSTANCE;
|
||||
|
||||
private Context context;
|
||||
@Nullable
|
||||
private String accessToken;
|
||||
@Nullable
|
||||
private TelemetryDefinition telemetry;
|
||||
@Nullable
|
||||
private AccountsManager accounts;
|
||||
|
||||
/**
|
||||
* Get an instance of Mapbox.
|
||||
* <p>
|
||||
* This class manages the Mapbox access token, application context, and connectivity state.
|
||||
* </p>
|
||||
*
|
||||
* @param context Android context which holds or is an application context
|
||||
* @param accessToken Mapbox access token
|
||||
* @return the single instance of Mapbox
|
||||
*/
|
||||
@UiThread
|
||||
@NonNull
|
||||
public static synchronized Mapbox getInstance(@NonNull Context context, @Nullable String accessToken) {
|
||||
ThreadUtils.init(context);
|
||||
ThreadUtils.checkThread(TAG);
|
||||
if (INSTANCE == null) {
|
||||
Context appContext = context.getApplicationContext();
|
||||
FileSource.initializeFileDirsPaths(appContext);
|
||||
INSTANCE = new Mapbox(appContext, accessToken);
|
||||
if (isAccessTokenValid(accessToken)) {
|
||||
initializeTelemetry();
|
||||
INSTANCE.accounts = new AccountsManager();
|
||||
}
|
||||
ConnectivityReceiver.instance(appContext);
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
Mapbox(@NonNull Context context, @Nullable String accessToken) {
|
||||
this.context = context;
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current active access token for this application.
|
||||
*
|
||||
* @return Mapbox access token
|
||||
*/
|
||||
@Nullable
|
||||
public static String getAccessToken() {
|
||||
validateMapbox();
|
||||
return INSTANCE.accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current active accessToken.
|
||||
*/
|
||||
public static void setAccessToken(String accessToken) {
|
||||
validateMapbox();
|
||||
INSTANCE.accessToken = accessToken;
|
||||
|
||||
// cleanup telemetry which is dependent on an access token
|
||||
if (INSTANCE.telemetry != null) {
|
||||
INSTANCE.telemetry.disableTelemetrySession();
|
||||
INSTANCE.telemetry = null;
|
||||
}
|
||||
|
||||
// initialize components dependent on a token
|
||||
if (isAccessTokenValid(accessToken)) {
|
||||
initializeTelemetry();
|
||||
INSTANCE.accounts = new AccountsManager();
|
||||
} else {
|
||||
INSTANCE.accounts = null;
|
||||
}
|
||||
FileSource.getInstance(getApplicationContext()).setAccessToken(accessToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a SKU token, refreshed if necessary. This method is meant for internal SDK
|
||||
* usage only.
|
||||
*
|
||||
* @return the SKU token
|
||||
*/
|
||||
public static String getSkuToken() {
|
||||
if (INSTANCE.accounts == null) {
|
||||
throw new MapboxConfigurationException(
|
||||
"A valid access token parameter is required when using a Mapbox service."
|
||||
+ "\nPlease see https://www.mapbox.com/help/create-api-access-token/ to learn how to create one."
|
||||
+ "\nMore information in this guide https://www.mapbox.com/help/first-steps-android-sdk/#access-tokens."
|
||||
+ "Currently provided token is: " + INSTANCE.accessToken);
|
||||
}
|
||||
return INSTANCE.accounts.getSkuToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* Application context
|
||||
*
|
||||
* @return the application context
|
||||
*/
|
||||
@NonNull
|
||||
public static Context getApplicationContext() {
|
||||
validateMapbox();
|
||||
return INSTANCE.context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually sets the connectivity state of the app. This is useful for apps which control their
|
||||
* own connectivity state and want to bypass any checks to the ConnectivityManager.
|
||||
*
|
||||
* @param connected flag to determine the connectivity state, true for connected, false for
|
||||
* disconnected, and null for ConnectivityManager to determine.
|
||||
*/
|
||||
public static synchronized void setConnected(Boolean connected) {
|
||||
validateMapbox();
|
||||
ConnectivityReceiver.instance(INSTANCE.context).setConnected(connected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether we have an internet connection available. Please do not rely on this
|
||||
* method in your apps. This method is used internally by the SDK.
|
||||
*
|
||||
* @return true if there is an internet connection, false otherwise
|
||||
*/
|
||||
public static synchronized Boolean isConnected() {
|
||||
validateMapbox();
|
||||
return ConnectivityReceiver.instance(INSTANCE.context).isConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes telemetry
|
||||
*/
|
||||
private static void initializeTelemetry() {
|
||||
try {
|
||||
INSTANCE.telemetry = getModuleProvider().obtainTelemetry();
|
||||
} catch (Exception exception) {
|
||||
String message = "Error occurred while initializing telemetry";
|
||||
Logger.e(TAG, message, exception);
|
||||
MapStrictMode.strictModeViolation(message, exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of Telemetry if initialised
|
||||
*
|
||||
* @return instance of telemetry
|
||||
*/
|
||||
@Nullable
|
||||
public static TelemetryDefinition getTelemetry() {
|
||||
return INSTANCE.telemetry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the module provider
|
||||
*
|
||||
* @return moduleProvider
|
||||
*/
|
||||
@NonNull
|
||||
public static ModuleProvider getModuleProvider() {
|
||||
if (moduleProvider == null) {
|
||||
moduleProvider = new ModuleProviderImpl();
|
||||
}
|
||||
return moduleProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runtime validation of Mapbox creation.
|
||||
*/
|
||||
private static void validateMapbox() {
|
||||
if (INSTANCE == null) {
|
||||
throw new MapboxConfigurationException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runtime validation of Mapbox access token
|
||||
*
|
||||
* @param accessToken the access token to validate
|
||||
* @return true is valid, false otherwise
|
||||
*/
|
||||
static boolean isAccessTokenValid(@Nullable String accessToken) {
|
||||
if (accessToken == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
accessToken = accessToken.trim().toLowerCase(MapboxConstants.MAPBOX_LOCALE);
|
||||
return accessToken.length() != 0 && (accessToken.startsWith("pk.") || accessToken.startsWith("sk."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use. Check if the {@link Mapbox#INSTANCE} is present.
|
||||
*/
|
||||
public static boolean hasInstance() {
|
||||
return INSTANCE != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.mapbox.mapboxsdk;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.mapbox.mapboxsdk.http.HttpRequest;
|
||||
import com.mapbox.mapboxsdk.maps.TelemetryDefinition;
|
||||
|
||||
/**
|
||||
* Injects concrete instances of configurable abstractions
|
||||
*/
|
||||
public interface ModuleProvider {
|
||||
|
||||
/**
|
||||
* Create a new concrete implementation of HttpRequest.
|
||||
*
|
||||
* @return a new instance of an HttpRequest
|
||||
*/
|
||||
@NonNull
|
||||
HttpRequest createHttpRequest();
|
||||
|
||||
/**
|
||||
* Get the concrete implementation of TelemetryDefinition
|
||||
*
|
||||
* @return a single instance of Telemetry
|
||||
*/
|
||||
@Nullable
|
||||
TelemetryDefinition obtainTelemetry();
|
||||
|
||||
|
||||
/**
|
||||
* Get the concrete implementation of LibraryLoaderProvider
|
||||
*
|
||||
* @return a new instance of LibraryLoaderProvider
|
||||
*/
|
||||
@NonNull
|
||||
LibraryLoaderProvider createLibraryLoaderProvider();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.mapbox.mapboxsdk;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.mapbox.mapboxsdk.http.HttpRequest;
|
||||
import com.mapbox.mapboxsdk.maps.TelemetryDefinition;
|
||||
import com.mapbox.mapboxsdk.module.http.HttpRequestImpl;
|
||||
import com.mapbox.mapboxsdk.module.loader.LibraryLoaderProviderImpl;
|
||||
import com.mapbox.mapboxsdk.module.telemetry.TelemetryImpl;
|
||||
|
||||
public class ModuleProviderImpl implements ModuleProvider {
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public HttpRequest createHttpRequest() {
|
||||
return new HttpRequestImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public TelemetryDefinition obtainTelemetry() {
|
||||
return new TelemetryImpl();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public LibraryLoaderProvider createLibraryLoaderProvider() {
|
||||
return new LibraryLoaderProviderImpl();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.mapbox.mapboxsdk.maps.MapView;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
|
||||
/**
|
||||
* Annotation is an overlay on top of a Map.
|
||||
* <p>
|
||||
* Known subclasses are {@link Polygon}, {@link Polyline} and {@link Marker}.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class manages attachment to a map and identification, but does not require
|
||||
* content to be placed at a geographical point.
|
||||
* </p>
|
||||
* @deprecated As of 7.0.0,
|
||||
* use <a href="https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-annotation">
|
||||
* Mapbox Annotation Plugin</a> instead
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class Annotation implements Comparable<Annotation> {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The annotation id
|
||||
* </p>
|
||||
* Internal C++ id is stored as unsigned int.
|
||||
*/
|
||||
private long id = -1; // -1 unless added to a MapView
|
||||
protected MapboxMap mapboxMap;
|
||||
protected MapView mapView;
|
||||
|
||||
protected Annotation() {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets the annotation's unique ID.
|
||||
* </p>
|
||||
* This ID is unique for a MapView instance and is suitable for associating your own extra
|
||||
* data with.
|
||||
*
|
||||
* @return the assigned id.
|
||||
*/
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method, used internally by the SDK.
|
||||
*/
|
||||
public void remove() {
|
||||
if (mapboxMap == null) {
|
||||
return;
|
||||
}
|
||||
mapboxMap.removeAnnotation(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method, used internally by the SDK.
|
||||
*
|
||||
* @param id the assigned id
|
||||
*/
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method, used internally by the SDK.
|
||||
*
|
||||
* @param mapboxMap the hosting mapbox map
|
||||
*/
|
||||
public void setMapboxMap(MapboxMap mapboxMap) {
|
||||
this.mapboxMap = mapboxMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hosting mapbox map.
|
||||
*
|
||||
* @return the MapboxMap
|
||||
*/
|
||||
protected MapboxMap getMapboxMap() {
|
||||
return mapboxMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method, used internally by the SDK.
|
||||
*
|
||||
* @param mapView the hosting map view
|
||||
*/
|
||||
public void setMapView(MapView mapView) {
|
||||
this.mapView = mapView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hosting map view.
|
||||
*
|
||||
* @return The MapView
|
||||
*/
|
||||
protected MapView getMapView() {
|
||||
return mapView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this Annotation object with another Annotation.
|
||||
*
|
||||
* @param annotation Another Annotation to compare with this object.
|
||||
* @return returns 0 if id's match, 1 if id is lower, -1 if id is higher of another Annotation
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(@NonNull Annotation annotation) {
|
||||
if (id < annotation.getId()) {
|
||||
return 1;
|
||||
} else if (id > annotation.getId()) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this Annotation object is equal to another Annotation.
|
||||
*
|
||||
* @param object Another Annotation to check equality with this object.
|
||||
* @return returns true both id's match else returns false.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(@Nullable Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
}
|
||||
if (object == null || !(object instanceof Annotation)) {
|
||||
return false;
|
||||
}
|
||||
Annotation that = (Annotation) object;
|
||||
return id == that.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives an integer which can be used as the bucket number for storing elements of the set/map.
|
||||
* This bucket number is the address of the element inside the set/map. There's no guarantee
|
||||
* that this hash value will be consistent between different Java implementations, or even
|
||||
* between different execution runs of the same program.
|
||||
*
|
||||
* @return integer value you can use for storing element.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) (getId() ^ (getId() >>> 32));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.support.annotation.IntDef;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Deprecated
|
||||
class ArrowDirection {
|
||||
@IntDef( {LEFT, RIGHT, TOP, BOTTOM})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface Value {
|
||||
}
|
||||
|
||||
static final int LEFT = 0;
|
||||
static final int RIGHT = 1;
|
||||
static final int TOP = 2;
|
||||
static final int BOTTOM = 3;
|
||||
|
||||
@Value
|
||||
private final int value;
|
||||
|
||||
ArrowDirection(@Value int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Value
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
|
||||
/**
|
||||
* Abstract builder class for composing custom Marker objects.
|
||||
* <p>
|
||||
* Extending this class requires implementing Parceable interface.
|
||||
*
|
||||
* @param <U> Type of the marker to be composed
|
||||
* @param <T> Type of the builder to be used for composing a custom Marker
|
||||
* @deprecated As of 7.0.0,
|
||||
* use <a href="https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-annotation">
|
||||
* Mapbox Annotation Plugin</a> instead
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class BaseMarkerOptions<U extends Marker, T extends BaseMarkerOptions<U, T>> implements Parcelable {
|
||||
|
||||
protected LatLng position;
|
||||
protected String snippet;
|
||||
protected String title;
|
||||
protected Icon icon;
|
||||
|
||||
/**
|
||||
* Set the geographical location of the Marker.
|
||||
*
|
||||
* @param position the location to position the {@link Marker}.
|
||||
* @return the object for which the method was called.
|
||||
*/
|
||||
public T position(LatLng position) {
|
||||
this.position = position;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the snippet of the Marker.
|
||||
*
|
||||
* @param snippet the snippet of the {@link Marker}.
|
||||
* @return the object for which the method was called.
|
||||
*/
|
||||
public T snippet(String snippet) {
|
||||
this.snippet = snippet;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the title of the Marker.
|
||||
*
|
||||
* @param title the title of the {@link Marker}.
|
||||
* @return the object for which the method was called.
|
||||
*/
|
||||
public T title(String title) {
|
||||
this.title = title;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the icon of the Marker.
|
||||
*
|
||||
* @param icon the icon of the {@link Marker}.
|
||||
* @return the object for which the method was called.
|
||||
*/
|
||||
public T icon(Icon icon) {
|
||||
this.icon = icon;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the icon of the Marker.
|
||||
*
|
||||
* @param icon the icon of the {@link Marker}.
|
||||
* @return the object for which the method was called.
|
||||
*/
|
||||
public T setIcon(Icon icon) {
|
||||
return icon(icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the geographical location of the Marker.
|
||||
*
|
||||
* @param position the location to position the {@link Marker}.
|
||||
* @return the object for which the method was called.
|
||||
*/
|
||||
public T setPosition(LatLng position) {
|
||||
return position(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the snippet of the Marker.
|
||||
*
|
||||
* @param snippet the snippet of the {@link Marker}.
|
||||
* @return the object for which the method was called.
|
||||
*/
|
||||
public T setSnippet(String snippet) {
|
||||
return snippet(snippet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the title of the Marker.
|
||||
*
|
||||
* @param title the title of the {@link Marker}.
|
||||
* @return the object for which the method was called.
|
||||
*/
|
||||
public T setTitle(String title) {
|
||||
return title(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the instance of the object for which this method was called.
|
||||
*
|
||||
* @return the object for which the this method was called.
|
||||
*/
|
||||
public abstract T getThis();
|
||||
|
||||
/**
|
||||
* Get the Marker.
|
||||
*
|
||||
* @return the Marker created from this builder.
|
||||
*/
|
||||
public abstract U getMarker();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.support.annotation.Keep;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Multipoint is an abstract annotation for combining geographical locations.
|
||||
*
|
||||
* @deprecated As of 7.0.0,
|
||||
* use <a href="https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-annotation">
|
||||
* Mapbox Annotation Plugin</a> instead
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class BasePointCollection extends Annotation {
|
||||
|
||||
@Keep
|
||||
private List<LatLng> points;
|
||||
@Keep
|
||||
private float alpha = 1.0f;
|
||||
|
||||
protected BasePointCollection() {
|
||||
super();
|
||||
points = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the points.
|
||||
*
|
||||
* @return A {@link List} of points.
|
||||
*/
|
||||
@NonNull
|
||||
public List<LatLng> getPoints() {
|
||||
return new ArrayList<>(points);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the points of this polyline. This method will take a copy of the points, so further
|
||||
* mutations to points will have no effect on this polyline.
|
||||
*
|
||||
* @param points A {@link List} of {@link LatLng} points making up the polyline.
|
||||
*/
|
||||
public void setPoints(@NonNull List<LatLng> points) {
|
||||
this.points = new ArrayList<>(points);
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a point to the polyline.
|
||||
*
|
||||
* @param point A {@link LatLng} point to be added.
|
||||
*/
|
||||
public void addPoint(LatLng point) {
|
||||
points.add(point);
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Value between 0 and 1 defining the polyline alpha.
|
||||
*
|
||||
* @return float value between 0 and 1.
|
||||
*/
|
||||
public float getAlpha() {
|
||||
return alpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this {@link BasePointCollection}s alpha.
|
||||
*
|
||||
* @param alpha float value between 0 and 1.
|
||||
*/
|
||||
public void setAlpha(float alpha) {
|
||||
this.alpha = alpha;
|
||||
update();
|
||||
}
|
||||
|
||||
abstract void update();
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
@Deprecated
|
||||
class Bubble extends Drawable {
|
||||
|
||||
private RectF rect;
|
||||
private float arrowWidth;
|
||||
private float arrowHeight;
|
||||
private float arrowPosition;
|
||||
private float cornersRadius;
|
||||
@NonNull
|
||||
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private float strokeWidth;
|
||||
private Paint strokePaint;
|
||||
private Path strokePath;
|
||||
@NonNull
|
||||
private Path path = new Path();
|
||||
|
||||
Bubble(@NonNull RectF rect, @NonNull ArrowDirection arrowDirection, float arrowWidth, float arrowHeight,
|
||||
float arrowPosition, float cornersRadius, int bubbleColor, float strokeWidth, int strokeColor) {
|
||||
this.rect = rect;
|
||||
this.arrowWidth = arrowWidth;
|
||||
this.arrowHeight = arrowHeight;
|
||||
this.arrowPosition = arrowPosition;
|
||||
this.cornersRadius = cornersRadius;
|
||||
paint.setColor(bubbleColor);
|
||||
this.strokeWidth = strokeWidth;
|
||||
|
||||
if (strokeWidth > 0) {
|
||||
strokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
strokePaint.setColor(strokeColor);
|
||||
strokePath = new Path();
|
||||
initPath(arrowDirection, path, strokeWidth);
|
||||
initPath(arrowDirection, strokePath, 0);
|
||||
} else {
|
||||
initPath(arrowDirection, path, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBoundsChange(Rect bounds) {
|
||||
super.onBoundsChange(bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
if (strokeWidth > 0) {
|
||||
canvas.drawPath(strokePath, strokePaint);
|
||||
}
|
||||
canvas.drawPath(path, paint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.TRANSLUCENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
paint.setAlpha(alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
paint.setColorFilter(cf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicWidth() {
|
||||
return (int) rect.width();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicHeight() {
|
||||
return (int) rect.height();
|
||||
}
|
||||
|
||||
private void initPath(@NonNull ArrowDirection arrowDirection, @NonNull Path path, float strokeWidth) {
|
||||
switch (arrowDirection.getValue()) {
|
||||
case ArrowDirection.LEFT:
|
||||
if (cornersRadius <= 0) {
|
||||
initLeftSquarePath(rect, path, strokeWidth);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strokeWidth > 0 && strokeWidth > cornersRadius) {
|
||||
initLeftSquarePath(rect, path, strokeWidth);
|
||||
break;
|
||||
}
|
||||
|
||||
initLeftRoundedPath(rect, path, strokeWidth);
|
||||
break;
|
||||
case ArrowDirection.TOP:
|
||||
if (cornersRadius <= 0) {
|
||||
initTopSquarePath(rect, path, strokeWidth);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strokeWidth > 0 && strokeWidth > cornersRadius) {
|
||||
initTopSquarePath(rect, path, strokeWidth);
|
||||
break;
|
||||
}
|
||||
|
||||
initTopRoundedPath(rect, path, strokeWidth);
|
||||
break;
|
||||
case ArrowDirection.RIGHT:
|
||||
if (cornersRadius <= 0) {
|
||||
initRightSquarePath(rect, path, strokeWidth);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strokeWidth > 0 && strokeWidth > cornersRadius) {
|
||||
initRightSquarePath(rect, path, strokeWidth);
|
||||
break;
|
||||
}
|
||||
|
||||
initRightRoundedPath(rect, path, strokeWidth);
|
||||
break;
|
||||
case ArrowDirection.BOTTOM:
|
||||
if (cornersRadius <= 0) {
|
||||
initBottomSquarePath(rect, path, strokeWidth);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strokeWidth > 0 && strokeWidth > cornersRadius) {
|
||||
initBottomSquarePath(rect, path, strokeWidth);
|
||||
break;
|
||||
}
|
||||
|
||||
initBottomRoundedPath(rect, path, strokeWidth);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void initLeftSquarePath(@NonNull RectF rect, @NonNull Path path, float strokeWidth) {
|
||||
path.moveTo(arrowWidth + rect.left + strokeWidth, rect.top + strokeWidth);
|
||||
path.lineTo(rect.width() - strokeWidth, rect.top + strokeWidth);
|
||||
|
||||
path.lineTo(rect.right - strokeWidth, rect.bottom - strokeWidth);
|
||||
|
||||
path.lineTo(rect.left + arrowWidth + strokeWidth, rect.bottom - strokeWidth);
|
||||
|
||||
path.lineTo(rect.left + arrowWidth + strokeWidth, arrowHeight + arrowPosition - (strokeWidth / 2));
|
||||
path.lineTo(rect.left + strokeWidth + strokeWidth, arrowPosition + arrowHeight / 2);
|
||||
path.lineTo(rect.left + arrowWidth + strokeWidth, arrowPosition + (strokeWidth / 2));
|
||||
|
||||
path.lineTo(rect.left + arrowWidth + strokeWidth, rect.top + strokeWidth);
|
||||
|
||||
path.close();
|
||||
}
|
||||
|
||||
private void initLeftRoundedPath(@NonNull RectF rect, @NonNull Path path, float strokeWidth) {
|
||||
path.moveTo(arrowWidth + rect.left + cornersRadius + strokeWidth, rect.top + strokeWidth);
|
||||
path.lineTo(rect.width() - cornersRadius - strokeWidth, rect.top + strokeWidth);
|
||||
path.arcTo(new RectF(rect.right - cornersRadius, rect.top + strokeWidth, rect.right - strokeWidth,
|
||||
cornersRadius + rect.top), 270, 90);
|
||||
|
||||
path.lineTo(rect.right - strokeWidth, rect.bottom - cornersRadius - strokeWidth);
|
||||
path.arcTo(new RectF(rect.right - cornersRadius, rect.bottom - cornersRadius,
|
||||
rect.right - strokeWidth, rect.bottom - strokeWidth), 0, 90);
|
||||
|
||||
path.lineTo(rect.left + arrowWidth + cornersRadius + strokeWidth, rect.bottom - strokeWidth);
|
||||
|
||||
path.arcTo(new RectF(rect.left + arrowWidth + strokeWidth, rect.bottom - cornersRadius,
|
||||
cornersRadius + rect.left + arrowWidth, rect.bottom - strokeWidth), 90, 90);
|
||||
|
||||
path.lineTo(rect.left + arrowWidth + strokeWidth, arrowHeight + arrowPosition - (strokeWidth / 2));
|
||||
|
||||
path.lineTo(rect.left + strokeWidth + strokeWidth, arrowPosition + arrowHeight / 2);
|
||||
|
||||
path.lineTo(rect.left + arrowWidth + strokeWidth, arrowPosition + (strokeWidth / 2));
|
||||
|
||||
path.lineTo(rect.left + arrowWidth + strokeWidth, rect.top + cornersRadius + strokeWidth);
|
||||
|
||||
path.arcTo(new RectF(rect.left + arrowWidth + strokeWidth, rect.top + strokeWidth, cornersRadius
|
||||
+ rect.left + arrowWidth, cornersRadius + rect.top), 180, 90);
|
||||
|
||||
path.close();
|
||||
}
|
||||
|
||||
private void initTopSquarePath(@NonNull RectF rect, @NonNull Path path, float strokeWidth) {
|
||||
path.moveTo(rect.left + arrowPosition + strokeWidth, rect.top + arrowHeight + strokeWidth);
|
||||
|
||||
path.lineTo(rect.left + arrowPosition + (strokeWidth / 2), rect.top + arrowHeight + strokeWidth);
|
||||
path.lineTo(rect.left + arrowWidth / 2 + arrowPosition, rect.top + strokeWidth + strokeWidth);
|
||||
path.lineTo(rect.left + arrowWidth + arrowPosition - (strokeWidth / 2), rect.top + arrowHeight + strokeWidth);
|
||||
path.lineTo(rect.right - strokeWidth, rect.top + arrowHeight + strokeWidth);
|
||||
|
||||
path.lineTo(rect.right - strokeWidth, rect.bottom - strokeWidth);
|
||||
|
||||
path.lineTo(rect.left + strokeWidth, rect.bottom - strokeWidth);
|
||||
|
||||
path.lineTo(rect.left + strokeWidth, rect.top + arrowHeight + strokeWidth);
|
||||
|
||||
path.lineTo(rect.left + arrowPosition + strokeWidth, rect.top + arrowHeight + strokeWidth);
|
||||
|
||||
path.close();
|
||||
}
|
||||
|
||||
private void initTopRoundedPath(@NonNull RectF rect, @NonNull Path path, float strokeWidth) {
|
||||
path.moveTo(rect.left + Math.min(arrowPosition, cornersRadius) + strokeWidth, rect.top + arrowHeight
|
||||
+ strokeWidth);
|
||||
path.lineTo(rect.left + arrowPosition + (strokeWidth / 2), rect.top + arrowHeight + strokeWidth);
|
||||
path.lineTo(rect.left + arrowWidth / 2 + arrowPosition, rect.top + strokeWidth + strokeWidth);
|
||||
path.lineTo(rect.left + arrowWidth + arrowPosition - (strokeWidth / 2), rect.top + arrowHeight + strokeWidth);
|
||||
path.lineTo(rect.right - cornersRadius - strokeWidth, rect.top + arrowHeight + strokeWidth);
|
||||
|
||||
path.arcTo(new RectF(rect.right - cornersRadius,
|
||||
rect.top + arrowHeight + strokeWidth, rect.right - strokeWidth, cornersRadius + rect.top + arrowHeight),
|
||||
270, 90);
|
||||
path.lineTo(rect.right - strokeWidth, rect.bottom - cornersRadius - strokeWidth);
|
||||
|
||||
path.arcTo(new RectF(rect.right - cornersRadius, rect.bottom - cornersRadius,
|
||||
rect.right - strokeWidth, rect.bottom - strokeWidth), 0, 90);
|
||||
path.lineTo(rect.left + cornersRadius + strokeWidth, rect.bottom - strokeWidth);
|
||||
|
||||
path.arcTo(new RectF(rect.left + strokeWidth, rect.bottom - cornersRadius,
|
||||
cornersRadius + rect.left, rect.bottom - strokeWidth), 90, 90);
|
||||
|
||||
path.lineTo(rect.left + strokeWidth, rect.top + arrowHeight + cornersRadius + strokeWidth);
|
||||
|
||||
path.arcTo(new RectF(rect.left + strokeWidth, rect.top + arrowHeight + strokeWidth, cornersRadius
|
||||
+ rect.left, cornersRadius + rect.top + arrowHeight), 180, 90);
|
||||
|
||||
path.close();
|
||||
}
|
||||
|
||||
private void initRightSquarePath(@NonNull RectF rect, @NonNull Path path, float strokeWidth) {
|
||||
path.moveTo(rect.left + strokeWidth, rect.top + strokeWidth);
|
||||
path.lineTo(rect.width() - arrowWidth - strokeWidth, rect.top + strokeWidth);
|
||||
|
||||
path.lineTo(rect.right - arrowWidth - strokeWidth, arrowPosition + (strokeWidth / 2));
|
||||
path.lineTo(rect.right - strokeWidth - strokeWidth, arrowPosition + arrowHeight / 2);
|
||||
path.lineTo(rect.right - arrowWidth - strokeWidth, arrowPosition + arrowHeight - (strokeWidth / 2));
|
||||
|
||||
path.lineTo(rect.right - arrowWidth - strokeWidth, rect.bottom - strokeWidth);
|
||||
|
||||
path.lineTo(rect.left + strokeWidth, rect.bottom - strokeWidth);
|
||||
path.lineTo(rect.left + strokeWidth, rect.top + strokeWidth);
|
||||
|
||||
path.close();
|
||||
}
|
||||
|
||||
private void initRightRoundedPath(@NonNull RectF rect, @NonNull Path path, float strokeWidth) {
|
||||
path.moveTo(rect.left + cornersRadius + strokeWidth, rect.top + strokeWidth);
|
||||
path.lineTo(rect.width() - cornersRadius - arrowWidth - strokeWidth, rect.top + strokeWidth);
|
||||
path.arcTo(new RectF(rect.right - cornersRadius - arrowWidth,
|
||||
rect.top + strokeWidth, rect.right - arrowWidth - strokeWidth, cornersRadius + rect.top), 270, 90);
|
||||
|
||||
path.lineTo(rect.right - arrowWidth - strokeWidth, arrowPosition + (strokeWidth / 2));
|
||||
path.lineTo(rect.right - strokeWidth - strokeWidth, arrowPosition + arrowHeight / 2);
|
||||
path.lineTo(rect.right - arrowWidth - strokeWidth, arrowPosition + arrowHeight - (strokeWidth / 2));
|
||||
path.lineTo(rect.right - arrowWidth - strokeWidth, rect.bottom - cornersRadius - strokeWidth);
|
||||
|
||||
path.arcTo(new RectF(rect.right - cornersRadius - arrowWidth, rect.bottom - cornersRadius,
|
||||
rect.right - arrowWidth - strokeWidth, rect.bottom - strokeWidth), 0, 90);
|
||||
path.lineTo(rect.left + arrowWidth + strokeWidth, rect.bottom - strokeWidth);
|
||||
|
||||
path.arcTo(new RectF(rect.left + strokeWidth, rect.bottom - cornersRadius,
|
||||
cornersRadius + rect.left, rect.bottom - strokeWidth), 90, 90);
|
||||
|
||||
path.arcTo(new RectF(rect.left + strokeWidth, rect.top + strokeWidth, cornersRadius
|
||||
+ rect.left, cornersRadius + rect.top), 180, 90);
|
||||
path.close();
|
||||
}
|
||||
|
||||
private void initBottomSquarePath(@NonNull RectF rect, @NonNull Path path, float strokeWidth) {
|
||||
path.moveTo(rect.left + strokeWidth, rect.top + strokeWidth);
|
||||
path.lineTo(rect.right - strokeWidth, rect.top + strokeWidth);
|
||||
path.lineTo(rect.right - strokeWidth, rect.bottom - arrowHeight - strokeWidth);
|
||||
|
||||
path.lineTo(rect.left + arrowWidth + arrowPosition - (strokeWidth / 2), rect.bottom - arrowHeight - strokeWidth);
|
||||
path.lineTo(rect.left + arrowPosition + arrowWidth / 2, rect.bottom - strokeWidth - strokeWidth);
|
||||
path.lineTo(rect.left + arrowPosition + (strokeWidth / 2), rect.bottom - arrowHeight - strokeWidth);
|
||||
path.lineTo(rect.left + arrowPosition + strokeWidth, rect.bottom - arrowHeight - strokeWidth);
|
||||
|
||||
path.lineTo(rect.left + strokeWidth, rect.bottom - arrowHeight - strokeWidth);
|
||||
path.lineTo(rect.left + strokeWidth, rect.top + strokeWidth);
|
||||
path.close();
|
||||
}
|
||||
|
||||
private void initBottomRoundedPath(@NonNull RectF rect, @NonNull Path path, float strokeWidth) {
|
||||
path.moveTo(rect.left + cornersRadius + strokeWidth, rect.top + strokeWidth);
|
||||
path.lineTo(rect.width() - cornersRadius - strokeWidth, rect.top + strokeWidth);
|
||||
path.arcTo(new RectF(rect.right - cornersRadius,
|
||||
rect.top + strokeWidth, rect.right - strokeWidth, cornersRadius + rect.top), 270, 90);
|
||||
|
||||
path.lineTo(rect.right - strokeWidth, rect.bottom - arrowHeight - cornersRadius - strokeWidth);
|
||||
path.arcTo(new RectF(rect.right - cornersRadius, rect.bottom - cornersRadius - arrowHeight,
|
||||
rect.right - strokeWidth, rect.bottom - arrowHeight - strokeWidth), 0, 90);
|
||||
|
||||
path.lineTo(rect.left + arrowWidth + arrowPosition - (strokeWidth / 2), rect.bottom - arrowHeight - strokeWidth);
|
||||
path.lineTo(rect.left + arrowPosition + arrowWidth / 2, rect.bottom - strokeWidth - strokeWidth);
|
||||
path.lineTo(rect.left + arrowPosition + (strokeWidth / 2), rect.bottom - arrowHeight - strokeWidth);
|
||||
path.lineTo(rect.left + Math.min(cornersRadius, arrowPosition) + strokeWidth, rect.bottom - arrowHeight
|
||||
- strokeWidth);
|
||||
|
||||
path.arcTo(new RectF(rect.left + strokeWidth, rect.bottom - cornersRadius - arrowHeight,
|
||||
cornersRadius + rect.left, rect.bottom - arrowHeight - strokeWidth), 90, 90);
|
||||
path.lineTo(rect.left + strokeWidth, rect.top + cornersRadius + strokeWidth);
|
||||
path.arcTo(new RectF(rect.left + strokeWidth, rect.top + strokeWidth, cornersRadius
|
||||
+ rect.left, cornersRadius + rect.top), 180, 90);
|
||||
path.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.RectF;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
|
||||
/**
|
||||
* Bubble View for Android with custom stroke width and color, arrow size, position and direction.
|
||||
* @deprecated As of 7.0.0,
|
||||
* use <a href="https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-annotation">
|
||||
* Mapbox Annotation Plugin</a> instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class BubbleLayout extends LinearLayout {
|
||||
|
||||
public static final float DEFAULT_STROKE_WIDTH = -1;
|
||||
private ArrowDirection arrowDirection;
|
||||
private float arrowWidth;
|
||||
private float arrowHeight;
|
||||
private float arrowPosition;
|
||||
private float cornersRadius;
|
||||
private Bubble bubble;
|
||||
private int bubbleColor;
|
||||
private float strokeWidth;
|
||||
private int strokeColor;
|
||||
|
||||
/**
|
||||
* Creates an instance of bubble layout.
|
||||
*
|
||||
* @param context The context used to inflate this bubble layout
|
||||
*/
|
||||
public BubbleLayout(@NonNull Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of bubble layout.
|
||||
*
|
||||
* @param context The context used to inflate this bubble layout
|
||||
* @param attrs The attribute set to initialise this bubble layout from
|
||||
*/
|
||||
public BubbleLayout(@NonNull Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of bubble layout.
|
||||
*
|
||||
* @param context The context used to inflate this bubble layout
|
||||
* @param attrs The attribute set to initialise this bubble layout from
|
||||
* @param defStyleAttr The default style to apply this bubble layout with
|
||||
*/
|
||||
public BubbleLayout(@NonNull Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.mapbox_BubbleLayout);
|
||||
@ArrowDirection.Value
|
||||
int location = a.getInt(R.styleable.mapbox_BubbleLayout_mapbox_bl_arrowDirection,
|
||||
ArrowDirection.LEFT);
|
||||
arrowDirection = new ArrowDirection(location);
|
||||
arrowWidth = a.getDimension(R.styleable.mapbox_BubbleLayout_mapbox_bl_arrowWidth,
|
||||
convertDpToPixel(8, context));
|
||||
arrowHeight = a.getDimension(R.styleable.mapbox_BubbleLayout_mapbox_bl_arrowHeight,
|
||||
convertDpToPixel(8, context));
|
||||
arrowPosition = a.getDimension(R.styleable.mapbox_BubbleLayout_mapbox_bl_arrowPosition,
|
||||
convertDpToPixel(12, context));
|
||||
cornersRadius = a.getDimension(R.styleable.mapbox_BubbleLayout_mapbox_bl_cornersRadius, 0);
|
||||
bubbleColor = a.getColor(R.styleable.mapbox_BubbleLayout_mapbox_bl_bubbleColor, Color.WHITE);
|
||||
strokeWidth =
|
||||
a.getDimension(R.styleable.mapbox_BubbleLayout_mapbox_bl_strokeWidth, DEFAULT_STROKE_WIDTH);
|
||||
strokeColor = a.getColor(R.styleable.mapbox_BubbleLayout_mapbox_bl_strokeColor, Color.GRAY);
|
||||
|
||||
a.recycle();
|
||||
initPadding();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
initDrawable(0, getWidth(), 0, getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(@NonNull Canvas canvas) {
|
||||
if (bubble != null) {
|
||||
bubble.draw(canvas);
|
||||
}
|
||||
super.dispatchDraw(canvas);
|
||||
}
|
||||
|
||||
static float convertDpToPixel(float dp, Context context) {
|
||||
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
|
||||
return dp * (metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the arrow direction.
|
||||
*
|
||||
* @return the arrow direction
|
||||
*/
|
||||
public ArrowDirection getArrowDirection() {
|
||||
return arrowDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the arrow direction.
|
||||
*
|
||||
* @param arrowDirection The direction of the arrow
|
||||
* @return this
|
||||
*/
|
||||
@NonNull
|
||||
public BubbleLayout setArrowDirection(ArrowDirection arrowDirection) {
|
||||
resetPadding();
|
||||
this.arrowDirection = arrowDirection;
|
||||
initPadding();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the arrow width.
|
||||
*
|
||||
* @return the width of the arrow
|
||||
*/
|
||||
public float getArrowWidth() {
|
||||
return arrowWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the arrow width.
|
||||
*
|
||||
* @param arrowWidth The width of the arrow
|
||||
* @return this
|
||||
*/
|
||||
@NonNull
|
||||
public BubbleLayout setArrowWidth(float arrowWidth) {
|
||||
resetPadding();
|
||||
this.arrowWidth = arrowWidth;
|
||||
initPadding();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the arrow height
|
||||
*
|
||||
* @return the height of the arrow
|
||||
*/
|
||||
public float getArrowHeight() {
|
||||
return arrowHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the arrow height.
|
||||
*
|
||||
* @param arrowHeight The height of the arrow
|
||||
* @return this
|
||||
*/
|
||||
@NonNull
|
||||
public BubbleLayout setArrowHeight(float arrowHeight) {
|
||||
resetPadding();
|
||||
this.arrowHeight = arrowHeight;
|
||||
initPadding();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the arrow position.
|
||||
*
|
||||
* @return the arrow position
|
||||
*/
|
||||
public float getArrowPosition() {
|
||||
return arrowPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the arrow position.
|
||||
*
|
||||
* @param arrowPosition The arrow position
|
||||
* @return this
|
||||
*/
|
||||
@NonNull
|
||||
public BubbleLayout setArrowPosition(float arrowPosition) {
|
||||
resetPadding();
|
||||
this.arrowPosition = arrowPosition;
|
||||
initPadding();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the corner radius
|
||||
*
|
||||
* @return the corner radius
|
||||
*/
|
||||
public float getCornersRadius() {
|
||||
return cornersRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the corner radius
|
||||
*
|
||||
* @param cornersRadius The corner radius
|
||||
* @return this
|
||||
*/
|
||||
@NonNull
|
||||
public BubbleLayout setCornersRadius(float cornersRadius) {
|
||||
this.cornersRadius = cornersRadius;
|
||||
requestLayout();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bubble color.
|
||||
*
|
||||
* @return the bubble color
|
||||
*/
|
||||
public int getBubbleColor() {
|
||||
return bubbleColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bubble color.
|
||||
*
|
||||
* @param bubbleColor The buble color
|
||||
* @return this
|
||||
*/
|
||||
@NonNull
|
||||
public BubbleLayout setBubbleColor(int bubbleColor) {
|
||||
this.bubbleColor = bubbleColor;
|
||||
requestLayout();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stroke width.
|
||||
*
|
||||
* @return the stroke width
|
||||
*/
|
||||
public float getStrokeWidth() {
|
||||
return strokeWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the stroke width.
|
||||
*
|
||||
* @param strokeWidth The stroke width
|
||||
* @return this
|
||||
*/
|
||||
@NonNull
|
||||
public BubbleLayout setStrokeWidth(float strokeWidth) {
|
||||
resetPadding();
|
||||
this.strokeWidth = strokeWidth;
|
||||
initPadding();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stroke color.
|
||||
*
|
||||
* @return the stroke color
|
||||
*/
|
||||
public int getStrokeColor() {
|
||||
return strokeColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the stroke color.
|
||||
*
|
||||
* @param strokeColor The stroke color
|
||||
* @return this
|
||||
*/
|
||||
@NonNull
|
||||
public BubbleLayout setStrokeColor(int strokeColor) {
|
||||
this.strokeColor = strokeColor;
|
||||
requestLayout();
|
||||
return this;
|
||||
}
|
||||
|
||||
private void initPadding() {
|
||||
int paddingLeft = getPaddingLeft();
|
||||
int paddingRight = getPaddingRight();
|
||||
int paddingTop = getPaddingTop();
|
||||
int paddingBottom = getPaddingBottom();
|
||||
switch (arrowDirection.getValue()) {
|
||||
case ArrowDirection.LEFT:
|
||||
paddingLeft += arrowWidth;
|
||||
break;
|
||||
case ArrowDirection.RIGHT:
|
||||
paddingRight += arrowWidth;
|
||||
break;
|
||||
case ArrowDirection.TOP:
|
||||
paddingTop += arrowHeight;
|
||||
break;
|
||||
case ArrowDirection.BOTTOM:
|
||||
paddingBottom += arrowHeight;
|
||||
break;
|
||||
}
|
||||
if (strokeWidth > 0) {
|
||||
paddingLeft += strokeWidth;
|
||||
paddingRight += strokeWidth;
|
||||
paddingTop += strokeWidth;
|
||||
paddingBottom += strokeWidth;
|
||||
}
|
||||
setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
|
||||
}
|
||||
|
||||
private void initDrawable(int left, int right, int top, int bottom) {
|
||||
if (right < left || bottom < top) {
|
||||
return;
|
||||
}
|
||||
|
||||
RectF rectF = new RectF(left, top, right, bottom);
|
||||
bubble = new Bubble(rectF, arrowDirection, arrowWidth, arrowHeight, arrowPosition, cornersRadius,
|
||||
bubbleColor, strokeWidth, strokeColor);
|
||||
}
|
||||
|
||||
private void resetPadding() {
|
||||
int paddingLeft = getPaddingLeft();
|
||||
int paddingRight = getPaddingRight();
|
||||
int paddingTop = getPaddingTop();
|
||||
int paddingBottom = getPaddingBottom();
|
||||
switch (arrowDirection.getValue()) {
|
||||
case ArrowDirection.LEFT:
|
||||
paddingLeft -= arrowWidth;
|
||||
break;
|
||||
case ArrowDirection.RIGHT:
|
||||
paddingRight -= arrowWidth;
|
||||
break;
|
||||
case ArrowDirection.TOP:
|
||||
paddingTop -= arrowHeight;
|
||||
break;
|
||||
case ArrowDirection.BOTTOM:
|
||||
paddingBottom -= arrowHeight;
|
||||
break;
|
||||
}
|
||||
if (strokeWidth > 0) {
|
||||
paddingLeft -= strokeWidth;
|
||||
paddingRight -= strokeWidth;
|
||||
paddingTop -= strokeWidth;
|
||||
paddingBottom -= strokeWidth;
|
||||
}
|
||||
setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
|
||||
@Deprecated
|
||||
class BubblePopupHelper {
|
||||
|
||||
@NonNull
|
||||
static PopupWindow create(@NonNull Context context, @NonNull BubbleLayout bubbleLayout) {
|
||||
PopupWindow popupWindow = new PopupWindow(context);
|
||||
|
||||
popupWindow.setContentView(bubbleLayout);
|
||||
popupWindow.setOutsideTouchable(true);
|
||||
popupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
popupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
popupWindow.setAnimationStyle(android.R.style.Animation_Dialog);
|
||||
// change background color to transparent
|
||||
Drawable drawable;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
drawable = context.getDrawable(R.drawable.mapbox_popup_window_transparent);
|
||||
} else {
|
||||
drawable = context.getResources().getDrawable(R.drawable.mapbox_popup_window_transparent);
|
||||
}
|
||||
popupWindow.setBackgroundDrawable(drawable);
|
||||
|
||||
return popupWindow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Icon is the visual representation of a Marker on a MapView.
|
||||
*
|
||||
* @deprecated As of 7.0.0,
|
||||
* use <a href="https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-annotation">
|
||||
* Mapbox Annotation Plugin</a> instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class Icon {
|
||||
|
||||
private Bitmap mBitmap;
|
||||
private String mId;
|
||||
|
||||
Icon(String id, Bitmap bitmap) {
|
||||
mId = id;
|
||||
mBitmap = bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* String identifier for this icon.
|
||||
*
|
||||
* @return String identifier for this icon.
|
||||
*/
|
||||
public String getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bitmap being used for this icon.
|
||||
*
|
||||
* @return The bitmap being used for the icon.
|
||||
*/
|
||||
public Bitmap getBitmap() {
|
||||
if (mBitmap != null && mBitmap.getConfig() != Bitmap.Config.ARGB_8888) {
|
||||
mBitmap = mBitmap.copy(Bitmap.Config.ARGB_8888, false);
|
||||
}
|
||||
return mBitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon bitmap scale.
|
||||
* <p>
|
||||
* Requires the bitmap to be set before calling this method.
|
||||
* </p>
|
||||
*
|
||||
* @return the scale of the bitmap
|
||||
*/
|
||||
public float getScale() {
|
||||
if (mBitmap == null) {
|
||||
throw new IllegalStateException("Required to set a Icon before calling getScale");
|
||||
}
|
||||
float density = mBitmap.getDensity();
|
||||
if (density == Bitmap.DENSITY_NONE) {
|
||||
density = DisplayMetrics.DENSITY_DEFAULT;
|
||||
}
|
||||
return density / DisplayMetrics.DENSITY_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon bitmap bytes.
|
||||
* <p>
|
||||
* Requires the bitmap to be set before calling this method.
|
||||
* </p>
|
||||
*
|
||||
* @return the bytes of the bitmap
|
||||
*/
|
||||
@NonNull
|
||||
public byte[] toBytes() {
|
||||
if (mBitmap == null) {
|
||||
throw new IllegalStateException("Required to set a Icon before calling toBytes");
|
||||
}
|
||||
ByteBuffer buffer = ByteBuffer.allocate(mBitmap.getRowBytes() * mBitmap.getHeight());
|
||||
mBitmap.copyPixelsToBuffer(buffer);
|
||||
return buffer.array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this icon object with another icon and determines if they match.
|
||||
*
|
||||
* @param object Another iconi to compare with this object.
|
||||
* @return True if the icon being passed in matches this icon object. Else, false.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(@Nullable Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
}
|
||||
if (object == null || getClass() != object.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Icon icon = (Icon) object;
|
||||
return mBitmap.equals(icon.mBitmap) && mId.equals(icon.mId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives an integer which can be used as the bucket number for storing elements of the set/map.
|
||||
* This bucket number is the address of the element inside the set/map. There's no guarantee
|
||||
* that this hash value will be consistent between different Java implementations, or even
|
||||
* between different execution runs of the same program.
|
||||
*
|
||||
* @return integer value you can use for storing element.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 0;
|
||||
if (mBitmap != null) {
|
||||
result = mBitmap.hashCode();
|
||||
}
|
||||
if (mId != null) {
|
||||
result = 31 * result + mId.hashCode();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.mapbox.mapboxsdk.MapStrictMode;
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
import com.mapbox.mapboxsdk.exceptions.TooManyIconsException;
|
||||
import com.mapbox.mapboxsdk.utils.BitmapUtils;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Factory for creating Icons from bitmap images.
|
||||
* <p>
|
||||
* icon is used to display bitmaps on top of the map using {@link Marker}.
|
||||
* </p>
|
||||
*
|
||||
* @deprecated As of 7.0.0,
|
||||
* use <a href="https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-annotation">
|
||||
* Mapbox Annotation Plugin</a> instead
|
||||
*/
|
||||
@Deprecated
|
||||
public final class IconFactory {
|
||||
|
||||
private static final String ICON_ID_PREFIX = "com.mapbox.icons.icon_";
|
||||
|
||||
private Context context;
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private static IconFactory instance;
|
||||
private Icon defaultMarker;
|
||||
private BitmapFactory.Options options;
|
||||
|
||||
private int nextId = 0;
|
||||
|
||||
/**
|
||||
* Get a single instance of IconFactory.
|
||||
*
|
||||
* @param context the context to derive the application context from
|
||||
* @return the single instance of IconFactory
|
||||
*/
|
||||
public static synchronized IconFactory getInstance(@NonNull Context context) {
|
||||
if (instance == null) {
|
||||
instance = new IconFactory(context.getApplicationContext());
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private IconFactory(@NonNull Context context) {
|
||||
this.context = context;
|
||||
DisplayMetrics realMetrics = null;
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
realMetrics = new DisplayMetrics();
|
||||
wm.getDefaultDisplay().getRealMetrics(realMetrics);
|
||||
}
|
||||
wm.getDefaultDisplay().getMetrics(metrics);
|
||||
|
||||
options = new BitmapFactory.Options();
|
||||
options.inScaled = true;
|
||||
options.inDensity = DisplayMetrics.DENSITY_DEFAULT;
|
||||
options.inTargetDensity = metrics.densityDpi;
|
||||
if (realMetrics != null) {
|
||||
options.inScreenDensity = realMetrics.densityDpi;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an icon from a given Bitmap image.
|
||||
*
|
||||
* @param bitmap image used for creating the Icon.
|
||||
* @return The icon using the given Bitmap image.
|
||||
*/
|
||||
public Icon fromBitmap(@NonNull Bitmap bitmap) {
|
||||
if (nextId < 0) {
|
||||
throw new TooManyIconsException();
|
||||
}
|
||||
String id = ICON_ID_PREFIX + ++nextId;
|
||||
return new Icon(id, bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an icon using the resource ID of a Bitmap image.
|
||||
*
|
||||
* @param resourceId The resource ID of a Bitmap image.
|
||||
* @return The icon that was loaded from the asset or {@code null} if failed to load.
|
||||
*/
|
||||
public Icon fromResource(@DrawableRes int resourceId) {
|
||||
Drawable drawable = BitmapUtils.getDrawableFromRes(context, resourceId);
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
|
||||
return fromBitmap(bitmapDrawable.getBitmap());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Failed to decode image. The resource provided must be a Bitmap.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an icon using the default marker icon used for {@link Marker}.
|
||||
*
|
||||
* @return An icon with the default {@link Marker} icon.
|
||||
*/
|
||||
public Icon defaultMarker() {
|
||||
if (defaultMarker == null) {
|
||||
defaultMarker = fromResource(R.drawable.mapbox_marker_icon_default);
|
||||
}
|
||||
return defaultMarker;
|
||||
}
|
||||
|
||||
private Icon fromInputStream(@NonNull InputStream is) {
|
||||
Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
|
||||
return fromBitmap(bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Icon using the name of a Bitmap image in the assets directory.
|
||||
*
|
||||
* @param assetName The name of a Bitmap image in the assets directory.
|
||||
* @return The Icon that was loaded from the asset or null if failed to load.
|
||||
*/
|
||||
public Icon fromAsset(@NonNull String assetName) {
|
||||
InputStream is;
|
||||
try {
|
||||
is = context.getAssets().open(assetName);
|
||||
} catch (IOException ioException) {
|
||||
MapStrictMode.strictModeViolation(ioException);
|
||||
return null;
|
||||
}
|
||||
return fromInputStream(is);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Icon using the absolute file path of a Bitmap image.
|
||||
*
|
||||
* @param absolutePath The absolute path of the Bitmap image.
|
||||
* @return The Icon that was loaded from the absolute path or null if failed to load.
|
||||
*/
|
||||
public Icon fromPath(@NonNull String absolutePath) {
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(absolutePath, options);
|
||||
return fromBitmap(bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Icon using the name of a Bitmap image file located in the internal storage.
|
||||
* In particular, this calls Context#openFileInput(String).
|
||||
*
|
||||
* @param fileName The name of the Bitmap image file.
|
||||
* @return The Icon that was loaded from the asset or null if failed to load.
|
||||
* @see <a href="https://developer.android.com/guide/topics/data/data-storage.html#filesInternal">
|
||||
* Using the Internal Storage</a>
|
||||
*/
|
||||
public Icon fromFile(@NonNull String fileName) {
|
||||
FileInputStream is;
|
||||
try {
|
||||
is = context.openFileInput(fileName);
|
||||
} catch (FileNotFoundException fileNotFoundException) {
|
||||
MapStrictMode.strictModeViolation(fileNotFoundException);
|
||||
return null;
|
||||
}
|
||||
return fromInputStream(is);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Icon using a previously created icon identifier along with a provided Bitmap.
|
||||
*
|
||||
* @param iconId The Icon identifier you'd like to recreate.
|
||||
* @param bitmap a Bitmap used to replace the current one.
|
||||
* @return The Icon using the new Bitmap.
|
||||
*/
|
||||
public static Icon recreate(@NonNull String iconId, @NonNull Bitmap bitmap) {
|
||||
return new Icon(iconId, bitmap);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PointF;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.maps.MapView;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* {@code InfoWindow} is a tooltip shown when a {@link Marker} is tapped. Only
|
||||
* one info window is displayed at a time. When the user clicks on a marker, the currently open info
|
||||
* window will be closed and the new info window will be displayed. If the user clicks the same
|
||||
* marker while its info window is currently open, the info window will be closed.
|
||||
* <p>
|
||||
* The info window is drawn oriented against the device's screen, centered above its associated
|
||||
* marker by default. The default info window contains the title in bold and snippet text below the title.
|
||||
* While either the title and
|
||||
* snippet are optional, at least one is required to open the info window.
|
||||
* </p>
|
||||
* @deprecated As of 7.0.0,
|
||||
* use <a href="https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-annotation">
|
||||
* Mapbox Annotation Plugin</a> instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class InfoWindow {
|
||||
|
||||
private WeakReference<Marker> boundMarker;
|
||||
private WeakReference<MapboxMap> mapboxMap;
|
||||
protected WeakReference<View> view;
|
||||
|
||||
private float markerHeightOffset;
|
||||
private float markerWidthOffset;
|
||||
private float viewWidthOffset;
|
||||
private float viewHeightOffset;
|
||||
private PointF coordinates;
|
||||
private boolean isVisible;
|
||||
|
||||
@LayoutRes
|
||||
private int layoutRes;
|
||||
|
||||
InfoWindow(MapView mapView, int layoutResId, MapboxMap mapboxMap) {
|
||||
layoutRes = layoutResId;
|
||||
View view = LayoutInflater.from(mapView.getContext()).inflate(layoutResId, mapView, false);
|
||||
initialize(view, mapboxMap);
|
||||
}
|
||||
|
||||
InfoWindow(@NonNull View view, MapboxMap mapboxMap) {
|
||||
initialize(view, mapboxMap);
|
||||
}
|
||||
|
||||
private void initialize(@NonNull View view, MapboxMap mapboxMap) {
|
||||
this.mapboxMap = new WeakReference<>(mapboxMap);
|
||||
isVisible = false;
|
||||
this.view = new WeakReference<>(view);
|
||||
|
||||
view.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
MapboxMap mapboxMap = InfoWindow.this.mapboxMap.get();
|
||||
if (mapboxMap != null) {
|
||||
MapboxMap.OnInfoWindowClickListener onInfoWindowClickListener = mapboxMap.getOnInfoWindowClickListener();
|
||||
boolean handledDefaultClick = false;
|
||||
if (onInfoWindowClickListener != null) {
|
||||
handledDefaultClick = onInfoWindowClickListener.onInfoWindowClick(getBoundMarker());
|
||||
}
|
||||
|
||||
if (!handledDefaultClick) {
|
||||
// default behavior: close it when clicking on the tooltip:
|
||||
closeInfoWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
view.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
MapboxMap mapboxMap = InfoWindow.this.mapboxMap.get();
|
||||
if (mapboxMap != null) {
|
||||
MapboxMap.OnInfoWindowLongClickListener listener = mapboxMap.getOnInfoWindowLongClickListener();
|
||||
if (listener != null) {
|
||||
listener.onInfoWindowLongClick(getBoundMarker());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void closeInfoWindow() {
|
||||
MapboxMap mapbox = mapboxMap.get();
|
||||
Marker marker = boundMarker.get();
|
||||
if (marker != null && mapbox != null) {
|
||||
mapbox.deselectMarker(marker);
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the info window at the specified position.
|
||||
*
|
||||
* @param boundMarker The marker on which is hooked the view.
|
||||
* @param position to place the window on the map.
|
||||
* @param offsetX The offset of the view to the position, in pixels. This allows to offset
|
||||
* the view from the object position.
|
||||
* @param offsetY The offset of the view to the position, in pixels. This allows to offset
|
||||
* the view from the object position.
|
||||
* @return this {@link InfoWindow}.
|
||||
*/
|
||||
@NonNull
|
||||
InfoWindow open(@NonNull MapView mapView, Marker boundMarker, @NonNull LatLng position, int offsetX, int offsetY) {
|
||||
setBoundMarker(boundMarker);
|
||||
|
||||
MapView.LayoutParams lp = new MapView.LayoutParams(MapView.LayoutParams.WRAP_CONTENT,
|
||||
MapView.LayoutParams.WRAP_CONTENT);
|
||||
|
||||
MapboxMap mapboxMap = this.mapboxMap.get();
|
||||
View view = this.view.get();
|
||||
if (view != null && mapboxMap != null) {
|
||||
view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
|
||||
|
||||
markerHeightOffset = offsetY;
|
||||
markerWidthOffset = -offsetX;
|
||||
|
||||
// Calculate default Android x,y coordinate
|
||||
coordinates = mapboxMap.getProjection().toScreenLocation(position);
|
||||
float x = coordinates.x - (view.getMeasuredWidth() / 2) + offsetX;
|
||||
float y = coordinates.y - view.getMeasuredHeight() + offsetY;
|
||||
|
||||
if (view instanceof BubbleLayout) {
|
||||
// only apply repositioning/margin for InfoWindowView
|
||||
Resources resources = mapView.getContext().getResources();
|
||||
|
||||
// get right/left popup window
|
||||
float rightSideInfowWindow = x + view.getMeasuredWidth();
|
||||
float leftSideInfoWindow = x;
|
||||
|
||||
// get right/left map view
|
||||
final float mapRight = mapView.getRight();
|
||||
final float mapLeft = mapView.getLeft();
|
||||
|
||||
float marginHorizontal = resources.getDimension(R.dimen.mapbox_infowindow_margin);
|
||||
float tipViewOffset = resources.getDimension(R.dimen.mapbox_infowindow_tipview_width) / 2;
|
||||
float tipViewMarginLeft = view.getMeasuredWidth() / 2 - tipViewOffset;
|
||||
|
||||
boolean outOfBoundsLeft = false;
|
||||
boolean outOfBoundsRight = false;
|
||||
|
||||
// only optimise margins if view is inside current viewport
|
||||
if (coordinates.x >= 0 && coordinates.x <= mapView.getWidth()
|
||||
&& coordinates.y >= 0 && coordinates.y <= mapView.getHeight()) {
|
||||
|
||||
// if out of bounds right
|
||||
if (rightSideInfowWindow > mapRight) {
|
||||
outOfBoundsRight = true;
|
||||
x -= rightSideInfowWindow - mapRight;
|
||||
tipViewMarginLeft += rightSideInfowWindow - mapRight + tipViewOffset;
|
||||
rightSideInfowWindow = x + view.getMeasuredWidth();
|
||||
}
|
||||
|
||||
// fit screen left
|
||||
if (leftSideInfoWindow < mapLeft) {
|
||||
outOfBoundsLeft = true;
|
||||
x += mapLeft - leftSideInfoWindow;
|
||||
tipViewMarginLeft -= mapLeft - leftSideInfoWindow + tipViewOffset;
|
||||
leftSideInfoWindow = x;
|
||||
}
|
||||
|
||||
// Add margin right
|
||||
if (outOfBoundsRight && mapRight - rightSideInfowWindow < marginHorizontal) {
|
||||
x -= marginHorizontal - (mapRight - rightSideInfowWindow);
|
||||
tipViewMarginLeft += marginHorizontal - (mapRight - rightSideInfowWindow) - tipViewOffset;
|
||||
leftSideInfoWindow = x;
|
||||
}
|
||||
|
||||
// Add margin left
|
||||
if (outOfBoundsLeft && leftSideInfoWindow - mapLeft < marginHorizontal) {
|
||||
x += marginHorizontal - (leftSideInfoWindow - mapLeft);
|
||||
tipViewMarginLeft -= (marginHorizontal - (leftSideInfoWindow - mapLeft)) - tipViewOffset;
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust tipView
|
||||
((BubbleLayout) view).setArrowPosition(tipViewMarginLeft);
|
||||
}
|
||||
|
||||
// set anchor popupwindowview
|
||||
view.setX(x);
|
||||
view.setY(y);
|
||||
|
||||
// Calculate x-offset and y-offset for update method
|
||||
viewWidthOffset = x - coordinates.x - offsetX;
|
||||
viewHeightOffset = -view.getMeasuredHeight() + offsetY;
|
||||
|
||||
|
||||
close(); // if it was already opened
|
||||
mapView.addView(view, lp);
|
||||
isVisible = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close this {@link InfoWindow} if it is visible, otherwise calling this will do nothing.
|
||||
*
|
||||
* @return This {@link InfoWindow}
|
||||
*/
|
||||
@NonNull
|
||||
InfoWindow close() {
|
||||
MapboxMap mapboxMap = this.mapboxMap.get();
|
||||
if (isVisible && mapboxMap != null) {
|
||||
isVisible = false;
|
||||
View view = this.view.get();
|
||||
if (view != null && view.getParent() != null) {
|
||||
((ViewGroup) view.getParent()).removeView(view);
|
||||
}
|
||||
|
||||
Marker marker = getBoundMarker();
|
||||
MapboxMap.OnInfoWindowCloseListener listener = mapboxMap.getOnInfoWindowCloseListener();
|
||||
if (listener != null) {
|
||||
listener.onInfoWindowClose(marker);
|
||||
}
|
||||
|
||||
setBoundMarker(null);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the view that is displayed when the InfoWindow opens. This retrieves data from
|
||||
* overlayItem and shows it in the tooltip.
|
||||
*
|
||||
* @param overlayItem the tapped overlay item
|
||||
*/
|
||||
void adaptDefaultMarker(@NonNull Marker overlayItem, MapboxMap mapboxMap, @NonNull MapView mapView) {
|
||||
View view = this.view.get();
|
||||
if (view == null) {
|
||||
view = LayoutInflater.from(mapView.getContext()).inflate(layoutRes, mapView, false);
|
||||
initialize(view, mapboxMap);
|
||||
}
|
||||
this.mapboxMap = new WeakReference<>(mapboxMap);
|
||||
String title = overlayItem.getTitle();
|
||||
TextView titleTextView = ((TextView) view.findViewById(R.id.infowindow_title));
|
||||
if (!TextUtils.isEmpty(title)) {
|
||||
titleTextView.setText(title);
|
||||
titleTextView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
titleTextView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
String snippet = overlayItem.getSnippet();
|
||||
TextView snippetTextView = ((TextView) view.findViewById(R.id.infowindow_description));
|
||||
if (!TextUtils.isEmpty(snippet)) {
|
||||
snippetTextView.setText(snippet);
|
||||
snippetTextView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
snippetTextView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
InfoWindow setBoundMarker(Marker boundMarker) {
|
||||
this.boundMarker = new WeakReference<>(boundMarker);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Marker getBoundMarker() {
|
||||
if (boundMarker == null) {
|
||||
return null;
|
||||
}
|
||||
return boundMarker.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Will result in getting this {@link InfoWindow} and updating the view being displayed.
|
||||
*/
|
||||
public void update() {
|
||||
MapboxMap mapboxMap = this.mapboxMap.get();
|
||||
Marker marker = boundMarker.get();
|
||||
View view = this.view.get();
|
||||
if (mapboxMap != null && marker != null && view != null) {
|
||||
coordinates = mapboxMap.getProjection().toScreenLocation(marker.getPosition());
|
||||
|
||||
if (view instanceof BubbleLayout) {
|
||||
view.setX(coordinates.x + viewWidthOffset - markerWidthOffset);
|
||||
} else {
|
||||
view.setX(coordinates.x - (view.getMeasuredWidth() / 2) - markerWidthOffset);
|
||||
}
|
||||
view.setY(coordinates.y + viewHeightOffset);
|
||||
}
|
||||
}
|
||||
|
||||
void onContentUpdate() {
|
||||
//recalculate y-offset and update position
|
||||
View view = this.view.get();
|
||||
if (view != null) {
|
||||
ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
|
||||
if (viewTreeObserver.isAlive()) {
|
||||
viewTreeObserver.addOnGlobalLayoutListener(contentUpdateListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private final ViewTreeObserver.OnGlobalLayoutListener contentUpdateListener =
|
||||
new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
View view = InfoWindow.this.view.get();
|
||||
if (view != null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
} else {
|
||||
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
|
||||
}
|
||||
viewHeightOffset = -view.getMeasuredHeight() + markerHeightOffset;
|
||||
update();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve this {@link InfoWindow}'s current view being used.
|
||||
*
|
||||
* @return This {@link InfoWindow}'s current View.
|
||||
*/
|
||||
@Nullable
|
||||
public View getView() {
|
||||
return view != null ? view.get() : null;
|
||||
}
|
||||
|
||||
boolean isVisible() {
|
||||
return isVisible;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.support.annotation.Keep;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.maps.MapView;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
|
||||
/**
|
||||
* Marker is an annotation that shows an icon image at a geographical location. The default marker
|
||||
* uses a provided icon. This icon can be customized using {@link IconFactory} to generate an
|
||||
* {@link Icon} using a provided image. Markers are added to the map by first giving a
|
||||
* {@link LatLng} and using {@link MapboxMap#addMarker(MarkerOptions)}. The marker icon will be
|
||||
* centered at this position so it is common to add padding to the icon image before usage.
|
||||
* <p>
|
||||
* Markers are designed to be interactive. They receive click events by default, and are often used
|
||||
* with event listeners to bring up info windows. An {@link InfoWindow} is displayed by default when
|
||||
* either a title or snippet is provided.
|
||||
* </p>
|
||||
* @deprecated As of 7.0.0,
|
||||
* use <a href="https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-annotation">
|
||||
* Mapbox Annotation Plugin</a> instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class Marker extends Annotation {
|
||||
|
||||
@Keep
|
||||
private LatLng position;
|
||||
private String snippet;
|
||||
@Nullable
|
||||
private Icon icon;
|
||||
//Redundantly stored for JNI access
|
||||
@Nullable
|
||||
@Keep
|
||||
private String iconId;
|
||||
private String title;
|
||||
|
||||
@Nullable
|
||||
private InfoWindow infoWindow;
|
||||
private boolean infoWindowShown;
|
||||
|
||||
private int topOffsetPixels;
|
||||
private int rightOffsetPixels;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Marker() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a instance of {@link Marker} using the builder of Marker.
|
||||
*
|
||||
* @param baseMarkerOptions The builder used to construct the Marker.
|
||||
*/
|
||||
public Marker(BaseMarkerOptions baseMarkerOptions) {
|
||||
this(baseMarkerOptions.position, baseMarkerOptions.icon, baseMarkerOptions.title, baseMarkerOptions.snippet);
|
||||
}
|
||||
|
||||
Marker(LatLng position, Icon icon, String title, String snippet) {
|
||||
this.position = position;
|
||||
this.title = title;
|
||||
this.snippet = snippet;
|
||||
setIcon(icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position of the marker.
|
||||
*
|
||||
* @return A {@link LatLng} object specifying the marker's current position.
|
||||
*/
|
||||
public LatLng getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the snippet of the marker.
|
||||
*
|
||||
* @return A string containing the marker's snippet.
|
||||
*/
|
||||
public String getSnippet() {
|
||||
return snippet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the snippet of the marker.
|
||||
*
|
||||
* @return A string containing the marker's snippet.
|
||||
*/
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method, used internally by the SDK.
|
||||
*/
|
||||
public void hideInfoWindow() {
|
||||
if (infoWindow != null) {
|
||||
infoWindow.close();
|
||||
}
|
||||
infoWindowShown = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method, used internally by the SDK.
|
||||
*
|
||||
* @return true if the infoWindow is shown
|
||||
*/
|
||||
public boolean isInfoWindowShown() {
|
||||
return infoWindowShown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location of the marker.
|
||||
*
|
||||
* @param position A {@link LatLng} defining the marker position.
|
||||
*/
|
||||
public void setPosition(LatLng position) {
|
||||
this.position = position;
|
||||
MapboxMap map = getMapboxMap();
|
||||
if (map != null) {
|
||||
map.updateMarker(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the snippet of the marker.
|
||||
*
|
||||
* @param snippet A String used in the marker info window. If {@code null}, the snippet is
|
||||
* cleared.
|
||||
*/
|
||||
public void setSnippet(String snippet) {
|
||||
this.snippet = snippet;
|
||||
refreshInfoWindowContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the icon of the marker.
|
||||
*
|
||||
* @param icon The {@link Icon} to be used as Marker image
|
||||
*/
|
||||
public void setIcon(@Nullable Icon icon) {
|
||||
this.icon = icon;
|
||||
this.iconId = icon != null ? icon.getId() : null;
|
||||
MapboxMap map = getMapboxMap();
|
||||
if (map != null) {
|
||||
map.updateMarker(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link Icon} currently used for the marker. If no Icon was set for the marker, the
|
||||
* default icon will be returned.
|
||||
*
|
||||
* @return The {@link Icon} the marker is using.
|
||||
*/
|
||||
@Nullable
|
||||
public Icon getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the title of the marker.
|
||||
*
|
||||
* @param title A String used in the marker info window. If {@code null}, the title is
|
||||
* cleared.
|
||||
*/
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
refreshInfoWindowContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link InfoWindow} the marker is using. If the marker hasn't had an info window
|
||||
* defined, this will return {@code null}.
|
||||
*
|
||||
* @return The info window the marker is using.
|
||||
*/
|
||||
@Nullable
|
||||
public InfoWindow getInfoWindow() {
|
||||
return infoWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update only for default Marker's InfoWindow content for Title and Snippet
|
||||
*/
|
||||
private void refreshInfoWindowContent() {
|
||||
if (isInfoWindowShown() && mapView != null && mapboxMap != null && mapboxMap.getInfoWindowAdapter() == null) {
|
||||
InfoWindow infoWindow = getInfoWindow(mapView);
|
||||
if (mapView.getContext() != null) {
|
||||
infoWindow.adaptDefaultMarker(this, mapboxMap, mapView);
|
||||
}
|
||||
MapboxMap map = getMapboxMap();
|
||||
if (map != null) {
|
||||
map.updateMarker(this);
|
||||
}
|
||||
infoWindow.onContentUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method, used internally by the SDK. Use {@link MapboxMap#selectMarker(Marker)}
|
||||
* if you want to programmatically display the markers info window.
|
||||
*
|
||||
* @param mapboxMap The hosting mapbox map.
|
||||
* @param mapView The hosting map view.
|
||||
* @return The info window that was shown.
|
||||
*/
|
||||
@Nullable
|
||||
public InfoWindow showInfoWindow(@NonNull MapboxMap mapboxMap, @NonNull MapView mapView) {
|
||||
setMapboxMap(mapboxMap);
|
||||
setMapView(mapView);
|
||||
MapboxMap.InfoWindowAdapter infoWindowAdapter = getMapboxMap().getInfoWindowAdapter();
|
||||
if (infoWindowAdapter != null) {
|
||||
// end developer is using a custom InfoWindowAdapter
|
||||
View content = infoWindowAdapter.getInfoWindow(this);
|
||||
if (content != null) {
|
||||
infoWindow = new InfoWindow(content, mapboxMap);
|
||||
showInfoWindow(infoWindow, mapView);
|
||||
return infoWindow;
|
||||
}
|
||||
}
|
||||
|
||||
InfoWindow infoWindow = getInfoWindow(mapView);
|
||||
if (mapView.getContext() != null) {
|
||||
infoWindow.adaptDefaultMarker(this, mapboxMap, mapView);
|
||||
}
|
||||
return showInfoWindow(infoWindow, mapView);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private InfoWindow showInfoWindow(InfoWindow iw, MapView mapView) {
|
||||
iw.open(mapView, this, getPosition(), rightOffsetPixels, topOffsetPixels);
|
||||
infoWindowShown = true;
|
||||
return iw;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private InfoWindow getInfoWindow(@NonNull MapView mapView) {
|
||||
if (infoWindow == null && mapView.getContext() != null) {
|
||||
infoWindow = new InfoWindow(mapView, R.layout.mapbox_infowindow_content, getMapboxMap());
|
||||
}
|
||||
return infoWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method, used internally by the SDK.
|
||||
*
|
||||
* @param topOffsetPixels the top offset pixels.
|
||||
*/
|
||||
public void setTopOffsetPixels(int topOffsetPixels) {
|
||||
this.topOffsetPixels = topOffsetPixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method, used internally by the SDK.
|
||||
*
|
||||
* @param rightOffsetPixels the right offset pixels.
|
||||
*/
|
||||
public void setRightOffsetPixels(int rightOffsetPixels) {
|
||||
this.rightOffsetPixels = rightOffsetPixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String with the marker position.
|
||||
*
|
||||
* @return A String with the marker position.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Marker [position[" + getPosition() + "]]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.mapbox.mapboxsdk.exceptions.InvalidMarkerPositionException;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Builder for composing {@link Marker} objects. See {@link Marker} for additional information.
|
||||
* </p>
|
||||
* <h3>Example</h3>
|
||||
* <pre>
|
||||
* mapView.addMarker(new MarkerOptions()
|
||||
* .title("Intersection")
|
||||
* .snippet("H St NW with 15th St NW")
|
||||
* .position(new LatLng(38.9002073, -77.03364419)));
|
||||
* </pre>
|
||||
* @deprecated As of 7.0.0,
|
||||
* use <a href="https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-annotation">
|
||||
* Mapbox Annotation Plugin</a> instead
|
||||
*/
|
||||
@Deprecated
|
||||
public final class MarkerOptions extends BaseMarkerOptions<Marker, MarkerOptions> implements Parcelable {
|
||||
|
||||
/**
|
||||
* Defines options for a Marker.
|
||||
*/
|
||||
public MarkerOptions() {
|
||||
}
|
||||
|
||||
protected MarkerOptions(Parcel in) {
|
||||
position((LatLng) in.readParcelable(LatLng.class.getClassLoader()));
|
||||
snippet(in.readString());
|
||||
title(in.readString());
|
||||
if (in.readByte() != 0) {
|
||||
// this means we have an icon
|
||||
String iconId = in.readString();
|
||||
Bitmap iconBitmap = in.readParcelable(Bitmap.class.getClassLoader());
|
||||
Icon icon = new Icon(iconId, iconBitmap);
|
||||
icon(icon);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MarkerOptions getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the kinds of special objects contained in this Parcelable's
|
||||
* marshalled representation.
|
||||
*
|
||||
* @return integer 0.
|
||||
*/
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten this object in to a Parcel.
|
||||
*
|
||||
* @param out The Parcel in which the object should be written.
|
||||
* @param flags Additional flags about how the object should be written. May be 0 or
|
||||
* {@link #PARCELABLE_WRITE_RETURN_VALUE}.
|
||||
*/
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeParcelable(getPosition(), flags);
|
||||
out.writeString(getSnippet());
|
||||
out.writeString(getTitle());
|
||||
Icon icon = getIcon();
|
||||
out.writeByte((byte) (icon != null ? 1 : 0));
|
||||
if (icon != null) {
|
||||
out.writeString(getIcon().getId());
|
||||
out.writeParcelable(getIcon().getBitmap(), flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method. Used internally by the SDK.
|
||||
*
|
||||
* @return Marker The build marker
|
||||
*/
|
||||
public Marker getMarker() {
|
||||
if (position == null) {
|
||||
throw new InvalidMarkerPositionException();
|
||||
}
|
||||
|
||||
return new Marker(position, icon, title, snippet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position set for this {@link MarkerOptions} object.
|
||||
*
|
||||
* @return A {@link LatLng} object specifying the marker's current position.
|
||||
*/
|
||||
public LatLng getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the snippet set for this {@link MarkerOptions} object.
|
||||
*
|
||||
* @return A string containing the marker's snippet.
|
||||
*/
|
||||
public String getSnippet() {
|
||||
return snippet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the title set for this {@link MarkerOptions} object.
|
||||
*
|
||||
* @return A string containing the marker's title.
|
||||
*/
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the custom icon set for this {@link MarkerOptions} object.
|
||||
*
|
||||
* @return A {@link Icon} object that the marker is using. If the icon wasn't set, default icon
|
||||
* will return.
|
||||
*/
|
||||
public Icon getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<MarkerOptions> CREATOR =
|
||||
new Parcelable.Creator<MarkerOptions>() {
|
||||
public MarkerOptions createFromParcel(@NonNull Parcel in) {
|
||||
return new MarkerOptions(in);
|
||||
}
|
||||
|
||||
public MarkerOptions[] newArray(int size) {
|
||||
return new MarkerOptions[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Compares this {@link MarkerOptions} object with another {@link MarkerOptions} and
|
||||
* determines if their properties match.
|
||||
*
|
||||
* @param o Another {@link MarkerOptions} to compare with this object.
|
||||
* @return True if marker properties match this {@link MarkerOptions} object.
|
||||
* Else, false.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MarkerOptions marker = (MarkerOptions) o;
|
||||
|
||||
if (getPosition() != null ? !getPosition().equals(marker.getPosition()) : marker.getPosition() != null) {
|
||||
return false;
|
||||
}
|
||||
if (getSnippet() != null ? !getSnippet().equals(marker.getSnippet()) : marker.getSnippet() != null) {
|
||||
return false;
|
||||
}
|
||||
if (getIcon() != null ? !getIcon().equals(marker.getIcon()) : marker.getIcon() != null) {
|
||||
return false;
|
||||
}
|
||||
return !(getTitle() != null ? !getTitle().equals(marker.getTitle()) : marker.getTitle() != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives an integer which can be used as the bucket number for storing elements of the set/map.
|
||||
* This bucket number is the address of the element inside the set/map. There's no guarantee
|
||||
* that this hash value will be consistent between different Java implementations, or even
|
||||
* between different execution runs of the same program.
|
||||
*
|
||||
* @return integer value you can use for storing element.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + (getPosition() != null ? getPosition().hashCode() : 0);
|
||||
result = 31 * result + (getSnippet() != null ? getSnippet().hashCode() : 0);
|
||||
result = 31 * result + (getIcon() != null ? getIcon().hashCode() : 0);
|
||||
result = 31 * result + (getTitle() != null ? getTitle().hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.support.annotation.Keep;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Polygon is a geometry annotation that's a closed loop of coordinates.
|
||||
* @deprecated As of 7.0.0,
|
||||
* use <a href="https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-annotation">
|
||||
* Mapbox Annotation Plugin</a> instead
|
||||
*/
|
||||
@Deprecated
|
||||
public final class Polygon extends BasePointCollection {
|
||||
|
||||
@Keep
|
||||
private int fillColor = Color.BLACK; // default fillColor is black
|
||||
@Keep
|
||||
private int strokeColor = Color.BLACK; // default strokeColor is black
|
||||
@Keep
|
||||
private List<List<LatLng>> holes;
|
||||
|
||||
Polygon() {
|
||||
super();
|
||||
holes = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the color of the fill region of the polygon.
|
||||
*
|
||||
* @return The color of the fill.
|
||||
*/
|
||||
public int getFillColor() {
|
||||
return fillColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the color of the stroke of the polygon.
|
||||
*
|
||||
* @return The color of the stroke.
|
||||
*/
|
||||
public int getStrokeColor() {
|
||||
return strokeColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the holes.
|
||||
*
|
||||
* @return A {@link List} of {@link List} of {@link LatLng} points making up the holes.
|
||||
*/
|
||||
public List<List<LatLng>> getHoles() {
|
||||
return new ArrayList<>(holes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color of the fill region of the polygon.
|
||||
*
|
||||
* @param color The color in ARGB format.
|
||||
*/
|
||||
public void setFillColor(int color) {
|
||||
fillColor = color;
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color of the stroke of the polygon.
|
||||
*
|
||||
* @param color The color in ARGB format.
|
||||
*/
|
||||
public void setStrokeColor(int color) {
|
||||
strokeColor = color;
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the holes of this polygon. This method will take a copy of the holes, so further
|
||||
* mutations to holes will have no effect on this polygon.
|
||||
*
|
||||
* @param holes A {@link List} of {@link List} of {@link LatLng} points making up the holes.
|
||||
*/
|
||||
public void setHoles(@NonNull List<? extends List<LatLng>> holes) {
|
||||
this.holes = new ArrayList<>(holes);
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a hole to the polygon.
|
||||
*
|
||||
* @param hole A {@link List} of {@link List} of {@link LatLng} points making up the hole to be added.
|
||||
*/
|
||||
void addHole(List<LatLng> hole) {
|
||||
holes.add(hole);
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
void update() {
|
||||
MapboxMap mapboxMap = getMapboxMap();
|
||||
if (mapboxMap != null) {
|
||||
mapboxMap.updatePolygon(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Builder for composing {@link Polygon} objects.
|
||||
* @deprecated As of 7.0.0,
|
||||
* use <a href="https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-annotation">
|
||||
* Mapbox Annotation Plugin</a> instead
|
||||
*/
|
||||
@Deprecated
|
||||
public final class PolygonOptions implements Parcelable {
|
||||
|
||||
public static final Parcelable.Creator<PolygonOptions> CREATOR =
|
||||
new Parcelable.Creator<PolygonOptions>() {
|
||||
public PolygonOptions createFromParcel(@NonNull Parcel in) {
|
||||
return new PolygonOptions(in);
|
||||
}
|
||||
|
||||
public PolygonOptions[] newArray(int size) {
|
||||
return new PolygonOptions[size];
|
||||
}
|
||||
};
|
||||
|
||||
private PolygonOptions(Parcel in) {
|
||||
polygon = new Polygon();
|
||||
List<LatLng> pointsList = new ArrayList<>();
|
||||
in.readList(pointsList, LatLng.class.getClassLoader());
|
||||
addAll(pointsList);
|
||||
List<List<LatLng>> holes = new ArrayList<>();
|
||||
in.readList(holes, LatLng.class.getClassLoader());
|
||||
addAllHoles(holes);
|
||||
alpha(in.readFloat());
|
||||
fillColor(in.readInt());
|
||||
strokeColor(in.readInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the kinds of special objects contained in this Parcelable's
|
||||
* marshalled representation.
|
||||
*
|
||||
* @return integer 0.
|
||||
*/
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten this object in to a Parcel.
|
||||
*
|
||||
* @param out The Parcel in which the object should be written.
|
||||
* @param flags Additional flags about how the object should be written. May be 0 or
|
||||
* {@link #PARCELABLE_WRITE_RETURN_VALUE}.
|
||||
*/
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeList(getPoints());
|
||||
out.writeList(getHoles());
|
||||
out.writeFloat(getAlpha());
|
||||
out.writeInt(getFillColor());
|
||||
out.writeInt(getStrokeColor());
|
||||
}
|
||||
|
||||
private Polygon polygon;
|
||||
|
||||
/**
|
||||
* Defines options for a polygon.
|
||||
*/
|
||||
public PolygonOptions() {
|
||||
polygon = new Polygon();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a vertex to the outline of the polygon being built.
|
||||
*
|
||||
* @param point {@link LatLng} point to be added to polygon geometry.
|
||||
* @return This {@link PolygonOptions} object with the given point added to the outline.
|
||||
*/
|
||||
@NonNull
|
||||
public PolygonOptions add(LatLng point) {
|
||||
polygon.addPoint(point);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds vertices to the outline of the polygon being built.
|
||||
*
|
||||
* @param points {@link LatLng} points to be added to polygon geometry.
|
||||
* @return This {@link PolygonOptions} object with the given points added to the outline.
|
||||
*/
|
||||
@NonNull
|
||||
public PolygonOptions add(LatLng... points) {
|
||||
for (LatLng point : points) {
|
||||
add(point);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds vertices to the outline of the polygon being built.
|
||||
*
|
||||
* @param points {@link Iterable} list made up of {@link LatLng} points defining the polygon
|
||||
* geometry
|
||||
* @return This {@link PolygonOptions} object with the given points added to the outline.
|
||||
*/
|
||||
@NonNull
|
||||
public PolygonOptions addAll(Iterable<LatLng> points) {
|
||||
for (LatLng point : points) {
|
||||
add(point);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a hole to the outline of the polygon being built.
|
||||
*
|
||||
* @param hole {@link List} list made up of {@link LatLng} points defining the hole
|
||||
* @return This {@link PolygonOptions} object with the given hole added to the outline.
|
||||
*/
|
||||
@NonNull
|
||||
public PolygonOptions addHole(List<LatLng> hole) {
|
||||
polygon.addHole(hole);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds holes to the outline of the polygon being built.
|
||||
*
|
||||
* @param holes {@link List} list made up of {@link LatLng} holes to be added to polygon geometry
|
||||
* @return This {@link PolygonOptions} object with the given holes added to the outline.
|
||||
*/
|
||||
@NonNull
|
||||
public PolygonOptions addHole(List<LatLng>... holes) {
|
||||
for (List<LatLng> hole : holes) {
|
||||
addHole(hole);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds holes to the outline of the polygon being built.
|
||||
*
|
||||
* @param holes {@link Iterable} list made up of {@link List} list of {@link LatLng} holes defining the hole geometry
|
||||
* @return This {@link PolygonOptions} object with the given holes added to the outline.
|
||||
*/
|
||||
@NonNull
|
||||
public PolygonOptions addAllHoles(Iterable<List<LatLng>> holes) {
|
||||
for (List<LatLng> hole : holes) {
|
||||
addHole(hole);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the alpha value of the polyline.
|
||||
*
|
||||
* @param alpha float value between 0 (not visible) and 1.
|
||||
* @return This {@link PolygonOptions} object with the given polygon alpha value.
|
||||
*/
|
||||
@NonNull
|
||||
public PolygonOptions alpha(float alpha) {
|
||||
polygon.setAlpha(alpha);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the alpha set for this {@link PolygonOptions} object.
|
||||
*
|
||||
* @return float value between 0 and 1 defining the alpha.
|
||||
*/
|
||||
public float getAlpha() {
|
||||
return polygon.getAlpha();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the polygon's fill color, as 32-bit ARGB. The default color is black.
|
||||
*
|
||||
* @param color 32-bit ARGB color.
|
||||
* @return This {@link PolylineOptions} object with a new color set.
|
||||
*/
|
||||
@NonNull
|
||||
public PolygonOptions fillColor(int color) {
|
||||
polygon.setFillColor(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the fill color set for this {@link PolygonOptions} object.
|
||||
*
|
||||
* @return The fill color of the polygon in ARGB format.
|
||||
*/
|
||||
public int getFillColor() {
|
||||
return polygon.getFillColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method. Used internally by the SDK.
|
||||
*
|
||||
* @return Polygon the Polygon to return
|
||||
*/
|
||||
public Polygon getPolygon() {
|
||||
return polygon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the polygon's stroke color, as 32-bit ARGB. The default color is black.
|
||||
*
|
||||
* @param color 32-bit ARGB color.
|
||||
* @return This {@link PolygonOptions} object with a new stroke color set.
|
||||
*/
|
||||
@NonNull
|
||||
public PolygonOptions strokeColor(int color) {
|
||||
polygon.setStrokeColor(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stroke color set for this {@link PolygonOptions} object.
|
||||
*
|
||||
* @return The stroke color of the polygon in ARGB format.
|
||||
*/
|
||||
public int getStrokeColor() {
|
||||
return polygon.getStrokeColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the points set for this {@link PolygonOptions} object.
|
||||
*
|
||||
* @return The list made up of {@link LatLng} points defining the polygon.
|
||||
*/
|
||||
public List<LatLng> getPoints() {
|
||||
// the getter gives us a copy, which is the safe thing to do...
|
||||
return polygon.getPoints();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the holes set for this {@link PolygonOptions} object.
|
||||
*
|
||||
* @return The list made up of {@link List} of {@link List} of {@link LatLng} points defining the holes.
|
||||
*/
|
||||
public List<List<LatLng>> getHoles() {
|
||||
return polygon.getHoles();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares this {@link PolygonOptions} object with another {@link PolygonOptions} and
|
||||
* determines if their color, alpha, stroke color, and vertices match.
|
||||
*
|
||||
* @param o Another {@link PolygonOptions} to compare with this object.
|
||||
* @return True if color, alpha, stroke color, vertices and holes match this {@link PolygonOptions}
|
||||
* {@link PolygonOptions} object. Else, false.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PolygonOptions polygon = (PolygonOptions) o;
|
||||
|
||||
if (Float.compare(polygon.getAlpha(), getAlpha()) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (getFillColor() != polygon.getFillColor()) {
|
||||
return false;
|
||||
}
|
||||
if (getStrokeColor() != polygon.getStrokeColor()) {
|
||||
return false;
|
||||
}
|
||||
if (getPoints() != null ? !getPoints().equals(polygon.getPoints()) : polygon.getPoints() != null) {
|
||||
return false;
|
||||
}
|
||||
return !(getHoles() != null ? !getHoles().equals(polygon.getHoles()) : polygon.getHoles() != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives an integer which can be used as the bucket number for storing elements of the set/map.
|
||||
* This bucket number is the address of the element inside the set/map. There's no guarantee
|
||||
* that this hash value will be consistent between different Java implementations, or even
|
||||
* between different execution runs of the same program.
|
||||
*
|
||||
* @return integer value you can use for storing element.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + (getAlpha() != +0.0f ? Float.floatToIntBits(getAlpha()) : 0);
|
||||
result = 31 * result + getFillColor();
|
||||
result = 31 * result + getStrokeColor();
|
||||
result = 31 * result + (getPoints() != null ? getPoints().hashCode() : 0);
|
||||
result = 31 * result + (getHoles() != null ? getHoles().hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.support.annotation.Keep;
|
||||
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
|
||||
/**
|
||||
* Polyline is a geometry feature with an unclosed list of coordinates drawn as a line
|
||||
* @deprecated As of 7.0.0,
|
||||
* use <a href="https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-annotation">
|
||||
* Mapbox Annotation Plugin</a> instead
|
||||
*/
|
||||
@Deprecated
|
||||
public final class Polyline extends BasePointCollection {
|
||||
|
||||
@Keep
|
||||
private int color = Color.BLACK; // default color is black
|
||||
@Keep
|
||||
private float width = 10; // As specified by Google API Docs (in pixels)
|
||||
|
||||
Polyline() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color of this polyline.
|
||||
*
|
||||
* @return The color in ARGB format.
|
||||
*/
|
||||
public int getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the width of this polyline.
|
||||
*
|
||||
* @return The width in screen pixels.
|
||||
*/
|
||||
public float getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color of the polyline.
|
||||
*
|
||||
* @param color - the color in ARGB format
|
||||
*/
|
||||
public void setColor(int color) {
|
||||
this.color = color;
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the width of the polyline.
|
||||
*
|
||||
* @param width in pixels
|
||||
*/
|
||||
public void setWidth(float width) {
|
||||
this.width = width;
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
void update() {
|
||||
MapboxMap mapboxMap = getMapboxMap();
|
||||
if (mapboxMap != null) {
|
||||
mapboxMap.updatePolyline(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Builder for composing {@link Polyline} objects.
|
||||
* @deprecated As of 7.0.0,
|
||||
* use <a href="https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-annotation">
|
||||
* Mapbox Annotation Plugin</a> instead
|
||||
*/
|
||||
@Deprecated
|
||||
public final class PolylineOptions implements Parcelable {
|
||||
|
||||
|
||||
public static final Parcelable.Creator<PolylineOptions> CREATOR =
|
||||
new Parcelable.Creator<PolylineOptions>() {
|
||||
public PolylineOptions createFromParcel(@NonNull Parcel in) {
|
||||
return new PolylineOptions(in);
|
||||
}
|
||||
|
||||
public PolylineOptions[] newArray(int size) {
|
||||
return new PolylineOptions[size];
|
||||
}
|
||||
};
|
||||
|
||||
private PolylineOptions(Parcel in) {
|
||||
polyline = new Polyline();
|
||||
ArrayList<LatLng> pointsList = new ArrayList<>();
|
||||
in.readList(pointsList, LatLng.class.getClassLoader());
|
||||
addAll(pointsList);
|
||||
alpha(in.readFloat());
|
||||
color(in.readInt());
|
||||
width(in.readFloat());
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the kinds of special objects contained in this Parcelable's
|
||||
* marshalled representation.
|
||||
*
|
||||
* @return integer 0.
|
||||
*/
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten this object in to a Parcel.
|
||||
*
|
||||
* @param out The Parcel in which the object should be written.
|
||||
* @param flags Additional flags about how the object should be written. May be 0 or
|
||||
* {@link #PARCELABLE_WRITE_RETURN_VALUE}.
|
||||
*/
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeList(getPoints());
|
||||
out.writeFloat(getAlpha());
|
||||
out.writeInt(getColor());
|
||||
out.writeFloat(getWidth());
|
||||
}
|
||||
|
||||
private Polyline polyline;
|
||||
|
||||
/**
|
||||
* Defines options for a polyline.
|
||||
*/
|
||||
public PolylineOptions() {
|
||||
polyline = new Polyline();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a vertex to the end of the polyline being built.
|
||||
*
|
||||
* @param point {@link LatLng} point to be added to polyline geometry.
|
||||
* @return This {@link PolylineOptions} object with the given point on the end.
|
||||
*/
|
||||
@NonNull
|
||||
public PolylineOptions add(LatLng point) {
|
||||
polyline.addPoint(point);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds vertices to the end of the polyline being built.
|
||||
*
|
||||
* @param points {@link LatLng} points defining the polyline geometry.
|
||||
* @return This {@link PolylineOptions} object with the given point on the end.
|
||||
*/
|
||||
@NonNull
|
||||
public PolylineOptions add(LatLng... points) {
|
||||
for (LatLng point : points) {
|
||||
add(point);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds vertices to the end of the polyline being built.
|
||||
*
|
||||
* @param points {@link Iterable} list made up of {@link LatLng} points defining the polyline
|
||||
* geometry
|
||||
* @return This {@link PolylineOptions} object with the given points on the end.
|
||||
*/
|
||||
@NonNull
|
||||
public PolylineOptions addAll(Iterable<LatLng> points) {
|
||||
for (LatLng point : points) {
|
||||
add(point);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the alpha value of the polyline.
|
||||
*
|
||||
* @param alpha float value between 0 (not visible) and 1.
|
||||
* @return This {@link PolylineOptions} object with the given polyline alpha value.
|
||||
*/
|
||||
@NonNull
|
||||
public PolylineOptions alpha(float alpha) {
|
||||
polyline.setAlpha(alpha);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the alpha set for this {@link PolylineOptions} object.
|
||||
*
|
||||
* @return float value between 0 and 1 defining the alpha.
|
||||
*/
|
||||
public float getAlpha() {
|
||||
return polyline.getAlpha();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color of the polyline as a 32-bit ARGB color. The default color is black.
|
||||
*
|
||||
* @param color 32-bit ARGB color.
|
||||
* @return This {@link PolylineOptions} object with a new color set.
|
||||
*/
|
||||
@NonNull
|
||||
public PolylineOptions color(int color) {
|
||||
polyline.setColor(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color set for this {@link PolylineOptions} object.
|
||||
*
|
||||
* @return The color of the polyline in ARGB format.
|
||||
*/
|
||||
public int getColor() {
|
||||
return polyline.getColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method. Used internally by the SDK.
|
||||
*
|
||||
* @return PolyLine The polyline build by this class.
|
||||
*/
|
||||
public Polyline getPolyline() {
|
||||
return polyline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the width set for this {@link PolylineOptions} object.
|
||||
*
|
||||
* @return The width of the polyline in screen pixels.
|
||||
*/
|
||||
public float getWidth() {
|
||||
return polyline.getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the width of the polyline in screen pixels. The default is 10.
|
||||
*
|
||||
* @param width float value defining width of polyline using unit pixels.
|
||||
* @return This {@link PolylineOptions} object with a new width set.
|
||||
*/
|
||||
@NonNull
|
||||
public PolylineOptions width(float width) {
|
||||
polyline.setWidth(width);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the points set for this {@link PolylineOptions} object.
|
||||
*
|
||||
* @return a {@link List} of {@link LatLng}s specifying the vertices of the polyline.
|
||||
*/
|
||||
public List<LatLng> getPoints() {
|
||||
// the getter gives us a copy, which is the safe thing to do...
|
||||
return polyline.getPoints();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this {@link PolylineOptions} object with another {@link PolylineOptions} and
|
||||
* determines if their color, alpha, width, and vertices match.
|
||||
*
|
||||
* @param o Another {@link PolylineOptions} to compare with this object.
|
||||
* @return True if color, alpha, width, and vertices match this {@link PolylineOptions} object.
|
||||
* Else, false.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PolylineOptions polyline = (PolylineOptions) o;
|
||||
|
||||
if (Float.compare(polyline.getAlpha(), getAlpha()) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (getColor() != polyline.getColor()) {
|
||||
return false;
|
||||
}
|
||||
if (Float.compare(polyline.getWidth(), getWidth()) != 0) {
|
||||
return false;
|
||||
}
|
||||
return !(getPoints() != null ? !getPoints().equals(polyline.getPoints()) : polyline.getPoints() != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives an integer which can be used as the bucket number for storing elements of the set/map.
|
||||
* This bucket number is the address of the element inside the set/map. There's no guarantee
|
||||
* that this hash value will be consistent between different Java implementations, or even
|
||||
* between different execution runs of the same program.
|
||||
*
|
||||
* @return integer value you can use for storing element.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + (getAlpha() != +0.0f ? Float.floatToIntBits(getAlpha()) : 0);
|
||||
result = 31 * result + getColor();
|
||||
result = 31 * result + (getWidth() != +0.0f ? Float.floatToIntBits(getWidth()) : 0);
|
||||
result = 31 * result + (getPoints() != null ? getPoints().hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains the Mapbox Maps Android Annotation API classes.
|
||||
*/
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.mapbox.mapboxsdk.attribution;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Attribution {
|
||||
|
||||
private static final String OPENSTREETMAP = "OpenStreetMap";
|
||||
private static final String OPENSTREETMAP_ABBR = "OSM";
|
||||
static final String TELEMETRY = "Telemetry Settings";
|
||||
static final String MAPBOX_URL = "https://www.mapbox.com/about/maps/";
|
||||
static final String TELEMETRY_URL = "https://www.mapbox.com/telemetry/";
|
||||
static final List<String> IMPROVE_MAP_URLS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
// Using a List makes URL backwards compatible
|
||||
IMPROVE_MAP_URLS.add("https://www.mapbox.com/feedback/");
|
||||
IMPROVE_MAP_URLS.add("https://www.mapbox.com/map-feedback/");
|
||||
IMPROVE_MAP_URLS.add("https://apps.mapbox.com/feedback/");
|
||||
}
|
||||
|
||||
private String title;
|
||||
private String url;
|
||||
|
||||
Attribution(String title, String url) {
|
||||
this.title = title;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getTitleAbbreviated() {
|
||||
if (title.equals(OPENSTREETMAP)) {
|
||||
return OPENSTREETMAP_ABBR;
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Attribution that = (Attribution) o;
|
||||
|
||||
if (title != null ? !title.equals(that.title) : that.title != null) {
|
||||
return false;
|
||||
}
|
||||
return url != null ? url.equals(that.url) : that.url == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = title != null ? title.hashCode() : 0;
|
||||
result = 31 * result + (url != null ? url.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.mapbox.mapboxsdk.attribution;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.PointF;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
public class AttributionLayout {
|
||||
|
||||
@Nullable
|
||||
private Bitmap logo;
|
||||
@Nullable
|
||||
private PointF anchorPoint;
|
||||
private boolean shortText;
|
||||
|
||||
public AttributionLayout(@Nullable Bitmap logo, @Nullable PointF anchorPoint, boolean shortText) {
|
||||
this.logo = logo;
|
||||
this.anchorPoint = anchorPoint;
|
||||
this.shortText = shortText;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Bitmap getLogo() {
|
||||
return logo;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PointF getAnchorPoint() {
|
||||
return anchorPoint;
|
||||
}
|
||||
|
||||
public boolean isShortText() {
|
||||
return shortText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AttributionLayout that = (AttributionLayout) o;
|
||||
|
||||
if (logo != null ? !logo.equals(that.logo) : that.logo != null) {
|
||||
return false;
|
||||
}
|
||||
return anchorPoint != null ? anchorPoint.equals(that.anchorPoint) : that.anchorPoint == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = logo != null ? logo.hashCode() : 0;
|
||||
result = 31 * result + (anchorPoint != null ? anchorPoint.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AttributionLayout{"
|
||||
+ "logo=" + logo
|
||||
+ ", anchorPoint=" + anchorPoint
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
package com.mapbox.mapboxsdk.attribution;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.PointF;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class AttributionMeasure {
|
||||
|
||||
private Bitmap logo;
|
||||
private Bitmap logoSmall;
|
||||
private Bitmap snapshot;
|
||||
private TextView textView;
|
||||
private TextView textViewShort;
|
||||
private float margin;
|
||||
|
||||
private boolean shorterText;
|
||||
|
||||
AttributionMeasure(Bitmap snapshot, Bitmap logo, Bitmap logoSmall, TextView tv, TextView tvShort, float margin) {
|
||||
this.snapshot = snapshot;
|
||||
this.logo = logo;
|
||||
this.logoSmall = logoSmall;
|
||||
this.textView = tv;
|
||||
this.textViewShort = tvShort;
|
||||
this.margin = margin;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AttributionLayout measure() {
|
||||
Chain chain = new Chain(
|
||||
new FullLogoLongTextCommand(),
|
||||
new FullLogoShortTextCommand(),
|
||||
new SmallLogoLongTextCommand(),
|
||||
new SmallLogoShortTextCommand(),
|
||||
new LongTextCommand(),
|
||||
new ShortTextCommand(),
|
||||
new NoTextCommand()
|
||||
);
|
||||
|
||||
AttributionLayout attributionLayout = chain.start(this);
|
||||
shorterText = attributionLayout.isShortText();
|
||||
return attributionLayout;
|
||||
}
|
||||
|
||||
|
||||
private static class FullLogoLongTextCommand implements Command {
|
||||
@Nullable
|
||||
public AttributionLayout execute(@NonNull AttributionMeasure measure) {
|
||||
float width = measure.getLogoContainerWidth() + measure.getTextViewContainerWidth();
|
||||
boolean fitBounds = width <= measure.getMaxSize();
|
||||
if (fitBounds) {
|
||||
PointF anchor = calculateAnchor(measure.snapshot, measure.textView, measure.margin);
|
||||
return new AttributionLayout(measure.logo, anchor, false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class FullLogoShortTextCommand implements Command {
|
||||
@Nullable
|
||||
@Override
|
||||
public AttributionLayout execute(@NonNull AttributionMeasure measure) {
|
||||
float width = measure.getLogoContainerWidth() + measure.getTextViewShortContainerWidth();
|
||||
boolean fitBounds = width <= measure.getMaxSizeShort();
|
||||
if (fitBounds) {
|
||||
PointF anchor = calculateAnchor(measure.snapshot, measure.textViewShort, measure.margin);
|
||||
return new AttributionLayout(measure.logo, anchor, true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SmallLogoLongTextCommand implements Command {
|
||||
@Nullable
|
||||
@Override
|
||||
public AttributionLayout execute(@NonNull AttributionMeasure measure) {
|
||||
float width = measure.getLogoSmallContainerWidth() + measure.getTextViewContainerWidth();
|
||||
boolean fitBounds = width <= measure.getMaxSize();
|
||||
if (fitBounds) {
|
||||
PointF anchor = calculateAnchor(measure.snapshot, measure.textView, measure.margin);
|
||||
return new AttributionLayout(measure.logoSmall, anchor, false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SmallLogoShortTextCommand implements Command {
|
||||
@Nullable
|
||||
@Override
|
||||
public AttributionLayout execute(@NonNull AttributionMeasure measure) {
|
||||
float width = measure.getLogoContainerWidth() + measure.getTextViewShortContainerWidth();
|
||||
boolean fitBounds = width <= measure.getMaxSizeShort();
|
||||
if (fitBounds) {
|
||||
PointF anchor = calculateAnchor(measure.snapshot, measure.textViewShort, measure.margin);
|
||||
return new AttributionLayout(measure.logoSmall, anchor, true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class LongTextCommand implements Command {
|
||||
@Nullable
|
||||
@Override
|
||||
public AttributionLayout execute(@NonNull AttributionMeasure measure) {
|
||||
float width = measure.getTextViewContainerWidth() + measure.margin;
|
||||
boolean fitBounds = width <= measure.getMaxSize();
|
||||
if (fitBounds) {
|
||||
return new AttributionLayout(null, calculateAnchor(measure.snapshot, measure.textView, measure.margin), false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ShortTextCommand implements Command {
|
||||
@Nullable
|
||||
@Override
|
||||
public AttributionLayout execute(@NonNull AttributionMeasure measure) {
|
||||
float width = measure.getTextViewShortContainerWidth() + measure.margin;
|
||||
boolean fitBounds = width <= measure.getMaxSizeShort();
|
||||
if (fitBounds) {
|
||||
PointF anchor = calculateAnchor(measure.snapshot, measure.textViewShort, measure.margin);
|
||||
return new AttributionLayout(null, anchor, true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class NoTextCommand implements Command {
|
||||
@NonNull
|
||||
@Override
|
||||
public AttributionLayout execute(AttributionMeasure measure) {
|
||||
return new AttributionLayout(null, null, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static PointF calculateAnchor(Bitmap snapshot, TextView textView, float margin) {
|
||||
return new PointF(
|
||||
snapshot.getWidth() - textView.getMeasuredWidth() - margin,
|
||||
snapshot.getHeight() - margin - textView.getMeasuredHeight()
|
||||
);
|
||||
}
|
||||
|
||||
public TextView getTextView() {
|
||||
return shorterText ? textViewShort : textView;
|
||||
}
|
||||
|
||||
private class Chain {
|
||||
public List<Command> commands;
|
||||
|
||||
Chain(Command... commands) {
|
||||
this.commands = Arrays.asList(commands);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AttributionLayout start(AttributionMeasure measure) {
|
||||
AttributionLayout attributionLayout = null;
|
||||
for (Command command : commands) {
|
||||
attributionLayout = command.execute(measure);
|
||||
if (attributionLayout != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return attributionLayout;
|
||||
}
|
||||
}
|
||||
|
||||
public interface Command {
|
||||
@Nullable
|
||||
AttributionLayout execute(AttributionMeasure measure);
|
||||
}
|
||||
|
||||
private float getTextViewContainerWidth() {
|
||||
return textView.getMeasuredWidth() + margin;
|
||||
}
|
||||
|
||||
private float getLogoContainerWidth() {
|
||||
return logo.getWidth() + (2 * margin);
|
||||
}
|
||||
|
||||
private float getTextViewShortContainerWidth() {
|
||||
return textViewShort.getMeasuredWidth() + margin;
|
||||
}
|
||||
|
||||
private float getLogoSmallContainerWidth() {
|
||||
return logoSmall.getWidth() + (2 * margin);
|
||||
}
|
||||
|
||||
private float getMaxSize() {
|
||||
return snapshot.getWidth() * 8 / 10;
|
||||
}
|
||||
|
||||
private float getMaxSizeShort() {
|
||||
return snapshot.getWidth();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private Bitmap snapshot;
|
||||
private Bitmap logo;
|
||||
private Bitmap logoSmall;
|
||||
private TextView textView;
|
||||
private TextView textViewShort;
|
||||
private float marginPadding;
|
||||
|
||||
@NonNull
|
||||
public Builder setSnapshot(Bitmap snapshot) {
|
||||
this.snapshot = snapshot;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder setLogo(Bitmap logo) {
|
||||
this.logo = logo;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder setLogoSmall(Bitmap logoSmall) {
|
||||
this.logoSmall = logoSmall;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder setTextView(TextView textView) {
|
||||
this.textView = textView;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder setTextViewShort(TextView textViewShort) {
|
||||
this.textViewShort = textViewShort;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder setMarginPadding(float marginPadding) {
|
||||
this.marginPadding = marginPadding;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public AttributionMeasure build() {
|
||||
return new AttributionMeasure(snapshot, logo, logoSmall, textView, textViewShort, marginPadding);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
package com.mapbox.mapboxsdk.attribution;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.Html;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.URLSpan;
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Responsible for parsing attribution data coming from Sources and MapSnapshot.
|
||||
* <p>
|
||||
* Exposes multiple configuration options to manipulate data being parsed.
|
||||
* Use the Options object to build these configurations.
|
||||
* </p>
|
||||
*/
|
||||
public class AttributionParser {
|
||||
|
||||
private static final String IMPROVE_THIS_MAP = "Improve this map";
|
||||
private final WeakReference<Context> context;
|
||||
private final Set<Attribution> attributions = new LinkedHashSet<>();
|
||||
private final String attributionData;
|
||||
private final boolean withImproveMap;
|
||||
private final boolean withCopyrightSign;
|
||||
private final boolean withTelemetryAttribution;
|
||||
private final boolean withMapboxAttribution;
|
||||
|
||||
AttributionParser(WeakReference<Context> context, String attributionData, boolean withImproveMap,
|
||||
boolean withCopyrightSign, boolean withTelemetryAttribution, boolean withMapboxAttribution) {
|
||||
this.context = context;
|
||||
this.attributionData = attributionData;
|
||||
this.withImproveMap = withImproveMap;
|
||||
this.withCopyrightSign = withCopyrightSign;
|
||||
this.withTelemetryAttribution = withTelemetryAttribution;
|
||||
this.withMapboxAttribution = withMapboxAttribution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parsed attributions.
|
||||
*
|
||||
* @return the attributions
|
||||
*/
|
||||
@NonNull
|
||||
public Set<Attribution> getAttributions() {
|
||||
return attributions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parsed attribution string.
|
||||
*
|
||||
* @return the parsed attribution string
|
||||
*/
|
||||
@NonNull
|
||||
public String createAttributionString() {
|
||||
return createAttributionString(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parsed attribution string.
|
||||
*
|
||||
* @param shortenedOutput if attribution string should contain shortened output
|
||||
* @return the parsed attribution string
|
||||
*/
|
||||
@NonNull
|
||||
public String createAttributionString(boolean shortenedOutput) {
|
||||
StringBuilder stringBuilder = new StringBuilder(withCopyrightSign ? "" : "© ");
|
||||
int counter = 0;
|
||||
for (Attribution attribution : attributions) {
|
||||
counter++;
|
||||
stringBuilder.append(!shortenedOutput ? attribution.getTitle() : attribution.getTitleAbbreviated());
|
||||
if (counter != attributions.size()) {
|
||||
stringBuilder.append(" / ");
|
||||
}
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Main attribution for configuration
|
||||
*/
|
||||
protected void parse() {
|
||||
parseAttributions();
|
||||
addAdditionalAttributions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse attributions
|
||||
*/
|
||||
private void parseAttributions() {
|
||||
SpannableStringBuilder htmlBuilder = (SpannableStringBuilder) fromHtml(attributionData);
|
||||
URLSpan[] urlSpans = htmlBuilder.getSpans(0, htmlBuilder.length(), URLSpan.class);
|
||||
for (URLSpan urlSpan : urlSpans) {
|
||||
parseUrlSpan(htmlBuilder, urlSpan);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an URLSpan containing an attribution.
|
||||
*
|
||||
* @param htmlBuilder the html builder
|
||||
* @param urlSpan the url span to be parsed
|
||||
*/
|
||||
private void parseUrlSpan(@NonNull SpannableStringBuilder htmlBuilder, @NonNull URLSpan urlSpan) {
|
||||
String url = urlSpan.getURL();
|
||||
if (isUrlValid(url)) {
|
||||
String anchor = parseAnchorValue(htmlBuilder, urlSpan);
|
||||
if (isImproveThisMapAnchor(anchor)) {
|
||||
anchor = translateImproveThisMapAnchor(anchor);
|
||||
}
|
||||
attributions.add(new Attribution(anchor, url));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked to validate if an url is valid to be included in the final attribution.
|
||||
*
|
||||
* @param url the url to be validated
|
||||
* @return if the url is valid
|
||||
*/
|
||||
private boolean isUrlValid(@NonNull String url) {
|
||||
return isValidForImproveThisMap(url) && isValidForMapbox(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked to validate if an anchor is equal to Improve this map coming from tilesets.
|
||||
*
|
||||
* @param anchor the anchor to be validated
|
||||
* @return if the url is valid
|
||||
*/
|
||||
private boolean isImproveThisMapAnchor(String anchor) {
|
||||
return anchor.equals(IMPROVE_THIS_MAP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked to replace the english Improve this map with localized variant.
|
||||
*
|
||||
* @param anchor the anchor to be translated
|
||||
* @return the translated anchor
|
||||
*/
|
||||
private String translateImproveThisMapAnchor(String anchor) {
|
||||
Context context = this.context.get();
|
||||
if (context != null) {
|
||||
anchor = context.getString(R.string.mapbox_telemetryImproveMap);
|
||||
}
|
||||
return anchor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked to validate if an url is valid for the improve map configuration.
|
||||
*
|
||||
* @param url the url to be validated
|
||||
* @return if the url is valid for improve this map
|
||||
*/
|
||||
private boolean isValidForImproveThisMap(@NonNull String url) {
|
||||
return withImproveMap || !(Attribution.IMPROVE_MAP_URLS.contains(url));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked to validate if an url is valid for the Mapbox configuration.
|
||||
*
|
||||
* @param url the url to be validated
|
||||
* @return if the url is valid for Mapbox
|
||||
*/
|
||||
private boolean isValidForMapbox(@NonNull String url) {
|
||||
return withMapboxAttribution || !url.equals(Attribution.MAPBOX_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the attribution by parsing the anchor value of html href tag.
|
||||
*
|
||||
* @param htmlBuilder the html builder
|
||||
* @param urlSpan the current urlSpan
|
||||
* @return the parsed anchor value
|
||||
*/
|
||||
@NonNull
|
||||
private String parseAnchorValue(SpannableStringBuilder htmlBuilder, URLSpan urlSpan) {
|
||||
int start = htmlBuilder.getSpanStart(urlSpan);
|
||||
int end = htmlBuilder.getSpanEnd(urlSpan);
|
||||
int length = end - start;
|
||||
char[] charKey = new char[length];
|
||||
htmlBuilder.getChars(start, end, charKey, 0);
|
||||
return stripCopyright(String.valueOf(charKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility to strip the copyright sign from an attribution
|
||||
*
|
||||
* @param anchor the attribution string to strip
|
||||
* @return the stripped attribution string without the copyright sign
|
||||
*/
|
||||
@NonNull
|
||||
private String stripCopyright(@NonNull String anchor) {
|
||||
if (!withCopyrightSign && anchor.startsWith("© ")) {
|
||||
anchor = anchor.substring(2, anchor.length());
|
||||
}
|
||||
return anchor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked to manually add attributions
|
||||
*/
|
||||
private void addAdditionalAttributions() {
|
||||
if (withTelemetryAttribution) {
|
||||
Context context = this.context.get();
|
||||
attributions.add(
|
||||
new Attribution(
|
||||
context != null ? context.getString(R.string.mapbox_telemetrySettings) : Attribution.TELEMETRY,
|
||||
Attribution.TELEMETRY_URL
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to a spanned html representation.
|
||||
*
|
||||
* @param html the string to convert
|
||||
* @return the spanned html representation
|
||||
*/
|
||||
private static Spanned fromHtml(String html) {
|
||||
Spanned result;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
|
||||
result = Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY);
|
||||
} else {
|
||||
result = Html.fromHtml(html);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder to configure using an AttributionParser.
|
||||
* <p>
|
||||
* AttributionData, set with {@link #withAttributionData(String...)}, is the only required property to build
|
||||
* the underlying AttributionParser. Other properties include trimming the copyright sign, adding telemetry
|
||||
* attribution or hiding attribution as improve this map and Mapbox.
|
||||
* </p>
|
||||
*/
|
||||
public static class Options {
|
||||
private final WeakReference<Context> context;
|
||||
private boolean withImproveMap = true;
|
||||
private boolean withCopyrightSign = true;
|
||||
private boolean withTelemetryAttribution = false;
|
||||
private boolean withMapboxAttribution = true;
|
||||
private String[] attributionDataStringArray;
|
||||
|
||||
public Options(@NonNull Context context) {
|
||||
this.context = new WeakReference<>(context);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Options withAttributionData(String... attributionData) {
|
||||
this.attributionDataStringArray = attributionData;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Options withImproveMap(boolean withImproveMap) {
|
||||
this.withImproveMap = withImproveMap;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Options withCopyrightSign(boolean withCopyrightSign) {
|
||||
this.withCopyrightSign = withCopyrightSign;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Options withTelemetryAttribution(boolean withTelemetryAttribution) {
|
||||
this.withTelemetryAttribution = withTelemetryAttribution;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Options withMapboxAttribution(boolean withMapboxAttribution) {
|
||||
this.withMapboxAttribution = withMapboxAttribution;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public AttributionParser build() {
|
||||
if (attributionDataStringArray == null) {
|
||||
throw new IllegalStateException("Using builder without providing attribution data");
|
||||
}
|
||||
|
||||
String fullAttributionString = parseAttribution(attributionDataStringArray);
|
||||
AttributionParser attributionParser = new AttributionParser(
|
||||
context,
|
||||
fullAttributionString,
|
||||
withImproveMap,
|
||||
withCopyrightSign,
|
||||
withTelemetryAttribution,
|
||||
withMapboxAttribution
|
||||
);
|
||||
attributionParser.parse();
|
||||
return attributionParser;
|
||||
}
|
||||
|
||||
private String parseAttribution(String[] attribution) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String attr : attribution) {
|
||||
if (!attr.isEmpty()) {
|
||||
builder.append(attr);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains the Mapbox Maps Android Attribution API classes.
|
||||
*/
|
||||
package com.mapbox.mapboxsdk.attribution;
|
||||
@@ -0,0 +1,397 @@
|
||||
package com.mapbox.mapboxsdk.camera;
|
||||
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.FloatRange;
|
||||
import android.support.annotation.Keep;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.Size;
|
||||
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
import com.mapbox.mapboxsdk.constants.MapboxConstants;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.utils.MathUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Resembles the position, angle, zoom and tilt of the user's viewpoint.
|
||||
*/
|
||||
public final class CameraPosition implements Parcelable {
|
||||
|
||||
public static final CameraPosition DEFAULT = new CameraPosition(new LatLng(), 0, 0, 0, new double[] {0, 0, 0, 0});
|
||||
|
||||
public static final Parcelable.Creator<CameraPosition> CREATOR =
|
||||
new Parcelable.Creator<CameraPosition>() {
|
||||
public CameraPosition createFromParcel(Parcel in) {
|
||||
double bearing = in.readDouble();
|
||||
LatLng target = in.readParcelable(LatLng.class.getClassLoader());
|
||||
double tilt = in.readDouble();
|
||||
double zoom = in.readDouble();
|
||||
double[] padding = new double[4];
|
||||
in.readDoubleArray(padding);
|
||||
return new CameraPosition(target, zoom, tilt, bearing, padding);
|
||||
}
|
||||
|
||||
public CameraPosition[] newArray(int size) {
|
||||
return new CameraPosition[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Direction that the camera is pointing in, in degrees clockwise from north.
|
||||
*/
|
||||
@Keep
|
||||
public final double bearing;
|
||||
|
||||
/**
|
||||
* The location that the camera is pointing at.
|
||||
*/
|
||||
@Keep
|
||||
public final LatLng target;
|
||||
|
||||
/**
|
||||
* The angle, in degrees, of the camera angle from the nadir (directly facing the Earth).
|
||||
* See {@link Builder#tilt(double)} for details of restrictions on the range of values.
|
||||
*/
|
||||
@Keep
|
||||
public final double tilt;
|
||||
|
||||
/**
|
||||
* Zoom level near the center of the screen. See {@link Builder#zoom(double)} for the definition of the camera's
|
||||
* zoom level.
|
||||
*/
|
||||
@Keep
|
||||
public final double zoom;
|
||||
|
||||
/**
|
||||
* Padding in pixels. Specified in left, top, right, bottom order.
|
||||
* See {@link Builder#padding(double[])} for the definition of the camera's padding.
|
||||
*/
|
||||
@Keep
|
||||
public final double[] padding;
|
||||
|
||||
/**
|
||||
* Constructs a CameraPosition.
|
||||
*
|
||||
* @param target The target location to align with the center of the screen.
|
||||
* @param zoom Zoom level at target. See zoom(float) for details of restrictions.
|
||||
* @param tilt The camera angle, in degrees, from the nadir (directly down). See tilt(float)
|
||||
* for details of restrictions.
|
||||
* @param bearing Direction that the camera is pointing in, in degrees clockwise from north.
|
||||
* This value will be normalized to be within 0 degrees inclusive and 360 degrees
|
||||
* exclusive.
|
||||
* @throws NullPointerException if target is null
|
||||
* @throws IllegalArgumentException if tilt is outside the range of 0 to 90 degrees inclusive.
|
||||
* @deprecated use {@link CameraPosition#CameraPosition(LatLng, double, double, double, double[])} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
CameraPosition(LatLng target, double zoom, double tilt, double bearing) {
|
||||
this(target, zoom, tilt, bearing, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a CameraPosition.
|
||||
*
|
||||
* @param target The target location to align with the center of the screen.
|
||||
* @param zoom Zoom level at target. See zoom(float) for details of restrictions.
|
||||
* @param tilt The camera angle, in degrees, from the nadir (directly down). See tilt(float)
|
||||
* for details of restrictions.
|
||||
* @param bearing Direction that the camera is pointing in, in degrees clockwise from north.
|
||||
* This value will be normalized to be within 0 degrees inclusive and 360 degrees
|
||||
* exclusive.
|
||||
* @param padding Padding in pixels. Specified in left, top, right, bottom order.
|
||||
* @throws NullPointerException if target is null
|
||||
* @throws IllegalArgumentException if tilt is outside the range of 0 to 90 degrees inclusive.
|
||||
*/
|
||||
@Keep
|
||||
CameraPosition(LatLng target, double zoom, double tilt, double bearing, double[] padding) {
|
||||
this.target = target;
|
||||
this.bearing = bearing;
|
||||
this.tilt = tilt;
|
||||
this.zoom = zoom;
|
||||
this.padding = padding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the kinds of special objects contained in this Parcelable's
|
||||
* marshalled representation.
|
||||
*
|
||||
* @return integer 0.
|
||||
*/
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten this object in to a Parcel.
|
||||
*
|
||||
* @param out The Parcel in which the object should be written.
|
||||
* @param flags Additional flags about how the object should be written. May be 0 or
|
||||
* {@link #PARCELABLE_WRITE_RETURN_VALUE}.
|
||||
*/
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeDouble(bearing);
|
||||
out.writeParcelable(target, flags);
|
||||
out.writeDouble(tilt);
|
||||
out.writeDouble(zoom);
|
||||
out.writeDoubleArray(padding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String with the camera target, zoom, bearing and tilt.
|
||||
*
|
||||
* @return A String with CameraPosition information.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Target: " + target + ", Zoom:" + zoom + ", Bearing:" + bearing + ", Tilt:" + tilt
|
||||
+ ", Padding:" + Arrays.toString(padding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this {@link CameraPosition} object with another {@link CameraPosition} and
|
||||
* determines if their target, zoom, tilt, and bearing match.
|
||||
*
|
||||
* @param o Another {@link CameraPosition} to compare with this object.
|
||||
* @return True if target, zoom, tilt, and bearing match this {@link CameraPosition} object.
|
||||
* Else, false.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CameraPosition cameraPosition = (CameraPosition) o;
|
||||
if (target != null && !target.equals(cameraPosition.target)) {
|
||||
return false;
|
||||
} else if (zoom != cameraPosition.zoom) {
|
||||
return false;
|
||||
} else if (tilt != cameraPosition.tilt) {
|
||||
return false;
|
||||
} else if (bearing != cameraPosition.bearing) {
|
||||
return false;
|
||||
} else if (!Arrays.equals(padding, cameraPosition.padding)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives an integer which can be used as the bucket number for storing elements of the set/map.
|
||||
* This bucket number is the address of the element inside the set/map. There's no guarantee
|
||||
* that this hash value will be consistent between different Java implementations, or even
|
||||
* between different execution runs of the same program.
|
||||
*
|
||||
* @return integer value you can use for storing element.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(bearing);
|
||||
result = (int) (temp ^ (temp >>> 32));
|
||||
result = 31 * result + (target != null ? target.hashCode() : 0);
|
||||
temp = Double.doubleToLongBits(tilt);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(zoom);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
result = 31 * result + Arrays.hashCode(padding);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for composing CameraPosition objects.
|
||||
*/
|
||||
public static final class Builder {
|
||||
|
||||
private double bearing = -1;
|
||||
@Nullable
|
||||
private LatLng target = null;
|
||||
private double tilt = -1;
|
||||
private double zoom = -1;
|
||||
private double[] padding = null;
|
||||
|
||||
/**
|
||||
* Create an empty builder.
|
||||
*/
|
||||
public Builder() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a builder with an existing CameraPosition data.
|
||||
*
|
||||
* @param previous Existing CameraPosition values to use
|
||||
*/
|
||||
public Builder(@Nullable CameraPosition previous) {
|
||||
super();
|
||||
if (previous != null) {
|
||||
this.bearing = previous.bearing;
|
||||
this.target = previous.target;
|
||||
this.tilt = previous.tilt;
|
||||
this.zoom = previous.zoom;
|
||||
this.padding = previous.padding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a builder with an existing CameraPosition data.
|
||||
*
|
||||
* @param typedArray TypedArray containing attribute values
|
||||
*/
|
||||
public Builder(@Nullable TypedArray typedArray) {
|
||||
super();
|
||||
if (typedArray != null) {
|
||||
this.bearing = typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraBearing, 0.0f);
|
||||
double lat = typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraTargetLat, 0.0f);
|
||||
double lng = typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraTargetLng, 0.0f);
|
||||
this.target = new LatLng(lat, lng);
|
||||
this.tilt = typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraTilt, 0.0f);
|
||||
this.zoom = typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraZoom, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a builder from an existing CameraPositionUpdate update.
|
||||
*
|
||||
* @param update Update containing camera options
|
||||
*/
|
||||
public Builder(@Nullable CameraUpdateFactory.CameraPositionUpdate update) {
|
||||
super();
|
||||
if (update != null) {
|
||||
bearing = update.getBearing();
|
||||
target = update.getTarget();
|
||||
tilt = update.getTilt();
|
||||
zoom = update.getZoom();
|
||||
padding = update.getPadding();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create builder from an existing CameraPositionUpdate update.
|
||||
*
|
||||
* @param update Update containing camera options
|
||||
*/
|
||||
public Builder(@Nullable CameraUpdateFactory.ZoomUpdate update) {
|
||||
super();
|
||||
if (update != null) {
|
||||
this.zoom = update.getZoom();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the direction that the camera is pointing in, in degrees clockwise from north.
|
||||
*
|
||||
* @param bearing Bearing
|
||||
* @return this
|
||||
*/
|
||||
@NonNull
|
||||
public Builder bearing(double bearing) {
|
||||
double direction = bearing;
|
||||
|
||||
while (direction >= 360) {
|
||||
direction -= 360;
|
||||
}
|
||||
while (direction < 0) {
|
||||
direction += 360;
|
||||
}
|
||||
|
||||
this.bearing = direction;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location where the camera is pointing at.
|
||||
*
|
||||
* @param location target of the camera
|
||||
* @return this
|
||||
*/
|
||||
@NonNull
|
||||
public Builder target(LatLng location) {
|
||||
this.target = location;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tilt of the camera in degrees
|
||||
* <p>
|
||||
* value is clamped to {@link MapboxConstants#MINIMUM_TILT} and {@link MapboxConstants#MAXIMUM_TILT}.
|
||||
* </p>
|
||||
*
|
||||
* @param tilt Tilt value of the camera
|
||||
* @return this
|
||||
*/
|
||||
@NonNull
|
||||
public Builder tilt(@FloatRange(from = MapboxConstants.MINIMUM_TILT,
|
||||
to = MapboxConstants.MAXIMUM_TILT) double tilt) {
|
||||
this.tilt = MathUtils.clamp(tilt, MapboxConstants.MINIMUM_TILT, MapboxConstants.MAXIMUM_TILT);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the zoom of the camera
|
||||
* <p>
|
||||
* Zoom ranges from {@link MapboxConstants#MINIMUM_ZOOM} to {@link MapboxConstants#MAXIMUM_ZOOM}
|
||||
* </p>
|
||||
*
|
||||
* @param zoom Zoom value of the camera
|
||||
* @return this
|
||||
*/
|
||||
@NonNull
|
||||
public Builder zoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM,
|
||||
to = MapboxConstants.MAXIMUM_ZOOM) double zoom) {
|
||||
this.zoom = zoom;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Padding in pixels that shifts the viewport by the specified amount.
|
||||
* Applied padding is going to persist and impact following camera transformations.
|
||||
* <p>
|
||||
* Specified in left, top, right, bottom order.
|
||||
* </p>
|
||||
*
|
||||
* @param padding Camera padding
|
||||
* @return this
|
||||
*/
|
||||
@NonNull
|
||||
public Builder padding(@Size(4) double[] padding) {
|
||||
this.padding = padding;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Padding in pixels that shifts the viewport by the specified amount.
|
||||
* Applied padding is going to persist and impact following camera transformations.
|
||||
* <p>
|
||||
* Specified in left, top, right, bottom order.
|
||||
* </p>
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
@NonNull
|
||||
public Builder padding(double left, double top, double right, double bottom) {
|
||||
this.padding = new double[] {left, top, right, bottom};
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the CameraPosition.
|
||||
*
|
||||
* @return CameraPosition
|
||||
*/
|
||||
public CameraPosition build() {
|
||||
return new CameraPosition(target, zoom, tilt, bearing, padding);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.mapbox.mapboxsdk.camera;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
|
||||
/**
|
||||
* Interface definition for camera updates.
|
||||
*/
|
||||
public interface CameraUpdate {
|
||||
|
||||
/**
|
||||
* Get the camera position from the camera update.
|
||||
*
|
||||
* @param mapboxMap Map object to build the position from
|
||||
* @return the camera position from the implementing camera update
|
||||
*/
|
||||
@Nullable
|
||||
CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,637 @@
|
||||
package com.mapbox.mapboxsdk.camera;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
import com.mapbox.mapboxsdk.maps.Projection;
|
||||
import com.mapbox.mapboxsdk.maps.UiSettings;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Factory for creating CameraUpdate objects.
|
||||
*/
|
||||
public final class CameraUpdateFactory {
|
||||
|
||||
/**
|
||||
* Returns a CameraUpdate that moves the camera to a specified CameraPosition.
|
||||
*
|
||||
* @param cameraPosition Camera Position to change to
|
||||
* @return CameraUpdate Final Camera Position
|
||||
*/
|
||||
public static CameraUpdate newCameraPosition(@NonNull CameraPosition cameraPosition) {
|
||||
return new CameraPositionUpdate(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt,
|
||||
cameraPosition.zoom, cameraPosition.padding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CameraUpdate that moves the center of the screen to a latitude and longitude
|
||||
* specified by a LatLng object. This centers the camera on the LatLng object.
|
||||
*
|
||||
* @param latLng Target location to change to
|
||||
* @return CameraUpdate Final Camera Position
|
||||
*/
|
||||
public static CameraUpdate newLatLng(@NonNull LatLng latLng) {
|
||||
return new CameraPositionUpdate(-1, latLng, -1, -1, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CameraUpdate that transforms the camera such that the specified
|
||||
* latitude/longitude bounds are centered on screen at the greatest possible zoom level while maintaining
|
||||
* current camera position bearing and tilt values.
|
||||
* <p>
|
||||
* You can specify padding, in order to inset the bounding box from the map view's edges.
|
||||
* The padding will not persist and impact following camera transformations.
|
||||
* </p>
|
||||
*
|
||||
* @param bounds Bounds to match Camera position with
|
||||
* @param padding Padding added to the bounds
|
||||
* @return CameraUpdate Final Camera Position
|
||||
*/
|
||||
public static CameraUpdate newLatLngBounds(@NonNull LatLngBounds bounds, int padding) {
|
||||
return newLatLngBounds(bounds, padding, padding, padding, padding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CameraUpdate that transforms the camera such that the specified
|
||||
* latitude/longitude bounds are centered on screen at the greatest possible zoom level while using
|
||||
* provided bearing and tilt values.
|
||||
* <p>
|
||||
* You can specify padding, in order to inset the bounding box from the map view's edges.
|
||||
* The padding will not persist and impact following camera transformations.
|
||||
* </p>
|
||||
*
|
||||
* @param bounds Bounds to match Camera position with
|
||||
* @param bearing Bearing to take in account when generating the bounds
|
||||
* @param tilt Tilt to take in account when generating the bounds
|
||||
* @param padding Padding added to the bounds
|
||||
* @return CameraUpdate Final Camera Position
|
||||
*/
|
||||
public static CameraUpdate newLatLngBounds(@NonNull LatLngBounds bounds, double bearing, double tilt, int padding) {
|
||||
return newLatLngBounds(bounds, bearing, tilt, padding, padding, padding, padding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CameraUpdate that transforms the camera such that the specified
|
||||
* latitude/longitude bounds are centered on screen at the greatest possible zoom level while maintaining
|
||||
* current camera position bearing and tilt values.
|
||||
* <p>
|
||||
* You can specify padding, in order to inset the bounding box from the map view's edges.
|
||||
* The padding will not persist and impact following camera transformations.
|
||||
* </p>
|
||||
*
|
||||
* @param bounds Bounds to base the Camera position out of
|
||||
* @param paddingLeft Padding left of the bounds
|
||||
* @param paddingTop Padding top of the bounds
|
||||
* @param paddingRight Padding right of the bounds
|
||||
* @param paddingBottom Padding bottom of the bounds
|
||||
* @return CameraUpdate Final Camera Position
|
||||
*/
|
||||
public static CameraUpdate newLatLngBounds(@NonNull LatLngBounds bounds, int paddingLeft, int paddingTop,
|
||||
int paddingRight, int paddingBottom) {
|
||||
return new CameraBoundsUpdate(bounds, null, null, paddingLeft, paddingTop, paddingRight, paddingBottom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CameraUpdate that transforms the camera such that the specified
|
||||
* latitude/longitude bounds are centered on screen at the greatest possible zoom level while using
|
||||
* provided bearing and tilt values.
|
||||
* <p>
|
||||
* You can specify padding, in order to inset the bounding box from the map view's edges.
|
||||
* The padding will not persist and impact following camera transformations.
|
||||
* </p>
|
||||
*
|
||||
* @param bounds Bounds to base the Camera position out of
|
||||
* @param bearing Bearing to take in account when generating the bounds
|
||||
* @param tilt Tilt to take in account when generating the bounds
|
||||
* @param paddingLeft Padding left of the bounds
|
||||
* @param paddingTop Padding top of the bounds
|
||||
* @param paddingRight Padding right of the bounds
|
||||
* @param paddingBottom Padding bottom of the bounds
|
||||
* @return CameraUpdate Final Camera Position
|
||||
*/
|
||||
public static CameraUpdate newLatLngBounds(@NonNull LatLngBounds bounds, double bearing, double tilt,
|
||||
int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) {
|
||||
return new CameraBoundsUpdate(bounds, bearing, tilt, paddingLeft, paddingTop, paddingRight, paddingBottom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CameraUpdate that moves the center of the screen to a latitude and longitude
|
||||
* specified by a LatLng object taking the specified padding into account.
|
||||
*
|
||||
* @param latLng Target location to change to
|
||||
* @param zoom Zoom level to change to
|
||||
* @return CameraUpdate Final Camera Position
|
||||
*/
|
||||
public static CameraUpdate newLatLngZoom(@NonNull LatLng latLng, double zoom) {
|
||||
return new CameraPositionUpdate(-1, latLng, -1, zoom, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CameraUpdate that moves the center of the screen to a latitude and longitude
|
||||
* specified by a LatLng object, and moves to the given zoom level.
|
||||
* Applied padding is going to persist and impact following camera transformations.
|
||||
*
|
||||
* @param latLng Target location to change to
|
||||
* @param left Left padding
|
||||
* @param top Top padding
|
||||
* @param right Right padding
|
||||
* @param bottom Bottom padding
|
||||
* @return CameraUpdate Final Camera Position
|
||||
*/
|
||||
public static CameraUpdate newLatLngPadding(@NonNull LatLng latLng,
|
||||
double left, double top, double right, double bottom) {
|
||||
return new CameraPositionUpdate(-1, latLng, -1, -1, new double[] {left, top, right, bottom});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CameraUpdate that shifts the zoom level of the current camera viewpoint.
|
||||
*
|
||||
* @param amount Amount of zoom level to change with
|
||||
* @param focus Focus point of zoom
|
||||
* @return CameraUpdate Final Camera Position
|
||||
*/
|
||||
public static CameraUpdate zoomBy(double amount, Point focus) {
|
||||
return new ZoomUpdate(amount, focus.x, focus.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CameraUpdate that shifts the zoom level of the current camera viewpoint.
|
||||
*
|
||||
* @param amount Amount of zoom level to change with
|
||||
* @return CameraUpdate Final Camera Position
|
||||
*/
|
||||
public static CameraUpdate zoomBy(double amount) {
|
||||
return new ZoomUpdate(ZoomUpdate.ZOOM_BY, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CameraUpdate that zooms in on the map by moving the viewpoint's height closer to
|
||||
* the Earth's surface. The zoom increment is 1.0.
|
||||
*
|
||||
* @return CameraUpdate Final Camera Position
|
||||
*/
|
||||
public static CameraUpdate zoomIn() {
|
||||
return new ZoomUpdate(ZoomUpdate.ZOOM_IN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CameraUpdate that zooms out on the map by moving the viewpoint's height farther
|
||||
* away from the Earth's surface. The zoom increment is -1.0.
|
||||
*
|
||||
* @return CameraUpdate Final Camera Position
|
||||
*/
|
||||
public static CameraUpdate zoomOut() {
|
||||
return new ZoomUpdate(ZoomUpdate.ZOOM_OUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CameraUpdate that moves the camera viewpoint to a particular zoom level.
|
||||
*
|
||||
* @param zoom Zoom level to change to
|
||||
* @return CameraUpdate Final Camera Position
|
||||
*/
|
||||
public static CameraUpdate zoomTo(double zoom) {
|
||||
return new ZoomUpdate(ZoomUpdate.ZOOM_TO, zoom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CameraUpdate that moves the camera viewpoint to a particular bearing.
|
||||
*
|
||||
* @param bearing Bearing to change to
|
||||
* @return CameraUpdate Final Camera Position
|
||||
*/
|
||||
public static CameraUpdate bearingTo(double bearing) {
|
||||
return new CameraPositionUpdate(bearing, null, -1, -1, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CameraUpdate that moves the camera viewpoint to a particular tilt.
|
||||
*
|
||||
* @param tilt Tilt to change to
|
||||
* @return CameraUpdate Final Camera Position
|
||||
*/
|
||||
public static CameraUpdate tiltTo(double tilt) {
|
||||
return new CameraPositionUpdate(-1, null, tilt, -1, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CameraUpdate that when animated changes the camera padding.
|
||||
* Applied padding is going to persist and impact following camera transformations.
|
||||
* <p>
|
||||
* Specified in left, top, right, bottom order.
|
||||
* </p>
|
||||
*
|
||||
* @param padding Padding to change to
|
||||
* @return CameraUpdate Final Camera Position
|
||||
*/
|
||||
public static CameraUpdate paddingTo(double[] padding) {
|
||||
return new CameraPositionUpdate(-1, null, -1, -1, padding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CameraUpdate that when animated changes the camera padding.
|
||||
* Applied padding is going to persist and impact following camera transformations.
|
||||
* <p>
|
||||
* Specified in left, top, right, bottom order.
|
||||
* </p>
|
||||
*
|
||||
* @return CameraUpdate Final Camera Position
|
||||
*/
|
||||
public static CameraUpdate paddingTo(double left, double top, double right, double bottom) {
|
||||
return paddingTo(new double[] {left, top, right, bottom});
|
||||
}
|
||||
|
||||
//
|
||||
// CameraUpdate types
|
||||
//
|
||||
|
||||
static final class CameraPositionUpdate implements CameraUpdate {
|
||||
|
||||
private final double bearing;
|
||||
private final LatLng target;
|
||||
private final double tilt;
|
||||
private final double zoom;
|
||||
private final double[] padding;
|
||||
|
||||
CameraPositionUpdate(double bearing, LatLng target, double tilt, double zoom, double[] padding) {
|
||||
this.bearing = bearing;
|
||||
this.target = target;
|
||||
this.tilt = tilt;
|
||||
this.zoom = zoom;
|
||||
this.padding = padding;
|
||||
}
|
||||
|
||||
public LatLng getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public double getBearing() {
|
||||
return bearing;
|
||||
}
|
||||
|
||||
public double getTilt() {
|
||||
return tilt;
|
||||
}
|
||||
|
||||
public double getZoom() {
|
||||
return zoom;
|
||||
}
|
||||
|
||||
public double[] getPadding() {
|
||||
return padding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) {
|
||||
if (target == null) {
|
||||
CameraPosition previousPosition = mapboxMap.getCameraPosition();
|
||||
return new CameraPosition.Builder(this)
|
||||
.target(previousPosition.target)
|
||||
.build();
|
||||
}
|
||||
return new CameraPosition.Builder(this).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CameraPositionUpdate that = (CameraPositionUpdate) o;
|
||||
|
||||
if (Double.compare(that.bearing, bearing) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (Double.compare(that.tilt, tilt) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (Double.compare(that.zoom, zoom) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (target != null ? !target.equals(that.target) : that.target != null) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.equals(padding, that.padding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(bearing);
|
||||
result = (int) (temp ^ (temp >>> 32));
|
||||
result = 31 * result + (target != null ? target.hashCode() : 0);
|
||||
temp = Double.doubleToLongBits(tilt);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(zoom);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
result = 31 * result + Arrays.hashCode(padding);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CameraPositionUpdate{"
|
||||
+ "bearing=" + bearing
|
||||
+ ", target=" + target
|
||||
+ ", tilt=" + tilt
|
||||
+ ", zoom=" + zoom
|
||||
+ ", padding=" + Arrays.toString(padding)
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
static final class CameraBoundsUpdate implements CameraUpdate {
|
||||
|
||||
private final LatLngBounds bounds;
|
||||
private final int[] padding;
|
||||
private final Double bearing;
|
||||
private final Double tilt;
|
||||
|
||||
CameraBoundsUpdate(LatLngBounds bounds, Double bearing, Double tilt, int[] padding) {
|
||||
this.bounds = bounds;
|
||||
this.padding = padding;
|
||||
this.bearing = bearing;
|
||||
this.tilt = tilt;
|
||||
}
|
||||
|
||||
CameraBoundsUpdate(LatLngBounds bounds, Double bearing, Double tilt, int paddingLeft,
|
||||
int paddingTop, int paddingRight, int paddingBottom) {
|
||||
this(bounds, bearing, tilt, new int[] {paddingLeft, paddingTop, paddingRight, paddingBottom});
|
||||
}
|
||||
|
||||
public LatLngBounds getBounds() {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
public int[] getPadding() {
|
||||
return padding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) {
|
||||
if (bearing == null && tilt == null) {
|
||||
// use current camera position tilt and bearing
|
||||
return mapboxMap.getCameraForLatLngBounds(bounds, padding);
|
||||
} else {
|
||||
// use provided tilt and bearing
|
||||
assert bearing != null;
|
||||
assert tilt != null;
|
||||
return mapboxMap.getCameraForLatLngBounds(bounds, padding, bearing, tilt);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CameraBoundsUpdate that = (CameraBoundsUpdate) o;
|
||||
|
||||
if (!bounds.equals(that.bounds)) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.equals(padding, that.padding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = bounds.hashCode();
|
||||
result = 31 * result + Arrays.hashCode(padding);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CameraBoundsUpdate{"
|
||||
+ "bounds=" + bounds
|
||||
+ ", padding=" + Arrays.toString(padding)
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
static final class CameraMoveUpdate implements CameraUpdate {
|
||||
|
||||
private float x;
|
||||
private float y;
|
||||
|
||||
CameraMoveUpdate(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) {
|
||||
UiSettings uiSettings = mapboxMap.getUiSettings();
|
||||
Projection projection = mapboxMap.getProjection();
|
||||
// Calculate the new center point
|
||||
float viewPortWidth = uiSettings.getWidth();
|
||||
float viewPortHeight = uiSettings.getHeight();
|
||||
int[] padding = mapboxMap.getPadding();
|
||||
|
||||
// we inverse the map padding, is reapplied when using moveTo/easeTo or animateTo
|
||||
PointF targetPoint = new PointF(
|
||||
(viewPortWidth - padding[0] + padding[1]) / 2 + x,
|
||||
(viewPortHeight + padding[1] - padding[3]) / 2 + y
|
||||
);
|
||||
|
||||
LatLng latLng = projection.fromScreenLocation(targetPoint);
|
||||
CameraPosition previousPosition = mapboxMap.getCameraPosition();
|
||||
CameraPosition position =
|
||||
new CameraPosition.Builder()
|
||||
.target(latLng)
|
||||
.zoom(previousPosition.zoom)
|
||||
.tilt(previousPosition.tilt)
|
||||
.bearing(previousPosition.bearing)
|
||||
.build();
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CameraMoveUpdate that = (CameraMoveUpdate) o;
|
||||
|
||||
if (Float.compare(that.x, x) != 0) {
|
||||
return false;
|
||||
}
|
||||
return Float.compare(that.y, y) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (x != +0.0f ? Float.floatToIntBits(x) : 0);
|
||||
result = 31 * result + (y != +0.0f ? Float.floatToIntBits(y) : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CameraMoveUpdate{"
|
||||
+ "x=" + x
|
||||
+ ", y=" + y
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
static final class ZoomUpdate implements CameraUpdate {
|
||||
|
||||
@IntDef( {ZOOM_IN, ZOOM_OUT, ZOOM_BY, ZOOM_TO, ZOOM_TO_POINT})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface Type {
|
||||
}
|
||||
|
||||
static final int ZOOM_IN = 0;
|
||||
static final int ZOOM_OUT = 1;
|
||||
static final int ZOOM_BY = 2;
|
||||
static final int ZOOM_TO = 3;
|
||||
static final int ZOOM_TO_POINT = 4;
|
||||
|
||||
@Type
|
||||
private final int type;
|
||||
private final double zoom;
|
||||
private float x;
|
||||
private float y;
|
||||
|
||||
ZoomUpdate(@Type int type) {
|
||||
this.type = type;
|
||||
this.zoom = 0;
|
||||
}
|
||||
|
||||
ZoomUpdate(@Type int type, double zoom) {
|
||||
this.type = type;
|
||||
this.zoom = zoom;
|
||||
}
|
||||
|
||||
ZoomUpdate(double zoom, float x, float y) {
|
||||
this.type = ZOOM_TO_POINT;
|
||||
this.zoom = zoom;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public double getZoom() {
|
||||
return zoom;
|
||||
}
|
||||
|
||||
@Type
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
double transformZoom(double currentZoom) {
|
||||
switch (getType()) {
|
||||
case CameraUpdateFactory.ZoomUpdate.ZOOM_IN:
|
||||
currentZoom++;
|
||||
break;
|
||||
case CameraUpdateFactory.ZoomUpdate.ZOOM_OUT:
|
||||
currentZoom--;
|
||||
if (currentZoom < 0) {
|
||||
currentZoom = 0;
|
||||
}
|
||||
break;
|
||||
case CameraUpdateFactory.ZoomUpdate.ZOOM_TO:
|
||||
currentZoom = getZoom();
|
||||
break;
|
||||
case CameraUpdateFactory.ZoomUpdate.ZOOM_BY:
|
||||
currentZoom = currentZoom + getZoom();
|
||||
break;
|
||||
case CameraUpdateFactory.ZoomUpdate.ZOOM_TO_POINT:
|
||||
currentZoom = currentZoom + getZoom();
|
||||
break;
|
||||
}
|
||||
return currentZoom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) {
|
||||
CameraPosition cameraPosition = mapboxMap.getCameraPosition();
|
||||
if (getType() != CameraUpdateFactory.ZoomUpdate.ZOOM_TO_POINT) {
|
||||
return new CameraPosition.Builder(cameraPosition)
|
||||
.zoom(transformZoom(cameraPosition.zoom))
|
||||
.build();
|
||||
} else {
|
||||
return new CameraPosition.Builder(cameraPosition)
|
||||
.zoom(transformZoom(cameraPosition.zoom))
|
||||
.target(mapboxMap.getProjection().fromScreenLocation(new PointF(getX(), getY())))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ZoomUpdate that = (ZoomUpdate) o;
|
||||
|
||||
if (type != that.type) {
|
||||
return false;
|
||||
}
|
||||
if (Double.compare(that.zoom, zoom) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (Float.compare(that.x, x) != 0) {
|
||||
return false;
|
||||
}
|
||||
return Float.compare(that.y, y) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
result = type;
|
||||
temp = Double.doubleToLongBits(zoom);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
result = 31 * result + (x != +0.0f ? Float.floatToIntBits(x) : 0);
|
||||
result = 31 * result + (y != +0.0f ? Float.floatToIntBits(y) : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ZoomUpdate{"
|
||||
+ "type=" + type
|
||||
+ ", zoom=" + zoom
|
||||
+ ", x=" + x
|
||||
+ ", y=" + y
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains the Mapbox Maps Android Camera API classes.
|
||||
*/
|
||||
package com.mapbox.mapboxsdk.camera;
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.mapbox.mapboxsdk.constants;
|
||||
|
||||
/**
|
||||
* Contains constants used throughout the sdk classes.
|
||||
*/
|
||||
public class GeometryConstants {
|
||||
|
||||
/**
|
||||
* The <a href='http://en.wikipedia.org/wiki/Earth_radius#Equatorial_radius'>equatorial radius</a>
|
||||
* value in meters
|
||||
*/
|
||||
public static final int RADIUS_EARTH_METERS = 6378137;
|
||||
|
||||
/**
|
||||
* This constant represents the lowest longitude value available to represent a wrapped geolocation.
|
||||
*/
|
||||
public static final double MIN_WRAP_LONGITUDE = -180;
|
||||
|
||||
/**
|
||||
* This constant represents the highest longitude value available to represent a wrapped geolocation.
|
||||
*/
|
||||
public static final double MAX_WRAP_LONGITUDE = 180;
|
||||
|
||||
/**
|
||||
* This constant represents the lowest longitude value available to represent a geolocation.
|
||||
*/
|
||||
public static final double MIN_LONGITUDE = -Double.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* This constant represents the highest longitude value available to represent a geolocation.
|
||||
*/
|
||||
public static final double MAX_LONGITUDE = Double.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* This constant represents the lowest latitude value available to represent a geolocation.
|
||||
*/
|
||||
public static final double MIN_LATITUDE = -90;
|
||||
|
||||
/**
|
||||
* This constant represents the latitude span when representing a geolocation.
|
||||
*/
|
||||
public static final double LATITUDE_SPAN = 180;
|
||||
|
||||
/**
|
||||
* This constant represents the longitude span when representing a geolocation.
|
||||
*/
|
||||
public static final double LONGITUDE_SPAN = 360;
|
||||
|
||||
/**
|
||||
* This constant represents the highest latitude value available to represent a geolocation.
|
||||
*/
|
||||
public static final double MAX_LATITUDE = 90;
|
||||
|
||||
/**
|
||||
* Maximum latitude value in Mercator projection.
|
||||
*/
|
||||
public static final double MAX_MERCATOR_LATITUDE = 85.05112877980659;
|
||||
|
||||
/**
|
||||
* Minimum latitude value in Mercator projection.
|
||||
*/
|
||||
public static final double MIN_MERCATOR_LATITUDE = -85.05112877980659;
|
||||
|
||||
private GeometryConstants() {
|
||||
// Private constructor to prevent initializing of this class.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package com.mapbox.mapboxsdk.constants;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* MapboxConstants exposes Mapbox related constants
|
||||
*/
|
||||
public class MapboxConstants {
|
||||
|
||||
/**
|
||||
* Default Locale for data processing (ex: String.toLowerCase(MAPBOX_LOCALE, "foo"))
|
||||
*/
|
||||
public static final Locale MAPBOX_LOCALE = Locale.US;
|
||||
|
||||
/**
|
||||
* The name of the desired preferences file for Android's SharedPreferences.
|
||||
*/
|
||||
public static final String MAPBOX_SHARED_PREFERENCES = "MapboxSharedPreferences";
|
||||
|
||||
/**
|
||||
* Key used to switch storage to external in AndroidManifest.xml
|
||||
*/
|
||||
public static final String KEY_META_DATA_SET_STORAGE_EXTERNAL = "com.mapbox.SetStorageExternal";
|
||||
|
||||
/**
|
||||
* Default value for KEY_META_DATA_SET_STORAGE_EXTERNAL (default is internal storage)
|
||||
*/
|
||||
public static final boolean DEFAULT_SET_STORAGE_EXTERNAL = false;
|
||||
|
||||
/**
|
||||
* Key used to switch Tile Download Measuring on/off in AndroidManifest.xml
|
||||
*/
|
||||
public static final String KEY_META_DATA_MEASURE_TILE_DOWNLOAD_ON = "com.mapbox.MeasureTileDownloadOn";
|
||||
|
||||
/**
|
||||
* Default value for KEY_META_DATA_MEASURE_TILE_DOWNLOAD_ON (default is off)
|
||||
*/
|
||||
public static final boolean DEFAULT_MEASURE_TILE_DOWNLOAD_ON = false;
|
||||
|
||||
public static final String KEY_PREFERENCE_SKU_TOKEN = "com.mapbox.mapboxsdk.accounts.skutoken";
|
||||
|
||||
public static final String KEY_META_DATA_MANAGE_SKU_TOKEN = "com.mapbox.ManageSkuToken";
|
||||
|
||||
public static final boolean DEFAULT_MANAGE_SKU_TOKEN = true;
|
||||
|
||||
/**
|
||||
* Default value for font fallback for local ideograph fonts
|
||||
*/
|
||||
public static final String DEFAULT_FONT = "sans-serif";
|
||||
|
||||
/**
|
||||
* Unmeasured state
|
||||
*/
|
||||
public static final float UNMEASURED = -1f;
|
||||
|
||||
/**
|
||||
* Default animation time
|
||||
*/
|
||||
public static final int ANIMATION_DURATION = 300;
|
||||
|
||||
/**
|
||||
* Default short animation time
|
||||
*/
|
||||
public static final int ANIMATION_DURATION_SHORT = 150;
|
||||
|
||||
/**
|
||||
* Animation time of a fling gesture
|
||||
*/
|
||||
public static final long ANIMATION_DURATION_FLING_BASE = ANIMATION_DURATION_SHORT;
|
||||
|
||||
/**
|
||||
* Velocity threshold for a fling gesture
|
||||
*/
|
||||
public static final long VELOCITY_THRESHOLD_IGNORE_FLING = 1000;
|
||||
|
||||
/**
|
||||
* Value by which the default rotation threshold will be increased when scaling
|
||||
*
|
||||
* @deprecated unused, see {@link com.mapbox.mapboxsdk.maps.UiSettings#setDisableRotateWhenScaling(boolean)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static final float ROTATION_THRESHOLD_INCREASE_WHEN_SCALING = 25f;
|
||||
|
||||
/**
|
||||
* Maximum absolute zoom change for multi-pointer scale velocity animation
|
||||
*/
|
||||
public static final double MAX_ABSOLUTE_SCALE_VELOCITY_CHANGE = 2.5;
|
||||
|
||||
/**
|
||||
* Maximum possible zoom change during the quick zoom gesture executed across the whole screen
|
||||
*/
|
||||
public static final double QUICK_ZOOM_MAX_ZOOM_CHANGE = 4.0;
|
||||
|
||||
/**
|
||||
* Scale velocity animation duration multiplier.
|
||||
*/
|
||||
public static final double SCALE_VELOCITY_ANIMATION_DURATION_MULTIPLIER = 150;
|
||||
|
||||
/**
|
||||
* Minimum angular velocity for rotation animation
|
||||
*
|
||||
* @deprecated unused, see {@link #ROTATE_VELOCITY_RATIO_THRESHOLD}
|
||||
*/
|
||||
@Deprecated
|
||||
public static final float MINIMUM_ANGULAR_VELOCITY = 1.5f;
|
||||
|
||||
/**
|
||||
* Last scale span delta to XY velocity ratio required to execute scale velocity animation.
|
||||
*/
|
||||
public static final double SCALE_VELOCITY_RATIO_THRESHOLD = 4 * 1e-3;
|
||||
|
||||
/**
|
||||
* Last rotation delta to XY velocity ratio required to execute rotation velocity animation.
|
||||
*/
|
||||
public static final double ROTATE_VELOCITY_RATIO_THRESHOLD = 2.2 * 1e-4;
|
||||
|
||||
/**
|
||||
* Time within which user needs to lift fingers for velocity animation to start.
|
||||
*/
|
||||
public static final long SCHEDULED_ANIMATION_TIMEOUT = 150L;
|
||||
|
||||
/**
|
||||
* Maximum angular velocity for rotation animation
|
||||
*/
|
||||
public static final float MAXIMUM_ANGULAR_VELOCITY = 30f;
|
||||
|
||||
/**
|
||||
* Factor to calculate tilt change based on pixel change during shove gesture.
|
||||
*/
|
||||
public static final float SHOVE_PIXEL_CHANGE_FACTOR = 0.1f;
|
||||
|
||||
/**
|
||||
* The currently supported minimum zoom level.
|
||||
*/
|
||||
public static final float MINIMUM_ZOOM = 0.0f;
|
||||
|
||||
/**
|
||||
* The currently supported maximum zoom level.
|
||||
*/
|
||||
public static final float MAXIMUM_ZOOM = 25.5f;
|
||||
|
||||
/**
|
||||
* The currently supported maximum tilt value.
|
||||
*/
|
||||
public static final double MAXIMUM_TILT = 60;
|
||||
|
||||
/**
|
||||
* The currently supported minimum tilt value.
|
||||
*/
|
||||
public static final double MINIMUM_TILT = 0;
|
||||
|
||||
/**
|
||||
* The currently supported maximum direction
|
||||
*/
|
||||
public static final double MAXIMUM_DIRECTION = 360;
|
||||
|
||||
/**
|
||||
* The currently supported minimum direction
|
||||
*/
|
||||
public static final double MINIMUM_DIRECTION = 0;
|
||||
|
||||
/**
|
||||
* The currently used minimum scale factor to clamp to when a quick zoom gesture occurs
|
||||
*
|
||||
* @deprecated unused
|
||||
*/
|
||||
@Deprecated
|
||||
public static final float MINIMUM_SCALE_FACTOR_CLAMP = 0.00f;
|
||||
|
||||
/**
|
||||
* The currently used maximum scale factor to clamp to when a quick zoom gesture occurs
|
||||
*
|
||||
* @deprecated unused
|
||||
*/
|
||||
@Deprecated
|
||||
public static final float MAXIMUM_SCALE_FACTOR_CLAMP = 0.15f;
|
||||
|
||||
/**
|
||||
* Zoom value multiplier for scale gestures.
|
||||
*/
|
||||
public static final float ZOOM_RATE = 0.65f;
|
||||
|
||||
/**
|
||||
* Fragment Argument Key for MapboxMapOptions
|
||||
*/
|
||||
public static final String FRAG_ARG_MAPBOXMAPOPTIONS = "MapboxMapOptions";
|
||||
|
||||
/**
|
||||
* Layer Id of annotations layer
|
||||
*/
|
||||
public static final String LAYER_ID_ANNOTATIONS = "com.mapbox.annotations.points";
|
||||
|
||||
// Save instance state keys
|
||||
public static final String STATE_HAS_SAVED_STATE = "mapbox_savedState";
|
||||
public static final String STATE_CAMERA_POSITION = "mapbox_cameraPosition";
|
||||
public static final String STATE_ZOOM_ENABLED = "mapbox_zoomEnabled";
|
||||
public static final String STATE_SCROLL_ENABLED = "mapbox_scrollEnabled";
|
||||
public static final String STATE_ROTATE_ENABLED = "mapbox_rotateEnabled";
|
||||
public static final String STATE_TILT_ENABLED = "mapbox_tiltEnabled";
|
||||
public static final String STATE_DOUBLE_TAP_ENABLED = "mapbox_doubleTapEnabled";
|
||||
public static final String STATE_QUICK_ZOOM_ENABLED = "mapbox_quickZoom";
|
||||
public static final String STATE_ZOOM_RATE = "mapbox_zoomRate";
|
||||
public static final String STATE_DEBUG_ACTIVE = "mapbox_debugActive";
|
||||
public static final String STATE_COMPASS_ENABLED = "mapbox_compassEnabled";
|
||||
public static final String STATE_COMPASS_GRAVITY = "mapbox_compassGravity";
|
||||
public static final String STATE_COMPASS_MARGIN_LEFT = "mapbox_compassMarginLeft";
|
||||
public static final String STATE_COMPASS_MARGIN_TOP = "mapbox_compassMarginTop";
|
||||
public static final String STATE_COMPASS_MARGIN_RIGHT = "mapbox_compassMarginRight";
|
||||
public static final String STATE_COMPASS_MARGIN_BOTTOM = "mapbox_compassMarginBottom";
|
||||
public static final String STATE_COMPASS_FADE_WHEN_FACING_NORTH = "mapbox_compassFade";
|
||||
public static final String STATE_COMPASS_IMAGE_BITMAP = "mapbox_compassImage";
|
||||
public static final String STATE_LOGO_GRAVITY = "mapbox_logoGravity";
|
||||
public static final String STATE_LOGO_MARGIN_LEFT = "mapbox_logoMarginLeft";
|
||||
public static final String STATE_LOGO_MARGIN_TOP = "mapbox_logoMarginTop";
|
||||
public static final String STATE_LOGO_MARGIN_RIGHT = "mapbox_logoMarginRight";
|
||||
public static final String STATE_LOGO_MARGIN_BOTTOM = "mapbox_logoMarginBottom";
|
||||
public static final String STATE_LOGO_ENABLED = "mapbox_logoEnabled";
|
||||
public static final String STATE_ATTRIBUTION_GRAVITY = "mapbox_attrGravity";
|
||||
public static final String STATE_ATTRIBUTION_MARGIN_LEFT = "mapbox_attrMarginLeft";
|
||||
public static final String STATE_ATTRIBUTION_MARGIN_TOP = "mapbox_attrMarginTop";
|
||||
public static final String STATE_ATTRIBUTION_MARGIN_RIGHT = "mapbox_attrMarginRight";
|
||||
public static final String STATE_ATTRIBUTION_MARGIN_BOTTOM = "mapbox_atrrMarginBottom";
|
||||
public static final String STATE_ATTRIBUTION_ENABLED = "mapbox_atrrEnabled";
|
||||
public static final String STATE_DESELECT_MARKER_ON_TAP = "mapbox_deselectMarkerOnTap";
|
||||
public static final String STATE_USER_FOCAL_POINT = "mapbox_userFocalPoint";
|
||||
public static final String STATE_SCALE_ANIMATION_ENABLED = "mapbox_scaleAnimationEnabled";
|
||||
public static final String STATE_ROTATE_ANIMATION_ENABLED = "mapbox_rotateAnimationEnabled";
|
||||
public static final String STATE_FLING_ANIMATION_ENABLED = "mapbox_flingAnimationEnabled";
|
||||
public static final String STATE_INCREASE_ROTATE_THRESHOLD = "mapbox_increaseRotateThreshold";
|
||||
public static final String STATE_DISABLE_ROTATE_WHEN_SCALING = "mapbox_disableRotateWhenScaling";
|
||||
public static final String STATE_INCREASE_SCALE_THRESHOLD = "mapbox_increaseScaleThreshold";
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.mapbox.mapboxsdk.constants;
|
||||
|
||||
@Deprecated
|
||||
public class TelemetryConstants {
|
||||
|
||||
public static final String TWO_FINGER_TAP = "TwoFingerTap";
|
||||
public static final String DOUBLE_TAP = "DoubleTap";
|
||||
public static final String SINGLE_TAP = "SingleTap";
|
||||
public static final String PAN = "Pan";
|
||||
public static final String PINCH = "Pinch";
|
||||
public static final String ROTATION = "Rotation";
|
||||
public static final String PITCH = "Pitch";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains the Mapbox Maps Android Constant API classes.
|
||||
*/
|
||||
package com.mapbox.mapboxsdk.constants;
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.mapbox.mapboxsdk.exceptions;
|
||||
|
||||
public class CalledFromWorkerThreadException extends RuntimeException {
|
||||
|
||||
public CalledFromWorkerThreadException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.mapbox.mapboxsdk.exceptions;
|
||||
|
||||
/**
|
||||
* A ConversionException is thrown when a conversion failed to execute.
|
||||
*/
|
||||
public class ConversionException extends RuntimeException {
|
||||
|
||||
public ConversionException(String detailMessage) {
|
||||
super(detailMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.mapbox.mapboxsdk.exceptions;
|
||||
|
||||
import com.mapbox.mapboxsdk.annotations.Icon;
|
||||
import com.mapbox.mapboxsdk.annotations.Marker;
|
||||
import com.mapbox.mapboxsdk.maps.MapView;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* An IconBitmapChangedException is thrown by MapView when a Marker is added
|
||||
* that has an Icon with a Bitmap that has been modified since the creation of the Icon.
|
||||
* </p>
|
||||
* You cannot modify a {@code Icon} after it has been added to the map in a {@code Marker}
|
||||
*
|
||||
* @see MapView
|
||||
* @see Icon
|
||||
* @see Marker
|
||||
*/
|
||||
public class IconBitmapChangedException extends RuntimeException {
|
||||
|
||||
public IconBitmapChangedException() {
|
||||
super("The added Marker has an Icon with a bitmap that has been modified. An Icon cannot be modified"
|
||||
+ "after it has been added to the map in a Marker.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.mapbox.mapboxsdk.exceptions;
|
||||
|
||||
/**
|
||||
* An InvalidLatLngBoundsException is thrown by LatLngBounds
|
||||
* when there aren't enough LatLng to create a bounds.
|
||||
*/
|
||||
public class InvalidLatLngBoundsException extends RuntimeException {
|
||||
|
||||
public InvalidLatLngBoundsException(int latLngsListSize) {
|
||||
super("Cannot create a LatLngBounds from " + latLngsListSize + " items");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mapbox.mapboxsdk.exceptions;
|
||||
|
||||
/**
|
||||
* A InvalidMarkerPositionException is thrown when a Marker object is created with an invalid LatLng position.
|
||||
*/
|
||||
public class InvalidMarkerPositionException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Creates a invalid marker position exception thrown when a Marker object is created with an invalid LatLng position.
|
||||
*/
|
||||
public InvalidMarkerPositionException() {
|
||||
super("Adding an invalid Marker to a Map. "
|
||||
+ "Missing the required position field. "
|
||||
+ "Provide a non null LatLng as position to the Marker.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.mapbox.mapboxsdk.exceptions;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* A MapboxConfigurationException is thrown by MapboxMap when the SDK hasn't been properly initialised.
|
||||
* <p>
|
||||
* This occurs either when {@link com.mapbox.mapboxsdk.Mapbox} is not correctly initialised or the provided access token
|
||||
* through {@link com.mapbox.mapboxsdk.Mapbox#getInstance(Context, String)} isn't valid.
|
||||
* </p>
|
||||
*
|
||||
* @see com.mapbox.mapboxsdk.Mapbox#getInstance(Context, String)
|
||||
*/
|
||||
public class MapboxConfigurationException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Creates a Mapbox configuration exception thrown by MapboxMap when the SDK hasn't been properly initialised.
|
||||
*/
|
||||
public MapboxConfigurationException() {
|
||||
super("\nUsing MapView requires calling Mapbox.getInstance(Context context, String accessToken) before "
|
||||
+ "inflating or creating the view. The access token parameter is required when using a Mapbox service."
|
||||
+ "\nPlease see https://www.mapbox.com/help/create-api-access-token/ to learn how to create one."
|
||||
+ "\nMore information in this guide https://www.mapbox.com/help/first-steps-android-sdk/#access-tokens.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Mapbox configuration exception thrown by MapboxMap when the SDK hasn't been properly initialised.
|
||||
*/
|
||||
public MapboxConfigurationException(@NonNull String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mapbox.mapboxsdk.exceptions;
|
||||
|
||||
import com.mapbox.mapboxsdk.annotations.IconFactory;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A TooManyIconsException is thrown by IconFactory when it
|
||||
* cannot create a Icon because there are already too many icons created.
|
||||
* </p>
|
||||
* You should try to reuse Icon objects whenever possible.
|
||||
*
|
||||
* @see IconFactory
|
||||
*/
|
||||
public class TooManyIconsException extends RuntimeException {
|
||||
|
||||
public TooManyIconsException() {
|
||||
super("Cannot create an Icon because there are already too many. Try reusing Icon objects whenever possible.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains the Mapbox Maps Android Exception API classes.
|
||||
*/
|
||||
package com.mapbox.mapboxsdk.exceptions;
|
||||
@@ -0,0 +1,334 @@
|
||||
package com.mapbox.mapboxsdk.geometry;
|
||||
|
||||
import android.location.Location;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.FloatRange;
|
||||
import android.support.annotation.Keep;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.mapbox.geojson.Point;
|
||||
import com.mapbox.mapboxsdk.constants.GeometryConstants;
|
||||
import com.mapbox.turf.TurfMeasurement;
|
||||
|
||||
import static com.mapbox.turf.TurfConstants.UNIT_METRES;
|
||||
|
||||
|
||||
/**
|
||||
* A geographical location which contains a single latitude, longitude pair, with
|
||||
* optional altitude.
|
||||
* <p>
|
||||
* Latitude and longitude are expressed as decimal degrees
|
||||
* in the WGS84 datum. By default, altitude is 0.0, or sea level.
|
||||
* </p>
|
||||
* <p>
|
||||
* Mapbox GL displays maps in the Mercator Projection and projects geographical
|
||||
* data automatically, so all data enters in the WGS84 datum.
|
||||
* </p>
|
||||
*/
|
||||
public class LatLng implements Parcelable {
|
||||
|
||||
/**
|
||||
* Inner class responsible for recreating Parcels into objects.
|
||||
*/
|
||||
public static final Parcelable.Creator<LatLng> CREATOR = new Parcelable.Creator<LatLng>() {
|
||||
public LatLng createFromParcel(@NonNull Parcel in) {
|
||||
return new LatLng(in);
|
||||
}
|
||||
|
||||
public LatLng[] newArray(int size) {
|
||||
return new LatLng[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Keep
|
||||
private double latitude;
|
||||
@Keep
|
||||
private double longitude;
|
||||
private double altitude = 0.0;
|
||||
|
||||
/**
|
||||
* Construct a new latitude, longitude point at (0, 0)
|
||||
*/
|
||||
public LatLng() {
|
||||
this.latitude = 0.0;
|
||||
this.longitude = 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new latitude, longitude point given double arguments
|
||||
*
|
||||
* @param latitude Latitude in degrees
|
||||
* @param longitude Longitude in degrees
|
||||
*/
|
||||
@Keep
|
||||
public LatLng(double latitude, double longitude) {
|
||||
setLatitude(latitude);
|
||||
setLongitude(longitude);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new latitude, longitude, altitude point given double arguments
|
||||
*
|
||||
* @param latitude Latitude in degrees
|
||||
* @param longitude Longitude in degress
|
||||
* @param altitude Altitude in meters
|
||||
*/
|
||||
public LatLng(double latitude, double longitude, double altitude) {
|
||||
setLatitude(latitude);
|
||||
setLongitude(longitude);
|
||||
setAltitude(altitude);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new latitude, longitude, altitude point given location argument
|
||||
*
|
||||
* @param location Android Location
|
||||
*/
|
||||
public LatLng(Location location) {
|
||||
this(location.getLatitude(), location.getLongitude(), location.getAltitude());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new latitude, longitude, altitude point given another latitude, longitude, altitude point.
|
||||
*
|
||||
* @param latLng LatLng to be cloned.
|
||||
*/
|
||||
public LatLng(LatLng latLng) {
|
||||
this.latitude = latLng.latitude;
|
||||
this.longitude = latLng.longitude;
|
||||
this.altitude = latLng.altitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new latitude, longitude, altitude tuple given a parcel.
|
||||
*
|
||||
* @param in the parcel containing the latitude, longitude, altitude values
|
||||
*/
|
||||
protected LatLng(Parcel in) {
|
||||
setLatitude(in.readDouble());
|
||||
setLongitude(in.readDouble());
|
||||
setAltitude(in.readDouble());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the latitude, in degrees.
|
||||
* <p>
|
||||
* This value is in the range of [-90, 90], see {@link GeometryConstants#MIN_LATITUDE} and
|
||||
* {@link GeometryConstants#MAX_LATITUDE}
|
||||
* </p>
|
||||
*
|
||||
* @param latitude the latitude value in degrees
|
||||
* @see GeometryConstants#MIN_LATITUDE
|
||||
* @see GeometryConstants#MAX_LATITUDE
|
||||
*/
|
||||
public void setLatitude(
|
||||
@FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double latitude) {
|
||||
if (Double.isNaN(latitude)) {
|
||||
throw new IllegalArgumentException("latitude must not be NaN");
|
||||
}
|
||||
if (Math.abs(latitude) > GeometryConstants.MAX_LATITUDE) {
|
||||
throw new IllegalArgumentException("latitude must be between -90 and 90");
|
||||
}
|
||||
this.latitude = latitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latitude, in degrees.
|
||||
* <p>
|
||||
* This value is in the range of [-90, 90], see {@link GeometryConstants#MIN_LATITUDE} and
|
||||
* {@link GeometryConstants#MAX_LATITUDE}
|
||||
* </p>
|
||||
*
|
||||
* @return the latitude value in degrees
|
||||
* @see GeometryConstants#MIN_LATITUDE
|
||||
* @see GeometryConstants#MAX_LATITUDE
|
||||
*/
|
||||
public double getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the longitude, in degrees.
|
||||
* <p>
|
||||
* This value is in the range of [-180, 180], see {@link GeometryConstants#MIN_LONGITUDE} and
|
||||
* {@link GeometryConstants#MAX_LONGITUDE}
|
||||
* </p>
|
||||
*
|
||||
* @param longitude the longitude value in degrees
|
||||
* @see GeometryConstants#MIN_LONGITUDE
|
||||
* @see GeometryConstants#MAX_LONGITUDE
|
||||
*/
|
||||
public void setLongitude(@FloatRange(from = GeometryConstants.MIN_LONGITUDE, to = GeometryConstants.MAX_LONGITUDE)
|
||||
double longitude) {
|
||||
if (Double.isNaN(longitude)) {
|
||||
throw new IllegalArgumentException("longitude must not be NaN");
|
||||
}
|
||||
if (Double.isInfinite(longitude)) {
|
||||
throw new IllegalArgumentException("longitude must not be infinite");
|
||||
}
|
||||
this.longitude = longitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the longitude, in degrees.
|
||||
* <p>
|
||||
* This value is in the range of [-180, 180], see {@link GeometryConstants#MIN_LONGITUDE} and
|
||||
* {@link GeometryConstants#MAX_LONGITUDE}
|
||||
* </p>
|
||||
*
|
||||
* @return the longitude value in degrees
|
||||
* @see GeometryConstants#MIN_LONGITUDE
|
||||
* @see GeometryConstants#MAX_LONGITUDE
|
||||
*/
|
||||
public double getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the altitude, in meters.
|
||||
*
|
||||
* @param altitude the altitude in meters
|
||||
*/
|
||||
public void setAltitude(double altitude) {
|
||||
this.altitude = altitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the altitude, in meters.
|
||||
*
|
||||
* @return the altitude value in meters
|
||||
*/
|
||||
public double getAltitude() {
|
||||
return altitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new LatLng object with a wrapped Longitude. This allows original data object
|
||||
* to remain unchanged.
|
||||
*
|
||||
* @return new LatLng object with wrapped Longitude
|
||||
*/
|
||||
@NonNull
|
||||
public LatLng wrap() {
|
||||
return new LatLng(latitude, wrap(longitude,
|
||||
GeometryConstants.MIN_WRAP_LONGITUDE, GeometryConstants.MAX_WRAP_LONGITUDE)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constrains value to the given range (including min & max) via modular arithmetic.
|
||||
* <p>
|
||||
* Same formula as used in Core GL (wrap.hpp)
|
||||
* std::fmod((std::fmod((value - min), d) + d), d) + min;
|
||||
* </p>
|
||||
* <p>
|
||||
* Multiples of max value will be wrapped to max.
|
||||
* </p>
|
||||
*
|
||||
* @param value Value to wrap
|
||||
* @param min Minimum value
|
||||
* @param max Maximum value
|
||||
* @return Wrapped value
|
||||
*/
|
||||
static double wrap(double value, double min, double max) {
|
||||
double delta = max - min;
|
||||
|
||||
double firstMod = (value - min) % delta;
|
||||
double secondMod = (firstMod + delta) % delta;
|
||||
|
||||
if (value >= max && secondMod == 0) {
|
||||
return max;
|
||||
}
|
||||
return secondMod + min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether some other object is "equal to" this one.
|
||||
*
|
||||
* @param object The object to compare
|
||||
* @return True if equal, false if not
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(@Nullable Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
}
|
||||
if (object == null || getClass() != object.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LatLng latLng = (LatLng) object;
|
||||
|
||||
return Double.compare(latLng.altitude, altitude) == 0 && Double.compare(latLng.latitude, latitude) == 0
|
||||
&& Double.compare(latLng.longitude, longitude) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hash code value for the object.
|
||||
*
|
||||
* @return the hash code value
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(latitude);
|
||||
result = (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(longitude);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(altitude);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the object.
|
||||
*
|
||||
* @return the string representation
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LatLng [latitude=" + latitude + ", longitude=" + longitude + ", altitude=" + altitude + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the kinds of special objects contained in this Parcelable instance's marshaled representation.
|
||||
*
|
||||
* @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance.
|
||||
*/
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten this object in to a Parcel.
|
||||
*
|
||||
* @param out The Parcel in which the object should be written.
|
||||
* @param flags Additional flags about how the object should be written
|
||||
*/
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel out, int flags) {
|
||||
out.writeDouble(latitude);
|
||||
out.writeDouble(longitude);
|
||||
out.writeDouble(altitude);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate distance between two points
|
||||
*
|
||||
* @param other Other LatLng to compare to
|
||||
* @return distance in meters
|
||||
*/
|
||||
public double distanceTo(@NonNull LatLng other) {
|
||||
return TurfMeasurement.distance(
|
||||
Point.fromLngLat(longitude, latitude),
|
||||
Point.fromLngLat(other.getLongitude(), other.getLatitude()),
|
||||
UNIT_METRES
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,608 @@
|
||||
package com.mapbox.mapboxsdk.geometry;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.FloatRange;
|
||||
import android.support.annotation.Keep;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.mapbox.mapboxsdk.constants.GeometryConstants;
|
||||
import com.mapbox.mapboxsdk.exceptions.InvalidLatLngBoundsException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.mapbox.mapboxsdk.constants.GeometryConstants.MAX_LATITUDE;
|
||||
import static com.mapbox.mapboxsdk.constants.GeometryConstants.MAX_LONGITUDE;
|
||||
import static com.mapbox.mapboxsdk.constants.GeometryConstants.MAX_WRAP_LONGITUDE;
|
||||
import static com.mapbox.mapboxsdk.constants.GeometryConstants.MIN_LATITUDE;
|
||||
import static com.mapbox.mapboxsdk.constants.GeometryConstants.MIN_LONGITUDE;
|
||||
import static com.mapbox.mapboxsdk.constants.GeometryConstants.MIN_WRAP_LONGITUDE;
|
||||
|
||||
/**
|
||||
* A geographical area representing a latitude/longitude aligned rectangle.
|
||||
* <p>
|
||||
* This class does not wrap values to the world bounds.
|
||||
* </p>
|
||||
*/
|
||||
public class LatLngBounds implements Parcelable {
|
||||
|
||||
@Keep
|
||||
private final double latitudeNorth;
|
||||
@Keep
|
||||
private final double latitudeSouth;
|
||||
@Keep
|
||||
private final double longitudeEast;
|
||||
@Keep
|
||||
private final double longitudeWest;
|
||||
|
||||
/**
|
||||
* Construct a new LatLngBounds based on its corners, given in NESW
|
||||
* order.
|
||||
* <p>
|
||||
* @since 7.0.0 LatLngBounds cannot be wrapped any more, i.e longitudeWest has to be
|
||||
* less or equal to longitudeEast.
|
||||
*
|
||||
* For example, to represent bounds spanning 20 degrees crossing antimeridian with
|
||||
* the NE point as (10, -170) and the SW point as (-10, 170),
|
||||
* use (10, -190) and (-10, -170), or (10, -170) and (-10, -150).
|
||||
*
|
||||
* @param northLatitude Northern Latitude
|
||||
* @param eastLongitude Eastern Longitude
|
||||
* @param southLatitude Southern Latitude
|
||||
* @param westLongitude Western Longitude
|
||||
*/
|
||||
@Keep
|
||||
LatLngBounds(final double northLatitude, final double eastLongitude,
|
||||
final double southLatitude, final double westLongitude) {
|
||||
this.latitudeNorth = northLatitude;
|
||||
this.longitudeEast = eastLongitude;
|
||||
this.latitudeSouth = southLatitude;
|
||||
this.longitudeWest = westLongitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the world bounds.
|
||||
*
|
||||
* @return the bounds representing the world
|
||||
*/
|
||||
public static LatLngBounds world() {
|
||||
return LatLngBounds.from(
|
||||
MAX_LATITUDE, MAX_WRAP_LONGITUDE,
|
||||
MIN_LATITUDE, MIN_WRAP_LONGITUDE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the centerpoint of this LatLngBounds by simple interpolation and returns
|
||||
* it as a point. This is a non-geodesic calculation which is not the geographic center.
|
||||
*
|
||||
* @return LatLng center of this LatLngBounds
|
||||
*/
|
||||
@NonNull
|
||||
public LatLng getCenter() {
|
||||
double latCenter = (this.latitudeNorth + this.latitudeSouth) / 2.0;
|
||||
double longCenter = (this.longitudeEast + this.longitudeWest) / 2.0;
|
||||
|
||||
return new LatLng(latCenter, longCenter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the north latitude value of this bounds.
|
||||
*
|
||||
* @return double latitude value for north
|
||||
*/
|
||||
public double getLatNorth() {
|
||||
return this.latitudeNorth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the south latitude value of this bounds.
|
||||
*
|
||||
* @return double latitude value for south
|
||||
*/
|
||||
public double getLatSouth() {
|
||||
return this.latitudeSouth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the east longitude value of this bounds.
|
||||
*
|
||||
* @return double longitude value for east
|
||||
*/
|
||||
public double getLonEast() {
|
||||
return this.longitudeEast;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the west longitude value of this bounds.
|
||||
*
|
||||
* @return double longitude value for west
|
||||
*/
|
||||
public double getLonWest() {
|
||||
return this.longitudeWest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latitude-longitude pair of the south west corner of this bounds.
|
||||
*
|
||||
* @return LatLng of the south west corner
|
||||
*/
|
||||
@NonNull
|
||||
public LatLng getSouthWest() {
|
||||
return new LatLng(latitudeSouth, longitudeWest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latitude-longitude paur if the north east corner of this bounds.
|
||||
*
|
||||
* @return LatLng of the north east corner
|
||||
*/
|
||||
@NonNull
|
||||
public LatLng getNorthEast() {
|
||||
return new LatLng(latitudeNorth, longitudeEast);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latitude-longitude pair of the south east corner of this bounds.
|
||||
*
|
||||
* @return LatLng of the south east corner
|
||||
*/
|
||||
@NonNull
|
||||
public LatLng getSouthEast() {
|
||||
return new LatLng(latitudeSouth, longitudeEast);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latitude-longitude pair of the north west corner of this bounds.
|
||||
*
|
||||
* @return LatLng of the north west corner
|
||||
*/
|
||||
@NonNull
|
||||
public LatLng getNorthWest() {
|
||||
return new LatLng(latitudeNorth, longitudeWest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the area spanned by this LatLngBounds
|
||||
*
|
||||
* @return LatLngSpan area
|
||||
*/
|
||||
@NonNull
|
||||
public LatLngSpan getSpan() {
|
||||
return new LatLngSpan(getLatitudeSpan(), getLongitudeSpan());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the absolute distance, in degrees, between the north and
|
||||
* south boundaries of this LatLngBounds
|
||||
*
|
||||
* @return Span distance
|
||||
*/
|
||||
public double getLatitudeSpan() {
|
||||
return Math.abs(this.latitudeNorth - this.latitudeSouth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the absolute distance, in degrees, between the west and
|
||||
* east boundaries of this LatLngBounds
|
||||
*
|
||||
* @return Span distance
|
||||
*/
|
||||
public double getLongitudeSpan() {
|
||||
return Math.abs(this.longitudeEast - this.longitudeWest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate if LatLngBounds is empty, determined if absolute distance is
|
||||
*
|
||||
* @return boolean indicating if span is empty
|
||||
*/
|
||||
public boolean isEmptySpan() {
|
||||
return getLongitudeSpan() == 0.0 || getLatitudeSpan() == 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representaton of the object.
|
||||
*
|
||||
* @return the string representation
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "N:" + this.latitudeNorth + "; E:" + this.longitudeEast + "; S:" + this.latitudeSouth
|
||||
+ "; W:" + this.longitudeWest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a LatLngBounds that contains all of a list of LatLng
|
||||
* objects. Empty lists will yield invalid LatLngBounds.
|
||||
*
|
||||
* @param latLngs List of LatLng objects
|
||||
* @return LatLngBounds
|
||||
*/
|
||||
static LatLngBounds fromLatLngs(final List<? extends LatLng> latLngs) {
|
||||
double minLat = MAX_LATITUDE;
|
||||
double minLon = MAX_LONGITUDE;
|
||||
double maxLat = MIN_LATITUDE;
|
||||
double maxLon = MIN_LONGITUDE;
|
||||
|
||||
for (final LatLng gp : latLngs) {
|
||||
final double latitude = gp.getLatitude();
|
||||
final double longitude = gp.getLongitude();
|
||||
minLat = Math.min(minLat, latitude);
|
||||
minLon = Math.min(minLon, longitude);
|
||||
maxLat = Math.max(maxLat, latitude);
|
||||
maxLon = Math.max(maxLon, longitude);
|
||||
}
|
||||
|
||||
return new LatLngBounds(maxLat, maxLon, minLat, minLon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of LatLng objects resembling this bounds.
|
||||
*
|
||||
* @return an array of 2 LatLng objects.
|
||||
*/
|
||||
@NonNull
|
||||
public LatLng[] toLatLngs() {
|
||||
return new LatLng[] {getNorthEast(), getSouthWest()};
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a LatLngBounds from doubles representing a LatLng pair.
|
||||
* <p>
|
||||
* This values of latNorth and latSouth should be in the range of [-90, 90],
|
||||
* see {@link GeometryConstants#MIN_LATITUDE} and {@link GeometryConstants#MAX_LATITUDE},
|
||||
* otherwise IllegalArgumentException will be thrown.
|
||||
* latNorth should be greater or equal latSouth, otherwise IllegalArgumentException will be thrown.
|
||||
* <p>
|
||||
* This method doesn't recalculate most east or most west boundaries.
|
||||
* Note @since 7.0.0 lonEast and lonWest will NOT be wrapped to be in the range of [-180, 180],
|
||||
* see {@link GeometryConstants#MIN_LONGITUDE} and {@link GeometryConstants#MAX_LONGITUDE}
|
||||
* lonEast should be greater or equal lonWest, otherwise IllegalArgumentException will be thrown.
|
||||
* </p>
|
||||
*/
|
||||
public static LatLngBounds from(
|
||||
@FloatRange(from = MIN_LATITUDE, to = MAX_LATITUDE) double latNorth,
|
||||
double lonEast,
|
||||
@FloatRange(from = MIN_LATITUDE, to = MAX_LATITUDE) double latSouth,
|
||||
double lonWest) {
|
||||
|
||||
checkParams(latNorth, lonEast, latSouth, lonWest);
|
||||
|
||||
return new LatLngBounds(latNorth, lonEast, latSouth, lonWest);
|
||||
}
|
||||
|
||||
private static void checkParams(
|
||||
@FloatRange(from = MIN_LATITUDE, to = MAX_LATITUDE) double latNorth,
|
||||
double lonEast,
|
||||
@FloatRange(from = MIN_LATITUDE, to = MAX_LATITUDE) double latSouth,
|
||||
double lonWest) {
|
||||
|
||||
if (Double.isNaN(latNorth) || Double.isNaN(latSouth)) {
|
||||
throw new IllegalArgumentException("latitude must not be NaN");
|
||||
}
|
||||
|
||||
if (Double.isNaN(lonEast) || Double.isNaN(lonWest)) {
|
||||
throw new IllegalArgumentException("longitude must not be NaN");
|
||||
}
|
||||
|
||||
if (Double.isInfinite(lonEast) || Double.isInfinite(lonWest)) {
|
||||
throw new IllegalArgumentException("longitude must not be infinite");
|
||||
}
|
||||
|
||||
if (latNorth > MAX_LATITUDE || latNorth < MIN_LATITUDE
|
||||
|| latSouth > MAX_LATITUDE || latSouth < MIN_LATITUDE) {
|
||||
throw new IllegalArgumentException("latitude must be between -90 and 90");
|
||||
}
|
||||
|
||||
if (latNorth < latSouth) {
|
||||
throw new IllegalArgumentException("latNorth cannot be less than latSouth");
|
||||
}
|
||||
|
||||
if (lonEast < lonWest) {
|
||||
throw new IllegalArgumentException("lonEast cannot be less than lonWest");
|
||||
}
|
||||
}
|
||||
|
||||
private static double lat_(int z, int y) {
|
||||
double n = Math.PI - 2.0 * Math.PI * y / Math.pow(2.0, z);
|
||||
return Math.toDegrees(Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))));
|
||||
}
|
||||
|
||||
private static double lon_(int z, int x) {
|
||||
return x / Math.pow(2.0, z) * 360.0 - GeometryConstants.MAX_WRAP_LONGITUDE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a LatLngBounds from a Tile identifier.
|
||||
* <p>
|
||||
* Returned bounds will have latitude in the range of Mercator projection.
|
||||
*
|
||||
* @param z Tile zoom level.
|
||||
* @param x Tile X coordinate.
|
||||
* @param y Tile Y coordinate.
|
||||
* @see GeometryConstants#MIN_MERCATOR_LATITUDE
|
||||
* @see GeometryConstants#MAX_MERCATOR_LATITUDE
|
||||
*/
|
||||
public static LatLngBounds from(int z, int x, int y) {
|
||||
return new LatLngBounds(lat_(z, y), lon_(z, x + 1), lat_(z, y + 1), lon_(z, x));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a LatLngBounds from current bounds with an additional latitude-longitude pair.
|
||||
*
|
||||
* @param latLng the latitude lognitude pair to include in the bounds.
|
||||
* @return the newly constructed bounds
|
||||
*/
|
||||
@NonNull
|
||||
public LatLngBounds include(@NonNull LatLng latLng) {
|
||||
return new LatLngBounds.Builder()
|
||||
.include(getNorthEast())
|
||||
.include(getSouthWest())
|
||||
.include(latLng)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this LatLngBounds matches another one via LatLng.
|
||||
*
|
||||
* @param o another object
|
||||
* @return a boolean indicating whether the LatLngBounds are equal
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o instanceof LatLngBounds) {
|
||||
LatLngBounds other = (LatLngBounds) o;
|
||||
return latitudeNorth == other.getLatNorth()
|
||||
&& latitudeSouth == other.getLatSouth()
|
||||
&& longitudeEast == other.getLonEast()
|
||||
&& longitudeWest == other.getLonWest();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private boolean containsLatitude(final double latitude) {
|
||||
return (latitude <= this.latitudeNorth)
|
||||
&& (latitude >= this.latitudeSouth);
|
||||
}
|
||||
|
||||
private boolean containsLongitude(final double longitude) {
|
||||
return (longitude <= this.longitudeEast)
|
||||
&& (longitude >= this.longitudeWest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this LatLngBounds contains a point.
|
||||
*
|
||||
* @param latLng the point which may be contained
|
||||
* @return true, if the point is contained within the bounds
|
||||
*/
|
||||
public boolean contains(@NonNull final LatLng latLng) {
|
||||
return containsLatitude(latLng.getLatitude())
|
||||
&& containsLongitude(latLng.getLongitude());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this LatLngBounds contains another bounds.
|
||||
*
|
||||
* @param other the bounds which may be contained
|
||||
* @return true, if the bounds is contained within the bounds
|
||||
*/
|
||||
public boolean contains(@NonNull final LatLngBounds other) {
|
||||
return contains(other.getNorthEast())
|
||||
&& contains(other.getSouthWest());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new LatLngBounds that stretches to contain both this and another LatLngBounds.
|
||||
*
|
||||
* @param bounds LatLngBounds to add
|
||||
* @return LatLngBounds
|
||||
*/
|
||||
@NonNull
|
||||
public LatLngBounds union(@NonNull LatLngBounds bounds) {
|
||||
return unionNoParamCheck(bounds.getLatNorth(), bounds.getLonEast(),
|
||||
bounds.getLatSouth(), bounds.getLonWest());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new LatLngBounds that stretches to contain both this and another LatLngBounds.
|
||||
*
|
||||
* <p>
|
||||
* This values of northLat and southLat should be in the range of [-90, 90],
|
||||
* see {@link GeometryConstants#MIN_LATITUDE} and {@link GeometryConstants#MAX_LATITUDE},
|
||||
* otherwise IllegalArgumentException will be thrown.
|
||||
* northLat should be greater or equal southLat, otherwise IllegalArgumentException will be thrown.
|
||||
*
|
||||
* <p>
|
||||
* eastLon should be greater or equal westLon, otherwise IllegalArgumentException will be thrown.
|
||||
*
|
||||
* @param northLat Northern Latitude corner point
|
||||
* @param eastLon Eastern Longitude corner point
|
||||
* @param southLat Southern Latitude corner point
|
||||
* @param westLon Western Longitude corner point
|
||||
* @return LatLngBounds
|
||||
*/
|
||||
@NonNull
|
||||
public LatLngBounds union(final double northLat, final double eastLon,
|
||||
final double southLat, final double westLon) {
|
||||
checkParams(northLat, eastLon, southLat, westLon);
|
||||
return unionNoParamCheck(northLat, eastLon, southLat, westLon);
|
||||
}
|
||||
|
||||
private LatLngBounds unionNoParamCheck(final double northLat, final double eastLon,
|
||||
final double southLat, final double westLon) {
|
||||
|
||||
return new LatLngBounds((this.latitudeNorth < northLat) ? northLat : this.latitudeNorth,
|
||||
(this.longitudeEast < eastLon) ? eastLon : this.longitudeEast,
|
||||
(this.latitudeSouth > southLat) ? southLat : this.latitudeSouth,
|
||||
(this.longitudeWest > westLon) ? westLon : this.longitudeWest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new LatLngBounds that is the intersection of this with another LatLngBounds,
|
||||
*
|
||||
* @param box LatLngBounds to intersect with
|
||||
* @return LatLngBounds
|
||||
*/
|
||||
@Nullable
|
||||
public LatLngBounds intersect(@NonNull LatLngBounds box) {
|
||||
return intersectNoParamCheck(box.getLatNorth(), box.getLonEast(), box.getLatSouth(), box.getLonWest());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a new LatLngBounds that is the intersection of this with another box.
|
||||
*
|
||||
* <p>
|
||||
* This values of northLat and southLat should be in the range of [-90, 90],
|
||||
* see {@link GeometryConstants#MIN_LATITUDE} and {@link GeometryConstants#MAX_LATITUDE},
|
||||
* otherwise IllegalArgumentException will be thrown.
|
||||
* northLat should be greater or equal southLat, otherwise IllegalArgumentException will be thrown.
|
||||
*
|
||||
* <p>
|
||||
* eastLon should be greater or equal westLon, otherwise IllegalArgumentException will be thrown.
|
||||
*
|
||||
* @param northLat Northern Latitude corner point
|
||||
* @param eastLon Eastern Longitude corner point
|
||||
* @param southLat Southern Latitude corner point
|
||||
* @param westLon Western Longitude corner point
|
||||
* @return LatLngBounds
|
||||
*/
|
||||
@NonNull
|
||||
public LatLngBounds intersect(final double northLat, final double eastLon,
|
||||
final double southLat, final double westLon) {
|
||||
checkParams(northLat, eastLon, southLat, westLon);
|
||||
return intersectNoParamCheck(northLat, eastLon, southLat, westLon);
|
||||
}
|
||||
|
||||
private LatLngBounds intersectNoParamCheck(final double northLat, final double eastLon,
|
||||
final double southLat, final double westLon) {
|
||||
|
||||
double minLonWest = Math.max(this.longitudeWest, westLon);
|
||||
double maxLonEast = Math.min(this.longitudeEast, eastLon);
|
||||
if (maxLonEast >= minLonWest) {
|
||||
double minLatSouth = Math.max(this.latitudeSouth, southLat);
|
||||
double maxLatNorth = Math.min(this.latitudeNorth, northLat);
|
||||
if (maxLatNorth >= minLatSouth) {
|
||||
return new LatLngBounds(maxLatNorth, maxLonEast, minLatSouth, minLonWest);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Inner class responsible for recreating Parcels into objects.
|
||||
*/
|
||||
public static final Parcelable.Creator<LatLngBounds> CREATOR =
|
||||
new Parcelable.Creator<LatLngBounds>() {
|
||||
@Override
|
||||
public LatLngBounds createFromParcel(@NonNull final Parcel in) {
|
||||
return readFromParcel(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LatLngBounds[] newArray(final int size) {
|
||||
return new LatLngBounds[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a hash code value for the object.
|
||||
*
|
||||
* @return the hash code
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) ((latitudeNorth + 90)
|
||||
+ ((latitudeSouth + 90) * 1000)
|
||||
+ ((longitudeEast + 180) * 1000000)
|
||||
+ ((longitudeWest + 180) * 1000000000));
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the kinds of special objects contained in this Parcelable instance's marshaled representation.
|
||||
*
|
||||
* @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance.
|
||||
*/
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten this object in to a Parcel.
|
||||
*
|
||||
* @param out The Parcel in which the object should be written.
|
||||
* @param flags Additional flags about how the object should be written
|
||||
*/
|
||||
@Override
|
||||
public void writeToParcel(@NonNull final Parcel out, final int flags) {
|
||||
out.writeDouble(this.latitudeNorth);
|
||||
out.writeDouble(this.longitudeEast);
|
||||
out.writeDouble(this.latitudeSouth);
|
||||
out.writeDouble(this.longitudeWest);
|
||||
}
|
||||
|
||||
private static LatLngBounds readFromParcel(final Parcel in) {
|
||||
final double northLat = in.readDouble();
|
||||
final double eastLon = in.readDouble();
|
||||
final double southLat = in.readDouble();
|
||||
final double westLon = in.readDouble();
|
||||
return new LatLngBounds(northLat, eastLon, southLat, westLon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for composing LatLngBounds objects.
|
||||
*/
|
||||
public static final class Builder {
|
||||
|
||||
private final List<LatLng> latLngList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Builds a new LatLngBounds.
|
||||
* <p>
|
||||
* Throws an {@link InvalidLatLngBoundsException} when no LatLngBounds can be created.
|
||||
* </p>
|
||||
*
|
||||
* @return the build LatLngBounds
|
||||
*/
|
||||
public LatLngBounds build() {
|
||||
if (latLngList.size() < 2) {
|
||||
throw new InvalidLatLngBoundsException(latLngList.size());
|
||||
}
|
||||
return LatLngBounds.fromLatLngs(latLngList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a LatLng object to the LatLngBounds.Builder.
|
||||
*
|
||||
* @param latLngs the List of LatLng objects to be added
|
||||
* @return this
|
||||
*/
|
||||
@NonNull
|
||||
public Builder includes(@NonNull List<LatLng> latLngs) {
|
||||
latLngList.addAll(latLngs);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a LatLng object to the LatLngBounds.Builder.
|
||||
*
|
||||
* @param latLng the LatLng to be added
|
||||
* @return this
|
||||
*/
|
||||
@NonNull
|
||||
public Builder include(@NonNull LatLng latLng) {
|
||||
latLngList.add(latLng);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.mapbox.mapboxsdk.geometry;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.Keep;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* A geographical area representing a non-aligned quadrilateral
|
||||
* <p>
|
||||
* This class does not wrap values to the world bounds
|
||||
* </p>
|
||||
*/
|
||||
public class LatLngQuad implements Parcelable {
|
||||
|
||||
@Keep
|
||||
private final LatLng topLeft;
|
||||
@Keep
|
||||
private final LatLng topRight;
|
||||
@Keep
|
||||
private final LatLng bottomRight;
|
||||
@Keep
|
||||
private final LatLng bottomLeft;
|
||||
|
||||
/**
|
||||
* Construct a new LatLngQuad based on its corners,
|
||||
* in order top left, top right, bottom left, bottom right
|
||||
*/
|
||||
@Keep
|
||||
public LatLngQuad(final LatLng topLeft, final LatLng topRight, final LatLng bottomRight, final LatLng bottomLeft) {
|
||||
this.topLeft = topLeft;
|
||||
this.topRight = topRight;
|
||||
this.bottomRight = bottomRight;
|
||||
this.bottomLeft = bottomLeft;
|
||||
}
|
||||
|
||||
public LatLng getTopLeft() {
|
||||
return this.topLeft;
|
||||
}
|
||||
|
||||
public LatLng getTopRight() {
|
||||
return this.topRight;
|
||||
}
|
||||
|
||||
public LatLng getBottomRight() {
|
||||
return this.bottomRight;
|
||||
}
|
||||
|
||||
public LatLng getBottomLeft() {
|
||||
return this.bottomLeft;
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<LatLngQuad> CREATOR = new Parcelable.Creator<LatLngQuad>() {
|
||||
@Override
|
||||
public LatLngQuad createFromParcel(@NonNull final Parcel in) {
|
||||
return readFromParcel(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LatLngQuad[] newArray(final int size) {
|
||||
return new LatLngQuad[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int code = topLeft.hashCode();
|
||||
code = (code ^ code >>> 31) + topRight.hashCode();
|
||||
code = (code ^ code >>> 31) + bottomRight.hashCode();
|
||||
code = (code ^ code >>> 31) + bottomLeft.hashCode();
|
||||
return code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull final Parcel out, final int arg1) {
|
||||
topLeft.writeToParcel(out, arg1);
|
||||
topRight.writeToParcel(out, arg1);
|
||||
bottomRight.writeToParcel(out, arg1);
|
||||
bottomLeft.writeToParcel(out, arg1);
|
||||
}
|
||||
|
||||
private static LatLngQuad readFromParcel(@NonNull final Parcel in) {
|
||||
final LatLng topLeft = new LatLng(in);
|
||||
final LatLng topRight = new LatLng(in);
|
||||
final LatLng bottomRight = new LatLng(in);
|
||||
final LatLng bottomLeft = new LatLng(in);
|
||||
return new LatLngQuad(topLeft, topRight, bottomRight, bottomLeft);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.mapbox.mapboxsdk.geometry;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* A geographical span defined by its latitude and longitude span.
|
||||
*/
|
||||
public class LatLngSpan implements Parcelable {
|
||||
|
||||
private double mLatitudeSpan;
|
||||
private double mLongitudeSpan;
|
||||
|
||||
private LatLngSpan(@NonNull Parcel in) {
|
||||
mLatitudeSpan = in.readDouble();
|
||||
mLongitudeSpan = in.readDouble();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LatLgnSpan.
|
||||
*
|
||||
* @param latitudeSpan The span used for latitude.
|
||||
* @param longitudeSpan The span used for longitude.
|
||||
*/
|
||||
public LatLngSpan(double latitudeSpan, double longitudeSpan) {
|
||||
mLatitudeSpan = latitudeSpan;
|
||||
mLongitudeSpan = longitudeSpan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latitude span.
|
||||
*
|
||||
* @return The latitude span.
|
||||
*/
|
||||
public double getLatitudeSpan() {
|
||||
return mLatitudeSpan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the latitude span.
|
||||
*
|
||||
* @param latitudeSpan The latitude span to set.
|
||||
*/
|
||||
public void setLatitudeSpan(double latitudeSpan) {
|
||||
mLatitudeSpan = latitudeSpan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns to longitude span.
|
||||
*
|
||||
* @return The longitude span.
|
||||
*/
|
||||
public double getLongitudeSpan() {
|
||||
return mLongitudeSpan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the longitude span.
|
||||
*
|
||||
* @param longitudeSpan The longitude span to set.
|
||||
*/
|
||||
public void setLongitudeSpan(double longitudeSpan) {
|
||||
mLongitudeSpan = longitudeSpan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether some other object is "equal to" this one.
|
||||
*
|
||||
* @param object The object to compare
|
||||
* @return True if equal, false if not
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
}
|
||||
if (object instanceof LatLngSpan) {
|
||||
LatLngSpan other = (LatLngSpan) object;
|
||||
return mLongitudeSpan == other.getLongitudeSpan()
|
||||
&& mLatitudeSpan == other.getLatitudeSpan();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class responsible for recreating Parcels into objects.
|
||||
*/
|
||||
public static final Parcelable.Creator<LatLngSpan> CREATOR =
|
||||
new Parcelable.Creator<LatLngSpan>() {
|
||||
@Override
|
||||
public LatLngSpan createFromParcel(@NonNull Parcel in) {
|
||||
return new LatLngSpan(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LatLngSpan[] newArray(int size) {
|
||||
return new LatLngSpan[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Describe the kinds of special objects contained in this Parcelable instance's marshaled representation.
|
||||
*
|
||||
* @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance.
|
||||
*/
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten this object in to a Parcel.
|
||||
*
|
||||
* @param out Parcel in which the object should be written
|
||||
* @param flags Additional flags about how the object should be written
|
||||
*/
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel out, int flags) {
|
||||
out.writeDouble(mLatitudeSpan);
|
||||
out.writeDouble(mLongitudeSpan);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hash code value for the object.
|
||||
*
|
||||
* @return hash code value of this
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(mLatitudeSpan);
|
||||
result = (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(mLongitudeSpan);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package com.mapbox.mapboxsdk.geometry;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.Keep;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* ProjectedMeters is a projection of longitude, latitude points in Mercator meters.
|
||||
* <p>
|
||||
* these have been projected into Mapbox GL's Mercator projection. Instead of decimal
|
||||
* degrees, it uses Mercator meters (which are notably not equivalent to SI meters)
|
||||
* except at the equator.
|
||||
* </p>
|
||||
*/
|
||||
public class ProjectedMeters implements Parcelable {
|
||||
|
||||
/**
|
||||
* Inner class responsible for recreating Parcels into objects.
|
||||
*/
|
||||
public static final Creator<ProjectedMeters> CREATOR = new Creator<ProjectedMeters>() {
|
||||
public ProjectedMeters createFromParcel(@NonNull Parcel in) {
|
||||
return new ProjectedMeters(in);
|
||||
}
|
||||
|
||||
public ProjectedMeters[] newArray(int size) {
|
||||
return new ProjectedMeters[size];
|
||||
}
|
||||
};
|
||||
|
||||
private double northing;
|
||||
private double easting;
|
||||
|
||||
/**
|
||||
* Creates a ProjectedMeters based on projected meters in north and east direction.
|
||||
*
|
||||
* @param northing the northing in meters
|
||||
* @param easting the easting in meters
|
||||
*/
|
||||
@Keep
|
||||
public ProjectedMeters(double northing, double easting) {
|
||||
this.northing = northing;
|
||||
this.easting = easting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ProjecteMeters based on another set of projected meters.
|
||||
*
|
||||
* @param projectedMeters The projected meters to be based on.
|
||||
*/
|
||||
public ProjectedMeters(ProjectedMeters projectedMeters) {
|
||||
this.northing = projectedMeters.northing;
|
||||
this.easting = projectedMeters.easting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ProjectedMeters from a Parcel.
|
||||
*
|
||||
* @param in The parcel to create from
|
||||
* @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance.
|
||||
*/
|
||||
private ProjectedMeters(Parcel in) {
|
||||
northing = in.readDouble();
|
||||
easting = in.readDouble();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get projected meters in north direction.
|
||||
*
|
||||
* @return Projected meters in north.
|
||||
*/
|
||||
public double getNorthing() {
|
||||
return northing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get projected meters in east direction.
|
||||
*
|
||||
* @return Projected meters in east.
|
||||
*/
|
||||
public double getEasting() {
|
||||
return easting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether some other object is "equal to" this one.
|
||||
*
|
||||
* @param other The object to compare this to
|
||||
* @return true if equal, false if not
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (other == null || getClass() != other.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ProjectedMeters projectedMeters = (ProjectedMeters) other;
|
||||
|
||||
return Double.compare(projectedMeters.easting, easting) == 0
|
||||
&& Double.compare(projectedMeters.northing, northing) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hash code value for the object.
|
||||
*
|
||||
* @return the hash code of this
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(easting);
|
||||
result = (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(northing);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the object.
|
||||
*
|
||||
* @return the string representation of this
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ProjectedMeters [northing=" + northing + ", easting=" + easting + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the kinds of special objects contained in this Parcelable instance's marshaled representation.
|
||||
*
|
||||
* @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance.
|
||||
*/
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten this object in to a Parcel.
|
||||
*
|
||||
* @param out The Parcel in which the object should be written.
|
||||
* @param flags Additional flags about how the object should be written
|
||||
*/
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel out, int flags) {
|
||||
out.writeDouble(northing);
|
||||
out.writeDouble(easting);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package com.mapbox.mapboxsdk.geometry;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Contains the four points defining the four-sided polygon that is visible in a map's camera.
|
||||
* This polygon can be a trapezoid instead of a rectangle, because a camera can have tilt.
|
||||
* If the camera is directly over the center of the camera, the shape is rectangular,
|
||||
* but if the camera is tilted, the shape will appear to be a trapezoid whose
|
||||
* smallest side is closest to the point of view.
|
||||
*/
|
||||
public class VisibleRegion implements Parcelable {
|
||||
|
||||
/**
|
||||
* LatLng object that defines the far left corner of the camera.
|
||||
*/
|
||||
public final LatLng farLeft;
|
||||
|
||||
/**
|
||||
* LatLng object that defines the far right corner of the camera.
|
||||
*/
|
||||
public final LatLng farRight;
|
||||
|
||||
/**
|
||||
* LatLng object that defines the bottom left corner of the camera.
|
||||
*/
|
||||
public final LatLng nearLeft;
|
||||
|
||||
/**
|
||||
* LatLng object that defines the bottom right corner of the camera.
|
||||
*/
|
||||
public final LatLng nearRight;
|
||||
|
||||
/**
|
||||
* The smallest bounding box that includes the visible region defined in this class.
|
||||
*/
|
||||
public final LatLngBounds latLngBounds;
|
||||
|
||||
/**
|
||||
* Creates a VisibleRegion from a Parcel.
|
||||
*
|
||||
* @param in The Parcel to create this from
|
||||
*/
|
||||
private VisibleRegion(Parcel in) {
|
||||
this.farLeft = in.readParcelable(LatLng.class.getClassLoader());
|
||||
this.farRight = in.readParcelable(LatLng.class.getClassLoader());
|
||||
this.nearLeft = in.readParcelable(LatLng.class.getClassLoader());
|
||||
this.nearRight = in.readParcelable(LatLng.class.getClassLoader());
|
||||
this.latLngBounds = in.readParcelable(LatLngBounds.class.getClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a VisibleRegion given the four corners of the camera.
|
||||
*
|
||||
* @param farLeft A LatLng object containing the latitude and longitude of the near left corner of the region.
|
||||
* @param farRight A LatLng object containing the latitude and longitude of the near left corner of the region.
|
||||
* @param nearLeft A LatLng object containing the latitude and longitude of the near left corner of the region.
|
||||
* @param nearRight A LatLng object containing the latitude and longitude of the near left corner of the region.
|
||||
* @param latLngBounds The smallest bounding box that includes the visible region defined in this class.
|
||||
*/
|
||||
public VisibleRegion(LatLng farLeft, LatLng farRight, LatLng nearLeft, LatLng nearRight, LatLngBounds latLngBounds) {
|
||||
this.farLeft = farLeft;
|
||||
this.farRight = farRight;
|
||||
this.nearLeft = nearLeft;
|
||||
this.nearRight = nearRight;
|
||||
this.latLngBounds = latLngBounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this VisibleRegion to another object.
|
||||
* If the other object is actually a pointer to this object,
|
||||
* or if all four corners and the bounds of the two objects are the same,
|
||||
* this method returns true. Otherwise, this method returns false.
|
||||
*
|
||||
* @param o The Object to compare with.
|
||||
* @return true if both objects are the same object.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof VisibleRegion)) {
|
||||
return false;
|
||||
}
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
VisibleRegion visibleRegion = (VisibleRegion) o;
|
||||
return farLeft.equals(visibleRegion.farLeft)
|
||||
&& farRight.equals(visibleRegion.farRight)
|
||||
&& nearLeft.equals(visibleRegion.nearLeft)
|
||||
&& nearRight.equals(visibleRegion.nearRight)
|
||||
&& latLngBounds.equals(visibleRegion.latLngBounds);
|
||||
}
|
||||
|
||||
/**
|
||||
* The string representation of the object.
|
||||
*
|
||||
* @return the string representation of this
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[farLeft [" + farLeft + "], farRight [" + farRight + "], nearLeft [" + nearLeft + "], nearRight ["
|
||||
+ nearRight + "], latLngBounds [" + latLngBounds + "]]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hash code value for the object.
|
||||
*
|
||||
* @return the hash code
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ((farLeft.hashCode() + 90)
|
||||
+ ((farRight.hashCode() + 90) * 1000)
|
||||
+ ((nearLeft.hashCode() + 180) * 1000000)
|
||||
+ ((nearRight.hashCode() + 180) * 1000000000));
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the kinds of special objects contained in this Parcelable instance's marshaled representation.
|
||||
*
|
||||
* @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance.
|
||||
*/
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten this object in to a Parcel.
|
||||
*
|
||||
* @param out The Parcel in which the object should be written.
|
||||
* @param flags Additional flags about how the object should be written
|
||||
*/
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel out, int flags) {
|
||||
out.writeParcelable(farLeft, flags);
|
||||
out.writeParcelable(farRight, flags);
|
||||
out.writeParcelable(nearLeft, flags);
|
||||
out.writeParcelable(nearRight, flags);
|
||||
out.writeParcelable(latLngBounds, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class responsible for recreating Parcels into objects.
|
||||
*/
|
||||
public static final Parcelable.Creator<VisibleRegion> CREATOR =
|
||||
new Parcelable.Creator<VisibleRegion>() {
|
||||
public VisibleRegion createFromParcel(@NonNull Parcel in) {
|
||||
return new VisibleRegion(in);
|
||||
}
|
||||
|
||||
public VisibleRegion[] newArray(int size) {
|
||||
return new VisibleRegion[size];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains the Mapbox Maps Android Geometry API classes.
|
||||
*/
|
||||
package com.mapbox.mapboxsdk.geometry;
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.mapbox.mapboxsdk.http;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.mapbox.mapboxsdk.MapStrictMode;
|
||||
import com.mapbox.mapboxsdk.Mapbox;
|
||||
|
||||
public class HttpIdentifier {
|
||||
|
||||
private HttpIdentifier() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the application identifier, consisting out the package name, version name and version code.
|
||||
*
|
||||
* @return the application identifier
|
||||
*/
|
||||
public static String getIdentifier() {
|
||||
return getIdentifier(Mapbox.getApplicationContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the application identifier, consisting out the package name, version name and version code.
|
||||
*
|
||||
* @param context the context used to retrieve the package manager from
|
||||
* @return the application identifier
|
||||
*/
|
||||
private static String getIdentifier(@NonNull Context context) {
|
||||
try {
|
||||
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||
return String.format("%s/%s (%s)", context.getPackageName(), packageInfo.versionName, packageInfo.versionCode);
|
||||
} catch (Exception exception) {
|
||||
MapStrictMode.strictModeViolation(exception);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.mapbox.mapboxsdk.http;
|
||||
|
||||
import android.util.Log;
|
||||
import com.mapbox.mapboxsdk.log.Logger;
|
||||
|
||||
import static com.mapbox.mapboxsdk.http.HttpRequest.CONNECTION_ERROR;
|
||||
import static com.mapbox.mapboxsdk.http.HttpRequest.TEMPORARY_ERROR;
|
||||
|
||||
public class HttpLogger {
|
||||
|
||||
private static final String TAG = "Mbgl-HttpRequest";
|
||||
|
||||
public static boolean logRequestUrl;
|
||||
public static boolean logEnabled = true;
|
||||
|
||||
private HttpLogger(){
|
||||
}
|
||||
|
||||
public static void logFailure(int type, String errorMessage, String requestUrl) {
|
||||
log(type == TEMPORARY_ERROR ? Log.DEBUG : type == CONNECTION_ERROR ? Log.INFO : Log.WARN,
|
||||
String.format(
|
||||
"Request failed due to a %s error: %s %s",
|
||||
type == TEMPORARY_ERROR ? "temporary" : type == CONNECTION_ERROR ? "connection" : "permanent",
|
||||
errorMessage,
|
||||
logRequestUrl ? requestUrl : ""
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static void log(int type, String errorMessage) {
|
||||
if (logEnabled) {
|
||||
Logger.log(type, TAG, errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.mapbox.mapboxsdk.http;
|
||||
|
||||
/**
|
||||
* Interface definition for performing http requests.
|
||||
* <p>
|
||||
* This allows to provide alternative implementations for the http interaction of this library.
|
||||
* </p>
|
||||
*/
|
||||
public interface HttpRequest {
|
||||
|
||||
int CONNECTION_ERROR = 0;
|
||||
int TEMPORARY_ERROR = 1;
|
||||
int PERMANENT_ERROR = 2;
|
||||
|
||||
/**
|
||||
* Executes the request.
|
||||
*
|
||||
* @param httpRequest callback to be invoked when we receive a response
|
||||
* @param nativePtr the pointer associated to the request
|
||||
* @param resourceUrl the resource url to download
|
||||
* @param etag http header, identifier for a specific version of a resource
|
||||
* @param modified http header, used to determine if a resource hasn't been modified since
|
||||
* @param offlineUsage flag to indicate a resource will be used for offline, appends offline=true as a query parameter
|
||||
*/
|
||||
void executeRequest(HttpResponder httpRequest, long nativePtr, String resourceUrl,
|
||||
String etag, String modified, boolean offlineUsage);
|
||||
|
||||
/**
|
||||
* Cancels the request.
|
||||
*/
|
||||
void cancelRequest();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.mapbox.mapboxsdk.http;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.mapbox.mapboxsdk.Mapbox;
|
||||
|
||||
public class HttpRequestUrl {
|
||||
|
||||
private HttpRequestUrl() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts a resource request url based on the host, query size, and offline requirement.
|
||||
* Mapbox resources downloaded for offline use are subject to separate Vector Tile and
|
||||
* Raster Tile API pricing and are not included in the Maps SDK’s “unlimited” requests.
|
||||
* See <a href="https://www.mapbox.com/pricing">our pricing page</a> for more information.
|
||||
*
|
||||
* @param host the host used as endpoint
|
||||
* @param resourceUrl the resource to download
|
||||
* @param querySize the query size of the resource request
|
||||
* @param offline the type of resource, either offline or online
|
||||
* @return the adapted resource url
|
||||
*/
|
||||
public static String buildResourceUrl(@NonNull String host, String resourceUrl, int querySize, boolean offline) {
|
||||
if (isValidMapboxEndpoint(host)) {
|
||||
if (querySize == 0) {
|
||||
resourceUrl = resourceUrl + "?";
|
||||
} else {
|
||||
resourceUrl = resourceUrl + "&";
|
||||
}
|
||||
// Only add SKU token to requests not tagged as "offline" usage.
|
||||
if (offline) {
|
||||
resourceUrl = resourceUrl + "offline=true";
|
||||
} else {
|
||||
resourceUrl = resourceUrl + "sku=" + Mapbox.getSkuToken();
|
||||
}
|
||||
}
|
||||
return resourceUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if the host used as endpoint is a valid Mapbox endpoint.
|
||||
*
|
||||
* @param host the host used as endpoint
|
||||
* @return true if a valid Mapbox endpoint
|
||||
*/
|
||||
private static boolean isValidMapboxEndpoint(String host) {
|
||||
return host.equals("mapbox.com")
|
||||
|| host.endsWith(".mapbox.com")
|
||||
|| host.equals("mapbox.cn")
|
||||
|| host.endsWith(".mapbox.cn");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.mapbox.mapboxsdk.http;
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when either a response was returned for a requested resource or
|
||||
* when an error occurred when requesting the resource.
|
||||
*/
|
||||
public interface HttpResponder {
|
||||
|
||||
/**
|
||||
* Invoked when a resource has finished.
|
||||
*
|
||||
* @param responseCode http response code
|
||||
* @param eTag http header, identifier for a specific version of a resource
|
||||
* @param lastModified http header, used to determine if a resource hasn't been modified since
|
||||
* @param cacheControl http header, used to determine cache strategy of a resource
|
||||
* @param expires http header, used to determine when a resource is stale
|
||||
* @param retryAfter http header, used to indicate when the service is expected to be unavailable to the client
|
||||
* @param xRateLimitReset http header, used to determine the remaining window before the rate limit resets
|
||||
* @param body http response body, in an array of bytes representation
|
||||
*/
|
||||
void onResponse(int responseCode, String eTag, String lastModified, String cacheControl, String expires,
|
||||
String retryAfter, String xRateLimitReset, byte[] body);
|
||||
|
||||
/**
|
||||
* Invoked when a resource failed to be retrieved.
|
||||
*
|
||||
* @param type the error type, either one of {@link HttpRequest#CONNECTION_ERROR},
|
||||
* {@link HttpRequest#TEMPORARY_ERROR} or {@link HttpRequest#PERMANENT_ERROR}
|
||||
* @param errorMessage the error message associated with the failure
|
||||
*/
|
||||
void handleFailure(int type, String errorMessage);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.mapbox.mapboxsdk.http;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.mapbox.mapboxsdk.MapStrictMode;
|
||||
import com.mapbox.mapboxsdk.Mapbox;
|
||||
import com.mapbox.mapboxsdk.log.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
class LocalRequestTask extends AsyncTask<String, Void, byte[]> {
|
||||
|
||||
private static final String TAG = "Mbgl-LocalRequestTask";
|
||||
|
||||
private OnLocalRequestResponse requestResponse;
|
||||
|
||||
LocalRequestTask(OnLocalRequestResponse requestResponse) {
|
||||
this.requestResponse = requestResponse;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected byte[] doInBackground(String... strings) {
|
||||
return loadFile(Mapbox.getApplicationContext().getAssets(),
|
||||
"integration/" + strings[0]
|
||||
.substring(8)
|
||||
.replaceAll("%20", " ")
|
||||
.replaceAll("%2c", ","));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(@Nullable byte[] bytes) {
|
||||
super.onPostExecute(bytes);
|
||||
if (bytes != null && requestResponse != null) {
|
||||
requestResponse.onResponse(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static byte[] loadFile(AssetManager assets, @NonNull String path) {
|
||||
byte[] buffer = null;
|
||||
InputStream input = null;
|
||||
try {
|
||||
input = assets.open(path);
|
||||
int size = input.available();
|
||||
buffer = new byte[size];
|
||||
input.read(buffer);
|
||||
} catch (IOException exception) {
|
||||
logFileError(exception);
|
||||
} finally {
|
||||
if (input != null) {
|
||||
try {
|
||||
input.close();
|
||||
} catch (IOException exception) {
|
||||
logFileError(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private static void logFileError(Exception exception) {
|
||||
String message = "Load file failed";
|
||||
Logger.e(TAG, message, exception);
|
||||
MapStrictMode.strictModeViolation(message, exception);
|
||||
}
|
||||
|
||||
public interface OnLocalRequestResponse {
|
||||
void onResponse(byte[] bytes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.mapbox.mapboxsdk.http;
|
||||
|
||||
import android.support.annotation.Keep;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.mapbox.mapboxsdk.Mapbox;
|
||||
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@Keep
|
||||
public class NativeHttpRequest implements HttpResponder {
|
||||
|
||||
private final HttpRequest httpRequest = Mapbox.getModuleProvider().createHttpRequest();
|
||||
|
||||
// Reentrancy is not needed, but "Lock" is an abstract class.
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
@Keep
|
||||
private long nativePtr;
|
||||
|
||||
@Keep
|
||||
private NativeHttpRequest(long nativePtr, String resourceUrl, String etag, String modified, boolean offlineUsage) {
|
||||
this.nativePtr = nativePtr;
|
||||
|
||||
if (resourceUrl.startsWith("local://")) {
|
||||
// used by render test to serve files from assets
|
||||
executeLocalRequest(resourceUrl);
|
||||
return;
|
||||
}
|
||||
httpRequest.executeRequest(this, nativePtr, resourceUrl, etag, modified, offlineUsage);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
httpRequest.cancelRequest();
|
||||
|
||||
// TODO: We need a lock here because we can try
|
||||
// to cancel at the same time the request is getting
|
||||
// answered on the OkHTTP thread. We could get rid of
|
||||
// this lock by using Runnable when we move Android
|
||||
// implementation of mbgl::RunLoop to Looper.
|
||||
lock.lock();
|
||||
nativePtr = 0;
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
public void onResponse(int responseCode, String etag, String lastModified, String cacheControl, String expires,
|
||||
String retryAfter, String xRateLimitReset, byte[] body) {
|
||||
lock.lock();
|
||||
if (nativePtr != 0) {
|
||||
nativeOnResponse(responseCode,
|
||||
etag,
|
||||
lastModified,
|
||||
cacheControl,
|
||||
expires,
|
||||
retryAfter,
|
||||
xRateLimitReset,
|
||||
body);
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
private void executeLocalRequest(String resourceUrl) {
|
||||
new LocalRequestTask(new LocalRequestTask.OnLocalRequestResponse() {
|
||||
@Override
|
||||
public void onResponse(@Nullable byte[] bytes) {
|
||||
if (bytes != null) {
|
||||
lock.lock();
|
||||
if (nativePtr != 0) {
|
||||
NativeHttpRequest.this.nativeOnResponse(200, null, null, null, null, null, null, bytes);
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}).execute(resourceUrl);
|
||||
}
|
||||
|
||||
public void handleFailure(int type, String errorMessage) {
|
||||
lock.lock();
|
||||
if (nativePtr != 0) {
|
||||
nativeOnFailure(type, errorMessage);
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
@Keep
|
||||
private native void nativeOnFailure(int type, String message);
|
||||
|
||||
@Keep
|
||||
private native void nativeOnResponse(int code, String etag, String modified, String cacheControl, String expires,
|
||||
String retryAfter, String xRateLimitReset, byte[] body);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Do not use this package. Internal use only.
|
||||
*/
|
||||
package com.mapbox.mapboxsdk.http;
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
class AnimatorListenerHolder {
|
||||
@MapboxAnimator.Type
|
||||
private final int animatorType;
|
||||
private final MapboxAnimator.AnimationsValueChangeListener listener;
|
||||
|
||||
AnimatorListenerHolder(@MapboxAnimator.Type int animatorType, MapboxAnimator.AnimationsValueChangeListener listener) {
|
||||
this.animatorType = animatorType;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@MapboxAnimator.Type
|
||||
public int getAnimatorType() {
|
||||
return animatorType;
|
||||
}
|
||||
|
||||
public MapboxAnimator.AnimationsValueChangeListener getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AnimatorListenerHolder that = (AnimatorListenerHolder) o;
|
||||
|
||||
if (animatorType != that.animatorType) {
|
||||
return false;
|
||||
}
|
||||
return listener != null ? listener.equals(that.listener) : that.listener == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = animatorType;
|
||||
result = 31 * result + (listener != null ? listener.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.mapbox.mapboxsdk.location.modes.CameraMode;
|
||||
import com.mapbox.mapboxsdk.location.modes.RenderMode;
|
||||
|
||||
/**
|
||||
* Interface defining the source of compass heading data that is
|
||||
* consumed by the {@link LocationComponent} when in compass related
|
||||
* {@link RenderMode} or
|
||||
* {@link CameraMode}s.
|
||||
*/
|
||||
public interface CompassEngine {
|
||||
|
||||
/**
|
||||
* Adds a {@link CompassListener} that can be used to
|
||||
* receive heading and state changes.
|
||||
*
|
||||
* @param compassListener to be added
|
||||
*/
|
||||
void addCompassListener(@NonNull CompassListener compassListener);
|
||||
|
||||
/**
|
||||
* Removes a {@link CompassListener} that can be used to
|
||||
* receive heading and state changes.
|
||||
*
|
||||
* @param compassListener to be removed
|
||||
*/
|
||||
void removeCompassListener(@NonNull CompassListener compassListener);
|
||||
|
||||
/**
|
||||
* Returns the last heading value produced and pushed via
|
||||
* a compass listener.
|
||||
*
|
||||
* @return last heading value
|
||||
*/
|
||||
float getLastHeading();
|
||||
|
||||
/**
|
||||
* Provides the last know accuracy status from the sensor manager.
|
||||
* <p>
|
||||
* An integer value which is identical to the {@code SensorManager} class constants:
|
||||
* <ul>
|
||||
* <li>{@link android.hardware.SensorManager#SENSOR_STATUS_NO_CONTACT}</li>
|
||||
* <li>{@link android.hardware.SensorManager#SENSOR_STATUS_UNRELIABLE}</li>
|
||||
* <li>{@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_LOW}</li>
|
||||
* <li>{@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_MEDIUM}</li>
|
||||
* <li>{@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_HIGH}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return last accuracy status
|
||||
*/
|
||||
int getLastAccuracySensorStatus();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
/**
|
||||
* Callbacks related to the compass
|
||||
*/
|
||||
public interface CompassListener {
|
||||
|
||||
/**
|
||||
* Callback's invoked when a new compass update occurs. You can listen into the compass updates
|
||||
* using {@link CompassEngine#addCompassListener(CompassListener)} and implementing these
|
||||
* callbacks. Note that this interface is also used internally to to update the UI chevron/arrow.
|
||||
*
|
||||
* @param userHeading the new compass heading
|
||||
*/
|
||||
void onCompassChanged(float userHeading);
|
||||
|
||||
/**
|
||||
* This gets invoked when the compass accuracy status changes from one value to another. It
|
||||
* provides an integer value which is identical to the {@code SensorManager} class constants:
|
||||
* <ul>
|
||||
* <li>{@link android.hardware.SensorManager#SENSOR_STATUS_NO_CONTACT}</li>
|
||||
* <li>{@link android.hardware.SensorManager#SENSOR_STATUS_UNRELIABLE}</li>
|
||||
* <li>{@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_LOW}</li>
|
||||
* <li>{@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_MEDIUM}</li>
|
||||
* <li>{@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_HIGH}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param compassStatus the new accuracy of this sensor, one of
|
||||
* {@code SensorManager.SENSOR_STATUS_*}
|
||||
*/
|
||||
void onCompassAccuracyChange(int compassStatus);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.animation.TypeEvaluator;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
|
||||
class LatLngEvaluator implements TypeEvaluator<LatLng> {
|
||||
|
||||
private final LatLng latLng = new LatLng();
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public LatLng evaluate(float fraction, @NonNull LatLng startValue, @NonNull LatLng endValue) {
|
||||
latLng.setLatitude(startValue.getLatitude()
|
||||
+ ((endValue.getLatitude() - startValue.getLatitude()) * fraction));
|
||||
latLng.setLongitude(startValue.getLongitude()
|
||||
+ ((endValue.getLongitude() - startValue.getLongitude()) * fraction));
|
||||
return latLng;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
import com.mapbox.mapboxsdk.utils.BitmapUtils;
|
||||
|
||||
import static com.mapbox.mapboxsdk.location.Utils.generateShadow;
|
||||
|
||||
class LayerBitmapProvider {
|
||||
|
||||
private final Context context;
|
||||
|
||||
LayerBitmapProvider(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
Bitmap generateBitmap(@DrawableRes int drawableRes, @ColorInt Integer tintColor) {
|
||||
Drawable drawable = BitmapUtils.getDrawableFromRes(context, drawableRes, tintColor);
|
||||
return BitmapUtils.getBitmapFromDrawable(drawable);
|
||||
}
|
||||
|
||||
Bitmap generateShadowBitmap(@NonNull LocationComponentOptions options) {
|
||||
Drawable shadowDrawable = BitmapUtils.getDrawableFromRes(context, R.drawable.mapbox_user_icon_shadow);
|
||||
return generateShadow(shadowDrawable, options.elevation());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.mapbox.geojson.Feature;
|
||||
import com.mapbox.geojson.Point;
|
||||
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_COMPASS_BEARING;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_GPS_BEARING;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_LOCATION_STALE;
|
||||
|
||||
class LayerFeatureProvider {
|
||||
|
||||
@NonNull
|
||||
Feature generateLocationFeature(@Nullable Feature locationFeature, @NonNull LocationComponentOptions options) {
|
||||
if (locationFeature != null) {
|
||||
return locationFeature;
|
||||
}
|
||||
locationFeature = Feature.fromGeometry(Point.fromLngLat(0.0, 0.0));
|
||||
locationFeature.addNumberProperty(PROPERTY_GPS_BEARING, 0f);
|
||||
locationFeature.addNumberProperty(PROPERTY_COMPASS_BEARING, 0f);
|
||||
locationFeature.addBooleanProperty(PROPERTY_LOCATION_STALE, options.enableStaleState());
|
||||
return locationFeature;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import com.mapbox.geojson.Feature;
|
||||
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
|
||||
import com.mapbox.mapboxsdk.style.layers.Layer;
|
||||
import com.mapbox.mapboxsdk.style.layers.Property;
|
||||
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
|
||||
import com.mapbox.mapboxsdk.style.sources.GeoJsonOptions;
|
||||
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
|
||||
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.ACCURACY_LAYER;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_LAYER;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BEARING_LAYER;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.FOREGROUND_LAYER;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.LOCATION_SOURCE;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_ALPHA;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_COLOR;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_RADIUS;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BACKGROUND_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BACKGROUND_STALE_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BEARING_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_COMPASS_BEARING;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_ICON_OFFSET;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_STALE_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_GPS_BEARING;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_LOCATION_STALE;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_SHADOW_ICON_OFFSET;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_LAYER;
|
||||
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
|
||||
import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
|
||||
import static com.mapbox.mapboxsdk.style.expressions.Expression.match;
|
||||
import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
|
||||
import static com.mapbox.mapboxsdk.style.expressions.Expression.switchCase;
|
||||
import static com.mapbox.mapboxsdk.style.layers.Property.ICON_ROTATION_ALIGNMENT_MAP;
|
||||
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor;
|
||||
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleOpacity;
|
||||
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circlePitchAlignment;
|
||||
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius;
|
||||
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleStrokeColor;
|
||||
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
|
||||
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconIgnorePlacement;
|
||||
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
|
||||
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconOffset;
|
||||
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconRotate;
|
||||
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconRotationAlignment;
|
||||
|
||||
class LayerSourceProvider {
|
||||
|
||||
private static final String EMPTY_STRING = "";
|
||||
|
||||
@NonNull
|
||||
GeoJsonSource generateSource(Feature locationFeature) {
|
||||
return new GeoJsonSource(
|
||||
LOCATION_SOURCE,
|
||||
locationFeature,
|
||||
new GeoJsonOptions().withMaxZoom(16)
|
||||
);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
Layer generateLayer(@NonNull String layerId) {
|
||||
SymbolLayer layer = new SymbolLayer(layerId, LOCATION_SOURCE);
|
||||
layer.setProperties(
|
||||
iconAllowOverlap(true),
|
||||
iconIgnorePlacement(true),
|
||||
iconRotationAlignment(ICON_ROTATION_ALIGNMENT_MAP),
|
||||
iconRotate(
|
||||
match(literal(layerId), literal(0f),
|
||||
stop(FOREGROUND_LAYER, get(PROPERTY_GPS_BEARING)),
|
||||
stop(BACKGROUND_LAYER, get(PROPERTY_GPS_BEARING)),
|
||||
stop(SHADOW_LAYER, get(PROPERTY_GPS_BEARING)),
|
||||
stop(BEARING_LAYER, get(PROPERTY_COMPASS_BEARING))
|
||||
)
|
||||
),
|
||||
iconImage(
|
||||
match(literal(layerId), literal(EMPTY_STRING),
|
||||
stop(FOREGROUND_LAYER, switchCase(
|
||||
get(PROPERTY_LOCATION_STALE), get(PROPERTY_FOREGROUND_STALE_ICON),
|
||||
get(PROPERTY_FOREGROUND_ICON))),
|
||||
stop(BACKGROUND_LAYER, switchCase(
|
||||
get(PROPERTY_LOCATION_STALE), get(PROPERTY_BACKGROUND_STALE_ICON),
|
||||
get(PROPERTY_BACKGROUND_ICON))),
|
||||
stop(SHADOW_LAYER, literal(SHADOW_ICON)),
|
||||
stop(BEARING_LAYER, get(PROPERTY_BEARING_ICON))
|
||||
)
|
||||
),
|
||||
iconOffset(
|
||||
match(literal(layerId), literal(new Float[] {0f, 0f}),
|
||||
stop(literal(FOREGROUND_LAYER), get(PROPERTY_FOREGROUND_ICON_OFFSET)),
|
||||
stop(literal(SHADOW_LAYER), get(PROPERTY_SHADOW_ICON_OFFSET))
|
||||
)
|
||||
)
|
||||
);
|
||||
return layer;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
Layer generateAccuracyLayer() {
|
||||
return new CircleLayer(ACCURACY_LAYER, LOCATION_SOURCE)
|
||||
.withProperties(
|
||||
circleRadius(get(PROPERTY_ACCURACY_RADIUS)),
|
||||
circleColor(get(PROPERTY_ACCURACY_COLOR)),
|
||||
circleOpacity(get(PROPERTY_ACCURACY_ALPHA)),
|
||||
circleStrokeColor(get(PROPERTY_ACCURACY_COLOR)),
|
||||
circlePitchAlignment(Property.CIRCLE_PITCH_ALIGNMENT_MAP)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,422 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.location.Location;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.util.SparseArray;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
|
||||
import com.mapbox.mapboxsdk.camera.CameraPosition;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.log.Logger;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
import com.mapbox.mapboxsdk.maps.Projection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.ACCURACY_RADIUS_ANIMATION_DURATION;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.COMPASS_UPDATE_RATE_MS;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.MAX_ANIMATION_DURATION_MS;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS;
|
||||
import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_CAMERA_COMPASS_BEARING;
|
||||
import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_CAMERA_GPS_BEARING;
|
||||
import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_CAMERA_LATLNG;
|
||||
import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_ACCURACY;
|
||||
import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_COMPASS_BEARING;
|
||||
import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_GPS_BEARING;
|
||||
import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_LATLNG;
|
||||
import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_TILT;
|
||||
import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_ZOOM;
|
||||
import static com.mapbox.mapboxsdk.location.Utils.immediateAnimation;
|
||||
|
||||
final class LocationAnimatorCoordinator {
|
||||
|
||||
private static final String TAG = "Mbgl-LocationAnimatorCoordinator";
|
||||
|
||||
@VisibleForTesting
|
||||
final SparseArray<MapboxAnimator> animatorArray = new SparseArray<>();
|
||||
|
||||
private final Projection projection;
|
||||
private Location previousLocation;
|
||||
private float previousAccuracyRadius = -1;
|
||||
private float previousCompassBearing = -1;
|
||||
private long locationUpdateTimestamp = -1;
|
||||
private float durationMultiplier;
|
||||
private final MapboxAnimatorProvider animatorProvider;
|
||||
private final MapboxAnimatorSetProvider animatorSetProvider;
|
||||
private boolean compassAnimationEnabled;
|
||||
private boolean accuracyAnimationEnabled;
|
||||
|
||||
@VisibleForTesting
|
||||
int maxAnimationFps = Integer.MAX_VALUE;
|
||||
|
||||
@VisibleForTesting
|
||||
final SparseArray<MapboxAnimator.AnimationsValueChangeListener> listeners = new SparseArray<>();
|
||||
|
||||
LocationAnimatorCoordinator(@NonNull Projection projection, @NonNull MapboxAnimatorSetProvider animatorSetProvider,
|
||||
@NonNull MapboxAnimatorProvider animatorProvider) {
|
||||
this.projection = projection;
|
||||
this.animatorProvider = animatorProvider;
|
||||
this.animatorSetProvider = animatorSetProvider;
|
||||
}
|
||||
|
||||
void updateAnimatorListenerHolders(@NonNull Set<AnimatorListenerHolder> listenerHolders) {
|
||||
listeners.clear();
|
||||
for (AnimatorListenerHolder holder : listenerHolders) {
|
||||
listeners.append(holder.getAnimatorType(), holder.getListener());
|
||||
}
|
||||
}
|
||||
|
||||
void feedNewLocation(@NonNull Location newLocation, @NonNull CameraPosition currentCameraPosition,
|
||||
boolean isGpsNorth) {
|
||||
if (previousLocation == null) {
|
||||
previousLocation = newLocation;
|
||||
locationUpdateTimestamp = SystemClock.elapsedRealtime() - TRANSITION_ANIMATION_DURATION_MS;
|
||||
}
|
||||
|
||||
LatLng previousLayerLatLng = getPreviousLayerLatLng();
|
||||
float previousLayerBearing = getPreviousLayerGpsBearing();
|
||||
LatLng previousCameraLatLng = currentCameraPosition.target;
|
||||
float previousCameraBearing = (float) currentCameraPosition.bearing;
|
||||
|
||||
LatLng targetLatLng = new LatLng(newLocation);
|
||||
float targetLayerBearing = newLocation.getBearing();
|
||||
float targetCameraBearing = newLocation.getBearing();
|
||||
targetCameraBearing = checkGpsNorth(isGpsNorth, targetCameraBearing);
|
||||
|
||||
updateLayerAnimators(previousLayerLatLng, targetLatLng, previousLayerBearing, targetLayerBearing);
|
||||
updateCameraAnimators(previousCameraLatLng, previousCameraBearing, targetLatLng, targetCameraBearing);
|
||||
|
||||
boolean snap = immediateAnimation(projection, previousCameraLatLng, targetLatLng)
|
||||
|| immediateAnimation(projection, previousLayerLatLng, targetLatLng);
|
||||
|
||||
long animationDuration = 0;
|
||||
if (!snap) {
|
||||
long previousUpdateTimeStamp = locationUpdateTimestamp;
|
||||
locationUpdateTimestamp = SystemClock.elapsedRealtime();
|
||||
|
||||
if (previousUpdateTimeStamp == 0) {
|
||||
animationDuration = 0;
|
||||
} else {
|
||||
animationDuration = (long) ((locationUpdateTimestamp - previousUpdateTimeStamp) * durationMultiplier)
|
||||
/* make animation slightly longer with durationMultiplier, defaults to 1.1f */;
|
||||
}
|
||||
|
||||
animationDuration = Math.min(animationDuration, MAX_ANIMATION_DURATION_MS);
|
||||
}
|
||||
|
||||
playAnimators(animationDuration,
|
||||
ANIMATOR_LAYER_LATLNG,
|
||||
ANIMATOR_LAYER_GPS_BEARING,
|
||||
ANIMATOR_CAMERA_LATLNG,
|
||||
ANIMATOR_CAMERA_GPS_BEARING);
|
||||
|
||||
previousLocation = newLocation;
|
||||
}
|
||||
|
||||
void feedNewCompassBearing(float targetCompassBearing, @NonNull CameraPosition currentCameraPosition) {
|
||||
if (previousCompassBearing < 0) {
|
||||
previousCompassBearing = targetCompassBearing;
|
||||
}
|
||||
|
||||
float previousLayerBearing = getPreviousLayerCompassBearing();
|
||||
float previousCameraBearing = (float) currentCameraPosition.bearing;
|
||||
|
||||
updateCompassAnimators(targetCompassBearing, previousLayerBearing, previousCameraBearing);
|
||||
playAnimators(
|
||||
compassAnimationEnabled ? COMPASS_UPDATE_RATE_MS : 0,
|
||||
ANIMATOR_LAYER_COMPASS_BEARING,
|
||||
ANIMATOR_CAMERA_COMPASS_BEARING);
|
||||
|
||||
previousCompassBearing = targetCompassBearing;
|
||||
}
|
||||
|
||||
void feedNewAccuracyRadius(float targetAccuracyRadius, boolean noAnimation) {
|
||||
if (previousAccuracyRadius < 0) {
|
||||
previousAccuracyRadius = targetAccuracyRadius;
|
||||
}
|
||||
|
||||
float previousAccuracyRadius = getPreviousAccuracyRadius();
|
||||
updateAccuracyAnimators(targetAccuracyRadius, previousAccuracyRadius);
|
||||
playAnimators(
|
||||
noAnimation || !accuracyAnimationEnabled ? 0 : ACCURACY_RADIUS_ANIMATION_DURATION,
|
||||
ANIMATOR_LAYER_ACCURACY);
|
||||
|
||||
this.previousAccuracyRadius = targetAccuracyRadius;
|
||||
}
|
||||
|
||||
void feedNewZoomLevel(double targetZoomLevel, @NonNull CameraPosition currentCameraPosition, long animationDuration,
|
||||
@Nullable MapboxMap.CancelableCallback callback) {
|
||||
updateZoomAnimator((float) targetZoomLevel, (float) currentCameraPosition.zoom, callback);
|
||||
playAnimators(animationDuration, ANIMATOR_ZOOM);
|
||||
}
|
||||
|
||||
void feedNewTilt(double targetTilt, @NonNull CameraPosition currentCameraPosition, long animationDuration,
|
||||
@Nullable MapboxMap.CancelableCallback callback) {
|
||||
updateTiltAnimator((float) targetTilt, (float) currentCameraPosition.tilt, callback);
|
||||
playAnimators(animationDuration, ANIMATOR_TILT);
|
||||
}
|
||||
|
||||
private LatLng getPreviousLayerLatLng() {
|
||||
LatLng previousLatLng;
|
||||
MapboxAnimator latLngAnimator = animatorArray.get(ANIMATOR_LAYER_LATLNG);
|
||||
if (latLngAnimator != null) {
|
||||
previousLatLng = (LatLng) latLngAnimator.getAnimatedValue();
|
||||
} else {
|
||||
previousLatLng = new LatLng(previousLocation);
|
||||
}
|
||||
return previousLatLng;
|
||||
}
|
||||
|
||||
private float getPreviousLayerGpsBearing() {
|
||||
MapboxFloatAnimator animator = (MapboxFloatAnimator) animatorArray.get(ANIMATOR_LAYER_GPS_BEARING);
|
||||
float previousBearing;
|
||||
if (animator != null) {
|
||||
previousBearing = (float) animator.getAnimatedValue();
|
||||
} else {
|
||||
previousBearing = previousLocation.getBearing();
|
||||
}
|
||||
return previousBearing;
|
||||
}
|
||||
|
||||
private float getPreviousLayerCompassBearing() {
|
||||
MapboxFloatAnimator animator = (MapboxFloatAnimator) animatorArray.get(ANIMATOR_LAYER_COMPASS_BEARING);
|
||||
|
||||
float previousBearing;
|
||||
if (animator != null) {
|
||||
previousBearing = (float) animator.getAnimatedValue();
|
||||
} else {
|
||||
previousBearing = previousCompassBearing;
|
||||
}
|
||||
return previousBearing;
|
||||
}
|
||||
|
||||
private float getPreviousAccuracyRadius() {
|
||||
MapboxAnimator animator = animatorArray.get(ANIMATOR_LAYER_ACCURACY);
|
||||
float previousRadius;
|
||||
if (animator != null) {
|
||||
previousRadius = (float) animator.getAnimatedValue();
|
||||
} else {
|
||||
previousRadius = previousAccuracyRadius;
|
||||
}
|
||||
return previousRadius;
|
||||
}
|
||||
|
||||
private void updateLayerAnimators(LatLng previousLatLng, LatLng targetLatLng,
|
||||
float previousBearing, float targetBearing) {
|
||||
createNewLatLngAnimator(ANIMATOR_LAYER_LATLNG, previousLatLng, targetLatLng);
|
||||
|
||||
// Because Location bearing values are normalized to [0, 360]
|
||||
// we need to do the same for the previous bearing value to determine the shortest path
|
||||
previousBearing = Utils.normalize(previousBearing);
|
||||
float normalizedLayerBearing = Utils.shortestRotation(targetBearing, previousBearing);
|
||||
createNewFloatAnimator(ANIMATOR_LAYER_GPS_BEARING, previousBearing, normalizedLayerBearing);
|
||||
}
|
||||
|
||||
private void updateCameraAnimators(LatLng previousCameraLatLng, float previousCameraBearing,
|
||||
LatLng targetLatLng, float targetBearing) {
|
||||
createNewLatLngAnimator(ANIMATOR_CAMERA_LATLNG, previousCameraLatLng, targetLatLng);
|
||||
|
||||
float normalizedCameraBearing = Utils.shortestRotation(targetBearing, previousCameraBearing);
|
||||
createNewFloatAnimator(ANIMATOR_CAMERA_GPS_BEARING, previousCameraBearing, normalizedCameraBearing);
|
||||
}
|
||||
|
||||
private void updateCompassAnimators(float targetCompassBearing, float previousLayerBearing,
|
||||
float previousCameraBearing) {
|
||||
float normalizedLayerBearing = Utils.shortestRotation(targetCompassBearing, previousLayerBearing);
|
||||
createNewFloatAnimator(ANIMATOR_LAYER_COMPASS_BEARING, previousLayerBearing, normalizedLayerBearing);
|
||||
|
||||
float normalizedCameraBearing = Utils.shortestRotation(targetCompassBearing, previousCameraBearing);
|
||||
createNewFloatAnimator(ANIMATOR_CAMERA_COMPASS_BEARING, previousCameraBearing, normalizedCameraBearing);
|
||||
}
|
||||
|
||||
private void updateAccuracyAnimators(float targetAccuracyRadius, float previousAccuracyRadius) {
|
||||
createNewFloatAnimator(ANIMATOR_LAYER_ACCURACY, previousAccuracyRadius, targetAccuracyRadius);
|
||||
}
|
||||
|
||||
private void updateZoomAnimator(float targetZoomLevel, float previousZoomLevel,
|
||||
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
|
||||
createNewCameraAdapterAnimator(ANIMATOR_ZOOM, previousZoomLevel, targetZoomLevel, cancelableCallback);
|
||||
}
|
||||
|
||||
private void updateTiltAnimator(float targetTilt, float previousTiltLevel,
|
||||
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
|
||||
createNewCameraAdapterAnimator(ANIMATOR_TILT, previousTiltLevel, targetTilt, cancelableCallback);
|
||||
}
|
||||
|
||||
private void createNewLatLngAnimator(@MapboxAnimator.Type int animatorType, LatLng previous, LatLng target) {
|
||||
cancelAnimator(animatorType);
|
||||
MapboxAnimator.AnimationsValueChangeListener listener = listeners.get(animatorType);
|
||||
if (listener != null) {
|
||||
animatorArray.put(animatorType, animatorProvider.latLngAnimator(previous, target, listener, maxAnimationFps));
|
||||
}
|
||||
}
|
||||
|
||||
private void createNewFloatAnimator(@MapboxAnimator.Type int animatorType, float previous, float target) {
|
||||
cancelAnimator(animatorType);
|
||||
MapboxAnimator.AnimationsValueChangeListener listener = listeners.get(animatorType);
|
||||
if (listener != null) {
|
||||
animatorArray.put(animatorType, animatorProvider.floatAnimator(previous, target, listener, maxAnimationFps));
|
||||
}
|
||||
}
|
||||
|
||||
private void createNewCameraAdapterAnimator(@MapboxAnimator.Type int animatorType, float previous, float target,
|
||||
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
|
||||
cancelAnimator(animatorType);
|
||||
MapboxAnimator.AnimationsValueChangeListener listener = listeners.get(animatorType);
|
||||
if (listener != null) {
|
||||
animatorArray.put(animatorType, animatorProvider.cameraAnimator(previous, target, listener, cancelableCallback));
|
||||
}
|
||||
}
|
||||
|
||||
private float checkGpsNorth(boolean isGpsNorth, float targetCameraBearing) {
|
||||
if (isGpsNorth) {
|
||||
targetCameraBearing = 0;
|
||||
}
|
||||
return targetCameraBearing;
|
||||
}
|
||||
|
||||
private void playAnimators(long duration, @MapboxAnimator.Type int... animatorTypes) {
|
||||
List<Animator> animators = new ArrayList<>();
|
||||
for (@MapboxAnimator.Type int animatorType : animatorTypes) {
|
||||
Animator animator = animatorArray.get(animatorType);
|
||||
if (animator != null) {
|
||||
animators.add(animator);
|
||||
}
|
||||
}
|
||||
animatorSetProvider.startAnimation(animators, new LinearInterpolator(), duration);
|
||||
}
|
||||
|
||||
void resetAllCameraAnimations(@NonNull CameraPosition currentCameraPosition, boolean isGpsNorth) {
|
||||
resetCameraCompassAnimation(currentCameraPosition);
|
||||
boolean snap = resetCameraLocationAnimations(currentCameraPosition, isGpsNorth);
|
||||
playAnimators(
|
||||
snap ? 0 : TRANSITION_ANIMATION_DURATION_MS,
|
||||
ANIMATOR_CAMERA_LATLNG,
|
||||
ANIMATOR_CAMERA_GPS_BEARING);
|
||||
}
|
||||
|
||||
private boolean resetCameraLocationAnimations(@NonNull CameraPosition currentCameraPosition, boolean isGpsNorth) {
|
||||
resetCameraGpsBearingAnimation(currentCameraPosition, isGpsNorth);
|
||||
return resetCameraLatLngAnimation(currentCameraPosition);
|
||||
}
|
||||
|
||||
private boolean resetCameraLatLngAnimation(@NonNull CameraPosition currentCameraPosition) {
|
||||
MapboxLatLngAnimator animator = (MapboxLatLngAnimator) animatorArray.get(ANIMATOR_CAMERA_LATLNG);
|
||||
if (animator == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LatLng currentTarget = animator.getTarget();
|
||||
LatLng previousCameraTarget = currentCameraPosition.target;
|
||||
createNewLatLngAnimator(ANIMATOR_CAMERA_LATLNG, previousCameraTarget, currentTarget);
|
||||
|
||||
return immediateAnimation(projection, previousCameraTarget, currentTarget);
|
||||
}
|
||||
|
||||
private void resetCameraGpsBearingAnimation(@NonNull CameraPosition currentCameraPosition, boolean isGpsNorth) {
|
||||
MapboxFloatAnimator animator = (MapboxFloatAnimator) animatorArray.get(ANIMATOR_CAMERA_GPS_BEARING);
|
||||
if (animator == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
float currentTargetBearing = animator.getTarget();
|
||||
currentTargetBearing = checkGpsNorth(isGpsNorth, currentTargetBearing);
|
||||
float previousCameraBearing = (float) currentCameraPosition.bearing;
|
||||
float normalizedCameraBearing = Utils.shortestRotation(currentTargetBearing, previousCameraBearing);
|
||||
createNewFloatAnimator(ANIMATOR_CAMERA_GPS_BEARING, previousCameraBearing, normalizedCameraBearing);
|
||||
}
|
||||
|
||||
private void resetCameraCompassAnimation(@NonNull CameraPosition currentCameraPosition) {
|
||||
MapboxFloatAnimator animator =
|
||||
(MapboxFloatAnimator) animatorArray.get(ANIMATOR_CAMERA_COMPASS_BEARING);
|
||||
if (animator == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
float currentTargetBearing = animator.getTarget();
|
||||
float previousCameraBearing = (float) currentCameraPosition.bearing;
|
||||
float normalizedCameraBearing = Utils.shortestRotation(currentTargetBearing, previousCameraBearing);
|
||||
createNewFloatAnimator(ANIMATOR_CAMERA_COMPASS_BEARING, previousCameraBearing, normalizedCameraBearing);
|
||||
}
|
||||
|
||||
void resetAllLayerAnimations() {
|
||||
MapboxLatLngAnimator latLngAnimator = (MapboxLatLngAnimator) animatorArray.get(ANIMATOR_LAYER_LATLNG);
|
||||
MapboxFloatAnimator gpsBearingAnimator = (MapboxFloatAnimator) animatorArray.get(ANIMATOR_LAYER_GPS_BEARING);
|
||||
MapboxFloatAnimator compassBearingAnimator =
|
||||
(MapboxFloatAnimator) animatorArray.get(ANIMATOR_LAYER_COMPASS_BEARING);
|
||||
|
||||
if (latLngAnimator != null && gpsBearingAnimator != null) {
|
||||
LatLng currentLatLng = (LatLng) latLngAnimator.getAnimatedValue();
|
||||
LatLng currentLatLngTarget = latLngAnimator.getTarget();
|
||||
createNewLatLngAnimator(ANIMATOR_LAYER_LATLNG, currentLatLng, currentLatLngTarget);
|
||||
|
||||
float currentGpsBearing = (float) gpsBearingAnimator.getAnimatedValue();
|
||||
float currentGpsBearingTarget = gpsBearingAnimator.getTarget();
|
||||
createNewFloatAnimator(ANIMATOR_LAYER_GPS_BEARING, currentGpsBearing, currentGpsBearingTarget);
|
||||
|
||||
long duration = latLngAnimator.getDuration() - latLngAnimator.getCurrentPlayTime();
|
||||
|
||||
playAnimators(duration, ANIMATOR_LAYER_LATLNG, ANIMATOR_LAYER_GPS_BEARING);
|
||||
}
|
||||
|
||||
if (compassBearingAnimator != null) {
|
||||
float currentLayerBearing = getPreviousLayerCompassBearing();
|
||||
float currentLayerBearingTarget = compassBearingAnimator.getTarget();
|
||||
createNewFloatAnimator(ANIMATOR_LAYER_COMPASS_BEARING, currentLayerBearing, currentLayerBearingTarget);
|
||||
playAnimators(
|
||||
compassAnimationEnabled ? COMPASS_UPDATE_RATE_MS : 0,
|
||||
ANIMATOR_LAYER_COMPASS_BEARING);
|
||||
}
|
||||
}
|
||||
|
||||
void cancelZoomAnimation() {
|
||||
cancelAnimator(ANIMATOR_ZOOM);
|
||||
}
|
||||
|
||||
void cancelTiltAnimation() {
|
||||
cancelAnimator(ANIMATOR_TILT);
|
||||
}
|
||||
|
||||
void cancelAllAnimations() {
|
||||
for (int i = 0; i < animatorArray.size(); i++) {
|
||||
@MapboxAnimator.Type int animatorType = animatorArray.keyAt(i);
|
||||
cancelAnimator(animatorType);
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelAnimator(@MapboxAnimator.Type int animatorType) {
|
||||
MapboxAnimator animator = animatorArray.get(animatorType);
|
||||
if (animator != null) {
|
||||
animator.cancel();
|
||||
animator.removeAllUpdateListeners();
|
||||
animator.removeAllListeners();
|
||||
animatorArray.put(animatorType, null);
|
||||
}
|
||||
}
|
||||
|
||||
void setTrackingAnimationDurationMultiplier(float trackingAnimationDurationMultiplier) {
|
||||
this.durationMultiplier = trackingAnimationDurationMultiplier;
|
||||
}
|
||||
|
||||
void setCompassAnimationEnabled(boolean compassAnimationEnabled) {
|
||||
this.compassAnimationEnabled = compassAnimationEnabled;
|
||||
}
|
||||
|
||||
void setAccuracyAnimationEnabled(boolean accuracyAnimationEnabled) {
|
||||
this.accuracyAnimationEnabled = accuracyAnimationEnabled;
|
||||
}
|
||||
|
||||
void setMaxAnimationFps(int maxAnimationFps) {
|
||||
if (maxAnimationFps <= 0) {
|
||||
Logger.e(TAG, "Max animation FPS cannot be less or equal to 0.");
|
||||
return;
|
||||
}
|
||||
this.maxAnimationFps = maxAnimationFps;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,437 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PointF;
|
||||
import android.location.Location;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.mapbox.android.gestures.AndroidGesturesManager;
|
||||
import com.mapbox.android.gestures.MoveGestureDetector;
|
||||
import com.mapbox.android.gestures.RotateGestureDetector;
|
||||
import com.mapbox.mapboxsdk.camera.CameraPosition;
|
||||
import com.mapbox.mapboxsdk.camera.CameraUpdate;
|
||||
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.location.modes.CameraMode;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
import com.mapbox.mapboxsdk.maps.Transform;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS;
|
||||
|
||||
final class LocationCameraController {
|
||||
|
||||
@CameraMode.Mode
|
||||
private int cameraMode;
|
||||
|
||||
private final MapboxMap mapboxMap;
|
||||
private final Transform transform;
|
||||
private final OnCameraTrackingChangedListener internalCameraTrackingChangedListener;
|
||||
private LocationComponentOptions options;
|
||||
private boolean adjustFocalPoint;
|
||||
|
||||
private final MoveGestureDetector moveGestureDetector;
|
||||
private final OnCameraMoveInvalidateListener onCameraMoveInvalidateListener;
|
||||
|
||||
private final AndroidGesturesManager initialGesturesManager;
|
||||
private final AndroidGesturesManager internalGesturesManager;
|
||||
|
||||
private boolean isTransitioning;
|
||||
|
||||
LocationCameraController(
|
||||
Context context,
|
||||
MapboxMap mapboxMap,
|
||||
Transform transform,
|
||||
OnCameraTrackingChangedListener internalCameraTrackingChangedListener,
|
||||
@NonNull LocationComponentOptions options,
|
||||
OnCameraMoveInvalidateListener onCameraMoveInvalidateListener) {
|
||||
this.mapboxMap = mapboxMap;
|
||||
this.transform = transform;
|
||||
|
||||
initialGesturesManager = mapboxMap.getGesturesManager();
|
||||
internalGesturesManager = new LocationGesturesManager(context);
|
||||
moveGestureDetector = internalGesturesManager.getMoveGestureDetector();
|
||||
mapboxMap.addOnRotateListener(onRotateListener);
|
||||
mapboxMap.addOnFlingListener(onFlingListener);
|
||||
mapboxMap.addOnMoveListener(onMoveListener);
|
||||
|
||||
this.internalCameraTrackingChangedListener = internalCameraTrackingChangedListener;
|
||||
this.onCameraMoveInvalidateListener = onCameraMoveInvalidateListener;
|
||||
initializeOptions(options);
|
||||
}
|
||||
|
||||
// Package private for testing purposes
|
||||
LocationCameraController(MapboxMap mapboxMap,
|
||||
Transform transform,
|
||||
MoveGestureDetector moveGestureDetector,
|
||||
OnCameraTrackingChangedListener internalCameraTrackingChangedListener,
|
||||
OnCameraMoveInvalidateListener onCameraMoveInvalidateListener,
|
||||
AndroidGesturesManager initialGesturesManager,
|
||||
AndroidGesturesManager internalGesturesManager) {
|
||||
this.mapboxMap = mapboxMap;
|
||||
this.transform = transform;
|
||||
this.moveGestureDetector = moveGestureDetector;
|
||||
this.internalCameraTrackingChangedListener = internalCameraTrackingChangedListener;
|
||||
this.onCameraMoveInvalidateListener = onCameraMoveInvalidateListener;
|
||||
this.internalGesturesManager = internalGesturesManager;
|
||||
this.initialGesturesManager = initialGesturesManager;
|
||||
}
|
||||
|
||||
void initializeOptions(LocationComponentOptions options) {
|
||||
this.options = options;
|
||||
if (options.trackingGesturesManagement()) {
|
||||
if (mapboxMap.getGesturesManager() != internalGesturesManager) {
|
||||
mapboxMap.setGesturesManager(internalGesturesManager, true, true);
|
||||
}
|
||||
adjustGesturesThresholds();
|
||||
} else if (mapboxMap.getGesturesManager() != initialGesturesManager) {
|
||||
mapboxMap.setGesturesManager(initialGesturesManager, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
void setCameraMode(@CameraMode.Mode int cameraMode) {
|
||||
setCameraMode(cameraMode, null, TRANSITION_ANIMATION_DURATION_MS, null, null, null, null);
|
||||
}
|
||||
|
||||
void setCameraMode(@CameraMode.Mode final int cameraMode, @Nullable Location lastLocation,
|
||||
long transitionDuration,
|
||||
@Nullable Double zoom, @Nullable Double bearing, @Nullable Double tilt,
|
||||
@Nullable OnLocationCameraTransitionListener internalTransitionListener) {
|
||||
if (this.cameraMode == cameraMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean wasTracking = isLocationTracking();
|
||||
this.cameraMode = cameraMode;
|
||||
|
||||
if (cameraMode != CameraMode.NONE) {
|
||||
mapboxMap.cancelTransitions();
|
||||
}
|
||||
|
||||
adjustGesturesThresholds();
|
||||
notifyCameraTrackingChangeListener(wasTracking);
|
||||
transitionToCurrentLocation(
|
||||
wasTracking, lastLocation, transitionDuration, zoom, bearing, tilt, internalTransitionListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates a camera animation to the current location if location tracking was engaged.
|
||||
* Notifies an internal listener when the transition's finished to invalidate animators and notify external listeners.
|
||||
*/
|
||||
private void transitionToCurrentLocation(boolean wasTracking, Location lastLocation,
|
||||
long transitionDuration,
|
||||
Double zoom, Double bearing, Double tilt,
|
||||
final OnLocationCameraTransitionListener internalTransitionListener) {
|
||||
if (!wasTracking && isLocationTracking() && lastLocation != null) {
|
||||
isTransitioning = true;
|
||||
LatLng target = new LatLng(lastLocation);
|
||||
|
||||
CameraPosition.Builder builder = new CameraPosition.Builder().target(target);
|
||||
if (zoom != null) {
|
||||
builder.zoom(zoom);
|
||||
}
|
||||
if (tilt != null) {
|
||||
builder.tilt(tilt);
|
||||
}
|
||||
if (bearing != null) {
|
||||
builder.bearing(bearing);
|
||||
} else {
|
||||
if (isLocationBearingTracking()) {
|
||||
builder.bearing(cameraMode == CameraMode.TRACKING_GPS_NORTH ? 0 : lastLocation.getBearing());
|
||||
}
|
||||
}
|
||||
|
||||
CameraUpdate update = CameraUpdateFactory.newCameraPosition(builder.build());
|
||||
MapboxMap.CancelableCallback callback = new MapboxMap.CancelableCallback() {
|
||||
@Override
|
||||
public void onCancel() {
|
||||
isTransitioning = false;
|
||||
if (internalTransitionListener != null) {
|
||||
internalTransitionListener.onLocationCameraTransitionCanceled(cameraMode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
isTransitioning = false;
|
||||
if (internalTransitionListener != null) {
|
||||
internalTransitionListener.onLocationCameraTransitionFinished(cameraMode);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
CameraPosition currentPosition = mapboxMap.getCameraPosition();
|
||||
if (Utils.immediateAnimation(mapboxMap.getProjection(), currentPosition.target, target)) {
|
||||
transform.moveCamera(
|
||||
mapboxMap,
|
||||
update,
|
||||
callback);
|
||||
} else {
|
||||
transform.animateCamera(
|
||||
mapboxMap,
|
||||
update,
|
||||
(int) transitionDuration,
|
||||
callback);
|
||||
}
|
||||
} else {
|
||||
if (internalTransitionListener != null) {
|
||||
internalTransitionListener.onLocationCameraTransitionFinished(cameraMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int getCameraMode() {
|
||||
return cameraMode;
|
||||
}
|
||||
|
||||
private void setBearing(float bearing) {
|
||||
if (isTransitioning) {
|
||||
return;
|
||||
}
|
||||
|
||||
transform.moveCamera(mapboxMap, CameraUpdateFactory.bearingTo(bearing), null);
|
||||
onCameraMoveInvalidateListener.onInvalidateCameraMove();
|
||||
}
|
||||
|
||||
private void setLatLng(@NonNull LatLng latLng) {
|
||||
if (isTransitioning) {
|
||||
return;
|
||||
}
|
||||
|
||||
transform.moveCamera(mapboxMap, CameraUpdateFactory.newLatLng(latLng), null);
|
||||
onCameraMoveInvalidateListener.onInvalidateCameraMove();
|
||||
|
||||
if (adjustFocalPoint) {
|
||||
PointF focalPoint = mapboxMap.getProjection().toScreenLocation(latLng);
|
||||
mapboxMap.getUiSettings().setFocalPoint(focalPoint);
|
||||
adjustFocalPoint = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void setZoom(float zoom) {
|
||||
if (isTransitioning) {
|
||||
return;
|
||||
}
|
||||
|
||||
transform.moveCamera(mapboxMap, CameraUpdateFactory.zoomTo(zoom), null);
|
||||
onCameraMoveInvalidateListener.onInvalidateCameraMove();
|
||||
}
|
||||
|
||||
private void setTilt(float tilt) {
|
||||
if (isTransitioning) {
|
||||
return;
|
||||
}
|
||||
|
||||
transform.moveCamera(mapboxMap, CameraUpdateFactory.tiltTo(tilt), null);
|
||||
onCameraMoveInvalidateListener.onInvalidateCameraMove();
|
||||
}
|
||||
|
||||
private final MapboxAnimator.AnimationsValueChangeListener<LatLng> latLngValueListener =
|
||||
new MapboxAnimator.AnimationsValueChangeListener<LatLng>() {
|
||||
@Override
|
||||
public void onNewAnimationValue(LatLng value) {
|
||||
setLatLng(value);
|
||||
}
|
||||
};
|
||||
|
||||
private final MapboxAnimator.AnimationsValueChangeListener<Float> gpsBearingValueListener =
|
||||
new MapboxAnimator.AnimationsValueChangeListener<Float>() {
|
||||
@Override
|
||||
public void onNewAnimationValue(Float value) {
|
||||
boolean trackingNorth = cameraMode == CameraMode.TRACKING_GPS_NORTH
|
||||
&& mapboxMap.getCameraPosition().bearing == 0;
|
||||
|
||||
if (!trackingNorth) {
|
||||
setBearing(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final MapboxAnimator.AnimationsValueChangeListener<Float> compassBearingValueListener =
|
||||
new MapboxAnimator.AnimationsValueChangeListener<Float>() {
|
||||
@Override
|
||||
public void onNewAnimationValue(Float value) {
|
||||
if (cameraMode == CameraMode.TRACKING_COMPASS
|
||||
|| cameraMode == CameraMode.NONE_COMPASS) {
|
||||
setBearing(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final MapboxAnimator.AnimationsValueChangeListener<Float> zoomValueListener =
|
||||
new MapboxAnimator.AnimationsValueChangeListener<Float>() {
|
||||
@Override
|
||||
public void onNewAnimationValue(Float value) {
|
||||
setZoom(value);
|
||||
}
|
||||
};
|
||||
|
||||
private final MapboxAnimator.AnimationsValueChangeListener<Float> tiltValueListener =
|
||||
new MapboxAnimator.AnimationsValueChangeListener<Float>() {
|
||||
@Override
|
||||
public void onNewAnimationValue(Float value) {
|
||||
setTilt(value);
|
||||
}
|
||||
};
|
||||
|
||||
Set<AnimatorListenerHolder> getAnimationListeners() {
|
||||
Set<AnimatorListenerHolder> holders = new HashSet<>();
|
||||
if (isLocationTracking()) {
|
||||
holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_CAMERA_LATLNG, latLngValueListener));
|
||||
}
|
||||
|
||||
if (isLocationBearingTracking()) {
|
||||
holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_CAMERA_GPS_BEARING, gpsBearingValueListener));
|
||||
}
|
||||
|
||||
if (isConsumingCompass()) {
|
||||
holders.add(new AnimatorListenerHolder(
|
||||
MapboxAnimator.ANIMATOR_CAMERA_COMPASS_BEARING,
|
||||
compassBearingValueListener));
|
||||
}
|
||||
|
||||
holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_ZOOM, zoomValueListener));
|
||||
holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_TILT, tiltValueListener));
|
||||
return holders;
|
||||
}
|
||||
|
||||
boolean isTransitioning() {
|
||||
return isTransitioning;
|
||||
}
|
||||
|
||||
private void adjustGesturesThresholds() {
|
||||
if (options.trackingGesturesManagement()) {
|
||||
if (isLocationTracking()) {
|
||||
adjustFocalPoint = true;
|
||||
moveGestureDetector.setMoveThreshold(options.trackingInitialMoveThreshold());
|
||||
} else {
|
||||
moveGestureDetector.setMoveThreshold(0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean isConsumingCompass() {
|
||||
return cameraMode == CameraMode.TRACKING_COMPASS
|
||||
|| cameraMode == CameraMode.NONE_COMPASS;
|
||||
}
|
||||
|
||||
private boolean isLocationTracking() {
|
||||
return cameraMode == CameraMode.TRACKING
|
||||
|| cameraMode == CameraMode.TRACKING_COMPASS
|
||||
|| cameraMode == CameraMode.TRACKING_GPS
|
||||
|| cameraMode == CameraMode.TRACKING_GPS_NORTH;
|
||||
}
|
||||
|
||||
private boolean isBearingTracking() {
|
||||
return cameraMode == CameraMode.NONE_COMPASS
|
||||
|| cameraMode == CameraMode.TRACKING_COMPASS
|
||||
|| cameraMode == CameraMode.NONE_GPS
|
||||
|| cameraMode == CameraMode.TRACKING_GPS
|
||||
|| cameraMode == CameraMode.TRACKING_GPS_NORTH;
|
||||
}
|
||||
|
||||
private boolean isLocationBearingTracking() {
|
||||
return cameraMode == CameraMode.TRACKING_GPS
|
||||
|| cameraMode == CameraMode.TRACKING_GPS_NORTH
|
||||
|| cameraMode == CameraMode.NONE_GPS;
|
||||
}
|
||||
|
||||
private void notifyCameraTrackingChangeListener(boolean wasTracking) {
|
||||
internalCameraTrackingChangedListener.onCameraTrackingChanged(cameraMode);
|
||||
if (wasTracking && !isLocationTracking()) {
|
||||
mapboxMap.getUiSettings().setFocalPoint(null);
|
||||
internalCameraTrackingChangedListener.onCameraTrackingDismissed();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@VisibleForTesting
|
||||
MapboxMap.OnMoveListener onMoveListener = new MapboxMap.OnMoveListener() {
|
||||
private boolean interrupt;
|
||||
|
||||
@Override
|
||||
public void onMoveBegin(@NonNull MoveGestureDetector detector) {
|
||||
if (options.trackingGesturesManagement()
|
||||
&& detector.getPointersCount() > 1
|
||||
&& detector.getMoveThreshold() != options.trackingMultiFingerMoveThreshold()
|
||||
&& isLocationTracking()) {
|
||||
detector.setMoveThreshold(options.trackingMultiFingerMoveThreshold());
|
||||
interrupt = true;
|
||||
} else {
|
||||
setCameraMode(CameraMode.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMove(@NonNull MoveGestureDetector detector) {
|
||||
if (interrupt) {
|
||||
detector.interrupt();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isLocationTracking() || isBearingTracking()) {
|
||||
setCameraMode(CameraMode.NONE);
|
||||
detector.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMoveEnd(@NonNull MoveGestureDetector detector) {
|
||||
if (options.trackingGesturesManagement() && !interrupt && isLocationTracking()) {
|
||||
detector.setMoveThreshold(options.trackingInitialMoveThreshold());
|
||||
}
|
||||
interrupt = false;
|
||||
}
|
||||
};
|
||||
|
||||
@NonNull
|
||||
private MapboxMap.OnRotateListener onRotateListener = new MapboxMap.OnRotateListener() {
|
||||
@Override
|
||||
public void onRotateBegin(@NonNull RotateGestureDetector detector) {
|
||||
if (isBearingTracking()) {
|
||||
setCameraMode(CameraMode.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRotate(@NonNull RotateGestureDetector detector) {
|
||||
// no implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRotateEnd(@NonNull RotateGestureDetector detector) {
|
||||
// no implementation
|
||||
}
|
||||
};
|
||||
|
||||
@NonNull
|
||||
private MapboxMap.OnFlingListener onFlingListener = new MapboxMap.OnFlingListener() {
|
||||
@Override
|
||||
public void onFling() {
|
||||
setCameraMode(CameraMode.NONE);
|
||||
}
|
||||
};
|
||||
|
||||
private class LocationGesturesManager extends AndroidGesturesManager {
|
||||
|
||||
LocationGesturesManager(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(@Nullable MotionEvent motionEvent) {
|
||||
if (motionEvent != null) {
|
||||
int action = motionEvent.getActionMasked();
|
||||
if (action == MotionEvent.ACTION_UP) {
|
||||
adjustGesturesThresholds();
|
||||
}
|
||||
}
|
||||
return super.onTouchEvent(motionEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,244 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.mapbox.android.core.location.LocationEngine;
|
||||
import com.mapbox.android.core.location.LocationEngineRequest;
|
||||
import com.mapbox.mapboxsdk.maps.Style;
|
||||
|
||||
/**
|
||||
* A class which holds the various options for activating the Maps SDK's {@link LocationComponent} to eventually
|
||||
* show the device location on the map.
|
||||
*/
|
||||
public class LocationComponentActivationOptions {
|
||||
|
||||
private final Context context;
|
||||
private final Style style;
|
||||
private final LocationEngine locationEngine;
|
||||
private final LocationEngineRequest locationEngineRequest;
|
||||
private final LocationComponentOptions locationComponentOptions;
|
||||
private final int styleRes;
|
||||
private final boolean useDefaultLocationEngine;
|
||||
|
||||
private LocationComponentActivationOptions(@NonNull Context context, @NonNull Style style,
|
||||
@Nullable LocationEngine locationEngine,
|
||||
@Nullable LocationEngineRequest locationEngineRequest,
|
||||
@Nullable LocationComponentOptions locationComponentOptions,
|
||||
int styleRes,
|
||||
boolean useDefaultLocationEngine) {
|
||||
this.context = context;
|
||||
this.style = style;
|
||||
this.locationEngine = locationEngine;
|
||||
this.locationEngineRequest = locationEngineRequest;
|
||||
this.locationComponentOptions = locationComponentOptions;
|
||||
this.styleRes = styleRes;
|
||||
this.useDefaultLocationEngine = useDefaultLocationEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to retrieve a {@link LocationComponentActivationOptions} object which is ready to build with
|
||||
*
|
||||
* @return a builder object
|
||||
*/
|
||||
@NonNull
|
||||
public static Builder builder(@NonNull Context context, @NonNull Style fullyLoadedMapStyle) {
|
||||
return new LocationComponentActivationOptions.Builder(context, fullyLoadedMapStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* The application's current context
|
||||
*
|
||||
* @return the application's current context
|
||||
*/
|
||||
@NonNull
|
||||
public Context context() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* The proxy object for current map style. More info at {@link Style}
|
||||
*
|
||||
* @return the map's fully loaded Style object
|
||||
*/
|
||||
@NonNull
|
||||
public Style style() {
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link LocationEngine} which the {@link LocationComponent} should use
|
||||
*
|
||||
* @return the engine
|
||||
*/
|
||||
@Nullable
|
||||
public LocationEngine locationEngine() {
|
||||
return locationEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* The location request which the {@link LocationComponent} should use
|
||||
*
|
||||
* @return the LocationEngineRequest object
|
||||
*/
|
||||
@Nullable
|
||||
public LocationEngineRequest locationEngineRequest() {
|
||||
return locationEngineRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* A built {@link LocationComponentOptions} object, which holds the various {@link LocationComponent} styling options
|
||||
*
|
||||
* @return the options for styling the actual LocationComponent
|
||||
*/
|
||||
@Nullable
|
||||
public LocationComponentOptions locationComponentOptions() {
|
||||
return locationComponentOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* The LocationComponent style resource (e.g. R.style.style_name)
|
||||
*
|
||||
* @return the style resource.
|
||||
*/
|
||||
public int styleRes() {
|
||||
return styleRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if you want to initialize and use the built-in location engine or false if there should be no
|
||||
* location engine initialized
|
||||
*
|
||||
* @return whether the default LocationEngine is used
|
||||
*/
|
||||
public boolean useDefaultLocationEngine() {
|
||||
return useDefaultLocationEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for constructing a new instance of {@link LocationComponentActivationOptions}.
|
||||
*/
|
||||
public static class Builder {
|
||||
private final Context context;
|
||||
private final Style style;
|
||||
private LocationEngine locationEngine;
|
||||
private LocationEngineRequest locationEngineRequest;
|
||||
private LocationComponentOptions locationComponentOptions;
|
||||
private int styleRes;
|
||||
|
||||
/**
|
||||
* Set to true as the default in case a true/false value isn't declared via the builder's
|
||||
* {@link LocationComponentActivationOptions#useDefaultLocationEngine()} method.
|
||||
* <p>
|
||||
* Please be aware that this activation boolean is ignored when a non-null
|
||||
* {@link LocationEngine} is set via the builder's `locationEngine()` method.
|
||||
*/
|
||||
private boolean useDefaultLocationEngine = true;
|
||||
|
||||
/**
|
||||
* Constructor for the {@link LocationComponentActivationOptions} builder class.
|
||||
* While other activation options are optional, the activation process always requires
|
||||
* context and a fully-loaded map {@link Style} object, which is why the two are in this
|
||||
* constructor.
|
||||
*/
|
||||
public Builder(@NonNull Context context, @NonNull Style style) {
|
||||
this.context = context;
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deliver your own {@link LocationEngine} to the LocationComponent.
|
||||
* <p>
|
||||
* The true/false
|
||||
* {@link LocationComponentActivationOptions.Builder#useDefaultLocationEngine()}
|
||||
* activation option is ignored when a non-null {@link LocationEngine} is set via
|
||||
* this `locationEngine()` method.
|
||||
*
|
||||
* @param locationEngine a {@link LocationEngine} object
|
||||
* @return the {@link Builder} object being constructed
|
||||
*/
|
||||
@NonNull
|
||||
public Builder locationEngine(@Nullable LocationEngine locationEngine) {
|
||||
this.locationEngine = locationEngine;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param locationEngineRequest the location request which the
|
||||
* {@link LocationComponent} should use
|
||||
* @return the {@link Builder} object being constructed
|
||||
*/
|
||||
public Builder locationEngineRequest(LocationEngineRequest locationEngineRequest) {
|
||||
this.locationEngineRequest = locationEngineRequest;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param locationComponentOptions a built {@link LocationComponentOptions} object,
|
||||
* which holds the various {@link LocationComponent}
|
||||
* styling options
|
||||
* @return the {@link Builder} object being constructed
|
||||
*/
|
||||
public Builder locationComponentOptions(LocationComponentOptions locationComponentOptions) {
|
||||
this.locationComponentOptions = locationComponentOptions;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param styleRes the LocationComponent style resource (e.g. R.style.style_name)
|
||||
* @return the {@link Builder} object being constructed
|
||||
*/
|
||||
public Builder styleRes(int styleRes) {
|
||||
this.styleRes = styleRes;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param useDefaultLocationEngine true if you want to initialize and use the
|
||||
* built-in location engine or false if there
|
||||
* should be no location engine initialized
|
||||
* This is ignored when null is set as the parameter
|
||||
* for {@link LocationComponentActivationOptions#builder
|
||||
* (Context, Style)#locationEngine()}.
|
||||
* @return the {@link Builder} object being constructed
|
||||
*/
|
||||
public Builder useDefaultLocationEngine(boolean useDefaultLocationEngine) {
|
||||
this.useDefaultLocationEngine = useDefaultLocationEngine;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method which actually builds the {@link LocationComponentActivationOptions} object while
|
||||
* taking the various options into account.
|
||||
*
|
||||
* @return a built {@link LocationComponentActivationOptions} object
|
||||
*/
|
||||
public LocationComponentActivationOptions build() {
|
||||
if (styleRes != 0 && locationComponentOptions != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"You've provided both a style resource and a LocationComponentOptions object to the "
|
||||
+ "LocationComponentActivationOptions builder. You can't use both and "
|
||||
+ "you must choose one of the two to style the LocationComponent.");
|
||||
}
|
||||
if (context == null) {
|
||||
throw new NullPointerException(
|
||||
"Context in LocationComponentActivationOptions is null.");
|
||||
}
|
||||
if (style == null) {
|
||||
throw new NullPointerException(
|
||||
"Style in LocationComponentActivationOptions is null. Make sure the "
|
||||
+ "Style object isn't null. Wait for the map to fully load before passing "
|
||||
+ "the Style object to LocationComponentActivationOptions.");
|
||||
}
|
||||
if (!style.isFullyLoaded()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Style in LocationComponentActivationOptions isn't fully loaded. Wait for the "
|
||||
+ "map to fully load before passing the Style object to "
|
||||
+ "LocationComponentActivationOptions.");
|
||||
}
|
||||
return new LocationComponentActivationOptions(context, style, locationEngine,
|
||||
locationEngineRequest, locationComponentOptions, styleRes, useDefaultLocationEngine);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.Surface;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.mapbox.mapboxsdk.log.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This manager class handles compass events such as starting the tracking of device bearing, or
|
||||
* when a new compass update occurs.
|
||||
*/
|
||||
class LocationComponentCompassEngine implements CompassEngine, SensorEventListener {
|
||||
|
||||
private static final String TAG = "Mbgl-LocationComponentCompassEngine";
|
||||
|
||||
// The rate sensor events will be delivered at. As the Android documentation states, this is only
|
||||
// a hint to the system and the events might actually be received faster or slower then this
|
||||
// specified rate. Since the minimum Android API levels about 9, we are able to set this value
|
||||
// ourselves rather than using one of the provided constants which deliver updates too quickly for
|
||||
// our use case. The default is set to 100ms
|
||||
static final int SENSOR_DELAY_MICROS = 100 * 1000;
|
||||
// Filtering coefficient 0 < ALPHA < 1
|
||||
private static final float ALPHA = 0.45f;
|
||||
|
||||
@NonNull
|
||||
private final WindowManager windowManager;
|
||||
@NonNull
|
||||
private final SensorManager sensorManager;
|
||||
private final List<CompassListener> compassListeners = new ArrayList<>();
|
||||
|
||||
// Not all devices have a compassSensor
|
||||
@Nullable
|
||||
private Sensor compassSensor;
|
||||
@Nullable
|
||||
private Sensor gravitySensor;
|
||||
@Nullable
|
||||
private Sensor magneticFieldSensor;
|
||||
|
||||
@NonNull
|
||||
private float[] truncatedRotationVectorValue = new float[4];
|
||||
@NonNull
|
||||
private float[] rotationMatrix = new float[9];
|
||||
private float[] rotationVectorValue;
|
||||
private float lastHeading;
|
||||
private int lastAccuracySensorStatus;
|
||||
|
||||
private long compassUpdateNextTimestamp;
|
||||
@Nullable
|
||||
private float[] gravityValues = new float[3];
|
||||
@Nullable
|
||||
private float[] magneticValues = new float[3];
|
||||
|
||||
/**
|
||||
* Construct a new instance of the this class. A internal compass listeners needed to separate it
|
||||
* from the cleared list of public listeners.
|
||||
*/
|
||||
LocationComponentCompassEngine(@NonNull WindowManager windowManager, @NonNull SensorManager sensorManager) {
|
||||
this.windowManager = windowManager;
|
||||
this.sensorManager = sensorManager;
|
||||
compassSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
|
||||
if (compassSensor == null) {
|
||||
if (isGyroscopeAvailable()) {
|
||||
Logger.d(TAG, "Rotation vector sensor not supported on device, "
|
||||
+ "falling back to orientation.");
|
||||
compassSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
|
||||
} else {
|
||||
Logger.d(TAG, "Rotation vector sensor not supported on device, "
|
||||
+ "falling back to accelerometer and magnetic field.");
|
||||
gravitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
||||
magneticFieldSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCompassListener(@NonNull CompassListener compassListener) {
|
||||
if (compassListeners.isEmpty()) {
|
||||
registerSensorListeners();
|
||||
}
|
||||
compassListeners.add(compassListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCompassListener(@NonNull CompassListener compassListener) {
|
||||
compassListeners.remove(compassListener);
|
||||
if (compassListeners.isEmpty()) {
|
||||
unregisterSensorListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLastAccuracySensorStatus() {
|
||||
return lastAccuracySensorStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getLastHeading() {
|
||||
return lastHeading;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(@NonNull SensorEvent event) {
|
||||
// check when the last time the compass was updated, return if too soon.
|
||||
long currentTime = SystemClock.elapsedRealtime();
|
||||
if (currentTime < compassUpdateNextTimestamp) {
|
||||
return;
|
||||
}
|
||||
if (lastAccuracySensorStatus == SensorManager.SENSOR_STATUS_UNRELIABLE) {
|
||||
Logger.d(TAG, "Compass sensor is unreliable, device calibration is needed.");
|
||||
return;
|
||||
}
|
||||
if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) {
|
||||
rotationVectorValue = getRotationVectorFromSensorEvent(event);
|
||||
updateOrientation();
|
||||
} else if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) {
|
||||
notifyCompassChangeListeners((event.values[0] + 360) % 360);
|
||||
} else if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
|
||||
gravityValues = lowPassFilter(getRotationVectorFromSensorEvent(event), gravityValues);
|
||||
updateOrientation();
|
||||
} else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
|
||||
magneticValues = lowPassFilter(getRotationVectorFromSensorEvent(event), magneticValues);
|
||||
updateOrientation();
|
||||
}
|
||||
|
||||
// Update the compassUpdateNextTimestamp
|
||||
compassUpdateNextTimestamp = currentTime + LocationComponentConstants.COMPASS_UPDATE_RATE_MS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
if (lastAccuracySensorStatus != accuracy) {
|
||||
for (CompassListener compassListener : compassListeners) {
|
||||
compassListener.onCompassAccuracyChange(accuracy);
|
||||
}
|
||||
lastAccuracySensorStatus = accuracy;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isGyroscopeAvailable() {
|
||||
return sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("SuspiciousNameCombination")
|
||||
private void updateOrientation() {
|
||||
if (rotationVectorValue != null) {
|
||||
SensorManager.getRotationMatrixFromVector(rotationMatrix, rotationVectorValue);
|
||||
} else {
|
||||
// Get rotation matrix given the gravity and geomagnetic matrices
|
||||
SensorManager.getRotationMatrix(rotationMatrix, null, gravityValues, magneticValues);
|
||||
}
|
||||
|
||||
final int worldAxisForDeviceAxisX;
|
||||
final int worldAxisForDeviceAxisY;
|
||||
|
||||
// Remap the axes as if the device screen was the instrument panel,
|
||||
// and adjust the rotation matrix for the device orientation.
|
||||
switch (windowManager.getDefaultDisplay().getRotation()) {
|
||||
case Surface.ROTATION_90:
|
||||
worldAxisForDeviceAxisX = SensorManager.AXIS_Z;
|
||||
worldAxisForDeviceAxisY = SensorManager.AXIS_MINUS_X;
|
||||
break;
|
||||
case Surface.ROTATION_180:
|
||||
worldAxisForDeviceAxisX = SensorManager.AXIS_MINUS_X;
|
||||
worldAxisForDeviceAxisY = SensorManager.AXIS_MINUS_Z;
|
||||
break;
|
||||
case Surface.ROTATION_270:
|
||||
worldAxisForDeviceAxisX = SensorManager.AXIS_MINUS_Z;
|
||||
worldAxisForDeviceAxisY = SensorManager.AXIS_X;
|
||||
break;
|
||||
case Surface.ROTATION_0:
|
||||
default:
|
||||
worldAxisForDeviceAxisX = SensorManager.AXIS_X;
|
||||
worldAxisForDeviceAxisY = SensorManager.AXIS_Z;
|
||||
break;
|
||||
}
|
||||
|
||||
float[] adjustedRotationMatrix = new float[9];
|
||||
SensorManager.remapCoordinateSystem(rotationMatrix, worldAxisForDeviceAxisX,
|
||||
worldAxisForDeviceAxisY, adjustedRotationMatrix);
|
||||
|
||||
// Transform rotation matrix into azimuth/pitch/roll
|
||||
float[] orientation = new float[3];
|
||||
SensorManager.getOrientation(adjustedRotationMatrix, orientation);
|
||||
|
||||
// The x-axis is all we care about here.
|
||||
notifyCompassChangeListeners((float) Math.toDegrees(orientation[0]));
|
||||
}
|
||||
|
||||
private void notifyCompassChangeListeners(float heading) {
|
||||
for (CompassListener compassListener : compassListeners) {
|
||||
compassListener.onCompassChanged(heading);
|
||||
}
|
||||
lastHeading = heading;
|
||||
}
|
||||
|
||||
private void registerSensorListeners() {
|
||||
if (isCompassSensorAvailable()) {
|
||||
// Does nothing if the sensors already registered.
|
||||
sensorManager.registerListener(this, compassSensor, SENSOR_DELAY_MICROS);
|
||||
} else {
|
||||
sensorManager.registerListener(this, gravitySensor, SENSOR_DELAY_MICROS);
|
||||
sensorManager.registerListener(this, magneticFieldSensor, SENSOR_DELAY_MICROS);
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterSensorListeners() {
|
||||
if (isCompassSensorAvailable()) {
|
||||
sensorManager.unregisterListener(this, compassSensor);
|
||||
} else {
|
||||
sensorManager.unregisterListener(this, gravitySensor);
|
||||
sensorManager.unregisterListener(this, magneticFieldSensor);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCompassSensorAvailable() {
|
||||
return compassSensor != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function, that filters newValues, considering previous values
|
||||
*
|
||||
* @param newValues array of float, that contains new data
|
||||
* @param smoothedValues array of float, that contains previous state
|
||||
* @return float filtered array of float
|
||||
*/
|
||||
@NonNull
|
||||
private float[] lowPassFilter(@NonNull float[] newValues, @Nullable float[] smoothedValues) {
|
||||
if (smoothedValues == null) {
|
||||
return newValues;
|
||||
}
|
||||
for (int i = 0; i < newValues.length; i++) {
|
||||
smoothedValues[i] = smoothedValues[i] + ALPHA * (newValues[i] - smoothedValues[i]);
|
||||
}
|
||||
return smoothedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls out the rotation vector from a SensorEvent, with a maximum length
|
||||
* vector of four elements to avoid potential compatibility issues.
|
||||
*
|
||||
* @param event the sensor event
|
||||
* @return the events rotation vector, potentially truncated
|
||||
*/
|
||||
@NonNull
|
||||
private float[] getRotationVectorFromSensorEvent(@NonNull SensorEvent event) {
|
||||
if (event.values.length > 4) {
|
||||
// On some Samsung devices SensorManager.getRotationMatrixFromVector
|
||||
// appears to throw an exception if rotation vector has length > 4.
|
||||
// For the purposes of this class the first 4 values of the
|
||||
// rotation vector are sufficient (see crbug.com/335298 for details).
|
||||
// Only affects Android 4.3
|
||||
System.arraycopy(event.values, 0, truncatedRotationVectorValue, 0, 4);
|
||||
return truncatedRotationVectorValue;
|
||||
} else {
|
||||
return event.values;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
/**
|
||||
* Contains all the constants being used for the {@link LocationComponent}.
|
||||
*/
|
||||
public final class LocationComponentConstants {
|
||||
|
||||
// Controls the compass update rate in milliseconds
|
||||
static final long COMPASS_UPDATE_RATE_MS = 500;
|
||||
|
||||
// Sets the transition animation duration when switching camera modes.
|
||||
static final long TRANSITION_ANIMATION_DURATION_MS = 750;
|
||||
|
||||
// Sets the max allowed time for the location icon animation from one LatLng to another.
|
||||
static final long MAX_ANIMATION_DURATION_MS = 2000;
|
||||
|
||||
// Sets the duration of change of accuracy radius when a different value is provided.
|
||||
static final long ACCURACY_RADIUS_ANIMATION_DURATION = 250;
|
||||
|
||||
// Default animation duration for zooming while tracking.
|
||||
static final long DEFAULT_TRACKING_ZOOM_ANIM_DURATION = 750;
|
||||
|
||||
// Default animation duration for tilting while tracking.
|
||||
static final long DEFAULT_TRACKING_TILT_ANIM_DURATION = 1250;
|
||||
|
||||
// Threshold value to perform immediate camera/layer position update.
|
||||
static final double INSTANT_LOCATION_TRANSITION_THRESHOLD = 50_000;
|
||||
|
||||
// Default interval between location updates
|
||||
static final long DEFAULT_INTERVAL_MILLIS = 1000;
|
||||
|
||||
// Default fastest acceptable interval between location updates
|
||||
static final long DEFAULT_FASTEST_INTERVAL_MILLIS = 1000;
|
||||
|
||||
// Sources
|
||||
|
||||
/**
|
||||
* Source ID of the location's GeoJsonSource.
|
||||
*/
|
||||
public static final String LOCATION_SOURCE = "mapbox-location-source";
|
||||
|
||||
static final String PROPERTY_GPS_BEARING = "mapbox-property-gps-bearing";
|
||||
static final String PROPERTY_COMPASS_BEARING = "mapbox-property-compass-bearing";
|
||||
static final String PROPERTY_LOCATION_STALE = "mapbox-property-location-stale";
|
||||
static final String PROPERTY_ACCURACY_RADIUS = "mapbox-property-accuracy-radius";
|
||||
static final String PROPERTY_ACCURACY_COLOR = "mapbox-property-accuracy-color";
|
||||
static final String PROPERTY_ACCURACY_ALPHA = "mapbox-property-accuracy-alpha";
|
||||
static final String PROPERTY_FOREGROUND_ICON_OFFSET = "mapbox-property-foreground-icon-offset";
|
||||
static final String PROPERTY_SHADOW_ICON_OFFSET = "mapbox-property-shadow-icon-offset";
|
||||
static final String PROPERTY_FOREGROUND_ICON = "mapbox-property-foreground-icon";
|
||||
static final String PROPERTY_BACKGROUND_ICON = "mapbox-property-background-icon";
|
||||
static final String PROPERTY_FOREGROUND_STALE_ICON = "mapbox-property-foreground-stale-icon";
|
||||
static final String PROPERTY_BACKGROUND_STALE_ICON = "mapbox-property-background-stale-icon";
|
||||
static final String PROPERTY_BEARING_ICON = "mapbox-property-shadow-icon";
|
||||
|
||||
// Layers
|
||||
|
||||
/**
|
||||
* Layer ID of the location shadow.
|
||||
*/
|
||||
public static final String SHADOW_LAYER = "mapbox-location-shadow-layer";
|
||||
|
||||
/**
|
||||
* Layer ID of the location foreground icon.
|
||||
*/
|
||||
public static final String FOREGROUND_LAYER = "mapbox-location-foreground-layer";
|
||||
|
||||
/**
|
||||
* Layer ID of the location background icon.
|
||||
*/
|
||||
public static final String BACKGROUND_LAYER = "mapbox-location-background-layer";
|
||||
|
||||
/**
|
||||
* Layer ID of the location accuracy.
|
||||
*/
|
||||
public static final String ACCURACY_LAYER = "mapbox-location-accuracy-layer";
|
||||
|
||||
/**
|
||||
* Layer ID of the location bearing icon.
|
||||
*/
|
||||
public static final String BEARING_LAYER = "mapbox-location-bearing-layer";
|
||||
|
||||
// Icons
|
||||
static final String FOREGROUND_ICON = "mapbox-location-icon";
|
||||
static final String BACKGROUND_ICON = "mapbox-location-stroke-icon";
|
||||
static final String FOREGROUND_STALE_ICON = "mapbox-location-stale-icon";
|
||||
static final String BACKGROUND_STALE_ICON = "mapbox-location-background-stale-icon";
|
||||
static final String SHADOW_ICON = "mapbox-location-shadow-icon";
|
||||
static final String BEARING_ICON = "mapbox-location-bearing-icon";
|
||||
|
||||
private LocationComponentConstants() {
|
||||
// Class should not be initialized
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
final class LocationComponentNotInitializedException extends RuntimeException {
|
||||
LocationComponentNotInitializedException() {
|
||||
super("The LocationComponent has to be activated with one of the LocationComponent#activateLocationComponent"
|
||||
+ " overloads before any other methods are invoked.");
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,48 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.mapbox.mapboxsdk.maps.Style;
|
||||
import com.mapbox.mapboxsdk.style.layers.Layer;
|
||||
|
||||
class LocationComponentPositionManager {
|
||||
|
||||
@NonNull
|
||||
private final Style style;
|
||||
|
||||
@Nullable
|
||||
private String layerAbove;
|
||||
|
||||
@Nullable
|
||||
private String layerBelow;
|
||||
|
||||
LocationComponentPositionManager(@NonNull Style style, @Nullable String layerAbove, @Nullable String layerBelow) {
|
||||
this.style = style;
|
||||
this.layerAbove = layerAbove;
|
||||
this.layerBelow = layerBelow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true whenever layer above/below configuration has changed and requires re-layout.
|
||||
*/
|
||||
boolean update(@Nullable String layerAbove, @Nullable String layerBelow) {
|
||||
boolean requiresUpdate =
|
||||
!(this.layerAbove == layerAbove || (this.layerAbove != null && this.layerAbove.equals(layerAbove)))
|
||||
|| !(this.layerBelow == layerBelow || (this.layerBelow != null && this.layerBelow.equals(layerBelow)));
|
||||
|
||||
this.layerAbove = layerAbove;
|
||||
this.layerBelow = layerBelow;
|
||||
return requiresUpdate;
|
||||
}
|
||||
|
||||
void addLayerToMap(@NonNull Layer layer) {
|
||||
if (layerAbove != null) {
|
||||
style.addLayerAbove(layer, layerAbove);
|
||||
} else if (layerBelow != null) {
|
||||
style.addLayerBelow(layer, layerBelow);
|
||||
} else {
|
||||
style.addLayer(layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,464 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.PointF;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mapbox.geojson.Feature;
|
||||
import com.mapbox.geojson.Point;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.location.modes.RenderMode;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
import com.mapbox.mapboxsdk.maps.Style;
|
||||
import com.mapbox.mapboxsdk.style.layers.Layer;
|
||||
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
|
||||
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.ACCURACY_LAYER;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_LAYER;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_STALE_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BEARING_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BEARING_LAYER;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.FOREGROUND_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.FOREGROUND_LAYER;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.FOREGROUND_STALE_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.LOCATION_SOURCE;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_ALPHA;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_COLOR;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_RADIUS;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BACKGROUND_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BACKGROUND_STALE_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BEARING_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_COMPASS_BEARING;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_ICON_OFFSET;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_STALE_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_GPS_BEARING;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_LOCATION_STALE;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_SHADOW_ICON_OFFSET;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_LAYER;
|
||||
import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
|
||||
import static com.mapbox.mapboxsdk.style.expressions.Expression.linear;
|
||||
import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
|
||||
import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom;
|
||||
import static com.mapbox.mapboxsdk.style.layers.Property.NONE;
|
||||
import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE;
|
||||
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize;
|
||||
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
|
||||
import static com.mapbox.mapboxsdk.utils.ColorUtils.colorToRgbaString;
|
||||
|
||||
final class LocationLayerController {
|
||||
|
||||
@RenderMode.Mode
|
||||
private int renderMode;
|
||||
|
||||
private final MapboxMap mapboxMap;
|
||||
private Style style;
|
||||
private final LayerSourceProvider layerSourceProvider;
|
||||
private final LayerBitmapProvider bitmapProvider;
|
||||
private LocationComponentOptions options;
|
||||
private final OnRenderModeChangedListener internalRenderModeChangedListener;
|
||||
|
||||
@VisibleForTesting
|
||||
final Set<String> layerSet = new HashSet<>();
|
||||
private Feature locationFeature;
|
||||
private GeoJsonSource locationSource;
|
||||
|
||||
private boolean isHidden = true;
|
||||
|
||||
private LocationComponentPositionManager positionManager;
|
||||
|
||||
LocationLayerController(MapboxMap mapboxMap, Style style, LayerSourceProvider layerSourceProvider,
|
||||
LayerFeatureProvider featureProvider, LayerBitmapProvider bitmapProvider,
|
||||
@NonNull LocationComponentOptions options,
|
||||
@NonNull OnRenderModeChangedListener internalRenderModeChangedListener) {
|
||||
this.mapboxMap = mapboxMap;
|
||||
this.style = style;
|
||||
this.layerSourceProvider = layerSourceProvider;
|
||||
this.bitmapProvider = bitmapProvider;
|
||||
this.locationFeature = featureProvider.generateLocationFeature(locationFeature, options);
|
||||
this.internalRenderModeChangedListener = internalRenderModeChangedListener;
|
||||
initializeComponents(style, options);
|
||||
}
|
||||
|
||||
void initializeComponents(Style style, LocationComponentOptions options) {
|
||||
this.style = style;
|
||||
this.positionManager = new LocationComponentPositionManager(style, options.layerAbove(), options.layerBelow());
|
||||
addLocationSource();
|
||||
addLayers();
|
||||
applyStyle(options);
|
||||
|
||||
if (isHidden) {
|
||||
hide();
|
||||
} else {
|
||||
show();
|
||||
}
|
||||
}
|
||||
|
||||
void applyStyle(@NonNull LocationComponentOptions options) {
|
||||
if (positionManager.update(options.layerAbove(), options.layerBelow())) {
|
||||
removeLayers();
|
||||
addLayers();
|
||||
if (isHidden) {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
this.options = options;
|
||||
|
||||
float elevation = options.elevation();
|
||||
// Only add icon elevation if the values greater than 0.
|
||||
if (elevation > 0) {
|
||||
styleShadow(options);
|
||||
}
|
||||
styleForeground(options);
|
||||
styleBackground(options);
|
||||
styleBearing(options);
|
||||
styleAccuracy(options.accuracyAlpha(), options.accuracyColor());
|
||||
styleScaling(options);
|
||||
determineIconsSource(options);
|
||||
|
||||
if (!isHidden) {
|
||||
show();
|
||||
}
|
||||
}
|
||||
|
||||
void setRenderMode(@RenderMode.Mode int renderMode) {
|
||||
if (this.renderMode == renderMode) {
|
||||
return;
|
||||
}
|
||||
this.renderMode = renderMode;
|
||||
|
||||
styleForeground(options);
|
||||
determineIconsSource(options);
|
||||
if (!isHidden) {
|
||||
show();
|
||||
}
|
||||
internalRenderModeChangedListener.onRenderModeChanged(renderMode);
|
||||
}
|
||||
|
||||
int getRenderMode() {
|
||||
return renderMode;
|
||||
}
|
||||
|
||||
//
|
||||
// Layer action
|
||||
//
|
||||
|
||||
void show() {
|
||||
isHidden = false;
|
||||
boolean isStale = locationFeature.getBooleanProperty(PROPERTY_LOCATION_STALE);
|
||||
switch (renderMode) {
|
||||
case RenderMode.NORMAL:
|
||||
setLayerVisibility(SHADOW_LAYER, true);
|
||||
setLayerVisibility(FOREGROUND_LAYER, true);
|
||||
setLayerVisibility(BACKGROUND_LAYER, true);
|
||||
setLayerVisibility(ACCURACY_LAYER, !isStale);
|
||||
setLayerVisibility(BEARING_LAYER, false);
|
||||
break;
|
||||
case RenderMode.COMPASS:
|
||||
setLayerVisibility(SHADOW_LAYER, true);
|
||||
setLayerVisibility(FOREGROUND_LAYER, true);
|
||||
setLayerVisibility(BACKGROUND_LAYER, true);
|
||||
setLayerVisibility(ACCURACY_LAYER, !isStale);
|
||||
setLayerVisibility(BEARING_LAYER, true);
|
||||
break;
|
||||
case RenderMode.GPS:
|
||||
setLayerVisibility(SHADOW_LAYER, false);
|
||||
setLayerVisibility(FOREGROUND_LAYER, true);
|
||||
setLayerVisibility(BACKGROUND_LAYER, true);
|
||||
setLayerVisibility(ACCURACY_LAYER, false);
|
||||
setLayerVisibility(BEARING_LAYER, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hide() {
|
||||
isHidden = true;
|
||||
for (String layerId : layerSet) {
|
||||
setLayerVisibility(layerId, false);
|
||||
}
|
||||
}
|
||||
|
||||
void updateForegroundOffset(double tilt) {
|
||||
JsonArray foregroundJsonArray = new JsonArray();
|
||||
foregroundJsonArray.add(0f);
|
||||
foregroundJsonArray.add((float) (-0.05 * tilt));
|
||||
locationFeature.addProperty(PROPERTY_FOREGROUND_ICON_OFFSET, foregroundJsonArray);
|
||||
|
||||
JsonArray backgroundJsonArray = new JsonArray();
|
||||
backgroundJsonArray.add(0f);
|
||||
backgroundJsonArray.add((float) (0.05 * tilt));
|
||||
locationFeature.addProperty(PROPERTY_SHADOW_ICON_OFFSET, backgroundJsonArray);
|
||||
|
||||
refreshSource();
|
||||
}
|
||||
|
||||
void updateForegroundBearing(float bearing) {
|
||||
if (renderMode != RenderMode.GPS) {
|
||||
setBearingProperty(PROPERTY_GPS_BEARING, bearing);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isHidden() {
|
||||
return isHidden;
|
||||
}
|
||||
|
||||
boolean isConsumingCompass() {
|
||||
return renderMode == RenderMode.COMPASS;
|
||||
}
|
||||
|
||||
private void setLayerVisibility(@NonNull String layerId, boolean visible) {
|
||||
Layer layer = style.getLayer(layerId);
|
||||
if (layer != null) {
|
||||
String targetVisibility = visible ? VISIBLE : NONE;
|
||||
if (!layer.getVisibility().value.equals(targetVisibility)) {
|
||||
layer.setProperties(visibility(visible ? VISIBLE : NONE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addLayers() {
|
||||
// positions the top-most reference layer
|
||||
Layer layer = layerSourceProvider.generateLayer(BEARING_LAYER);
|
||||
positionManager.addLayerToMap(layer);
|
||||
layerSet.add(layer.getId());
|
||||
|
||||
// adds remaining layers while keeping the order
|
||||
addSymbolLayer(FOREGROUND_LAYER, BEARING_LAYER);
|
||||
addSymbolLayer(BACKGROUND_LAYER, FOREGROUND_LAYER);
|
||||
addSymbolLayer(SHADOW_LAYER, BACKGROUND_LAYER);
|
||||
addAccuracyLayer();
|
||||
}
|
||||
|
||||
private void addSymbolLayer(@NonNull String layerId, @NonNull String beforeLayerId) {
|
||||
Layer layer = layerSourceProvider.generateLayer(layerId);
|
||||
addLayerToMap(layer, beforeLayerId);
|
||||
}
|
||||
|
||||
private void addAccuracyLayer() {
|
||||
Layer accuracyLayer = layerSourceProvider.generateAccuracyLayer();
|
||||
addLayerToMap(accuracyLayer, BACKGROUND_LAYER);
|
||||
}
|
||||
|
||||
private void addLayerToMap(Layer layer, @NonNull String idBelowLayer) {
|
||||
style.addLayerBelow(layer, idBelowLayer);
|
||||
layerSet.add(layer.getId());
|
||||
}
|
||||
|
||||
private void removeLayers() {
|
||||
for (String layerId : layerSet) {
|
||||
style.removeLayer(layerId);
|
||||
}
|
||||
layerSet.clear();
|
||||
}
|
||||
|
||||
private void setBearingProperty(@NonNull String propertyId, float bearing) {
|
||||
locationFeature.addNumberProperty(propertyId, bearing);
|
||||
refreshSource();
|
||||
}
|
||||
|
||||
private void updateAccuracyRadius(float accuracy) {
|
||||
locationFeature.addNumberProperty(PROPERTY_ACCURACY_RADIUS, accuracy);
|
||||
refreshSource();
|
||||
}
|
||||
|
||||
//
|
||||
// Source actions
|
||||
//
|
||||
|
||||
private void addLocationSource() {
|
||||
locationSource = layerSourceProvider.generateSource(locationFeature);
|
||||
style.addSource(locationSource);
|
||||
}
|
||||
|
||||
private void refreshSource() {
|
||||
GeoJsonSource source = style.getSourceAs(LOCATION_SOURCE);
|
||||
if (source != null) {
|
||||
locationSource.setGeoJson(locationFeature);
|
||||
}
|
||||
}
|
||||
|
||||
private void setLocationPoint(Point locationPoint) {
|
||||
JsonObject properties = locationFeature.properties();
|
||||
if (properties != null) {
|
||||
locationFeature = Feature.fromGeometry(locationPoint, properties);
|
||||
refreshSource();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Styling
|
||||
//
|
||||
|
||||
private void styleBackground(LocationComponentOptions options) {
|
||||
Bitmap backgroundBitmap = bitmapProvider.generateBitmap(
|
||||
options.backgroundDrawable(), options.backgroundTintColor()
|
||||
);
|
||||
Bitmap backgroundStaleBitmap = bitmapProvider.generateBitmap(
|
||||
options.backgroundDrawableStale(), options.backgroundStaleTintColor()
|
||||
);
|
||||
style.addImage(BACKGROUND_ICON, backgroundBitmap);
|
||||
style.addImage(BACKGROUND_STALE_ICON, backgroundStaleBitmap);
|
||||
}
|
||||
|
||||
private void styleShadow(@NonNull LocationComponentOptions options) {
|
||||
style.addImage(SHADOW_ICON, bitmapProvider.generateShadowBitmap(options));
|
||||
}
|
||||
|
||||
private void styleBearing(LocationComponentOptions options) {
|
||||
Bitmap bearingBitmap = bitmapProvider.generateBitmap(options.bearingDrawable(), options.bearingTintColor());
|
||||
style.addImage(BEARING_ICON, bearingBitmap);
|
||||
}
|
||||
|
||||
private void styleAccuracy(float accuracyAlpha, @ColorInt int accuracyColor) {
|
||||
locationFeature.addNumberProperty(PROPERTY_ACCURACY_ALPHA, accuracyAlpha);
|
||||
locationFeature.addStringProperty(PROPERTY_ACCURACY_COLOR, colorToRgbaString(accuracyColor));
|
||||
refreshSource();
|
||||
}
|
||||
|
||||
private void styleForeground(LocationComponentOptions options) {
|
||||
Bitmap foregroundBitmap = bitmapProvider.generateBitmap(
|
||||
options.foregroundDrawable(), options.foregroundTintColor()
|
||||
);
|
||||
Bitmap foregroundBitmapStale = bitmapProvider.generateBitmap(
|
||||
options.foregroundDrawableStale(), options.foregroundStaleTintColor()
|
||||
);
|
||||
if (renderMode == RenderMode.GPS) {
|
||||
foregroundBitmap = bitmapProvider.generateBitmap(
|
||||
options.gpsDrawable(), options.foregroundTintColor()
|
||||
);
|
||||
foregroundBitmapStale = bitmapProvider.generateBitmap(
|
||||
options.gpsDrawable(), options.foregroundStaleTintColor()
|
||||
);
|
||||
}
|
||||
style.addImage(FOREGROUND_ICON, foregroundBitmap);
|
||||
style.addImage(FOREGROUND_STALE_ICON, foregroundBitmapStale);
|
||||
}
|
||||
|
||||
private void styleScaling(@NonNull LocationComponentOptions options) {
|
||||
for (String layerId : layerSet) {
|
||||
Layer layer = style.getLayer(layerId);
|
||||
if (layer instanceof SymbolLayer) {
|
||||
layer.setProperties(
|
||||
iconSize(
|
||||
interpolate(linear(), zoom(),
|
||||
stop(mapboxMap.getMinZoomLevel(), options.minZoomIconScale()),
|
||||
stop(mapboxMap.getMaxZoomLevel(), options.maxZoomIconScale())
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void determineIconsSource(LocationComponentOptions options) {
|
||||
String foregroundIconString = buildIconString(
|
||||
renderMode == RenderMode.GPS ? options.gpsName() : options.foregroundName(), FOREGROUND_ICON);
|
||||
String foregroundStaleIconString = buildIconString(options.foregroundStaleName(), FOREGROUND_STALE_ICON);
|
||||
String backgroundIconString = buildIconString(options.backgroundName(), BACKGROUND_ICON);
|
||||
String backgroundStaleIconString = buildIconString(options.backgroundStaleName(), BACKGROUND_STALE_ICON);
|
||||
String bearingIconString = buildIconString(options.bearingName(), BEARING_ICON);
|
||||
|
||||
locationFeature.addStringProperty(PROPERTY_FOREGROUND_ICON, foregroundIconString);
|
||||
locationFeature.addStringProperty(PROPERTY_BACKGROUND_ICON, backgroundIconString);
|
||||
locationFeature.addStringProperty(PROPERTY_FOREGROUND_STALE_ICON, foregroundStaleIconString);
|
||||
locationFeature.addStringProperty(PROPERTY_BACKGROUND_STALE_ICON, backgroundStaleIconString);
|
||||
locationFeature.addStringProperty(PROPERTY_BEARING_ICON, bearingIconString);
|
||||
refreshSource();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private String buildIconString(@Nullable String bitmapName, @NonNull String drawableName) {
|
||||
if (bitmapName != null) {
|
||||
return bitmapName;
|
||||
}
|
||||
return drawableName;
|
||||
}
|
||||
|
||||
void setLocationsStale(boolean isStale) {
|
||||
locationFeature.addBooleanProperty(PROPERTY_LOCATION_STALE, isStale);
|
||||
refreshSource();
|
||||
if (renderMode != RenderMode.GPS) {
|
||||
setLayerVisibility(ACCURACY_LAYER, !isStale);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Map click event
|
||||
//
|
||||
|
||||
boolean onMapClick(@NonNull LatLng point) {
|
||||
PointF screenLoc = mapboxMap.getProjection().toScreenLocation(point);
|
||||
List<Feature> features = mapboxMap.queryRenderedFeatures(screenLoc,
|
||||
BACKGROUND_LAYER,
|
||||
FOREGROUND_LAYER,
|
||||
BEARING_LAYER
|
||||
);
|
||||
return !features.isEmpty();
|
||||
}
|
||||
|
||||
private final MapboxAnimator.AnimationsValueChangeListener<LatLng> latLngValueListener =
|
||||
new MapboxAnimator.AnimationsValueChangeListener<LatLng>() {
|
||||
@Override
|
||||
public void onNewAnimationValue(LatLng value) {
|
||||
Point point = Point.fromLngLat(value.getLongitude(), value.getLatitude());
|
||||
setLocationPoint(point);
|
||||
}
|
||||
};
|
||||
|
||||
private final MapboxAnimator.AnimationsValueChangeListener<Float> gpsBearingValueListener =
|
||||
new MapboxAnimator.AnimationsValueChangeListener<Float>() {
|
||||
@Override
|
||||
public void onNewAnimationValue(Float value) {
|
||||
setBearingProperty(PROPERTY_GPS_BEARING, value);
|
||||
}
|
||||
};
|
||||
|
||||
private final MapboxAnimator.AnimationsValueChangeListener<Float> compassBearingValueListener =
|
||||
new MapboxAnimator.AnimationsValueChangeListener<Float>() {
|
||||
@Override
|
||||
public void onNewAnimationValue(Float value) {
|
||||
setBearingProperty(PROPERTY_COMPASS_BEARING, value);
|
||||
}
|
||||
};
|
||||
|
||||
private final MapboxAnimator.AnimationsValueChangeListener<Float> accuracyValueListener =
|
||||
new MapboxAnimator.AnimationsValueChangeListener<Float>() {
|
||||
@Override
|
||||
public void onNewAnimationValue(Float value) {
|
||||
updateAccuracyRadius(value);
|
||||
}
|
||||
};
|
||||
|
||||
Set<AnimatorListenerHolder> getAnimationListeners() {
|
||||
Set<AnimatorListenerHolder> holders = new HashSet<>();
|
||||
holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_LAYER_LATLNG, latLngValueListener));
|
||||
|
||||
if (renderMode == RenderMode.GPS) {
|
||||
holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_LAYER_GPS_BEARING, gpsBearingValueListener));
|
||||
} else if (renderMode == RenderMode.COMPASS) {
|
||||
holders.add(
|
||||
new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_LAYER_COMPASS_BEARING, compassBearingValueListener));
|
||||
}
|
||||
|
||||
if (renderMode == RenderMode.COMPASS || renderMode == RenderMode.NORMAL) {
|
||||
holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_LAYER_ACCURACY, accuracyValueListener));
|
||||
}
|
||||
|
||||
return holders;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.TypeEvaluator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Abstract class for all of the location component animators.
|
||||
*
|
||||
* @param <K> Data type that will be animated.
|
||||
*/
|
||||
abstract class MapboxAnimator<K> extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener {
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef( {
|
||||
ANIMATOR_LAYER_LATLNG,
|
||||
ANIMATOR_CAMERA_LATLNG,
|
||||
ANIMATOR_LAYER_GPS_BEARING,
|
||||
ANIMATOR_LAYER_COMPASS_BEARING,
|
||||
ANIMATOR_CAMERA_GPS_BEARING,
|
||||
ANIMATOR_CAMERA_COMPASS_BEARING,
|
||||
ANIMATOR_LAYER_ACCURACY,
|
||||
ANIMATOR_ZOOM,
|
||||
ANIMATOR_TILT
|
||||
})
|
||||
@interface Type {
|
||||
}
|
||||
|
||||
static final int ANIMATOR_LAYER_LATLNG = 0;
|
||||
static final int ANIMATOR_CAMERA_LATLNG = 1;
|
||||
static final int ANIMATOR_LAYER_GPS_BEARING = 2;
|
||||
static final int ANIMATOR_LAYER_COMPASS_BEARING = 3;
|
||||
static final int ANIMATOR_CAMERA_GPS_BEARING = 4;
|
||||
static final int ANIMATOR_CAMERA_COMPASS_BEARING = 5;
|
||||
static final int ANIMATOR_LAYER_ACCURACY = 6;
|
||||
static final int ANIMATOR_ZOOM = 7;
|
||||
static final int ANIMATOR_TILT = 8;
|
||||
|
||||
private final AnimationsValueChangeListener<K> updateListener;
|
||||
private final K target;
|
||||
private K animatedValue;
|
||||
|
||||
private final double minUpdateInterval;
|
||||
private long timeElapsed;
|
||||
|
||||
MapboxAnimator(@NonNull K previous, @NonNull K target, @NonNull AnimationsValueChangeListener<K> updateListener,
|
||||
int maxAnimationFps) {
|
||||
minUpdateInterval = 1E9 / maxAnimationFps;
|
||||
setObjectValues(previous, target);
|
||||
setEvaluator(provideEvaluator());
|
||||
this.updateListener = updateListener;
|
||||
this.target = target;
|
||||
addUpdateListener(this);
|
||||
addListener(new AnimatorListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
animatedValue = (K) animation.getAnimatedValue();
|
||||
|
||||
long currentTime = System.nanoTime();
|
||||
if ((currentTime - timeElapsed) < minUpdateInterval) {
|
||||
return;
|
||||
}
|
||||
|
||||
postUpdates();
|
||||
timeElapsed = currentTime;
|
||||
}
|
||||
|
||||
private class AnimatorListener extends AnimatorListenerAdapter {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
postUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
private void postUpdates() {
|
||||
updateListener.onNewAnimationValue(animatedValue);
|
||||
}
|
||||
|
||||
K getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
abstract TypeEvaluator provideEvaluator();
|
||||
|
||||
interface AnimationsValueChangeListener<K> {
|
||||
void onNewAnimationValue(K value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
|
||||
final class MapboxAnimatorProvider {
|
||||
|
||||
private static MapboxAnimatorProvider INSTANCE;
|
||||
|
||||
private MapboxAnimatorProvider() {
|
||||
// private constructor
|
||||
}
|
||||
|
||||
public static MapboxAnimatorProvider getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new MapboxAnimatorProvider();
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
MapboxLatLngAnimator latLngAnimator(LatLng previous, LatLng target,
|
||||
MapboxAnimator.AnimationsValueChangeListener updateListener,
|
||||
int maxAnimationFps) {
|
||||
return new MapboxLatLngAnimator(previous, target, updateListener, maxAnimationFps);
|
||||
}
|
||||
|
||||
MapboxFloatAnimator floatAnimator(Float previous, Float target,
|
||||
MapboxAnimator.AnimationsValueChangeListener updateListener,
|
||||
int maxAnimationFps) {
|
||||
return new MapboxFloatAnimator(previous, target, updateListener, maxAnimationFps);
|
||||
}
|
||||
|
||||
MapboxCameraAnimatorAdapter cameraAnimator(Float previous, Float target,
|
||||
MapboxAnimator.AnimationsValueChangeListener updateListener,
|
||||
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
|
||||
return new MapboxCameraAnimatorAdapter(previous, target, updateListener, cancelableCallback);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class MapboxAnimatorSetProvider {
|
||||
private static MapboxAnimatorSetProvider instance;
|
||||
|
||||
private MapboxAnimatorSetProvider() {
|
||||
// private constructor
|
||||
}
|
||||
|
||||
static MapboxAnimatorSetProvider getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new MapboxAnimatorSetProvider();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
void startAnimation(@NonNull List<Animator> animators, @NonNull Interpolator interpolator,
|
||||
long duration) {
|
||||
AnimatorSet locationAnimatorSet = new AnimatorSet();
|
||||
locationAnimatorSet.playTogether(animators);
|
||||
locationAnimatorSet.setInterpolator(interpolator);
|
||||
locationAnimatorSet.setDuration(duration);
|
||||
locationAnimatorSet.start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
|
||||
class MapboxCameraAnimatorAdapter extends MapboxFloatAnimator {
|
||||
@Nullable
|
||||
private final MapboxMap.CancelableCallback cancelableCallback;
|
||||
|
||||
MapboxCameraAnimatorAdapter(Float previous, Float target,
|
||||
AnimationsValueChangeListener updateListener,
|
||||
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
|
||||
super(previous, target, updateListener, Integer.MAX_VALUE);
|
||||
this.cancelableCallback = cancelableCallback;
|
||||
addListener(new MapboxAnimatorListener());
|
||||
}
|
||||
|
||||
private final class MapboxAnimatorListener extends AnimatorListenerAdapter {
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
if (cancelableCallback != null) {
|
||||
cancelableCallback.onCancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (cancelableCallback != null) {
|
||||
cancelableCallback.onFinish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.animation.FloatEvaluator;
|
||||
import android.animation.TypeEvaluator;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
class MapboxFloatAnimator extends MapboxAnimator<Float> {
|
||||
MapboxFloatAnimator(Float previous, Float target, AnimationsValueChangeListener updateListener, int maxAnimationFps) {
|
||||
super(previous, target, updateListener, maxAnimationFps);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
TypeEvaluator provideEvaluator() {
|
||||
return new FloatEvaluator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.animation.TypeEvaluator;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
|
||||
class MapboxLatLngAnimator extends MapboxAnimator<LatLng> {
|
||||
|
||||
MapboxLatLngAnimator(LatLng previous, LatLng target, AnimationsValueChangeListener updateListener,
|
||||
int maxAnimationFps) {
|
||||
super(previous, target, updateListener, maxAnimationFps);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
TypeEvaluator provideEvaluator() {
|
||||
return new LatLngEvaluator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
interface OnCameraMoveInvalidateListener {
|
||||
|
||||
void onInvalidateCameraMove();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import com.mapbox.mapboxsdk.location.modes.CameraMode;
|
||||
|
||||
/**
|
||||
* Listener that gets invoked when camera tracking state changes.
|
||||
*/
|
||||
public interface OnCameraTrackingChangedListener {
|
||||
/**
|
||||
* Invoked whenever camera tracking is broken.
|
||||
* This callback gets invoked just after {@link #onCameraTrackingChanged(int)}, if needed.
|
||||
*/
|
||||
void onCameraTrackingDismissed();
|
||||
|
||||
/**
|
||||
* Invoked on every {@link CameraMode} change.
|
||||
*
|
||||
* @param currentMode current active {@link CameraMode}.
|
||||
*/
|
||||
void onCameraTrackingChanged(@CameraMode.Mode int currentMode);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import com.mapbox.mapboxsdk.location.modes.CameraMode;
|
||||
|
||||
/**
|
||||
* Callback for {@link CameraMode } transition state.
|
||||
*/
|
||||
public interface OnLocationCameraTransitionListener {
|
||||
/**
|
||||
* Invoked when the camera mode transition animation has been finished.
|
||||
*
|
||||
* @param cameraMode camera mode change that initiated the transition
|
||||
*/
|
||||
void onLocationCameraTransitionFinished(@CameraMode.Mode int cameraMode);
|
||||
|
||||
/**
|
||||
* Invoked when the camera mode transition animation has been canceled.
|
||||
* <p>
|
||||
* The camera mode is set regardless of the cancellation of the transition animation.
|
||||
*
|
||||
* @param cameraMode camera mode change that initiated the transition
|
||||
*/
|
||||
void onLocationCameraTransitionCanceled(@CameraMode.Mode int cameraMode);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
/**
|
||||
* The Location Component exposes an API for listening to when the user clicks on the location
|
||||
* layer icon visible on the map. When this event occurs, the {@link #onLocationComponentClick()} method
|
||||
* gets invoked.
|
||||
*/
|
||||
public interface OnLocationClickListener {
|
||||
|
||||
/**
|
||||
* Called whenever user clicks on the location layer drawn on the map.
|
||||
*/
|
||||
void onLocationComponentClick();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
/**
|
||||
* The Location Component exposes an API for listening to when the user long clicks on the location
|
||||
* layer icon visible on the map. when this event occurs, the {@link #onLocationComponentLongClick()} method
|
||||
* gets invoked.
|
||||
*/
|
||||
public interface OnLocationLongClickListener {
|
||||
|
||||
/**
|
||||
* Called whenever user long clicks on the location layer drawn on the map.
|
||||
*/
|
||||
void onLocationComponentLongClick();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user