Skip to content

Commit 2275707

Browse files
authored
restore overview camera when routes change during route preview state (#6840)
1 parent 2d5ccaa commit 2275707

File tree

9 files changed

+146
-83
lines changed

9 files changed

+146
-83
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Improved `NavigationView` camera behavior to go back into overview state if routes change during route preview state.

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.mapbox.navigation.ui.app.internal.controller
22

3-
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
43
import com.mapbox.navigation.core.MapboxNavigation
54
import com.mapbox.navigation.ui.app.internal.Action
65
import com.mapbox.navigation.ui.app.internal.State
@@ -13,7 +12,6 @@ import com.mapbox.navigation.ui.voice.api.MapboxAudioGuidance
1312
* This class is responsible for playing voice instructions. Use the [AudioAction] to turning the
1413
* audio on or off.
1514
*/
16-
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
1715
class AudioGuidanceStateController(
1816
private val store: Store
1917
) : StateController() {

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.mapbox.navigation.ui.app.internal.controller
22

3-
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
43
import com.mapbox.navigation.core.MapboxNavigation
54
import com.mapbox.navigation.ui.app.internal.Action
65
import com.mapbox.navigation.ui.app.internal.State
@@ -9,8 +8,9 @@ import com.mapbox.navigation.ui.app.internal.camera.CameraAction
98
import com.mapbox.navigation.ui.app.internal.camera.CameraState
109
import com.mapbox.navigation.ui.app.internal.camera.TargetCameraMode
1110
import com.mapbox.navigation.ui.app.internal.navigation.NavigationState
11+
import com.mapbox.navigation.ui.app.internal.routefetch.RoutePreviewState
12+
import kotlinx.coroutines.flow.distinctUntilChanged
1213

13-
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
1414
class CameraStateController(
1515
private val store: Store,
1616
) : StateController() {
@@ -21,8 +21,16 @@ class CameraStateController(
2121
override fun onAttached(mapboxNavigation: MapboxNavigation) {
2222
super.onAttached(mapboxNavigation)
2323

24-
store.select { it.navigation }.observe { navigationState ->
25-
when (navigationState) {
24+
store.state.distinctUntilChanged { old, new ->
25+
if (new.navigation is NavigationState.RoutePreview) {
26+
new.previewRoutes !is RoutePreviewState.Ready ||
27+
old.navigation is NavigationState.RoutePreview &&
28+
new.previewRoutes == old.previewRoutes
29+
} else {
30+
new.navigation == old.navigation
31+
}
32+
}.observe { state ->
33+
when (state.navigation) {
2634
NavigationState.FreeDrive, NavigationState.RoutePreview -> {
2735
store.dispatch(CameraAction.SetCameraMode(TargetCameraMode.Overview))
2836
}

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
package com.mapbox.navigation.ui.app.internal.controller
22

3-
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
43
import com.mapbox.navigation.ui.app.internal.Action
54
import com.mapbox.navigation.ui.app.internal.State
65
import com.mapbox.navigation.ui.app.internal.Store
76
import com.mapbox.navigation.ui.app.internal.destination.DestinationAction
87

9-
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
108
class DestinationStateController(
119
store: Store
1210
) : StateController() {

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.mapbox.navigation.ui.app.internal.controller
22

3-
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
43
import com.mapbox.navigation.core.MapboxNavigation
54
import com.mapbox.navigation.core.internal.extensions.flowLocationMatcherResult
65
import com.mapbox.navigation.core.trip.session.LocationMatcherResult
@@ -9,7 +8,6 @@ import com.mapbox.navigation.ui.app.internal.State
98
import com.mapbox.navigation.ui.app.internal.Store
109
import com.mapbox.navigation.ui.app.internal.location.LocationAction
1110

12-
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
1311
class LocationStateController(
1412
private val store: Store
1513
) : StateController() {

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.mapbox.navigation.ui.app.internal.controller
22

3-
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
43
import com.mapbox.navigation.core.MapboxNavigation
54
import com.mapbox.navigation.core.internal.extensions.flowOnFinalDestinationArrival
65
import com.mapbox.navigation.ui.app.internal.Action
@@ -16,7 +15,6 @@ import kotlinx.coroutines.launch
1615
* [NavigationStateAction] received.
1716
* @param store the default [NavigationState]
1817
*/
19-
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
2018
class NavigationStateController(
2119
private val store: Store
2220
) : StateController() {

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
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.internal.extensions.flowRoutesUpdated
@@ -9,7 +8,6 @@ import com.mapbox.navigation.ui.app.internal.State
98
import com.mapbox.navigation.ui.app.internal.Store
109
import com.mapbox.navigation.ui.app.internal.routefetch.RoutesAction
1110

12-
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
1311
class RouteStateController(private val store: Store) : StateController() {
1412
init {
1513
store.register(this)
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package com.mapbox.navigation.ui.app.internal.controller
22

3-
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
43
import com.mapbox.navigation.ui.app.internal.Reducer
54
import com.mapbox.navigation.ui.base.lifecycle.UIComponent
65

7-
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
86
abstract class StateController : UIComponent(), Reducer

libnavui-app/src/test/java/com/mapbox/navigation/ui/app/internal/controller/CameraStateControllerTest.kt

Lines changed: 133 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ package com.mapbox.navigation.ui.app.internal.controller
22

33
import com.mapbox.geojson.Point
44
import com.mapbox.maps.EdgeInsets
5+
import com.mapbox.navigation.base.route.NavigationRoute
56
import com.mapbox.navigation.core.MapboxNavigation
67
import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp
78
import com.mapbox.navigation.testing.MainCoroutineRule
89
import com.mapbox.navigation.ui.app.internal.camera.CameraAction
910
import com.mapbox.navigation.ui.app.internal.camera.CameraAction.SetCameraMode
1011
import com.mapbox.navigation.ui.app.internal.camera.TargetCameraMode
1112
import com.mapbox.navigation.ui.app.internal.navigation.NavigationState
13+
import com.mapbox.navigation.ui.app.internal.routefetch.RoutePreviewState
1214
import com.mapbox.navigation.ui.app.testing.TestStore
1315
import io.mockk.every
1416
import io.mockk.mockk
@@ -40,7 +42,7 @@ class CameraStateControllerTest {
4042
}
4143

4244
@Test
43-
fun `when action toIdle updates camera mode`() = coroutineRule.runBlockingTest {
45+
fun `when action toIdle updates camera mode`() {
4446
val sut = CameraStateController(testStore)
4547
sut.onAttached(mockMapboxNavigation())
4648

@@ -51,21 +53,20 @@ class CameraStateControllerTest {
5153
}
5254

5355
@Test
54-
fun `when action toIdle should copy currentCamera mode value to savedCameraMode`() =
55-
coroutineRule.runBlockingTest {
56-
val sut = CameraStateController(testStore)
57-
sut.onAttached(mockMapboxNavigation())
56+
fun `when action toIdle should copy currentCamera mode value to savedCameraMode`() {
57+
val sut = CameraStateController(testStore)
58+
sut.onAttached(mockMapboxNavigation())
5859

59-
val initialCameraMode = TargetCameraMode.Following
60-
testStore.dispatch(SetCameraMode(initialCameraMode))
61-
testStore.dispatch(SetCameraMode(TargetCameraMode.Idle))
60+
val initialCameraMode = TargetCameraMode.Following
61+
testStore.dispatch(SetCameraMode(initialCameraMode))
62+
testStore.dispatch(SetCameraMode(TargetCameraMode.Idle))
6263

63-
val cameraState = testStore.state.value.camera
64-
assertEquals(initialCameraMode, cameraState.savedCameraMode)
65-
}
64+
val cameraState = testStore.state.value.camera
65+
assertEquals(initialCameraMode, cameraState.savedCameraMode)
66+
}
6667

6768
@Test
68-
fun `when action toOverview updates camera mode`() = coroutineRule.runBlockingTest {
69+
fun `when action toOverview updates camera mode`() {
6970
val sut = CameraStateController(testStore)
7071
sut.onAttached(mockMapboxNavigation())
7172

@@ -76,29 +77,27 @@ class CameraStateControllerTest {
7677
}
7778

7879
@Test
79-
fun `when action toFollowing updates camera mode and zoomUpdatesAllowed`() =
80-
coroutineRule.runBlockingTest {
81-
val sut = CameraStateController(testStore)
82-
sut.onAttached(mockMapboxNavigation())
80+
fun `when action toFollowing updates camera mode and zoomUpdatesAllowed`() {
81+
val sut = CameraStateController(testStore)
82+
sut.onAttached(mockMapboxNavigation())
8383

84-
testStore.dispatch(SetCameraMode(TargetCameraMode.Following))
84+
testStore.dispatch(SetCameraMode(TargetCameraMode.Following))
8585

86-
val cameraState = testStore.state.value.camera
87-
assertEquals(TargetCameraMode.Following, cameraState.cameraMode)
88-
}
86+
val cameraState = testStore.state.value.camera
87+
assertEquals(TargetCameraMode.Following, cameraState.cameraMode)
88+
}
8989

9090
@Test
91-
fun `when action UpdatePadding updates cameraPadding`() =
92-
coroutineRule.runBlockingTest {
93-
val padding = EdgeInsets(1.0, 2.0, 3.0, 4.0)
94-
val sut = CameraStateController(testStore)
95-
sut.onAttached(mockMapboxNavigation())
91+
fun `when action UpdatePadding updates cameraPadding`() {
92+
val padding = EdgeInsets(1.0, 2.0, 3.0, 4.0)
93+
val sut = CameraStateController(testStore)
94+
sut.onAttached(mockMapboxNavigation())
9695

97-
testStore.dispatch(CameraAction.UpdatePadding(padding))
96+
testStore.dispatch(CameraAction.UpdatePadding(padding))
9897

99-
val cameraState = testStore.state.value.camera
100-
assertEquals(padding, cameraState.cameraPadding)
101-
}
98+
val cameraState = testStore.state.value.camera
99+
assertEquals(padding, cameraState.cameraPadding)
100+
}
102101

103102
@Test
104103
fun `on SaveMapState action should save map camera state in the store`() {
@@ -119,64 +118,131 @@ class CameraStateControllerTest {
119118
}
120119

121120
@Test
122-
fun `camera is set to overview in route preview mode`() =
123-
coroutineRule.runBlockingTest {
124-
val sut = CameraStateController(testStore)
125-
sut.onAttached(mockMapboxNavigation())
121+
fun `camera is unchanged in route preview mode without preview routes`() {
122+
val sut = CameraStateController(testStore)
123+
sut.onAttached(mockMapboxNavigation())
124+
125+
testStore.updateState { it.copy(navigation = NavigationState.DestinationPreview) }
126+
testStore.dispatch(SetCameraMode(TargetCameraMode.Following))
127+
testStore.updateState { state ->
128+
state.copy(
129+
navigation = NavigationState.RoutePreview,
130+
previewRoutes = RoutePreviewState.Empty,
131+
)
132+
}
126133

127-
val state = testStore.state.value.copy(navigation = NavigationState.RoutePreview)
128-
testStore.setState(state)
134+
assertEquals(TargetCameraMode.Following, testStore.state.value.camera.cameraMode)
135+
}
129136

130-
assertEquals(TargetCameraMode.Overview, testStore.state.value.camera.cameraMode)
137+
@Test
138+
fun `camera is set to overview in route preview mode with preview routes`() {
139+
val sut = CameraStateController(testStore)
140+
sut.onAttached(mockMapboxNavigation())
141+
142+
testStore.updateState { it.copy(navigation = NavigationState.DestinationPreview) }
143+
testStore.dispatch(SetCameraMode(TargetCameraMode.Following))
144+
testStore.updateState { state ->
145+
state.copy(
146+
navigation = NavigationState.RoutePreview,
147+
previewRoutes = RoutePreviewState.Ready(listOf(mockk())),
148+
)
131149
}
132150

151+
assertEquals(TargetCameraMode.Overview, testStore.state.value.camera.cameraMode)
152+
}
153+
133154
@Test
134-
fun `camera is set to overview in free drive mode`() =
135-
coroutineRule.runBlockingTest {
136-
val sut = CameraStateController(testStore)
137-
sut.onAttached(mockMapboxNavigation())
155+
fun `camera is restored to overview in route preview mode with new preview routes`() {
156+
val sut = CameraStateController(testStore)
157+
sut.onAttached(mockMapboxNavigation())
158+
159+
testStore.updateState { state ->
160+
state.copy(
161+
navigation = NavigationState.RoutePreview,
162+
previewRoutes = RoutePreviewState.Ready(listOf(mockk())),
163+
)
164+
}
165+
testStore.dispatch(SetCameraMode(TargetCameraMode.Following))
166+
testStore.updateState { state ->
167+
state.copy(
168+
navigation = NavigationState.RoutePreview,
169+
previewRoutes = RoutePreviewState.Ready(listOf(mockk())),
170+
)
171+
}
172+
173+
assertEquals(TargetCameraMode.Overview, testStore.state.value.camera.cameraMode)
174+
}
138175

139-
val state = testStore.state.value.copy(navigation = NavigationState.FreeDrive)
140-
testStore.setState(state)
176+
@Test
177+
fun `camera is unchanged in route preview mode with the same preview routes`() {
178+
val sut = CameraStateController(testStore)
179+
sut.onAttached(mockMapboxNavigation())
180+
val route = mockk<NavigationRoute>()
141181

142-
assertEquals(TargetCameraMode.Overview, testStore.state.value.camera.cameraMode)
182+
testStore.updateState { state ->
183+
state.copy(
184+
navigation = NavigationState.RoutePreview,
185+
previewRoutes = RoutePreviewState.Ready(listOf(route)),
186+
)
143187
}
188+
testStore.dispatch(SetCameraMode(TargetCameraMode.Following))
189+
testStore.updateState { state ->
190+
state.copy(
191+
navigation = NavigationState.RoutePreview,
192+
previewRoutes = RoutePreviewState.Ready(listOf(route)),
193+
)
194+
}
195+
196+
assertEquals(TargetCameraMode.Following, testStore.state.value.camera.cameraMode)
197+
}
144198

145199
@Test
146-
fun `camera is set to following in active navigation mode`() =
147-
coroutineRule.runBlockingTest {
148-
val sut = CameraStateController(testStore)
149-
sut.onAttached(mockMapboxNavigation())
200+
fun `camera is set to overview in free drive mode`() {
201+
val sut = CameraStateController(testStore)
202+
sut.onAttached(mockMapboxNavigation())
150203

151-
val state = testStore.state.value.copy(navigation = NavigationState.ActiveNavigation)
152-
testStore.setState(state)
204+
testStore.updateState { it.copy(navigation = NavigationState.DestinationPreview) }
205+
testStore.dispatch(SetCameraMode(TargetCameraMode.Following))
206+
testStore.updateState { it.copy(navigation = NavigationState.FreeDrive) }
153207

154-
assertEquals(TargetCameraMode.Following, testStore.state.value.camera.cameraMode)
155-
}
208+
assertEquals(TargetCameraMode.Overview, testStore.state.value.camera.cameraMode)
209+
}
156210

157211
@Test
158-
fun `camera is set to following in arrival mode`() =
159-
coroutineRule.runBlockingTest {
160-
val sut = CameraStateController(testStore)
161-
sut.onAttached(mockMapboxNavigation())
212+
fun `camera is set to following in active navigation mode`() {
213+
val sut = CameraStateController(testStore)
214+
sut.onAttached(mockMapboxNavigation())
162215

163-
val state = testStore.state.value.copy(navigation = NavigationState.Arrival)
164-
testStore.setState(state)
216+
testStore.updateState { it.copy(navigation = NavigationState.RoutePreview) }
217+
testStore.dispatch(SetCameraMode(TargetCameraMode.Overview))
218+
testStore.updateState { it.copy(navigation = NavigationState.ActiveNavigation) }
165219

166-
assertEquals(TargetCameraMode.Following, testStore.state.value.camera.cameraMode)
167-
}
220+
assertEquals(TargetCameraMode.Following, testStore.state.value.camera.cameraMode)
221+
}
168222

169223
@Test
170-
fun `camera is set to idle in destination preview mode`() =
171-
coroutineRule.runBlockingTest {
172-
val sut = CameraStateController(testStore)
173-
sut.onAttached(mockMapboxNavigation())
224+
fun `camera is set to following in arrival mode`() {
225+
val sut = CameraStateController(testStore)
226+
sut.onAttached(mockMapboxNavigation())
174227

175-
val state = testStore.state.value.copy(navigation = NavigationState.DestinationPreview)
176-
testStore.setState(state)
228+
testStore.updateState { it.copy(navigation = NavigationState.ActiveNavigation) }
229+
testStore.dispatch(SetCameraMode(TargetCameraMode.Overview))
230+
testStore.updateState { it.copy(navigation = NavigationState.Arrival) }
177231

178-
assertEquals(TargetCameraMode.Idle, testStore.state.value.camera.cameraMode)
179-
}
232+
assertEquals(TargetCameraMode.Following, testStore.state.value.camera.cameraMode)
233+
}
234+
235+
@Test
236+
fun `camera is set to idle in destination preview mode`() {
237+
val sut = CameraStateController(testStore)
238+
sut.onAttached(mockMapboxNavigation())
239+
240+
testStore.updateState { it.copy(navigation = NavigationState.FreeDrive) }
241+
testStore.dispatch(SetCameraMode(TargetCameraMode.Overview))
242+
testStore.updateState { it.copy(navigation = NavigationState.DestinationPreview) }
243+
244+
assertEquals(TargetCameraMode.Idle, testStore.state.value.camera.cameraMode)
245+
}
180246

181247
private fun mockMapboxNavigation(): MapboxNavigation {
182248
val mapboxNavigation = mockk<MapboxNavigation>(relaxed = true)

0 commit comments

Comments
 (0)