Skip to content

Commit 942ea75

Browse files
committed
Make ReplayRouteSession handle route changes
1 parent 5b86726 commit 942ea75

File tree

8 files changed

+106
-124
lines changed

8 files changed

+106
-124
lines changed

android-auto-app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ dependencies {
7272
// This example is used for development so it may depend on unstable versions.
7373
// Examples based on final versions can be found in the examples repository.
7474
// https://github.com/mapbox/mapbox-navigation-android-examples
75-
implementation("com.mapbox.navigation:ui-dropin:2.10.0-rc.1")
76-
implementation("com.mapbox.search:mapbox-search-android:1.0.0-beta.42")
75+
implementation("com.mapbox.navigation:ui-dropin:2.10.0")
76+
implementation("com.mapbox.search:mapbox-search-android:1.0.0-beta.43")
7777

7878
// Dependencies needed for this example.
7979
implementation dependenciesList.androidXCore

android-auto-app/src/main/java/com/mapbox/navigation/examples/androidauto/car/MainCarSession.kt

Lines changed: 11 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
package com.mapbox.navigation.examples.androidauto.car
22

3-
import android.annotation.SuppressLint
43
import android.content.Intent
54
import android.content.res.Configuration
65
import androidx.car.app.Screen
76
import androidx.car.app.Session
87
import androidx.car.app.model.ActionStrip
98
import androidx.lifecycle.DefaultLifecycleObserver
10-
import androidx.lifecycle.Lifecycle
119
import androidx.lifecycle.LifecycleOwner
1210
import androidx.lifecycle.lifecycleScope
13-
import androidx.lifecycle.repeatOnLifecycle
1411
import com.mapbox.android.core.permissions.PermissionsManager
1512
import com.mapbox.androidauto.MapboxCarContext
1613
import com.mapbox.androidauto.action.MapboxScreenActionStripProvider
@@ -30,12 +27,11 @@ import com.mapbox.maps.applyDefaultParams
3027
import com.mapbox.maps.extension.androidauto.MapboxCarMap
3128
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
3229
import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp
33-
import com.mapbox.navigation.core.lifecycle.requireMapboxNavigation
34-
import com.mapbox.navigation.core.replay.route.ReplayRouteSession
35-
import com.mapbox.navigation.core.trip.session.TripSessionState
30+
import com.mapbox.navigation.core.trip.MapboxTripStarter
3631
import com.mapbox.navigation.examples.androidauto.CarAppSyncComponent
37-
import kotlinx.coroutines.flow.collect
38-
import kotlinx.coroutines.launch
32+
import kotlinx.coroutines.flow.filter
33+
import kotlinx.coroutines.flow.launchIn
34+
import kotlinx.coroutines.flow.onEach
3935

4036
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
4137
class MainCarSession : Session() {
@@ -59,10 +55,11 @@ class MainCarSession : Session() {
5955
}
6056
}
6157
}
62-
private val mapboxNavigation by requireMapboxNavigation()
63-
private val replayRouteSession = ReplayRouteSession()
58+
private val mapboxTripStarter = MapboxTripStarter.getRegisteredInstance()
6459

6560
init {
61+
MapboxNavigationApp.attach(this)
62+
6663
// Decide how you want the car and app to interact. In this example, the car and app
6764
// are kept in sync where they essentially mirror each other.
6865
CarAppSyncComponent.getInstance().setCarSession(this)
@@ -145,31 +142,9 @@ class MainCarSession : Session() {
145142
// computer terminal.
146143
// adb shell dumpsys activity service com.mapbox.navigation.examples.androidauto.car.MainCarAppService AUTO_DRIVE
147144
private fun observeAutoDrive() {
148-
lifecycleScope.launch {
149-
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
150-
mapboxCarContext.mapboxNavigationManager.autoDriveEnabledFlow.collect {
151-
refreshTripSession()
152-
}
153-
}
154-
}
155-
}
156-
157-
@SuppressLint("MissingPermission")
158-
private fun refreshTripSession() {
159-
val isAutoDriveEnabled = mapboxCarContext.mapboxNavigationManager
160-
.autoDriveEnabledFlow.value
161-
if (!PermissionsManager.areLocationPermissionsGranted(carContext)) {
162-
mapboxNavigation.stopTripSession()
163-
return
164-
}
165-
166-
if (isAutoDriveEnabled) {
167-
MapboxNavigationApp.registerObserver(replayRouteSession)
168-
} else {
169-
MapboxNavigationApp.unregisterObserver(replayRouteSession)
170-
if (mapboxNavigation.getTripSessionState() != TripSessionState.STARTED) {
171-
mapboxNavigation.startTripSession()
172-
}
173-
}
145+
mapboxCarContext.mapboxNavigationManager.autoDriveEnabledFlow
146+
.filter { it }
147+
.onEach { mapboxTripStarter.enableReplayRoute() }
148+
.launchIn(lifecycleScope)
174149
}
175150
}

