Skip to content

Commit 0a11316

Browse files
Add MapRecorder support for Flutter SDK (#1069)
Add MapRecorder support Implements MapRecorder functionality to achieve feature parity with Android and iOS native SDKs. - Add Pigeon-generated platform communication layer - Implement MapRecorderController for Android and iOS platforms - Create MapRecorder wrapper class with user-facing API - Integrate into MapboxMap via recorder property - Add working example demonstrating record/replay/pause features - Mark API as @experimental matching native SDK status Co-authored-by: protheeuz <[email protected]>
1 parent 0332899 commit 0a11316

File tree

15 files changed

+1794
-0
lines changed

15 files changed

+1794
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
### main
22

3+
* Add experimental MapRecorder API to record and replay map interactions for debugging and performance testing.
4+
35
### 2.17.0-rc.1
46

57
* [iOS] Fix annotation interaction handlers (tap, drag etc.) not working after annotation update.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package com.mapbox.maps.mapbox_maps
2+
3+
import com.mapbox.maps.MapboxExperimental
4+
import com.mapbox.maps.MapboxMap
5+
import com.mapbox.maps.MapboxMapRecorder
6+
import com.mapbox.maps.mapbox_maps.pigeons.MapPlayerOptions
7+
import com.mapbox.maps.mapbox_maps.pigeons.MapRecorderOptions
8+
import com.mapbox.maps.mapbox_maps.pigeons._MapRecorderMessenger
9+
import java.nio.ByteBuffer
10+
11+
/**
12+
* Controller for MapRecorder functionality.
13+
*
14+
* Provides functions to record and replay API calls of a MapboxMap instance.
15+
* These recordings can be used to debug issues which require multiple steps to reproduce.
16+
* Additionally, playbacks can be used for performance testing custom scenarios.
17+
*/
18+
@OptIn(MapboxExperimental::class)
19+
class MapRecorderController(
20+
private val mapboxMap: MapboxMap
21+
) : _MapRecorderMessenger {
22+
23+
private var recorder: MapboxMapRecorder? = null
24+
25+
/**
26+
* Get or create the recorder instance.
27+
*/
28+
private fun getRecorder(): MapboxMapRecorder {
29+
if (recorder == null) {
30+
recorder = mapboxMap.createRecorder()
31+
}
32+
return recorder!!
33+
}
34+
35+
override fun startRecording(options: MapRecorderOptions) {
36+
val nativeOptions = com.mapbox.maps.MapRecorderOptions.Builder()
37+
.apply {
38+
options.timeWindow?.let { timeWindow(it) }
39+
loggingEnabled(options.loggingEnabled)
40+
compressed(options.compressed)
41+
}
42+
.build()
43+
44+
getRecorder().startRecording(nativeOptions)
45+
}
46+
47+
override fun stopRecording(callback: (Result<ByteArray>) -> Unit) {
48+
try {
49+
val data = getRecorder().stopRecording()
50+
val bytes = ByteArray(data.remaining())
51+
data.get(bytes)
52+
callback(Result.success(bytes))
53+
} catch (e: Exception) {
54+
callback(Result.failure(e))
55+
}
56+
}
57+
58+
override fun replay(
59+
recordedSequence: ByteArray,
60+
options: MapPlayerOptions,
61+
callback: (Result<Unit>) -> Unit
62+
) {
63+
try {
64+
val nativeOptions = com.mapbox.maps.MapPlayerOptions.Builder()
65+
.playbackCount(options.playbackCount.toInt())
66+
.playbackSpeedMultiplier(options.playbackSpeedMultiplier)
67+
.avoidPlaybackPauses(options.avoidPlaybackPauses)
68+
.build()
69+
70+
val buffer = ByteBuffer.wrap(recordedSequence)
71+
getRecorder().replay(buffer, nativeOptions) {
72+
callback(Result.success(Unit))
73+
}
74+
} catch (e: Exception) {
75+
callback(Result.failure(e))
76+
}
77+
}
78+
79+
override fun togglePauseReplay() {
80+
getRecorder().togglePauseReplay()
81+
}
82+
83+
override fun getPlaybackState(): String {
84+
return getRecorder().getPlaybackState()
85+
}
86+
87+
fun dispose() {
88+
recorder = null
89+
}
90+
}

android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import com.mapbox.maps.mapbox_maps.pigeons._AnimationManager
3131
import com.mapbox.maps.mapbox_maps.pigeons._CameraManager
3232
import com.mapbox.maps.mapbox_maps.pigeons._LocationComponentSettingsInterface
3333
import com.mapbox.maps.mapbox_maps.pigeons._MapInterface
34+
import com.mapbox.maps.mapbox_maps.pigeons._MapRecorderMessenger
3435
import com.mapbox.maps.mapbox_maps.pigeons._PerformanceStatisticsApi
3536
import com.mapbox.maps.mapbox_maps.pigeons._ViewportMessenger
3637
import com.mapbox.maps.plugin.animation.camera
@@ -122,6 +123,7 @@ class MapboxMapController(
122123
private val compassController: CompassController
123124
private val viewportController: ViewportController
124125
private val performanceStatisticsController: PerformanceStatisticsController
126+
private val mapRecorderController: MapRecorderController
125127

126128
private val eventHandler: MapboxEventHandler
127129

@@ -206,6 +208,7 @@ class MapboxMapController(
206208
compassController = CompassController(mapView)
207209
viewportController = ViewportController(mapView.viewport, mapView.camera, context, mapboxMap)
208210
performanceStatisticsController = PerformanceStatisticsController(mapboxMap, this.messenger, this.channelSuffix)
211+
mapRecorderController = MapRecorderController(mapboxMap)
209212
changeUserAgent(pluginVersion)
210213

211214
StyleManager.setUp(messenger, styleController, this.channelSuffix)
@@ -222,6 +225,7 @@ class MapboxMapController(
222225
CompassSettingsInterface.setUp(messenger, compassController, this.channelSuffix)
223226
_ViewportMessenger.setUp(messenger, viewportController, this.channelSuffix)
224227
_PerformanceStatisticsApi.setUp(messenger, performanceStatisticsController, this.channelSuffix)
228+
_MapRecorderMessenger.setUp(messenger, mapRecorderController, this.channelSuffix)
225229

226230
methodChannel = MethodChannel(messenger, "plugins.flutter.io.$channelSuffix")
227231
methodChannel.setMethodCallHandler(this)
@@ -288,6 +292,8 @@ class MapboxMapController(
288292
AttributionSettingsInterface.setUp(messenger, null, channelSuffix)
289293
_ViewportMessenger.setUp(messenger, null, channelSuffix)
290294
_PerformanceStatisticsApi.setUp(messenger, null, channelSuffix)
295+
_MapRecorderMessenger.setUp(messenger, null, channelSuffix)
296+
mapRecorderController.dispose()
291297
}
292298

293299
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {

0 commit comments

Comments
 (0)