Skip to content

Commit 7264dc3

Browse files
authored
experimental routes preview (#6495)
1 parent 166964d commit 7264dc3

File tree

22 files changed

+1824
-2
lines changed

22 files changed

+1824
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Mapbox welcomes participation and contributions from everyone.
44

55
## Unreleased
66
#### Features
7+
- Added experimental routes preview state, see `MapboxNavigaton#setRoutesPreview`, `MapboxNavigaton#changeRoutesPreviewPrimaryRoute`, `MapboxNavigaton#registerRoutesPreviewObserver`, `MapboxNavigaton#getRoutesPreview`. [#6495](https://github.com/mapbox/mapbox-navigation-android/pull/6495)
78
#### Bug fixes and improvements
89

910
## Mapbox Navigation SDK 2.10.0-alpha.2 - 04 November, 2022
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
package com.mapbox.navigation.instrumentation_tests.core
2+
3+
import android.location.Location
4+
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
5+
import com.mapbox.navigation.base.options.NavigationOptions
6+
import com.mapbox.navigation.base.route.NavigationRoute
7+
import com.mapbox.navigation.base.trip.model.RouteProgressState
8+
import com.mapbox.navigation.core.MapboxNavigation
9+
import com.mapbox.navigation.core.MapboxNavigationProvider
10+
import com.mapbox.navigation.core.directions.session.RoutesExtra
11+
import com.mapbox.navigation.core.directions.session.RoutesUpdatedResult
12+
import com.mapbox.navigation.core.preview.RoutesPreviewExtra
13+
import com.mapbox.navigation.core.preview.RoutesPreviewUpdate
14+
import com.mapbox.navigation.instrumentation_tests.activity.EmptyTestActivity
15+
import com.mapbox.navigation.instrumentation_tests.utils.MapboxNavigationRule
16+
import com.mapbox.navigation.instrumentation_tests.utils.coroutines.routeProgressUpdates
17+
import com.mapbox.navigation.instrumentation_tests.utils.coroutines.routesPreviewUpdates
18+
import com.mapbox.navigation.instrumentation_tests.utils.coroutines.routesUpdates
19+
import com.mapbox.navigation.instrumentation_tests.utils.coroutines.sdkTest
20+
import com.mapbox.navigation.instrumentation_tests.utils.routes.RoutesProvider
21+
import com.mapbox.navigation.instrumentation_tests.utils.routes.RoutesProvider.toNavigationRoutes
22+
import com.mapbox.navigation.testing.ui.BaseTest
23+
import com.mapbox.navigation.testing.ui.utils.getMapboxAccessTokenFromResources
24+
import com.mapbox.navigation.testing.ui.utils.runOnMainSync
25+
import kotlinx.coroutines.flow.first
26+
import org.junit.Assert.assertEquals
27+
import org.junit.Assert.assertNotEquals
28+
import org.junit.Assert.assertNull
29+
import org.junit.Before
30+
import org.junit.Rule
31+
import org.junit.Test
32+
33+
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
34+
class RoutesPreviewTest : BaseTest<EmptyTestActivity>(EmptyTestActivity::class.java) {
35+
36+
override fun setupMockLocation(): Location = mockLocationUpdatesRule.generateLocationUpdate {
37+
latitude = 38.894721
38+
longitude = -77.031991
39+
}
40+
41+
@get:Rule
42+
val mapboxNavigationRule = MapboxNavigationRule()
43+
private lateinit var mapboxNavigation: MapboxNavigation
44+
45+
@Before
46+
fun setUp() {
47+
runOnMainSync {
48+
mapboxNavigation = MapboxNavigationProvider.create(
49+
NavigationOptions.Builder(activity)
50+
.accessToken(getMapboxAccessTokenFromResources(activity))
51+
.build()
52+
)
53+
}
54+
}
55+
56+
@Test
57+
fun transitions_free_drive_to_preview_to_active_guidance_to_free_drive() = sdkTest {
58+
var currentRoutesPreview: RoutesPreviewUpdate? = null
59+
mapboxNavigation.registerRoutesPreviewObserver { update ->
60+
currentRoutesPreview = update
61+
}
62+
var currentRoutes: RoutesUpdatedResult? = null
63+
mapboxNavigation.registerRoutesObserver { update ->
64+
currentRoutes = update
65+
}
66+
// initial free drive
67+
mapboxNavigation.startTripSession()
68+
assertNull(currentRoutesPreview)
69+
assertNull(currentRoutes)
70+
// set routes preview
71+
val routes = RoutesProvider.dc_very_short(activity).toNavigationRoutes()
72+
mapboxNavigation.setRoutesPreview(routes)
73+
mapboxNavigation.routesPreviewUpdates()
74+
.first { it.reason == RoutesPreviewExtra.PREVIEW_NEW }
75+
assertEquals(RoutesPreviewExtra.PREVIEW_NEW, currentRoutesPreview?.reason)
76+
assertEquals(routes, currentRoutesPreview!!.routesPreview!!.routesList)
77+
assertNull(currentRoutes)
78+
// start active guidance
79+
mapboxNavigation.setNavigationRoutes(currentRoutesPreview!!.routesPreview!!.routesList)
80+
mapboxNavigation.setRoutesPreview(emptyList())
81+
mapboxNavigation.routesUpdates()
82+
.first { it.reason == RoutesExtra.ROUTES_UPDATE_REASON_NEW }
83+
mapboxNavigation.routeProgressUpdates()
84+
.first { it.currentState == RouteProgressState.TRACKING }
85+
mapboxNavigation.routesPreviewUpdates()
86+
.first { it.reason == RoutesPreviewExtra.PREVIEW_CLEAN_UP }
87+
assertEquals(RoutesPreviewExtra.PREVIEW_CLEAN_UP, currentRoutesPreview?.reason)
88+
assertNull(currentRoutesPreview!!.routesPreview)
89+
assertEquals(RoutesExtra.ROUTES_UPDATE_REASON_NEW, currentRoutes!!.reason)
90+
assertEquals(routes, currentRoutes!!.navigationRoutes)
91+
// back to free drive
92+
mapboxNavigation.setNavigationRoutes(emptyList())
93+
mapboxNavigation.routesUpdates()
94+
.first { it.reason == RoutesExtra.ROUTES_UPDATE_REASON_CLEAN_UP }
95+
assertEquals(RoutesPreviewExtra.PREVIEW_CLEAN_UP, currentRoutesPreview?.reason)
96+
assertNull(currentRoutesPreview!!.routesPreview)
97+
assertEquals(RoutesExtra.ROUTES_UPDATE_REASON_CLEAN_UP, currentRoutes!!.reason)
98+
assertEquals(emptyList<NavigationRoute>(), currentRoutes!!.navigationRoutes)
99+
}
100+
101+
@Test
102+
fun route_preview_in_parallel_to_active_guidance() = sdkTest {
103+
var currentRoutesPreview: RoutesPreviewUpdate? = null
104+
mapboxNavigation.registerRoutesPreviewObserver { update ->
105+
currentRoutesPreview = update
106+
}
107+
var currentRoutes: RoutesUpdatedResult? = null
108+
mapboxNavigation.registerRoutesObserver { update ->
109+
currentRoutes = update
110+
}
111+
// initial free drive
112+
mapboxNavigation.startTripSession()
113+
assertNull(currentRoutesPreview)
114+
assertNull(currentRoutes)
115+
// set routes preview
116+
val initialRoutes = RoutesProvider.dc_very_short(activity).toNavigationRoutes()
117+
mapboxNavigation.setRoutesPreview(initialRoutes)
118+
mapboxNavigation.routesPreviewUpdates()
119+
.first { it.reason == RoutesPreviewExtra.PREVIEW_NEW }
120+
// start active guidance
121+
mapboxNavigation.setNavigationRoutes(currentRoutesPreview!!.routesPreview!!.routesList)
122+
mapboxNavigation.setRoutesPreview(emptyList())
123+
mapboxNavigation.routeProgressUpdates()
124+
.first { it.currentState == RouteProgressState.TRACKING }
125+
// preview a different route not leaving action guidance
126+
val updatedRoutes = RoutesProvider.dc_very_short_two_legs(activity).toNavigationRoutes()
127+
mapboxNavigation.setRoutesPreview(updatedRoutes)
128+
mapboxNavigation.routesPreviewUpdates()
129+
.first { it.routesPreview?.routesList == updatedRoutes }
130+
assertEquals(
131+
"active guidance should track initial routes",
132+
initialRoutes,
133+
currentRoutes?.navigationRoutes
134+
)
135+
// user decided to switch to previewed routes
136+
mapboxNavigation.setNavigationRoutes(currentRoutesPreview!!.routesPreview!!.routesList)
137+
mapboxNavigation.setRoutesPreview(emptyList())
138+
mapboxNavigation.routeProgressUpdates().first {
139+
it.navigationRoute == updatedRoutes[0]
140+
}
141+
}
142+
143+
@Test
144+
fun start_active_guidance_from_previewed_alternative_route() = sdkTest {
145+
// set routes preview
146+
val routes = RoutesProvider.dc_short_with_alternative(activity).toNavigationRoutes()
147+
mapboxNavigation.setRoutesPreview(routes)
148+
val preview = mapboxNavigation.routesPreviewUpdates()
149+
.first { it.reason == RoutesPreviewExtra.PREVIEW_NEW }
150+
// switch to alternative route
151+
mapboxNavigation.changeRoutesPreviewPrimaryRoute(
152+
preview.routesPreview!!.originalRoutesList[1]
153+
)
154+
val updatedPreview = mapboxNavigation.routesPreviewUpdates()
155+
.first { it != preview }
156+
// start active guidance
157+
mapboxNavigation.setNavigationRoutes(updatedPreview.routesPreview!!.routesList)
158+
mapboxNavigation.setRoutesPreview(emptyList())
159+
val routesUpdate = mapboxNavigation.routesUpdates()
160+
.first { it.reason == RoutesExtra.ROUTES_UPDATE_REASON_NEW }
161+
assertEquals(
162+
listOf(
163+
routes[1],
164+
routes[0]
165+
),
166+
routesUpdate.navigationRoutes
167+
)
168+
val previewAlternativeMetadata = updatedPreview.routesPreview!!.alternativesMetadata.first()
169+
val activeGuidanceAlternativeMetadata = mapboxNavigation
170+
.getAlternativeMetadataFor(routes[0])!!
171+
assertEquals(
172+
0,
173+
previewAlternativeMetadata.alternativeId
174+
)
175+
assertNotEquals(
176+
previewAlternativeMetadata.alternativeId,
177+
activeGuidanceAlternativeMetadata.alternativeId
178+
)
179+
assertEquals(
180+
previewAlternativeMetadata.infoFromStartOfPrimary,
181+
activeGuidanceAlternativeMetadata.infoFromStartOfPrimary
182+
)
183+
assertEquals(
184+
previewAlternativeMetadata.forkIntersectionOfAlternativeRoute,
185+
activeGuidanceAlternativeMetadata.forkIntersectionOfAlternativeRoute
186+
)
187+
assertEquals(
188+
previewAlternativeMetadata.infoFromFork,
189+
activeGuidanceAlternativeMetadata.infoFromFork
190+
)
191+
assertEquals(
192+
previewAlternativeMetadata.forkIntersectionOfPrimaryRoute,
193+
activeGuidanceAlternativeMetadata.forkIntersectionOfPrimaryRoute
194+
)
195+
}
196+
}

instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/coroutines/Adapters.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.mapbox.api.directions.v5.models.BannerInstructions
66
import com.mapbox.api.directions.v5.models.RouteOptions
77
import com.mapbox.api.directions.v5.models.VoiceInstructions
88
import com.mapbox.bindgen.Expected
9+
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
910
import com.mapbox.navigation.base.route.NavigationRoute
1011
import com.mapbox.navigation.base.route.NavigationRouterCallback
1112
import com.mapbox.navigation.base.route.RouterFailure
@@ -18,6 +19,8 @@ import com.mapbox.navigation.core.RoutesSetError
1819
import com.mapbox.navigation.core.RoutesSetSuccess
1920
import com.mapbox.navigation.core.directions.session.RoutesObserver
2021
import com.mapbox.navigation.core.directions.session.RoutesUpdatedResult
22+
import com.mapbox.navigation.core.preview.RoutesPreviewObserver
23+
import com.mapbox.navigation.core.preview.RoutesPreviewUpdate
2124
import com.mapbox.navigation.core.trip.session.BannerInstructionsObserver
2225
import com.mapbox.navigation.core.trip.session.RoadObjectsOnRouteObserver
2326
import com.mapbox.navigation.core.trip.session.RouteProgressObserver
@@ -43,6 +46,20 @@ fun MapboxNavigation.routesUpdates(): Flow<RoutesUpdatedResult> {
4346
}
4447
}
4548

49+
@ExperimentalPreviewMapboxNavigationAPI
50+
fun MapboxNavigation.routesPreviewUpdates(): Flow<RoutesPreviewUpdate> {
51+
val navigation = this
52+
return callbackFlow {
53+
val observer = RoutesPreviewObserver {
54+
trySend(it)
55+
}
56+
navigation.registerRoutesPreviewObserver(observer)
57+
awaitClose {
58+
navigation.unregisterRoutesPreviewObserver(observer)
59+
}
60+
}
61+
}
62+
4663
fun MapboxNavigation.routeProgressUpdates(): Flow<RouteProgress> {
4764
val navigation = this
4865
return callbackFlow {

libnavigation-core/api/current.txt

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ package com.mapbox.navigation.core {
1313
@UiThread public final class MapboxNavigation {
1414
ctor public MapboxNavigation(com.mapbox.navigation.base.options.NavigationOptions navigationOptions);
1515
method public void cancelRouteRequest(long requestId);
16+
method @com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI @kotlin.jvm.Throws(exceptionClasses=IllegalArgumentException::class) public void changeRoutesPreviewPrimaryRoute(com.mapbox.navigation.base.route.NavigationRoute newPrimaryRoute) throws java.lang.IllegalArgumentException;
1617
method public com.mapbox.navigation.core.routealternatives.AlternativeRouteMetadata? getAlternativeMetadataFor(com.mapbox.navigation.base.route.NavigationRoute navigationRoute);
1718
method public java.util.List<com.mapbox.navigation.core.routealternatives.AlternativeRouteMetadata> getAlternativeMetadataFor(java.util.List<com.mapbox.navigation.base.route.NavigationRoute> navigationRoutes);
1819
method public com.mapbox.navigator.Experimental getExperimental();
@@ -26,6 +27,7 @@ package com.mapbox.navigation.core {
2627
method public com.mapbox.navigation.core.trip.session.eh.RoadObjectMatcher getRoadObjectMatcher();
2728
method public com.mapbox.navigation.core.trip.session.eh.RoadObjectsStore getRoadObjectsStore();
2829
method @Deprecated public java.util.List<com.mapbox.api.directions.v5.models.DirectionsRoute> getRoutes();
30+
method @com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public com.mapbox.navigation.core.preview.RoutesPreview? getRoutesPreview();
2931
method public com.mapbox.navigation.core.navigator.TilesetDescriptorFactory getTilesetDescriptorFactory();
3032
method public com.mapbox.navigation.core.trip.session.TripSessionState getTripSessionState();
3133
method public Integer? getZLevel();
@@ -53,6 +55,7 @@ package com.mapbox.navigation.core {
5355
method public void registerRouteProgressObserver(com.mapbox.navigation.core.trip.session.RouteProgressObserver routeProgressObserver);
5456
method @com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public void registerRouteRefreshStateObserver(com.mapbox.navigation.core.routerefresh.RouteRefreshStatesObserver routeRefreshStatesObserver);
5557
method public void registerRoutesObserver(com.mapbox.navigation.core.directions.session.RoutesObserver routesObserver);
58+
method @com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public void registerRoutesPreviewObserver(com.mapbox.navigation.core.preview.RoutesPreviewObserver observer);
5659
method public void registerTripSessionStateObserver(com.mapbox.navigation.core.trip.session.TripSessionStateObserver tripSessionStateObserver);
5760
method public void registerVoiceInstructionsObserver(com.mapbox.navigation.core.trip.session.VoiceInstructionsObserver voiceInstructionsObserver);
5861
method public void requestAlternativeRoutes();
@@ -73,6 +76,8 @@ package com.mapbox.navigation.core {
7376
method public void setRerouteOptionsAdapter(com.mapbox.navigation.core.reroute.RerouteOptionsAdapter? rerouteOptionsAdapter);
7477
method @Deprecated public void setRoutes(java.util.List<? extends com.mapbox.api.directions.v5.models.DirectionsRoute> routes, int initialLegIndex = 0);
7578
method @Deprecated public void setRoutes(java.util.List<? extends com.mapbox.api.directions.v5.models.DirectionsRoute> routes);
79+
method @com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public void setRoutesPreview(java.util.List<com.mapbox.navigation.base.route.NavigationRoute> routes, int primaryRouteIndex = 0);
80+
method @com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public void setRoutesPreview(java.util.List<com.mapbox.navigation.base.route.NavigationRoute> routes);
7681
method public void setTripNotificationInterceptor(com.mapbox.navigation.base.trip.notification.TripNotificationInterceptor? interceptor);
7782
method @com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public void startReplayTripSession(boolean withForegroundService = true);
7883
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void startTripSession(boolean withForegroundService = true);
@@ -92,6 +97,7 @@ package com.mapbox.navigation.core {
9297
method public void unregisterRouteProgressObserver(com.mapbox.navigation.core.trip.session.RouteProgressObserver routeProgressObserver);
9398
method @com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public void unregisterRouteRefreshStateObserver(com.mapbox.navigation.core.routerefresh.RouteRefreshStatesObserver routeRefreshStatesObserver);
9499
method public void unregisterRoutesObserver(com.mapbox.navigation.core.directions.session.RoutesObserver routesObserver);
100+
method @com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public void unregisterRoutesPreviewObserver(com.mapbox.navigation.core.preview.RoutesPreviewObserver observer);
95101
method public void unregisterTripSessionStateObserver(com.mapbox.navigation.core.trip.session.TripSessionStateObserver tripSessionStateObserver);
96102
method public void unregisterVoiceInstructionsObserver(com.mapbox.navigation.core.trip.session.VoiceInstructionsObserver voiceInstructionsObserver);
97103
property public final com.mapbox.navigator.Experimental experimental;
@@ -384,6 +390,43 @@ package com.mapbox.navigation.core.navigator {
384390

385391
}
386392

393+
package com.mapbox.navigation.core.preview {
394+
395+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public final class RoutesPreview {
396+
method public java.util.List<com.mapbox.navigation.core.routealternatives.AlternativeRouteMetadata> getAlternativesMetadata();
397+
method public java.util.List<com.mapbox.navigation.base.route.NavigationRoute> getOriginalRoutesList();
398+
method public com.mapbox.navigation.base.route.NavigationRoute getPrimaryRoute();
399+
method public int getPrimaryRouteIndex();
400+
method public java.util.List<com.mapbox.navigation.base.route.NavigationRoute> getRoutesList();
401+
property public final java.util.List<com.mapbox.navigation.core.routealternatives.AlternativeRouteMetadata> alternativesMetadata;
402+
property public final java.util.List<com.mapbox.navigation.base.route.NavigationRoute> originalRoutesList;
403+
property public final com.mapbox.navigation.base.route.NavigationRoute primaryRoute;
404+
property public final int primaryRouteIndex;
405+
property public final java.util.List<com.mapbox.navigation.base.route.NavigationRoute> routesList;
406+
}
407+
408+
public final class RoutesPreviewExtra {
409+
field public static final com.mapbox.navigation.core.preview.RoutesPreviewExtra INSTANCE;
410+
field public static final String PREVIEW_CLEAN_UP = "PREVIEW_CLEAN_UP";
411+
field public static final String PREVIEW_NEW = "PREVIEW_NEW";
412+
}
413+
414+
@StringDef({com.mapbox.navigation.core.preview.RoutesPreviewExtra.PREVIEW_NEW, com.mapbox.navigation.core.preview.RoutesPreviewExtra.PREVIEW_CLEAN_UP}) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public static @interface RoutesPreviewExtra.RoutePreviewUpdateReason {
415+
}
416+
417+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public fun interface RoutesPreviewObserver {
418+
method public void routesPreviewUpdated(com.mapbox.navigation.core.preview.RoutesPreviewUpdate update);
419+
}
420+
421+
@com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public final class RoutesPreviewUpdate {
422+
method public String getReason();
423+
method public com.mapbox.navigation.core.preview.RoutesPreview? getRoutesPreview();
424+
property public final String reason;
425+
property public final com.mapbox.navigation.core.preview.RoutesPreview? routesPreview;
426+
}
427+
428+
}
429+
387430
package com.mapbox.navigation.core.replay {
388431

389432
@UiThread public final class MapboxReplayer {

0 commit comments

Comments
 (0)