@@ -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)
@@ -439,6 +485,7 @@ class MapboxNavigationActivity : AppCompatActivity() {
439485 mapboxNavigation.registerRouteProgressObserver(routeProgressObserver)
440486 mapboxNavigation.registerLocationObserver(locationObserver)
441487 mapboxNavigation.registerVoiceInstructionsObserver(voiceInstructionsObserver)
488+ mapboxNavigation.registerRoutesPreviewObserver(routesPreviewObserver)
442489 }
443490
444491 override fun onStop () {
@@ -448,6 +495,7 @@ class MapboxNavigationActivity : AppCompatActivity() {
448495 mapboxNavigation.unregisterRouteProgressObserver(routeProgressObserver)
449496 mapboxNavigation.unregisterLocationObserver(locationObserver)
450497 mapboxNavigation.unregisterVoiceInstructionsObserver(voiceInstructionsObserver)
498+ mapboxNavigation.unregisterRoutesPreviewObserver(routesPreviewObserver)
451499 }
452500
453501 override fun onDestroy () {
@@ -465,19 +513,23 @@ class MapboxNavigationActivity : AppCompatActivity() {
465513 Point .fromLngLat(it.longitude, it.latitude)
466514 } ? : return
467515
516+ waypoints.add(destination)
517+ val coordinates = listOf (origin) + waypoints
518+ val layersList = listOf (mapboxNavigation.getZLevel()) + waypoints.map { null }
468519 mapboxNavigation.requestRoutes(
469520 RouteOptions .builder()
470521 .applyDefaultNavigationOptions()
471522 .applyLanguageAndVoiceUnitOptions(this )
472- .coordinatesList(listOf (origin, destination))
473- .layersList(listOf (mapboxNavigation.getZLevel(), null ))
523+ .alternatives(true )
524+ .coordinatesList(coordinates)
525+ .layersList(layersList)
474526 .build(),
475527 object : NavigationRouterCallback {
476528 override fun onRoutesReady (
477529 routes : List <NavigationRoute >,
478530 routerOrigin : RouterOrigin
479531 ) {
480- setRouteAndStartNavigation (routes)
532+ setRoutesPreview (routes)
481533 }
482534
483535 override fun onFailure (
@@ -494,6 +546,19 @@ class MapboxNavigationActivity : AppCompatActivity() {
494546 )
495547 }
496548
549+ private fun setRoutesPreview (routes : List <NavigationRoute >) {
550+ binding.navigateButton.apply {
551+ visibility = View .VISIBLE
552+ setOnClickListener {
553+ visibility = View .GONE
554+ setRouteAndStartNavigation(mapboxNavigation.getRoutesPreview()!! .routesList)
555+ mapboxNavigation.setRoutesPreview(emptyList())
556+ waypoints.clear()
557+ }
558+ }
559+ mapboxNavigation.setRoutesPreview(routes)
560+ }
561+
497562 private fun setRouteAndStartNavigation (route : List <NavigationRoute >) {
498563 // set route
499564 mapboxNavigation.setNavigationRoutes(route)
0 commit comments