Skip to content

Commit 8859993

Browse files
committed
Add a ReplayHistorySession
1 parent 09b5248 commit 8859993

File tree

15 files changed

+602
-150
lines changed

15 files changed

+602
-150
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Added ReplayHistorySession and MapboxTripStarter.enableReplayHistory()

examples/src/main/java/com/mapbox/navigation/examples/MainActivity.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import androidx.core.app.ActivityCompat
1111
import androidx.core.content.ContextCompat
1212
import androidx.recyclerview.widget.LinearLayoutManager
1313
import com.mapbox.android.core.permissions.PermissionsListener
14+
import com.mapbox.common.LogConfiguration
15+
import com.mapbox.common.LoggingLevel
1416
import com.mapbox.navigation.examples.core.IndependentRouteGenerationActivity
1517
import com.mapbox.navigation.examples.core.MapboxBuildingHighlightActivity
1618
import com.mapbox.navigation.examples.core.MapboxCustomStyleActivity
@@ -39,6 +41,7 @@ class MainActivity : AppCompatActivity(), PermissionsListener {
3941

4042
override fun onCreate(savedInstanceState: Bundle?) {
4143
super.onCreate(savedInstanceState)
44+
LogConfiguration.setLoggingLevel(LoggingLevel.DEBUG)
4245
binding = LayoutActivityMainBinding.inflate(layoutInflater)
4346
setContentView(binding.root)
4447

examples/src/main/java/com/mapbox/navigation/examples/core/ReplayHistoryActivity.kt

Lines changed: 18 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import android.content.res.Configuration
66
import android.content.res.Resources
77
import android.location.Location
88
import android.os.Bundle
9-
import android.view.View
109
import android.widget.Button
1110
import android.widget.SeekBar
1211
import androidx.appcompat.app.AppCompatActivity
@@ -23,11 +22,8 @@ import com.mapbox.maps.plugin.locationcomponent.location
2322
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
2423
import com.mapbox.navigation.base.options.NavigationOptions
2524
import com.mapbox.navigation.core.MapboxNavigation
26-
import com.mapbox.navigation.core.MapboxNavigationProvider
2725
import com.mapbox.navigation.core.directions.session.RoutesObserver
28-
import com.mapbox.navigation.core.replay.MapboxReplayer
29-
import com.mapbox.navigation.core.replay.history.ReplayEventBase
30-
import com.mapbox.navigation.core.replay.history.ReplaySetNavigationRoute
26+
import com.mapbox.navigation.core.replay.history.ReplayHistorySession
3127
import com.mapbox.navigation.core.trip.session.LocationMatcherResult
3228
import com.mapbox.navigation.core.trip.session.LocationObserver
3329
import com.mapbox.navigation.core.trip.session.RouteProgressObserver
@@ -50,21 +46,22 @@ import com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineOptions
5046
import com.mapbox.navigation.ui.maps.route.line.model.RouteLine
5147
import com.mapbox.navigation.ui.maps.route.line.model.RouteLineColorResources
5248
import com.mapbox.navigation.ui.maps.route.line.model.RouteLineResources
49+
import com.mapbox.navigation.utils.internal.logI
5350
import kotlinx.coroutines.CoroutineScope
5451
import kotlinx.coroutines.Dispatchers
5552
import kotlinx.coroutines.Job
5653
import kotlinx.coroutines.launch
57-
import java.util.Collections
54+
import java.util.*
5855

5956
private const val DEFAULT_INITIAL_ZOOM = 15.0
6057

58+
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
6159
class ReplayHistoryActivity : AppCompatActivity() {
6260

6361
private var loadNavigationJob: Job? = null
6462
private val navigationLocationProvider = NavigationLocationProvider()
6563
private lateinit var historyFileLoader: HistoryFileLoader
6664
private lateinit var mapboxNavigation: MapboxNavigation
67-
private lateinit var mapboxReplayer: MapboxReplayer
6865
private lateinit var locationComponent: LocationComponentPlugin
6966
private lateinit var navigationCamera: NavigationCamera
7067
private lateinit var viewportDataSource: MapboxNavigationViewportDataSource
@@ -103,6 +100,7 @@ class ReplayHistoryActivity : AppCompatActivity() {
103100
40.0 * pixelDensity
104101
)
105102
}
103+
private val replayHistorySession = ReplayHistorySession()
106104

107105
private val initialCameraOptions: CameraOptions? = CameraOptions.Builder()
108106
.zoom(DEFAULT_INITIAL_ZOOM)
@@ -163,7 +161,7 @@ class ReplayHistoryActivity : AppCompatActivity() {
163161
super.onDestroy()
164162
routeLineApi.cancel()
165163
routeLineView.cancel()
166-
mapboxReplayer.finish()
164+
replayHistorySession.onDetached(mapboxNavigation)
167165
mapboxNavigation.onDestroy()
168166
if (::locationComponent.isInitialized) {
169167
locationComponent.removeOnIndicatorPositionChangedListener(onPositionChangedListener)
@@ -212,15 +210,16 @@ class ReplayHistoryActivity : AppCompatActivity() {
212210
viewportDataSource.onLocationChanged(locationMatcherResult.enhancedLocation)
213211
viewportDataSource.evaluate()
214212
if (!isLocationInitialized) {
213+
logI("ReplayHistoryActivity") {
214+
"onNewLocationMatcherResult initialize location"
215+
}
215216
isLocationInitialized = true
216-
val instantTransition = NavigationCameraTransitionOptions.Builder()
217-
.maxDuration(0)
218-
.build()
219-
navigationCamera.requestNavigationCameraToOverview(
220-
stateTransitionOptions = instantTransition,
217+
navigationCamera.requestNavigationCameraToFollowing(
218+
stateTransitionOptions = NavigationCameraTransitionOptions.Builder()
219+
.maxDuration(0)
220+
.build(),
221221
)
222222
}
223-
224223
navigationLocationProvider.changePosition(
225224
locationMatcherResult.enhancedLocation,
226225
locationMatcherResult.keyPoints,
@@ -308,21 +307,12 @@ class ReplayHistoryActivity : AppCompatActivity() {
308307
@SuppressLint("MissingPermission")
309308
private fun initNavigation() {
310309
historyFileLoader = HistoryFileLoader()
311-
mapboxNavigation = MapboxNavigationProvider.create(
310+
mapboxNavigation = MapboxNavigation(
312311
NavigationOptions.Builder(this)
313312
.accessToken(Utils.getMapboxAccessToken(this))
314313
.build()
315314
)
316-
startReplayTripSession()
317-
}
318-
319-
/**
320-
* This is showcasing a new way to replay rides at runtime.
321-
*/
322-
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
323-
private fun startReplayTripSession() {
324-
mapboxReplayer = mapboxNavigation.mapboxReplayer
325-
mapboxNavigation.startReplayTripSession()
315+
replayHistorySession.onAttached(mapboxNavigation)
326316
}
327317

328318
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
@@ -336,24 +326,9 @@ class ReplayHistoryActivity : AppCompatActivity() {
336326
@SuppressLint("MissingPermission")
337327
private fun handleHistoryFileSelected() {
338328
loadNavigationJob = CoroutineScope(Dispatchers.Main).launch {
339-
val events = historyFileLoader
340-
.loadReplayHistory(this@ReplayHistoryActivity)
341-
mapboxReplayer.clearEvents()
342-
mapboxReplayer.pushEvents(events)
343-
binding.playReplay.visibility = View.VISIBLE
344-
mapboxNavigation.resetTripSession()
345-
mapboxNavigation.setRoutes(emptyList())
329+
val historyReader = historyFileLoader.loadReplayHistory(this@ReplayHistoryActivity)
330+
replayHistorySession.setHistoryFile(historyReader.filePath)
346331
isLocationInitialized = false
347-
mapboxReplayer.playFirstLocation()
348-
}
349-
}
350-
351-
@SuppressLint("SetTextI18n")
352-
private fun updateReplayStatus(playbackEvents: List<ReplayEventBase>) {
353-
playbackEvents.lastOrNull()?.eventTimestamp?.let {
354-
val currentSecond = mapboxReplayer.eventSeconds(it).toInt()
355-
val durationSecond = mapboxReplayer.durationSeconds().toInt()
356-
binding.playerStatus.text = "$currentSecond:$durationSecond"
357332
}
358333
}
359334

@@ -368,7 +343,7 @@ class ReplayHistoryActivity : AppCompatActivity() {
368343
binding.seekBar.setOnSeekBarChangeListener(
369344
object : SeekBar.OnSeekBarChangeListener {
370345
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
371-
mapboxReplayer.playbackSpeed(progress.toDouble())
346+
mapboxNavigation.mapboxReplayer.playbackSpeed(progress.toDouble())
372347
binding.seekBarText.text = getString(
373348
R.string.replay_playback_speed_seekbar,
374349
progress
@@ -379,26 +354,5 @@ class ReplayHistoryActivity : AppCompatActivity() {
379354
override fun onStopTrackingTouch(seekBar: SeekBar) {}
380355
}
381356
)
382-
383-
binding.playReplay.setOnClickListener {
384-
mapboxReplayer.play()
385-
binding.playReplay.visibility = View.GONE
386-
navigationCamera.requestNavigationCameraToFollowing()
387-
}
388-
389-
mapboxReplayer.registerObserver { events ->
390-
updateReplayStatus(events)
391-
events.forEach {
392-
when (it) {
393-
is ReplaySetNavigationRoute -> setRoute(it)
394-
}
395-
}
396-
}
397-
}
398-
399-
private fun setRoute(replaySetRoute: ReplaySetNavigationRoute) {
400-
replaySetRoute.route?.let { directionRoute ->
401-
mapboxNavigation.setNavigationRoutes(Collections.singletonList(directionRoute))
402-
}
403357
}
404358
}

examples/src/main/java/com/mapbox/navigation/examples/core/replay/HistoryFileLoader.kt

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,28 @@ package com.mapbox.navigation.examples.core.replay
33
import android.annotation.SuppressLint
44
import android.content.Context
55
import com.mapbox.navigation.core.history.MapboxHistoryReader
6-
import com.mapbox.navigation.core.replay.history.ReplayEventBase
7-
import com.mapbox.navigation.core.replay.history.ReplayHistoryMapper
8-
import com.mapbox.navigation.core.replay.history.ReplaySetNavigationRoute
96
import kotlinx.coroutines.Dispatchers
107
import kotlinx.coroutines.withContext
118

129
class HistoryFileLoader {
13-
private val replayHistoryMapper = ReplayHistoryMapper.Builder().setRouteMapper {
14-
ReplaySetNavigationRoute.Builder(eventTimestamp = it.eventTimestamp)
15-
.route(it.navigationRoute)
16-
.build()
17-
}.build()
1810
private val historyFilesDirectory = HistoryFilesDirectory()
1911

2012
@SuppressLint("MissingPermission")
2113
suspend fun loadReplayHistory(
2214
context: Context
23-
): List<ReplayEventBase> = withContext(Dispatchers.IO) {
24-
loadSelectedHistory() ?: loadDefaultReplayHistory(context)
15+
): MapboxHistoryReader = withContext(Dispatchers.IO) {
16+
HistoryFilesActivity.selectedHistory ?: loadDefaultReplayHistory(context)
2517
}
2618

27-
private suspend fun loadSelectedHistory(): List<ReplayEventBase>? =
28-
withContext(Dispatchers.IO) {
29-
HistoryFilesActivity.selectedHistory?.asSequence()?.mapNotNull { historyEvent ->
30-
replayHistoryMapper.mapToReplayEvent(historyEvent)
31-
}?.toList()
32-
}
33-
3419
private suspend fun loadDefaultReplayHistory(
3520
context: Context
36-
): List<ReplayEventBase> = withContext(Dispatchers.IO) {
21+
): MapboxHistoryReader = withContext(Dispatchers.IO) {
3722
val fileName = "replay-history-activity.json"
3823
val inputStream = context.assets.open(fileName)
3924
val outputFile = historyFilesDirectory.outputFile(context, fileName)
4025
outputFile.outputStream().use { fileOut ->
4126
inputStream.copyTo(fileOut)
4227
}
4328
MapboxHistoryReader(outputFile.absolutePath)
44-
.asSequence()
45-
.mapNotNull { replayHistoryMapper.mapToReplayEvent(it) }
46-
.toList()
4729
}
4830
}

examples/src/main/res/layout/activity_replay_history_layout.xml

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,23 +34,11 @@
3434
android:text="@string/select_history"
3535
/>
3636

37-
<androidx.appcompat.widget.AppCompatButton
38-
android:id="@+id/playReplay"
39-
android:layout_width="0dp"
40-
android:layout_height="wrap_content"
41-
app:layout_constraintEnd_toEndOf="parent"
42-
app:layout_constraintStart_toStartOf="parent"
43-
app:layout_constraintBottom_toBottomOf="parent"
44-
android:background="@color/colorPrimary"
45-
android:text="@string/play_history"
46-
android:textColor="@android:color/white"
47-
/>
48-
4937
<com.google.android.material.card.MaterialCardView
5038
android:layout_width="match_parent"
5139
android:layout_height="wrap_content"
5240
android:theme="@style/Theme.MaterialComponents.Light"
53-
app:layout_constraintBottom_toTopOf="@id/playReplay"
41+
app:layout_constraintBottom_toBottomOf="parent"
5442
app:cardElevation="3dp"
5543
app:cardUseCompatPadding="true">
5644

@@ -71,17 +59,10 @@
7159
android:id="@+id/seekBar"
7260
android:layout_width="match_parent"
7361
android:layout_height="wrap_content"
74-
android:paddingBottom="6dp"
62+
android:paddingBottom="30dp"
7563
android:paddingTop="6dp"
7664
/>
7765

78-
<TextView
79-
android:id="@+id/playerStatus"
80-
android:layout_width="match_parent"
81-
android:layout_height="wrap_content"
82-
android:padding="6dp"
83-
android:text="Paused"/>
84-
8566
</LinearLayout>
8667

8768
</com.google.android.material.card.MaterialCardView>

libnavigation-core/api/current.txt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ package com.mapbox.navigation.core.history {
270270
method public String getFilePath();
271271
method public boolean hasNext();
272272
method public com.mapbox.navigation.core.history.model.HistoryEvent next();
273+
method public java.util.List<com.mapbox.navigation.core.history.model.HistoryEvent> takeLocations(int count);
273274
property public final String filePath;
274275
}
275276

@@ -529,6 +530,9 @@ package com.mapbox.navigation.core.replay.history {
529530
property public final Double? time;
530531
}
531532

533+
public final class ReplayEventLocationMapperKt {
534+
}
535+
532536
public final class ReplayEventUpdateLocation implements com.mapbox.navigation.core.replay.history.ReplayEventBase {
533537
ctor public ReplayEventUpdateLocation(@com.google.gson.annotations.SerializedName("event_timestamp") double eventTimestamp, @com.google.gson.annotations.SerializedName("location") com.mapbox.navigation.core.replay.history.ReplayEventLocation location);
534538
method public double component1();
@@ -570,6 +574,33 @@ package com.mapbox.navigation.core.replay.history {
570574
method public com.mapbox.navigation.core.replay.history.ReplayHistoryMapper.Builder statusMapper(com.mapbox.navigation.core.replay.history.ReplayHistoryEventMapper<com.mapbox.navigation.core.history.model.HistoryEventGetStatus>? statusMapper);
571575
}
572576

577+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public final class ReplayHistorySession implements com.mapbox.navigation.core.lifecycle.MapboxNavigationObserver {
578+
ctor public ReplayHistorySession();
579+
method public kotlinx.coroutines.flow.StateFlow<com.mapbox.navigation.core.replay.history.ReplayHistorySessionOptions> getOptions();
580+
method public void onAttached(com.mapbox.navigation.core.MapboxNavigation mapboxNavigation);
581+
method public void onDetached(com.mapbox.navigation.core.MapboxNavigation mapboxNavigation);
582+
method public void setHistoryFile(String absolutePath);
583+
method public void setOptions(com.mapbox.navigation.core.replay.history.ReplayHistorySessionOptions options);
584+
}
585+
586+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public final class ReplayHistorySessionOptions {
587+
method public boolean getEnableSetRoute();
588+
method public String? getFilePath();
589+
method public com.mapbox.navigation.core.replay.history.ReplayHistoryMapper getReplayHistoryMapper();
590+
method public com.mapbox.navigation.core.replay.history.ReplayHistorySessionOptions.Builder toBuilder();
591+
property public final boolean enableSetRoute;
592+
property public final String? filePath;
593+
property public final com.mapbox.navigation.core.replay.history.ReplayHistoryMapper replayHistoryMapper;
594+
}
595+
596+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public static final class ReplayHistorySessionOptions.Builder {
597+
ctor public ReplayHistorySessionOptions.Builder();
598+
method public com.mapbox.navigation.core.replay.history.ReplayHistorySessionOptions build();
599+
method public com.mapbox.navigation.core.replay.history.ReplayHistorySessionOptions.Builder enableSetRoute(boolean enableSetRoute);
600+
method public com.mapbox.navigation.core.replay.history.ReplayHistorySessionOptions.Builder filePath(String? filePath);
601+
method public com.mapbox.navigation.core.replay.history.ReplayHistorySessionOptions.Builder replayHistoryMapper(com.mapbox.navigation.core.replay.history.ReplayHistoryMapper replayHistoryMapper);
602+
}
603+
573604
public final class ReplaySetNavigationRoute implements com.mapbox.navigation.core.replay.history.ReplayEventBase {
574605
method public double getEventTimestamp();
575606
method public com.mapbox.navigation.base.route.NavigationRoute? getRoute();
@@ -1027,8 +1058,10 @@ package com.mapbox.navigation.core.trip {
10271058
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public final class MapboxTripStarter implements com.mapbox.navigation.core.lifecycle.MapboxNavigationObserver {
10281059
method public static com.mapbox.navigation.core.trip.MapboxTripStarter create();
10291060
method public com.mapbox.navigation.core.trip.MapboxTripStarter enableMapMatching();
1061+
method public com.mapbox.navigation.core.trip.MapboxTripStarter enableReplayHistory(com.mapbox.navigation.core.replay.history.ReplayHistorySessionOptions? options = null);
10301062
method public com.mapbox.navigation.core.trip.MapboxTripStarter enableReplayRoute(com.mapbox.navigation.core.replay.route.ReplayRouteSessionOptions? options = null);
10311063
method public static com.mapbox.navigation.core.trip.MapboxTripStarter getRegisteredInstance();
1064+
method public com.mapbox.navigation.core.replay.history.ReplayHistorySessionOptions getReplayHistorySessionOptions();
10321065
method public com.mapbox.navigation.core.replay.route.ReplayRouteSessionOptions getReplayRouteSessionOptions();
10331066
method public void onAttached(com.mapbox.navigation.core.MapboxNavigation mapboxNavigation);
10341067
method public void onDetached(com.mapbox.navigation.core.MapboxNavigation mapboxNavigation);

libnavigation-core/src/main/java/com/mapbox/navigation/core/history/MapboxHistoryReader.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.mapbox.navigation.core.history
22

33
import com.mapbox.navigation.core.history.model.HistoryEvent
44
import com.mapbox.navigation.core.history.model.HistoryEventMapper
5+
import com.mapbox.navigation.core.history.model.HistoryEventUpdateLocation
56
import com.mapbox.navigator.HistoryReader
67

78
/**
@@ -38,6 +39,26 @@ class MapboxHistoryReader(
3839
hasNext = loadNext()
3940
}
4041

42+
/**
43+
* Loads the next [count] location events from the history file and returns all of the
44+
* [HistoryEvent] in a list. The size of the list returned will often be larger than [count]
45+
* because there are other types of events in the history file.
46+
*
47+
* @param count the maximum number of [HistoryEventUpdateLocation] to take
48+
*/
49+
fun takeLocations(count: Int): List<HistoryEvent> {
50+
val historyEvents = mutableListOf<HistoryEvent>()
51+
var locationCount = 0
52+
while (locationCount < count && hasNext) {
53+
val event = next()
54+
if (event is HistoryEventUpdateLocation) {
55+
locationCount++
56+
}
57+
historyEvents.add(event)
58+
}
59+
return historyEvents
60+
}
61+
4162
private fun loadNext(): Boolean {
4263
val historyRecord = nativeHistoryReader.next()
4364
next = if (historyRecord != null) {

0 commit comments

Comments
 (0)