diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index d94d21718..494b37eee 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -1181,6 +1181,20 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity" />
+
+
+
+
+
(100000)
+
+ fun add(fps: Double) {
+ fpsValues.add(fps)
+ }
+
+ fun reset() {
+ fpsValues.clear()
+ }
+
+ fun low1p(): Double {
+ fpsValues.sort()
+ return fpsValues.slice(0..(fpsValues.size / 100)).average()
+ }
+
+ fun average(): Double {
+ return fpsValues.average()
+ }
+}
+
+data class Result(val average: Double, val low1p: Double)
+
+data class Results(
+ var map: MutableMap> = mutableMapOf>().withDefault { emptyList() })
+ {
+
+ fun addResult(styleName: String, fpsStore: FpsStore) {
+ val newResults = map.getValue(styleName).plus(Result(fpsStore.average(), fpsStore.low1p()))
+ map[styleName] = newResults
+ }
+}
+
+/**
+ * Benchmark using a [android.view.TextureView]
+ */
+class BenchmarkActivity : AppCompatActivity() {
+ private lateinit var mapView: MapView
+ private lateinit var maplibreMap: MapLibreMap
+ private var handler: Handler? = null
+ private var delayed: Runnable? = null
+ private var fpsStore = FpsStore()
+ private var results = Results()
+ private var runsLeft = 5
+
+ // the styles used for the benchmark
+ // can be overridden adding with developer-config.xml
+ // ```xml
+ //
+ // - Americana
+ //
+ //
+ // - https://zelonewolf.github.io/openstreetmap-americana/style.json
+ //
+ // ```
+ private var styles = listOf(Pair("Demotiles", "https://demotiles.maplibre.org/style.json"))
+
+ @SuppressLint("DiscouragedApi")
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_benchmark)
+ handler = Handler(mainLooper)
+ setupToolbar()
+ setupMapView(savedInstanceState)
+
+ val styleNames = resources.getStringArray(applicationContext.resources.getIdentifier(
+ "benchmark_style_names",
+ "array",
+ applicationContext.packageName))
+ val styleURLs = resources.getStringArray(applicationContext.resources.getIdentifier(
+ "benchmark_style_urls",
+ "array",
+ applicationContext.packageName))
+ if (styleNames.isNotEmpty() && styleNames.size == styleURLs.size) {
+ styles = styleNames.zip(styleURLs)
+ }
+ }
+
+ private fun setupToolbar() {
+ val actionBar = supportActionBar
+ if (actionBar != null) {
+ supportActionBar!!.setDisplayHomeAsUpEnabled(true)
+ supportActionBar!!.setHomeButtonEnabled(true)
+ }
+ }
+
+ private fun setupMapView(savedInstanceState: Bundle?) {
+ mapView = findViewById(R.id.mapView) as MapView
+ mapView.getMapAsync { maplibreMap: MapLibreMap ->
+ this@BenchmarkActivity.maplibreMap = maplibreMap
+ maplibreMap.setStyle(styles[0].second)
+ setFpsView(maplibreMap)
+
+ // Start an animation on the map as well
+ flyTo(maplibreMap, 0, 0,14.0)
+ }
+ }
+
+ private fun flyTo(maplibreMap: MapLibreMap, place: Int, style: Int, zoom: Double) {
+ maplibreMap.animateCamera(
+ CameraUpdateFactory.newLatLngZoom(PLACES[place], zoom),
+ 10000,
+ object : CancelableCallback {
+ override fun onCancel() {
+ delayed = Runnable {
+ delayed = null
+ flyTo(maplibreMap, place, style, zoom)
+ }
+ delayed?.let {
+ handler!!.postDelayed(it, 2000)
+ }
+ }
+
+ override fun onFinish() {
+ if (place == PLACES.size - 1) { // done with tour
+ results.addResult(styles[style].first, fpsStore)
+ fpsStore.reset()
+
+ println("FPS results $results")
+
+ if (style < styles.size - 1) { // continue with next style
+ maplibreMap.setStyle(styles[style + 1].second)
+ flyTo(maplibreMap, 0, style + 1, zoom)
+ } else if (runsLeft > 0) { // start over
+ --runsLeft
+ maplibreMap.setStyle(styles[0].second)
+ flyTo(maplibreMap, 0, 0, zoom)
+ } else {
+ finish()
+ }
+ return
+ }
+
+ // continue with next place
+ flyTo(maplibreMap, place + 1, style, zoom)
+ }
+ }
+ )
+ }
+
+ private fun setFpsView(maplibreMap: MapLibreMap) {
+ maplibreMap.setOnFpsChangedListener { fps: Double ->
+ fpsStore.add(fps)
+ }
+ }
+
+ override fun onStart() {
+ super.onStart()
+ mapView.onStart()
+ }
+
+ override fun onResume() {
+ super.onResume()
+ mapView.onResume()
+ }
+
+ override fun onPause() {
+ super.onPause()
+ mapView.onPause()
+ }
+
+ override fun onStop() {
+ super.onStop()
+ mapView.onStop()
+ if (handler != null && delayed != null) {
+ handler!!.removeCallbacks(delayed!!)
+ }
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+ mapView.onSaveInstanceState(outState)
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ mapView.onDestroy()
+ }
+
+ override fun onLowMemory() {
+ super.onLowMemory()
+ mapView.onLowMemory()
+ }
+
+ companion object {
+ private val PLACES = arrayOf(
+ LatLng(37.7749, -122.4194), // SF
+ LatLng(38.9072, -77.0369), // DC
+ LatLng(52.3702, 4.8952), // AMS
+ LatLng(60.1699, 24.9384), // HEL
+ LatLng(-13.1639, -74.2236), // AYA
+ LatLng(52.5200, 13.4050), // BER
+ LatLng(12.9716, 77.5946), // BAN
+ LatLng(31.2304, 121.4737) // SHA
+ )
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_benchmark.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_benchmark.xml
new file mode 100644
index 000000000..4a9c8d9d0
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_benchmark.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
index 6b21dc424..1b8d9d849 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
@@ -4,4 +4,7 @@
fps30
fps60
This is an empty Fragment
+ Benchmark
+ World Tour Benchmark
+ Writes out avg. fps and 1% fps after world tour with .flyTo()