Skip to content

Commit af140a1

Browse files
Zayankovskyabhishek1508
authored andcommitted
synchronize drop-in navigation routes with mapbox navigation
1 parent 2a26f6e commit af140a1

File tree

6 files changed

+52
-85
lines changed

6 files changed

+52
-85
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Mapbox welcomes participation and contributions from everyone.
1111
- Fixed an issue that prevented any registered `OnBackPressedCallback`s to fire when `NavigationView` is attached. [#6483](https://github.com/mapbox/mapbox-navigation-android/pull/6483)
1212
- Removed `NavigationViewListener.onBackPressed()`. You can register your own [OnBackPressedCallback](https://developer.android.com/reference/androidx/activity/OnBackPressedCallback) to intercept any `NavigationView` BACK press events. [#6483](https://github.com/mapbox/mapbox-navigation-android/pull/6483)
1313
- Fixed an issue with `NavigationView` that caused trip session not to start after granting location permissions that were requested outside of Drop-In UI by an application. [#6489](https://github.com/mapbox/mapbox-navigation-android/pull/6489)
14+
- Fixed an issue with `NavigationView` that caused unexpected camera movements when switching between navigation states. [#6487](https://github.com/mapbox/mapbox-navigation-android/pull/6487)
1415

1516
## Mapbox Navigation SDK 2.9.0-beta.2 - 14 October, 2022
1617
### Changelog

libnavui-app/src/main/java/com/mapbox/navigation/ui/app/internal/controller/RouteStateController.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ package com.mapbox.navigation.ui.app.internal.controller
33
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
44
import com.mapbox.navigation.base.route.NavigationRoute
55
import com.mapbox.navigation.core.MapboxNavigation
6+
import com.mapbox.navigation.core.internal.extensions.flowRoutesUpdated
67
import com.mapbox.navigation.ui.app.internal.Action
78
import com.mapbox.navigation.ui.app.internal.State
89
import com.mapbox.navigation.ui.app.internal.Store
910
import com.mapbox.navigation.ui.app.internal.routefetch.RoutesAction
1011

1112
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
12-
class RouteStateController(store: Store) : StateController() {
13+
class RouteStateController(private val store: Store) : StateController() {
1314
init {
1415
store.register(this)
1516
}
@@ -19,6 +20,9 @@ class RouteStateController(store: Store) : StateController() {
1920
override fun onAttached(mapboxNavigation: MapboxNavigation) {
2021
super.onAttached(mapboxNavigation)
2122
this.mapboxNavigation = mapboxNavigation
23+
mapboxNavigation.flowRoutesUpdated().observe { result ->
24+
store.dispatch(RoutesAction.SynchronizeRoutes(result.navigationRoutes))
25+
}
2226
}
2327

2428
override fun onDetached(mapboxNavigation: MapboxNavigation) {
@@ -46,6 +50,9 @@ class RouteStateController(store: Store) : StateController() {
4650
mapboxNavigation.setNavigationRoutes(action.routes, action.legIndex)
4751
action.routes
4852
}
53+
is RoutesAction.SynchronizeRoutes -> {
54+
action.routes
55+
}
4956
}
5057
}
5158
}

libnavui-app/src/main/java/com/mapbox/navigation/ui/app/internal/routefetch/RoutesAction.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.mapbox.navigation.ui.app.internal.routefetch
22

33
import com.mapbox.api.directions.v5.models.RouteLeg
44
import com.mapbox.navigation.base.route.NavigationRoute
5+
import com.mapbox.navigation.core.MapboxNavigation
56
import com.mapbox.navigation.ui.app.internal.Action
67

78
/**
@@ -15,4 +16,11 @@ sealed class RoutesAction : Action {
1516
* @param legIndex optional index of [RouteLeg]
1617
*/
1718
data class SetRoutes(val routes: List<NavigationRoute>, val legIndex: Int = 0) : RoutesAction()
19+
20+
/**
21+
* The action is used to synchronize the [NavigationRoute]
22+
* supplied to [MapboxNavigation] and NavigationView.
23+
* @param routes list of [NavigationRoute]
24+
*/
25+
data class SynchronizeRoutes(val routes: List<NavigationRoute>) : RoutesAction()
1826
}
Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,56 @@
11
package com.mapbox.navigation.ui.app.internal.controller
22

3-
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
43
import com.mapbox.navigation.base.route.NavigationRoute
54
import com.mapbox.navigation.core.MapboxNavigation
65
import com.mapbox.navigation.core.directions.session.RoutesObserver
76
import com.mapbox.navigation.ui.app.internal.routefetch.RoutesAction
87
import com.mapbox.navigation.ui.app.testing.TestStore
98
import io.mockk.every
109
import io.mockk.mockk
11-
import io.mockk.spyk
12-
import kotlinx.coroutines.ExperimentalCoroutinesApi
10+
import io.mockk.verify
1311
import org.junit.Assert.assertEquals
1412
import org.junit.Test
1513

16-
@OptIn(ExperimentalCoroutinesApi::class, ExperimentalPreviewMapboxNavigationAPI::class)
1714
internal class RouteStateControllerTest {
1815

16+
private val store = TestStore()
17+
private val sut = RouteStateController(store)
18+
private val mapboxNavigation = mockk<MapboxNavigation>(relaxed = true)
19+
private val routes = listOf(mockk<NavigationRoute>())
20+
1921
@Test
2022
fun `when RoutesAction SetRoute it sets route to mapboxNavigation`() {
21-
val store = spyk(TestStore())
22-
val sut = RouteStateController(store)
23-
val mapboxNavigation = mockk<MapboxNavigation>(relaxed = true)
24-
val routes = listOf(mockk<NavigationRoute>())
25-
every { mapboxNavigation.registerRoutesObserver(any()) } answers {
26-
firstArg<RoutesObserver>().onRoutesChanged(
27-
mockk {
28-
every { navigationRoutes } returns routes
29-
}
30-
)
31-
}
23+
sut.onAttached(mapboxNavigation)
24+
25+
store.dispatch(RoutesAction.SetRoutes(routes, legIndex = 1))
26+
27+
assertEquals(store.state.value.routes, routes)
28+
verify(exactly = 1) { mapboxNavigation.setNavigationRoutes(routes, initialLegIndex = 1) }
29+
}
3230

31+
@Test
32+
fun `when RoutesAction SynchronizeRoutes it does not set route to mapboxNavigation`() {
3333
sut.onAttached(mapboxNavigation)
3434

35-
store.dispatch(RoutesAction.SetRoutes(routes))
35+
store.dispatch(RoutesAction.SynchronizeRoutes(routes))
3636

37-
assertEquals(store.state.value.routes.size, routes.size)
37+
assertEquals(store.state.value.routes, routes)
38+
verify(exactly = 0) { mapboxNavigation.setNavigationRoutes(any(), any(), any()) }
3839
}
3940

4041
@Test
41-
fun `when RoutesAction SetRouteIndex it sets route to mapboxNavigation`() {
42-
val store = spyk(TestStore())
43-
val sut = RouteStateController(store)
44-
val mapboxNavigation = mockk<MapboxNavigation>(relaxed = true)
45-
val routes = listOf(mockk<NavigationRoute>())
42+
fun `when RoutesObserver is invoked it does not set route to mapboxNavigation`() {
4643
every { mapboxNavigation.registerRoutesObserver(any()) } answers {
4744
firstArg<RoutesObserver>().onRoutesChanged(
4845
mockk {
49-
every { navigationRoutes } returns routes
46+
every { navigationRoutes } returns this@RouteStateControllerTest.routes
5047
}
5148
)
5249
}
5350

5451
sut.onAttached(mapboxNavigation)
5552

56-
store.dispatch(RoutesAction.SetRoutes(routes, 1))
57-
58-
assertEquals(store.state.value.routes.size, routes.size)
53+
assertEquals(store.state.value.routes, routes)
54+
verify(exactly = 0) { mapboxNavigation.setNavigationRoutes(any(), any(), any()) }
5955
}
6056
}

libnavui-dropin/src/main/java/com/mapbox/navigation/dropin/camera/CameraComponent.kt

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import com.mapbox.maps.toCameraOptions
66
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
77
import com.mapbox.navigation.core.MapboxNavigation
88
import com.mapbox.navigation.core.internal.extensions.flowRouteProgress
9-
import com.mapbox.navigation.core.internal.extensions.flowRoutesUpdated
109
import com.mapbox.navigation.dropin.navigationview.NavigationViewContext
1110
import com.mapbox.navigation.ui.app.internal.camera.CameraAction
1211
import com.mapbox.navigation.ui.app.internal.camera.CameraAction.SetCameraMode
@@ -22,12 +21,9 @@ import com.mapbox.navigation.ui.maps.camera.lifecycle.NavigationBasicGesturesHan
2221
import com.mapbox.navigation.ui.maps.camera.transition.NavigationCameraTransitionOptions
2322
import com.mapbox.navigation.ui.maps.internal.extensions.flowNavigationCameraState
2423
import com.mapbox.navigation.utils.internal.logD
25-
import kotlinx.coroutines.flow.SharingStarted
2624
import kotlinx.coroutines.flow.collect
2725
import kotlinx.coroutines.flow.combine
2826
import kotlinx.coroutines.flow.drop
29-
import kotlinx.coroutines.flow.map
30-
import kotlinx.coroutines.flow.stateIn
3127
import kotlinx.coroutines.launch
3228

3329
@ExperimentalPreviewMapboxNavigationAPI
@@ -80,11 +76,11 @@ internal class CameraComponent constructor(
8076

8177
restoreCameraState()
8278
controlCameraFrameOverrides()
83-
syncNavigationCameraState()
8479
updateCameraLocation()
8580

8681
onRouteProgressUpdates(mapboxNavigation)
87-
onRouteUpdates(mapboxNavigation)
82+
onRouteUpdates()
83+
syncNavigationCameraState()
8884
}
8985

9086
override fun onDetached(mapboxNavigation: MapboxNavigation) {
@@ -192,24 +188,12 @@ internal class CameraComponent constructor(
192188
}
193189
}
194190

195-
private fun onRouteUpdates(mapboxNavigation: MapboxNavigation) {
196-
val routesFlow = combine(
197-
store.select { it.previewRoutes },
198-
mapboxNavigation.flowRoutesUpdated()
199-
.map { it.navigationRoutes }
200-
.stateIn(
201-
coroutineScope,
202-
SharingStarted.WhileSubscribed(),
203-
mapboxNavigation.getNavigationRoutes(),
204-
),
205-
) { previewRoutes, navigationRoutes ->
206-
when {
207-
navigationRoutes.isNotEmpty() -> navigationRoutes
208-
previewRoutes is RoutePreviewState.Ready -> previewRoutes.routes
209-
else -> emptyList()
191+
private fun onRouteUpdates() {
192+
store.select { state ->
193+
state.routes.ifEmpty {
194+
(state.previewRoutes as? RoutePreviewState.Ready)?.routes.orEmpty()
210195
}
211-
}
212-
routesFlow.observe { routes ->
196+
}.observe { routes ->
213197
if (routes.isEmpty()) {
214198
viewportDataSource.clearRouteData()
215199
viewportDataSource.evaluate()

libnavui-dropin/src/test/java/com/mapbox/navigation/dropin/camera/CameraComponentTest.kt

Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import com.mapbox.maps.toCameraOptions
1111
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
1212
import com.mapbox.navigation.base.route.NavigationRoute
1313
import com.mapbox.navigation.core.MapboxNavigation
14-
import com.mapbox.navigation.core.directions.session.RoutesObserver
15-
import com.mapbox.navigation.core.directions.session.RoutesUpdatedResult
1614
import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp
1715
import com.mapbox.navigation.core.trip.session.LocationMatcherResult
1816
import com.mapbox.navigation.core.trip.session.RouteProgressObserver
@@ -370,15 +368,9 @@ class CameraComponentTest {
370368
@Test
371369
fun `when route progress updates with empty list camera viewport should clear route data`() =
372370
coroutineRule.runBlockingTest {
373-
val slot = slot<RoutesObserver>()
374-
every { mockMapboxNavigation.registerRoutesObserver(capture(slot)) } returns Unit
375371
cameraComponent.onAttached(mockMapboxNavigation)
376372

377-
slot.captured.onRoutesChanged(
378-
mockk {
379-
every { navigationRoutes } returns emptyList()
380-
}
381-
)
373+
testStore.setState(testStore.state.value.copy(routes = emptyList()))
382374

383375
verify {
384376
mockViewPortDataSource.clearRouteData()
@@ -390,14 +382,8 @@ class CameraComponentTest {
390382
fun `when route is set camera viewport should update route data`() =
391383
coroutineRule.runBlockingTest {
392384
val mockNavigationRoute = mockk<NavigationRoute>(relaxed = true)
393-
val slot = slot<RoutesObserver>()
394-
every { mockMapboxNavigation.registerRoutesObserver(capture(slot)) } just Runs
395385
cameraComponent.onAttached(mockMapboxNavigation)
396-
slot.captured.onRoutesChanged(
397-
mockk {
398-
every { navigationRoutes } returns listOf(mockNavigationRoute)
399-
},
400-
)
386+
testStore.setState(testStore.state.value.copy(routes = listOf(mockNavigationRoute)))
401387
verifyOrder {
402388
mockViewPortDataSource.onRouteChanged(mockNavigationRoute)
403389
mockViewPortDataSource.evaluate()
@@ -458,7 +444,6 @@ class CameraComponentTest {
458444
fun `when route is set camera is not changed in free drive mode`() =
459445
coroutineRule.runBlockingTest {
460446
val mockNavigationRoute = mockk<NavigationRoute>(relaxed = true)
461-
every { mockMapboxNavigation.registerRoutesObserver(any()) } returns Unit
462447
every { mockMapboxNavigation.getNavigationRoutes() } returns listOf(mockNavigationRoute)
463448
cameraComponent.onAttached(mockMapboxNavigation)
464449
testStore.setState(
@@ -476,44 +461,30 @@ class CameraComponentTest {
476461
coroutineRule.runBlockingTest {
477462
val previewNavigationRoute = mockk<NavigationRoute>(relaxed = true)
478463
val activeNavigationRoute = mockk<NavigationRoute>(relaxed = true)
479-
val slot = slot<RoutesObserver>()
480-
every { mockMapboxNavigation.registerRoutesObserver(capture(slot)) } returns Unit
481464
cameraComponent.onAttached(mockMapboxNavigation)
482465
testStore.setState(
483466
testStore.state.value.copy(
484467
navigation = NavigationState.ActiveNavigation,
485468
previewRoutes = RoutePreviewState.Ready(listOf(previewNavigationRoute)),
469+
routes = listOf(activeNavigationRoute),
486470
),
487471
)
488-
slot.captured.onRoutesChanged(
489-
mockk {
490-
every { navigationRoutes } returns listOf(activeNavigationRoute)
491-
}
492-
)
493-
verifyOrder {
494-
mockViewPortDataSource.onRouteChanged(previewNavigationRoute)
495-
mockViewPortDataSource.onRouteChanged(activeNavigationRoute)
496-
}
472+
verify(exactly = 0) { mockViewPortDataSource.onRouteChanged(previewNavigationRoute) }
473+
verify(exactly = 1) { mockViewPortDataSource.onRouteChanged(activeNavigationRoute) }
497474
}
498475

499476
@Test
500477
fun `when called detach route updates should not happen`() =
501478
coroutineRule.runBlockingTest {
502-
val slot = slot<RoutesObserver>()
503-
every { mockMapboxNavigation.registerRoutesObserver(capture(slot)) } returns Unit
504479
cameraComponent.onAttached(mockMapboxNavigation)
505480
testStore.setState(
506481
testStore.state.value.copy(
507482
navigation = NavigationState.Arrival
508483
)
509484
)
510-
cameraComponent.onAttached(mockMapboxNavigation)
511485
cameraComponent.onDetached(mockMapboxNavigation)
512486

513-
val change = mockk<RoutesUpdatedResult> {
514-
every { navigationRoutes } returns listOf(mockk())
515-
}
516-
slot.captured.onRoutesChanged(change)
487+
testStore.setState(testStore.state.value.copy(routes = listOf(mockk())))
517488

518489
verify(exactly = 0) {
519490
mockViewPortDataSource.onRouteChanged(any<NavigationRoute>())

0 commit comments

Comments
 (0)