changelog/unreleased/bugfixes/6913.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- Make `MapboxTripStarterType.ReplayRoute` and `ReplayRouteSession` automatically move to the origin of the route after `MapboxNavigation.setNavigationRoutes`.
2+
- Make `ReplayRouteSession` observe the `ReplayRouteSessionOptions` so changes can be made without creating a new instance of the session.
3+
- Change `ReplayRouteSession.getOptions()` to return a `StateFlow` so the options can be observed.

libnavigation-core/api/current.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ package com.mapbox.navigation.core.replay.route {
703703

704704
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public final class ReplayRouteSession implements com.mapbox.navigation.core.lifecycle.MapboxNavigationObserver {
705705
ctor public ReplayRouteSession();
706-
method public com.mapbox.navigation.core.replay.route.ReplayRouteSessionOptions getOptions();
706+
method public kotlinx.coroutines.flow.StateFlow<com.mapbox.navigation.core.replay.route.ReplayRouteSessionOptions> getOptions();
707707
method public void onAttached(com.mapbox.navigation.core.MapboxNavigation mapboxNavigation);
708708
method public void onDetached(com.mapbox.navigation.core.MapboxNavigation mapboxNavigation);
709709
method public com.mapbox.navigation.core.replay.route.ReplayRouteSession setOptions(com.mapbox.navigation.core.replay.route.ReplayRouteSessionOptions options);

libnavigation-core/src/main/java/com/mapbox/navigation/core/replay/route/ReplayRouteSession.kt

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import com.mapbox.navigation.base.route.NavigationRoute
1212
import com.mapbox.navigation.base.trip.model.RouteProgress
1313
import com.mapbox.navigation.core.MapboxNavigation
1414
import com.mapbox.navigation.core.directions.session.RoutesObserver
15-
import com.mapbox.navigation.core.history.MapboxHistoryReaderProvider
1615
import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp
1716
import com.mapbox.navigation.core.lifecycle.MapboxNavigationObserver
1817
import com.mapbox.navigation.core.replay.MapboxReplayer
@@ -24,6 +23,7 @@ import com.mapbox.navigation.utils.internal.logW
2423
import kotlinx.coroutines.CoroutineScope
2524
import kotlinx.coroutines.Dispatchers
2625
import kotlinx.coroutines.SupervisorJob
26+
import kotlinx.coroutines.cancel
2727
import kotlinx.coroutines.flow.Flow
2828
import kotlinx.coroutines.flow.MutableStateFlow
2929
import kotlinx.coroutines.flow.StateFlow
@@ -67,25 +67,33 @@ import java.util.Collections
6767
class ReplayRouteSession : MapboxNavigationObserver {
6868

6969
private lateinit var replayRouteMapper: ReplayRouteMapper
70+
private lateinit var coroutineScope: CoroutineScope
7071
private val optionsFlow = MutableStateFlow(ReplayRouteSessionOptions.Builder().build())
7172
private var mapboxNavigation: MapboxNavigation? = null
7273
private var lastLocationEvent: ReplayEventUpdateLocation? = null
7374
private var polylineDecodeStream: ReplayPolylineDecodeStream? = null
7475
private var currentRoute: NavigationRoute? = null
75-
private var coroutineScope: CoroutineScope? = null
76+
set(value) {
77+
field = value
78+
isNewRouteInitialized = value != null
79+
}
80+
private var isNewRouteInitialized = false
7681

7782
private val routeProgressObserver = RouteProgressObserver { routeProgress ->
7883
if (currentRoute?.id != routeProgress.navigationRoute.id) {
7984
currentRoute = routeProgress.navigationRoute
80-
onRouteChanged(routeProgress)
85+
onRouteProgressRouteChanged(routeProgress)
8186
}
8287
}
8388

8489
private val routesObserver = RoutesObserver { result ->
85-
if (result.navigationRoutes.isEmpty()) {
90+
val route = result.navigationRoutes.firstOrNull()
91+
if (route == null) {
8692
mapboxNavigation?.resetReplayLocation()
8793
currentRoute = null
8894
polylineDecodeStream = null
95+
} else if (!isNewRouteInitialized && currentRoute?.id != route.id) {
96+
onInitializeNewRoute(route)
8997
}
9098
}
9199

@@ -113,24 +121,23 @@ class ReplayRouteSession : MapboxNavigationObserver {
113121
}
114122

115123
override fun onAttached(mapboxNavigation: MapboxNavigation) {
116-
val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
117-
.also { this.coroutineScope = it }
124+
this.coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
118125
this.mapboxNavigation = mapboxNavigation
119126
mapboxNavigation.startReplayTripSession()
120-
mapboxNavigation.registerRouteProgressObserver(routeProgressObserver)
127+
observeStateFlow(mapboxNavigation).launchIn(coroutineScope)
121128
mapboxNavigation.registerRoutesObserver(routesObserver)
129+
mapboxNavigation.registerRouteProgressObserver(routeProgressObserver)
122130
mapboxNavigation.mapboxReplayer.registerObserver(replayEventsObserver)
123-
mapboxNavigation.mapboxReplayer.play()
124-
observeStateFlow(mapboxNavigation).launchIn(coroutineScope)
125131
}
126132

127133
private fun observeStateFlow(mapboxNavigation: MapboxNavigation): Flow<*> {
128134
return optionsFlow.mapDistinct { it.replayRouteOptions }.onEach { replayRouteOptions ->
129135
mapboxNavigation.mapboxReplayer.clearEvents()
130136
this.replayRouteMapper = ReplayRouteMapper(replayRouteOptions)
131-
val routes = mapboxNavigation.getNavigationRoutes()
132-
mapboxNavigation.setNavigationRoutes(emptyList())
133-
mapboxNavigation.setNavigationRoutes(routes)
137+
currentRoute = null
138+
mapboxNavigation.resetTripSession {
139+
mapboxNavigation.mapboxReplayer.play()
140+
}
134141
}
135142
}
136143

@@ -158,6 +165,9 @@ class ReplayRouteSession : MapboxNavigationObserver {
158165
}
159166

160167
override fun onDetached(mapboxNavigation: MapboxNavigation) {
168+
if (this::coroutineScope.isInitialized) {
169+
coroutineScope.cancel()
170+
}
161171
mapboxNavigation.unregisterRoutesObserver(routesObserver)
162172
mapboxNavigation.unregisterRouteProgressObserver(routeProgressObserver)
163173
mapboxNavigation.mapboxReplayer.unregisterObserver(replayEventsObserver)
@@ -167,28 +177,43 @@ class ReplayRouteSession : MapboxNavigationObserver {
167177
this.currentRoute = null
168178
}
169179

170-
private fun onRouteChanged(routeProgress: RouteProgress) {
180+
private fun onInitializeNewRoute(route: NavigationRoute) {
181+
mapboxNavigation?.mapboxReplayer?.clearEvents()
182+
this.replayRouteMapper = ReplayRouteMapper(optionsFlow.value.replayRouteOptions)
183+
mapboxNavigation?.resetTripSession {
184+
route.routeOptions.coordinatesList().firstOrNull()?.let {
185+
val replayFirstLocation = replayRouteMapper.mapPointList(listOf(it))
186+
mapboxNavigation?.mapboxReplayer?.pushEvents(replayFirstLocation)
187+
}
188+
mapboxNavigation?.mapboxReplayer?.play()
189+
}
190+
isNewRouteInitialized = true
191+
}
192+
193+
private fun onRouteProgressRouteChanged(routeProgress: RouteProgress) {
171194
val navigationRoute = routeProgress.navigationRoute
172195
val mapboxReplayer = mapboxNavigation?.mapboxReplayer ?: return
173196
mapboxReplayer.clearEvents()
174-
mapboxReplayer.play()
175-
val geometries = navigationRoute.directionsRoute.routeOptions()!!.geometries()
176-
val usesPolyline6 = geometries.contains(DirectionsCriteria.GEOMETRY_POLYLINE6)
177-
val geometry = navigationRoute.directionsRoute.geometry()
178-
if (!usesPolyline6 || geometry.isNullOrEmpty()) {
179-
logW(LOG_CATEGORY) {
180-
"The NavigationRouteReplay must have geometry encoded with polyline6 " +
181-
"$geometries $geometry"
197+
mapboxNavigation?.resetTripSession {
198+
mapboxReplayer.play()
199+
val geometries = navigationRoute.directionsRoute.routeOptions()!!.geometries()
200+
val usesPolyline6 = geometries.contains(DirectionsCriteria.GEOMETRY_POLYLINE6)
201+
val geometry = navigationRoute.directionsRoute.geometry()
202+
if (!usesPolyline6 || geometry.isNullOrEmpty()) {
203+
logW(LOG_CATEGORY) {
204+
"The NavigationRouteReplay must have geometry encoded with polyline6 " +
205+
"$geometries $geometry"
206+
}
207+
return@resetTripSession
182208
}
183-
return
184-
}
185-
polylineDecodeStream = ReplayPolylineDecodeStream(geometry, 6)
209+
polylineDecodeStream = ReplayPolylineDecodeStream(geometry, 6)
186210

187-
// Skip up to the current geometry index. There is some imprecision here because the
188-
// distance traveled is not equal to a route index.
189-
polylineDecodeStream?.skip(routeProgress.currentRouteGeometryIndex)
211+
// Skip up to the current geometry index. There is some imprecision here because the
212+
// distance traveled is not equal to a route index.
213+
polylineDecodeStream?.skip(routeProgress.currentRouteGeometryIndex)
190214

191-
pushMorePoints()
215+
pushMorePoints()
216+
}
192217
}
193218

194219
private fun isLastEventPlayed(events: List<ReplayEventBase>): Boolean {

libnavigation-core/src/main/java/com/mapbox/navigation/core/trip/MapboxTripStarter.kt

Lines changed: 11 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import kotlinx.coroutines.flow.onEach
3737
*/
3838
@ExperimentalPreviewMapboxNavigationAPI
3939
class MapboxTripStarter internal constructor(
40-
private val services: MapboxTripStarterServices = MapboxTripStarterServices()
40+
services: MapboxTripStarterServices = MapboxTripStarterServices()
4141
) : MapboxNavigationObserver {
4242

4343
private val tripType = MutableStateFlow<MapboxTripStarterType>(
@@ -152,14 +152,16 @@ class MapboxTripStarter internal constructor(
152152
isLocationPermissionGranted.onEach { granted ->
153153
onMapMatchingEnabled(mapboxNavigation, granted)
154154
}
155-
MapboxTripStarterType.ReplayRoute ->
156-
replayRouteSession.getOptions().onEach { options ->
157-
onReplayRouteEnabled(mapboxNavigation, options)
158-
}
159-
MapboxTripStarterType.ReplayHistory ->
160-
replayHistorySession.getOptions().onEach { options ->
161-
onReplayHistoryEnabled(mapboxNavigation, options)
162-
}
155+
MapboxTripStarterType.ReplayRoute -> {
156+
replayHistorySession.onDetached(mapboxNavigation)
157+
replayRouteSession.onAttached(mapboxNavigation)
158+
replayRouteSession.getOptions()
159+
}
160+
MapboxTripStarterType.ReplayHistory -> {
161+
replayRouteSession.onDetached(mapboxNavigation)
162+
replayHistorySession.onAttached(mapboxNavigation)
163+
replayHistorySession.getOptions()
164+
}
163165
}
164166
}
165167
}
@@ -186,36 +188,6 @@ class MapboxTripStarter internal constructor(
186188
}
187189
}
188190

189-
/**
190-
* Internally called when the trip type has been set to replay route.
191-
*
192-
* @param mapboxNavigation
193-
* @param options parameters for the [ReplayRouteSession]
194-
*/
195-
private fun onReplayRouteEnabled(
196-
mapboxNavigation: MapboxNavigation,
197-
options: ReplayRouteSessionOptions
198-
) {
199-
replayHistorySession.onDetached(mapboxNavigation)
200-
replayRouteSession.setOptions(options)
201-
replayRouteSession.onAttached(mapboxNavigation)
202-
}
203-
204-
/**
205-
* Internally called when the trip type has been set to replay history.
206-
*
207-
* @param mapboxNavigation
208-
* @param options parameters for the [ReplayHistorySession]
209-
*/
210-
private fun onReplayHistoryEnabled(
211-
mapboxNavigation: MapboxNavigation,
212-
options: ReplayHistorySessionOptions
213-
) {
214-
replayRouteSession.onDetached(mapboxNavigation)
215-
replayHistorySession.setOptions(options)
216-
replayHistorySession.onAttached(mapboxNavigation)
217-
}
218-
219191
/**
220192
* Internally called when the trip session needs to be stopped.
221193
*

libnavigation-core/src/test/java/com/mapbox/navigation/core/replay/route/ReplayRouteSessionTest.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ class ReplayRouteSessionTest {
145145
sut.setOptions(firstOptions)
146146

147147
assertNotEquals(firstOptions, initialOptions)
148-
assertEquals(firstOptions, sut.getOptions())
148+
assertEquals(firstOptions, sut.getOptions().value)
149149
}
150150

151151
@Test
@@ -158,7 +158,7 @@ class ReplayRouteSessionTest {
158158
sut.setOptions(firstOptions)
159159

160160
assertNotEquals(firstOptions, initialOptions)
161-
assertEquals(firstOptions, sut.getOptions())
161+
assertEquals(firstOptions, sut.getOptions().value)
162162
}
163163

164164
@Test
@@ -355,7 +355,6 @@ class ReplayRouteSessionTest {
355355
progressObserver.captured.onRouteProgressChanged(secondRouteProgress)
356356

357357
verify(exactly = 2) {
358-
replayer.clearEvents()
359358
replayer.pushEvents(any())
360359
}
361360
verifyOrder {

0 commit comments

Comments
 (0)