@@ -5,6 +5,7 @@ import android.content.res.Configuration
55import android.content.res.Resources
66import android.location.Location
77import android.os.Bundle
8+ import android.view.View
89import android.view.View.INVISIBLE
910import android.view.View.VISIBLE
1011import android.widget.Toast
@@ -19,8 +20,10 @@ import com.mapbox.maps.MapboxMap
1920import com.mapbox.maps.Style.Companion.MAPBOX_STREETS
2021import com.mapbox.maps.plugin.LocationPuck2D
2122import com.mapbox.maps.plugin.animation.camera
23+ import com.mapbox.maps.plugin.gestures.OnMapClickListener
2224import com.mapbox.maps.plugin.gestures.gestures
2325import com.mapbox.maps.plugin.locationcomponent.location
26+ import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
2427import com.mapbox.navigation.base.TimeFormat
2528import com.mapbox.navigation.base.extensions.applyDefaultNavigationOptions
2629import com.mapbox.navigation.base.extensions.applyLanguageAndVoiceUnitOptions
@@ -35,6 +38,7 @@ import com.mapbox.navigation.core.MapboxNavigation
3538import com.mapbox.navigation.core.MapboxNavigationProvider
3639import com.mapbox.navigation.core.directions.session.RoutesObserver
3740import com.mapbox.navigation.core.formatter.MapboxDistanceFormatter
41+ import com.mapbox.navigation.core.preview.RoutesPreviewObserver
3842import com.mapbox.navigation.core.trip.session.LocationMatcherResult
3943import com.mapbox.navigation.core.trip.session.LocationObserver
4044import com.mapbox.navigation.core.trip.session.NavigationSessionStateObserver
@@ -52,11 +56,10 @@ import com.mapbox.navigation.ui.maps.location.NavigationLocationProvider
5256import com.mapbox.navigation.ui.maps.route.arrow.api.MapboxRouteArrowApi
5357import com.mapbox.navigation.ui.maps.route.arrow.api.MapboxRouteArrowView
5458import com.mapbox.navigation.ui.maps.route.arrow.model.RouteArrowOptions
55- import com.mapbox.navigation.ui.maps.route.line.MapboxRouteLineApiExtensions.setRoutes
59+ import com.mapbox.navigation.ui.maps.route.line.MapboxRouteLineApiExtensions.findClosestRoute
5660import com.mapbox.navigation.ui.maps.route.line.api.MapboxRouteLineApi
5761import com.mapbox.navigation.ui.maps.route.line.api.MapboxRouteLineView
5862import com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineOptions
59- import com.mapbox.navigation.ui.maps.route.line.model.RouteLine
6063import com.mapbox.navigation.ui.tripprogress.api.MapboxTripProgressApi
6164import com.mapbox.navigation.ui.tripprogress.model.DistanceRemainingFormatter
6265import com.mapbox.navigation.ui.tripprogress.model.EstimatedTimeToArrivalFormatter
@@ -75,6 +78,7 @@ import kotlinx.coroutines.Dispatchers
7578import kotlinx.coroutines.launch
7679import java.util.Locale
7780
81+ @OptIn(ExperimentalPreviewMapboxNavigationAPI ::class )
7882class MapboxNavigationActivity : AppCompatActivity () {
7983
8084 /* ----- Layout binding reference ----- */
@@ -89,6 +93,8 @@ class MapboxNavigationActivity : AppCompatActivity() {
8993 // location puck integration
9094 private val navigationLocationProvider = NavigationLocationProvider ()
9195
96+ private val waypoints = mutableListOf<Point >()
97+
9298 // camera
9399 private lateinit var navigationCamera: NavigationCamera
94100 private lateinit var viewportDataSource: MapboxNavigationViewportDataSource
@@ -229,18 +235,17 @@ class MapboxNavigationActivity : AppCompatActivity() {
229235 }
230236
231237 private val routesObserver = RoutesObserver { result ->
232- if (result.routes .isNotEmpty()) {
238+ if (result.navigationRoutes .isNotEmpty()) {
233239 // generate route geometries asynchronously and render them
234- CoroutineScope ( Dispatchers . Main ).launch {
235- val result = routeLineAPI.setRoutes(
236- listOf ( RouteLine ( result.routes.first(), null ) )
237- )
240+ routeLineAPI.setNavigationRoutes(
241+ result.navigationRoutes,
242+ mapboxNavigation.getAlternativeMetadataFor( result.navigationRoutes )
243+ ) {
238244 val style = mapboxMap.getStyle()
239245 if (style != null ) {
240- routeLineView.renderRouteDrawData(style, result )
246+ routeLineView.renderRouteDrawData(style, it )
241247 }
242248 }
243-
244249 // update the camera position to account for the new route
245250 viewportDataSource.onRouteChanged(result.routes.first())
246251 viewportDataSource.evaluate()
@@ -263,11 +268,52 @@ class MapboxNavigationActivity : AppCompatActivity() {
263268 }
264269 }
265270
271+ private val routesPreviewObserver = RoutesPreviewObserver { update ->
272+ val routePreview = update.routesPreview
273+ if (routePreview != null ) {
274+ routeLineAPI.setNavigationRoutes(
275+ routePreview.routesList,
276+ routePreview.alternativesMetadata
277+ ) {
278+ val style = mapboxMap.getStyle()
279+ if (style != null ) {
280+ routeLineView.renderRouteDrawData(style, it)
281+ }
282+ }
283+ // update the camera position to account for the new route
284+ viewportDataSource.onRouteChanged(routePreview.primaryRoute)
285+ viewportDataSource.evaluate()
286+
287+ binding.mapView.gestures.removeOnMapClickListener(previewMapClickListener)
288+ binding.mapView.gestures.addOnMapClickListener(previewMapClickListener)
289+ } else {
290+ binding.mapView.gestures.removeOnMapClickListener(previewMapClickListener)
291+ }
292+ }
293+
266294 private val navigationSessionStateObserver = NavigationSessionStateObserver {
267295 logD(" NavigationSessionState=$it " , LOG_CATEGORY )
268296 logD(" sessionId=${mapboxNavigation.getNavigationSessionState().sessionId} " , LOG_CATEGORY )
269297 }
270298
299+ private val routeClickPadding = com.mapbox.android.gestures.Utils .dpToPx(30f )
300+
301+ private val previewMapClickListener = OnMapClickListener {
302+ CoroutineScope (Dispatchers .Main ).launch {
303+ val result = routeLineAPI.findClosestRoute(
304+ it,
305+ binding.mapView.getMapboxMap(),
306+ routeClickPadding
307+ )
308+
309+ val routeFound = result.value?.navigationRoute
310+ if (routeFound != null && routeFound != routeLineAPI.getPrimaryNavigationRoute()) {
311+ mapboxNavigation.changeRoutesPreviewPrimaryRoute(routeFound)
312+ }
313+ }
314+ false
315+ }
316+
271317 @SuppressLint(" MissingPermission" )
272318 override fun onCreate (savedInstanceState : Bundle ? ) {
273319 super .onCreate(savedInstanceState)
@@ -438,6 +484,7 @@ class MapboxNavigationActivity : AppCompatActivity() {
438484 mapboxNavigation.registerRouteProgressObserver(routeProgressObserver)
439485 mapboxNavigation.registerLocationObserver(locationObserver)
440486 mapboxNavigation.registerVoiceInstructionsObserver(voiceInstructionsObserver)
487+ mapboxNavigation.registerRoutesPreviewObserver(routesPreviewObserver)
441488 }
442489
443490 override fun onStop () {
@@ -447,6 +494,7 @@ class MapboxNavigationActivity : AppCompatActivity() {
447494 mapboxNavigation.unregisterRouteProgressObserver(routeProgressObserver)
448495 mapboxNavigation.unregisterLocationObserver(locationObserver)
449496 mapboxNavigation.unregisterVoiceInstructionsObserver(voiceInstructionsObserver)
497+ mapboxNavigation.unregisterRoutesPreviewObserver(routesPreviewObserver)
450498 }
451499
452500 override fun onDestroy () {
@@ -464,19 +512,23 @@ class MapboxNavigationActivity : AppCompatActivity() {
464512 Point .fromLngLat(it.longitude, it.latitude)
465513 } ? : return
466514
515+ waypoints.add(destination)
516+ val coordinates = listOf (origin) + waypoints
517+ val layersList = listOf (mapboxNavigation.getZLevel()) + waypoints.map { null }
467518 mapboxNavigation.requestRoutes(
468519 RouteOptions .builder()
469520 .applyDefaultNavigationOptions()
470521 .applyLanguageAndVoiceUnitOptions(this )
471- .coordinatesList(listOf (origin, destination))
472- .layersList(listOf (mapboxNavigation.getZLevel(), null ))
522+ .alternatives(true )
523+ .coordinatesList(coordinates)
524+ .layersList(layersList)
473525 .build(),
474526 object : NavigationRouterCallback {
475527 override fun onRoutesReady (
476528 routes : List <NavigationRoute >,
477529 routerOrigin : RouterOrigin
478530 ) {
479- setRouteAndStartNavigation (routes)
531+ setRoutesPreview (routes)
480532 }
481533
482534 override fun onFailure (
@@ -493,6 +545,19 @@ class MapboxNavigationActivity : AppCompatActivity() {
493545 )
494546 }
495547
548+ private fun setRoutesPreview (routes : List <NavigationRoute >) {
549+ binding.navigateButton.apply {
550+ visibility = View .VISIBLE
551+ setOnClickListener {
552+ visibility = View .GONE
553+ setRouteAndStartNavigation(mapboxNavigation.getRoutesPreview()!! .routesList)
554+ mapboxNavigation.setRoutesPreview(emptyList())
555+ waypoints.clear()
556+ }
557+ }
558+ mapboxNavigation.setRoutesPreview(routes)
559+ }
560+
496561 private fun setRouteAndStartNavigation (route : List <NavigationRoute >) {
497562 // set route
498563 mapboxNavigation.setNavigationRoutes(route)
0 commit comments