Add render tests to Gradle project root at platform/android (#2162)

This commit is contained in:
Bart Louwers
2024-03-04 21:44:44 +01:00
committed by GitHub
parent 0c2cb3d23f
commit 1de098e66f
10 changed files with 75 additions and 193 deletions

View File

@@ -1,12 +1,65 @@
# Developing - MapLibre Native for Android
These instructions are for developers interested in making code-level contributions to the SDK itself.
These instructions are for developers interested in making code-level contributions to MapLibre Native for Android.
## Getting the source
Clone the git repository and pull in submodules:
```bash
git clone git@github.com:maplibre/maplibre-native.git
cd maplibre-native
git submodule update --init --recursive
cd platform/android
```
## Requirements
[Android Studio](https://developer.android.com/studio) needs to be installed.
Open the `platform/android` directory to get started.
## Setting an API Key
_The test application (used for development purposes) uses MapTiler vector tiles, which require a MapTiler account and API key._
With the first Gradle invocation, Gradle will take the value of the `MLN_API_KEY` environment variable and save it to `MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml`. If the environment variable wasn't set, you can edit `developer-config.xml` manually and add your API key to the `api_key` resource.
## Running the TestApp
Run the configuration for the `MapboxGLAndroidSDKTestApp` module and select a device or emulator to deploy on.
<p align="left">
<img src="https://github.com/maplibre/maplibre-native/assets/649392/5494925e-8cbb-4d5d-8033-8a2f141ede3c" alt="Android TestApp menu" width="15%"> <img src="https://github.com/maplibre/maplibre-native/assets/649392/f169db51-615d-4fca-b297-ac6197bec674" alt="Android TestApp showing Demotiles" width="15%">
</p>
## Render Tests
To run the render tests for Android, run the configuration for the `androidRenderTest.app` module.
## Instrumentation Tests
To run the instrumentatin tests, find the `MapboxGLAndroidSDKTestApp` project in the sidebar, go to `src > androidTest`, right click and select "Run 'All Tests'".
<img width="246" alt="image" src="https://github.com/maplibre/maplibre-native/assets/649392/d9daf630-cb7f-4035-b426-fc496081fed9">
Your device needs remain unlocked for the duration of the tests.
The instrumentation tests take a long time to run (depending on the device, about an hour). You can modify the run configuration in Android Studio and add the regex `^(?!.*org\.maplibre\.android\.benchmark).*$` to exclude the benchmark.
### AWS Device Farm
The instrumentation tests are running on AWS Device Farm. To see the results and the logs, go to:
https://us-west-2.console.aws.amazon.com/devicefarm/home?region=us-east-1#/mobile/projects/20687d72-0e46-403e-8f03-0941850665bc/runs
You can log with the `maplibre` alias, with `maplibre` as username and `maplibre` as password (this is a read-only account).
## Kotlin
All new code should be written in [Kotlin](https://kotlinlang.org/).
### Style Checking
## Style Checking
To check Kotlin style, we use [ktlint](https://pinterest.github.io/ktlint/). This linter is based on the [official Kotlin coding conventions](https://kotlinlang.org/docs/coding-conventions.html). We intergrate with it using the [kotlinder](https://github.com/jeremymailen/kotlinter-gradle) Gradle plugin.
@@ -20,114 +73,4 @@ To format all Kotlin source files, use:
```
$ ./gradlew formatKotlin
```
## Getting the source
Clone the git repository and pull in submodules:
```bash
git clone git@github.com:maplibre/maplibre-native.git
cd maplibre-native
git submodule update --init --recursive
cd platform/android
```
## Installing dependencies
These dependencies are required for all operating systems and all platform targets.
- Latest stable [Android Studio](https://developer.android.com/studio/index.html)
- Update the Mapbox Maps SDK for Android with the latest
- Android SDK Build-Tools
- Android Platform-Tools
- Android SDK Tools
- CMake
- NDK
- LLDB
- Modern C++ compiler that supports `-std=c++14`\*
- clang++ 3.5 or later or
- g++-4.9 or later
- [Node.js](https://nodejs.org/)
- make sure [npm](https://www.npmjs.com) is installed as well
- [ccache](https://ccache.samba.org/) (optional)
**Note**: We partially support C++14 because GCC 4.9 does not fully implement the
final draft of the C++14 standard. More information in [DEVELOPING.md](../../DEVELOPING.md).
**Note**: On macOS you can install clang with installing the [Apple command line developer tools](https://developer.apple.com/download/).
## Opening the project
### macOS
* Environment
* Android Studio
* node
* `JAVA_HOME="/Applications/Android Studio.app/Contents/jre/Contents/Home"`
* `ANDROID_SDK_ROOT=~/Library/Android/sdk`
* In Android Studio, go to Tools > [SDK Manager](https://developer.android.com/studio/projects/install-ndk#specific-version) to install CMake and the specific NDK version specified in [`dependencies.gradle`](https://github.com/maplibre/maplibre-native/blob/main/platform/android/gradle/dependencies.gradle).
Open `platform/android` with Android Studio.
### Linux
run `make android-configuration` in the root folder of the project and open in Android Studio.
If you are using Arch Linux, install [ncurses5-compat-libs](https://aur.archlinux.org/packages/ncurses5-compat-libs).
## Project configuration
### Setup Checkstyle
Mapbox uses specific IDE settings related to code and check style.
See [checkstyle guide](https://github.com/mapbox/mapbox-gl-native-android/wiki/Setting-up-Mapbox-checkstyle) for configuration details.
#### Resolving duplicate file entries
With buck build support, Android Studio can complain about duplicate source files. To remove this warning, open `MapboxGLAndroidSDK.iml` find the list of `excludeFolder` entries and add `<excludeFolder url="file://$MODULE_DIR$/../../../misc/" />` line.
### Setting API Key
_The test application (used for development purposes) uses MapTiler vector tiles, which require a MapTiler account and API key._
With the first gradle invocation, gradle will take the value of the `MLN_API_KEY` environment variable and save it to `MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml`. If the environment variable wasn't set, you can edit `developer-config.xml` manually and add your api key to the `api_key` resource.
## Running project
Run the configuration for the `MapboxGLAndroidSDKTestApp` module and select a device or emulator to deploy on. Based on the selected device, the c++ code will be compiled for the related processor architecture. You can see the project compiling in the `View > Tool Windows > Gradle Console`.
More information about building and distributing this project in [DISTRIBUTE.md](DISTRIBUTE.md).
## Additional resources
### Using the SDK snapshot
Instead of using the latest stable release of the Maps SDK for Android, you can use a "snapshot" or the beta version if there is one available. Our snapshots are built every time a Github pull request adds code to this repository's `master` branch. If you'd like to use a snapshot build, your Android project's gradle file should have -SNAPSHOT appended to the SDK version number:
```java
// Mapbox SDK dependency
implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.1.0-SNAPSHOT'
```
You also need to have the section below in your build.gradle root folder to be able to resolve the SNAPSHOT dependencies:
```
allprojects {
repositories {
jcenter()
maven { url 'https://oss.jfrog.org/artifactory/oss-snapshot-local/' }
}
}
```
### Symbolicating native crashes
When hitting native crashes you can use ndk-stack to symbolicate crashes.
More information in [this](https://github.com/mapbox/mapbox-gl-native-android/wiki/Getting-line-numbers-from-an-Android-crash-with-ndk-stack) guide.
## Instrumentation Tests
The results of the instrumentation tests can be accessed on the AWS Console:
https://us-west-2.console.aws.amazon.com/devicefarm/home?region=us-east-1#/mobile/projects/20687d72-0e46-403e-8f03-0941850665bc/runs
You can log with the `maplibre` alias, with `maplibre` as username and `maplibre` as password (this is a read-only account).
```

View File

@@ -234,7 +234,7 @@ run-android-test-app-center:
# Uploads the compiled Android SDK to Maven Central Staging
.PHONY: run-android-publish
run-android-publish:
$(MLN_ANDROID_GRADLE_SINGLE_JOB) --debug -Pmapbox.abis=all :MapboxGLAndroidSDK:publishReleasePublicationToSonatypeRepository closeAndReleaseSonatypeStagingRepository
$(MLN_ANDROID_GRADLE_SINGLE_JOB)-Pmapbox.abis=all :MapboxGLAndroidSDK:publishReleasePublicationToSonatypeRepository closeAndReleaseSonatypeStagingRepository
# Dump system graphics information for the test app
.PHONY: android-gfxinfo

View File

@@ -21,11 +21,7 @@ import java.nio.channels.FileChannel
/**
* The offline manager is the main entry point for offline-related functionality.
*
*
* It'll help you list and create offline regions.
*
*
* @see [Offline Maps Information/](https://docs.mapbox.com/help/troubleshooting/mobile-offline/)
*/
@UiThread
class OfflineManager private constructor(context: Context) {
@@ -106,9 +102,6 @@ class OfflineManager private constructor(context: Context) {
fun onError(error: String)
}
/*
* Constructor
*/
init {
this.context = context.applicationContext
fileSource = FileSource.getInstance(this.context)
@@ -126,11 +119,9 @@ class OfflineManager private constructor(context: Context) {
/**
* Retrieve all regions in the offline database.
*
*
* The query will be executed asynchronously and the results passed to the given
* callback on the main thread.
*
*
* @param callback the callback to be invoked
*/
fun listOfflineRegions(callback: ListOfflineRegionsCallback) {
@@ -158,26 +149,21 @@ class OfflineManager private constructor(context: Context) {
/**
* Merge offline regions from a secondary database into the main offline database.
*
*
* When the merge is completed, or fails, the [MergeOfflineRegionsCallback] will be invoked on the main thread.
* The callback reference is **strongly kept** throughout the process,
* so it needs to be wrapped in a weak reference or released on the client side if necessary.
*
*
* The secondary database may need to be upgraded to the latest schema.
* This is done in-place and requires write-access to the provided path.
* If the app's process doesn't have write-access to the provided path,
* the file will be copied to the temporary, internal directory for the duration of the merge.
*
*
* Only resources and tiles that belong to a region will be copied over. Identical
* regions will be flattened into a single new region in the main database.
*
*
* The operation will be aborted and [MergeOfflineRegionsCallback.onError] with an appropriate message
* will be invoked if the merge would result in the offline tile count limit being exceeded.
*
*
* Merged regions may not be in a completed status if the secondary database
* does not contain all the tiles or resources required by the region definition.
*
@@ -218,12 +204,10 @@ class OfflineManager private constructor(context: Context) {
/**
* Delete existing database and re-initialize.
*
*
* When the operation is complete or encounters an error, the given callback will be
* executed on the database thread; it is the responsibility of the SDK bindings
* executed on the database thread; it is the responsibility of the platform bindings
* to re-execute a user-provided callback on the main thread.
*
*
* @param callback the callback to be invoked when the database was reset or when the operation erred.
*/
fun resetDatabase(callback: FileSourceCallback?) {
@@ -248,17 +232,13 @@ class OfflineManager private constructor(context: Context) {
/**
* Packs the existing database file into a minimal amount of disk space.
*
*
* This operation has a performance impact as it will vacuum the database,
* forcing it to move pages on the filesystem.
*
*
*
* When the operation is complete or encounters an error, the given callback will be
* executed on the database thread; it is the responsibility of the SDK bindings
* to re-execute a user-provided callback on the main thread.
*
*
* @param callback the callback to be invoked when the database was reset or when the operation erred.
*/
fun packDatabase(callback: FileSourceCallback?) {
@@ -283,18 +263,15 @@ class OfflineManager private constructor(context: Context) {
/**
* Forces re-validation of the ambient cache.
*
*
* Forces MapLibre GL Native to revalidate resources stored in the ambient
* Forces MapLibre Native to revalidate resources stored in the ambient
* cache with the tile server before using them, making sure they
* are the latest version. This is more efficient than cleaning the
* cache because if the resource is considered valid after the server
* lookup, it will not get downloaded again.
*
*
* Resources overlapping with offline regions will not be affected
* by this call.
*
*
* @param callback the callback to be invoked when the ambient cache was invalidated or when the operation erred.
*/
fun invalidateAmbientCache(callback: FileSourceCallback?) {
@@ -319,13 +296,10 @@ class OfflineManager private constructor(context: Context) {
/**
* Erase resources from the ambient cache, freeing storage space.
*
*
* Erases the ambient cache, freeing resources.
* Note that this operation can be potentially slow if packing the database
* occurs automatically ([OfflineManager.runPackDatabaseAutomatically]).
*
*
*
* Resources overlapping with offline regions will not be affected
* by this call.
*
@@ -354,7 +328,6 @@ class OfflineManager private constructor(context: Context) {
/**
* Sets the maximum size in bytes for the ambient cache.
*
*
* This call is potentially expensive because it will try
* to trim the data in case the database is larger than the
* size defined. The size of offline regions are not affected
@@ -362,25 +335,17 @@ class OfflineManager private constructor(context: Context) {
* to not exceed the maximum size defined, taking into account
* the current size for the offline regions.
*
*
*
* Note that if you use the SDK's offline functionality, your ability to set the ambient cache size will be limited.
* Space that offline regions take up detract from the space available for ambient caching, and the ambient cache
* size does not block offline downloads. For example: if the maximum cache size is set to 50 MB and 40 MB are
* already used by offline regions, the ambient cache size will effectively be 10 MB.
*
*
*
* Setting the size to 0 will disable the cache if there is no
* offline region on the database.
*
* <[
*
*
* This method should always be called at the start of an app, before setting the style and loading a map.
* Otherwise, the map will instantiate with the default cache size of 50 MB.
*
*
* @param size the maximum size of the ambient cache
* @param callback the callback to be invoked when the the maximum size has been set or when the operation erred.
*/
@@ -458,23 +423,13 @@ class OfflineManager private constructor(context: Context) {
* Creates an offline region in the database by downloading the resources needed to use
* the given region offline.
*
*
* As of version 8.3.0 of the Maps SDK for Android, offline tile requests are no longer exempt from
* billing on mobile. Developers are subject to separate Vector Tile or Raster Tile API pricing for
* offline use. See [our pricing page](https://www.mapbox.com/pricing/) for more information.
*
*
*
* When the initial database queries have completed, the provided callback will be
* executed on the main thread.
*
*
*
* Note that the resulting region will be in an inactive download state; to begin
* downloading resources, call `OfflineRegion.setDownloadState(DownloadState.STATE_ACTIVE)`,
* optionally registering an `OfflineRegionObserver` beforehand.
*
*
* @param definition the offline region definition
* @param metadata the metadata in bytes
* @param callback the callback to be invoked
@@ -521,15 +476,13 @@ class OfflineManager private constructor(context: Context) {
}
/**
* Sets the maximum number of MapLibre-hosted tiles that may be downloaded and stored on the current device.
* Sets the maximum number of tiles that may be downloaded and stored on the current device.
* By default, the limit is set to 6,000.
*
*
* Once this limit is reached, [OfflineRegion.OfflineRegionObserver.mapboxTileCountLimitExceeded]
* fires every additional attempt to download additional tiles until already downloaded tiles are removed
* by calling [OfflineRegion.delete].
*
*
* @param limit the maximum number of tiles allowed to be downloaded
*/
@Keep
@@ -539,7 +492,6 @@ class OfflineManager private constructor(context: Context) {
* Sets whether database file packing occurs automatically.
* By default, the automatic database file packing is enabled.
*
*
* If packing is enabled, database file packing occurs automatically
* after an offline region is deleted by calling
* [OfflineRegion.delete]
@@ -548,7 +500,6 @@ class OfflineManager private constructor(context: Context) {
* If packing is disabled, disk space will not be freed after
* resources are removed unless [OfflineManager.packDatabase] is explicitly called.
*
*
* @param autopack flag setting the automatic database file packing.
*/
@@ -593,7 +544,6 @@ class OfflineManager private constructor(context: Context) {
* Use this method to pre-warm the cache with resources you know
* will be requested.
*
*
* This call is asynchronous: the data may not be immediately available
* for in-progress requests, although subsequent requests should have
* access to the cached data.

View File

@@ -187,7 +187,7 @@ class MapGestureDetectorTest : BaseTest() {
onView(withId(R.id.mapView)).perform(quickScale(-300f, withVelocity = false, duration = 750L))
rule.runOnUiThread {
Assert.assertEquals(3.0, maplibreMap.cameraPosition.zoom, 0.0001)
Assert.assertEquals(3.0, maplibreMap.cameraPosition.zoom, 0.001)
}
}
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">MapLibre Android SDK TestApp</string>
<string name="app_name">MapLibre Native Android TestApp</string>
<string name="fps30">fps30</string>
<string name="fps60">fps60</string>
<string name="this_is_an_empty_fragment">This is an empty Fragment</string>

View File

@@ -1,21 +1,6 @@
ext["signing.keyId"] = ''
ext["signing.password"] = ''
ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID')
ext["signing.password"] = System.getenv('SIGNING_PASSWORD')
ext["signing.secretKeyRingFile"] = "${projectDir}/../signing-key.gpg"
ext["ossrhUsername"] = ''
ext["ossrhPassword"] = ''
ext["sonatypeStagingProfileId"] = ''
File secretPropsFile = project.rootProject.file('local.properties')
if (secretPropsFile.exists()) {
// Read local.properties file first if it exists
Properties p = new Properties()
new FileInputStream(secretPropsFile).withCloseable { is -> p.load(is) }
p.each { name, value -> ext[name] = value }
} else {
// Use system environment variables
ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME')
ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD')
ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID')
ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID')
ext["signing.password"] = System.getenv('SIGNING_PASSWORD')
}
ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME')
ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD')
ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID')

View File

@@ -1 +1,4 @@
include ':MapboxGLAndroidSDK', ':MapboxGLAndroidSDKTestApp', ':MapboxGLAndroidSDKLint'
include ':MapboxGLAndroidSDK', ':MapboxGLAndroidSDKTestApp', ':MapboxGLAndroidSDKLint'
def renderTestProjectDir = new File(rootDir, '../../render-test/android')
includeBuild(renderTestProjectDir)

View File

@@ -12,7 +12,7 @@ android {
abi = project.getProperty('mapbox.abis')
}
ndk {
if (abi != 'all') {
if (abi != 'all' && abi != 'none') {
abiFilters abi.split(' ')
} else {
abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a', 'x86_64'

View File

@@ -4,7 +4,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.0.2'
classpath 'com.android.tools.build:gradle:8.1.4'
}
}

View File

@@ -1,2 +1,3 @@
include ':app'
rootProject.name = "androidRenderTest"
include ":app"