diff --git a/CHANGELOG.md b/CHANGELOG.md index e1931091c67..6c881195508 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Mapbox welcomes participation and contributions from everyone. - `ViewOptionsCustomization.showEndNavigationButton` can be used to show/hide info panel's end navigation button. The default is `true`. [#6506](https://github.com/mapbox/mapbox-navigation-android/pull/6506) - Introduced `ViewOptionsCustomization.showPoiName` and `ViewOptionsCustomization.showArrivalText` that allows showing/hiding of the POI and arrival text view. [#6515](https://github.com/mapbox/mapbox-navigation-android/pull/6515) - Introduced `ViewBinderCustomization.infoPanelPoiNameBinder` and `ViewBinderCustomization.infoPanelArrivalTextBinder` that allows injection of custom Info Panel POI and arrival text view into `NavigationView`. [#6515](https://github.com/mapbox/mapbox-navigation-android/pull/6515) +- Added experimental routes preview state, see `MapboxNavigaton#setRoutesPreview`, `MapboxNavigaton#changeRoutesPreviewPrimaryRoute`, `MapboxNavigaton#registerRoutesPreviewObserver`, `MapboxNavigaton#getRoutesPreview`. [#6495](https://github.com/mapbox/mapbox-navigation-android/pull/6495) #### Bug fixes and improvements - :warning: Removed `MapboxMapScalebarParams` and replaced `ViewStyleCustomization.mapScalebarParams` by `ViewOptionsCustomization.showMapScalebar` [#6523](https://github.com/mapbox/mapbox-navigation-android/pull/6523) - Refactored `ScalebarComponent` to use `ViewOptionsCustomization.distanceFormatterOptions` to change between imperial and metric based unit. [#6523](https://github.com/mapbox/mapbox-navigation-android/pull/6523) diff --git a/examples/src/main/java/com/mapbox/navigation/examples/core/MapboxNavigationActivity.kt b/examples/src/main/java/com/mapbox/navigation/examples/core/MapboxNavigationActivity.kt index 84ea81b9275..80cc3fd7fb1 100644 --- a/examples/src/main/java/com/mapbox/navigation/examples/core/MapboxNavigationActivity.kt +++ b/examples/src/main/java/com/mapbox/navigation/examples/core/MapboxNavigationActivity.kt @@ -5,6 +5,7 @@ import android.content.res.Configuration import android.content.res.Resources import android.location.Location import android.os.Bundle +import android.view.View import android.view.View.INVISIBLE import android.view.View.VISIBLE import android.widget.Toast @@ -19,8 +20,10 @@ import com.mapbox.maps.MapboxMap import com.mapbox.maps.Style.Companion.MAPBOX_STREETS import com.mapbox.maps.plugin.LocationPuck2D import com.mapbox.maps.plugin.animation.camera +import com.mapbox.maps.plugin.gestures.OnMapClickListener import com.mapbox.maps.plugin.gestures.gestures import com.mapbox.maps.plugin.locationcomponent.location +import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI import com.mapbox.navigation.base.TimeFormat import com.mapbox.navigation.base.extensions.applyDefaultNavigationOptions import com.mapbox.navigation.base.extensions.applyLanguageAndVoiceUnitOptions @@ -35,6 +38,7 @@ import com.mapbox.navigation.core.MapboxNavigation import com.mapbox.navigation.core.MapboxNavigationProvider import com.mapbox.navigation.core.directions.session.RoutesObserver import com.mapbox.navigation.core.formatter.MapboxDistanceFormatter +import com.mapbox.navigation.core.preview.RoutesPreviewObserver import com.mapbox.navigation.core.trip.session.LocationMatcherResult import com.mapbox.navigation.core.trip.session.LocationObserver import com.mapbox.navigation.core.trip.session.NavigationSessionStateObserver @@ -52,11 +56,10 @@ import com.mapbox.navigation.ui.maps.location.NavigationLocationProvider import com.mapbox.navigation.ui.maps.route.arrow.api.MapboxRouteArrowApi import com.mapbox.navigation.ui.maps.route.arrow.api.MapboxRouteArrowView import com.mapbox.navigation.ui.maps.route.arrow.model.RouteArrowOptions -import com.mapbox.navigation.ui.maps.route.line.MapboxRouteLineApiExtensions.setRoutes +import com.mapbox.navigation.ui.maps.route.line.MapboxRouteLineApiExtensions.findClosestRoute import com.mapbox.navigation.ui.maps.route.line.api.MapboxRouteLineApi import com.mapbox.navigation.ui.maps.route.line.api.MapboxRouteLineView import com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineOptions -import com.mapbox.navigation.ui.maps.route.line.model.RouteLine import com.mapbox.navigation.ui.tripprogress.api.MapboxTripProgressApi import com.mapbox.navigation.ui.tripprogress.model.DistanceRemainingFormatter import com.mapbox.navigation.ui.tripprogress.model.EstimatedTimeToArrivalFormatter @@ -75,6 +78,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import java.util.Locale +@OptIn(ExperimentalPreviewMapboxNavigationAPI::class) class MapboxNavigationActivity : AppCompatActivity() { /* ----- Layout binding reference ----- */ @@ -89,6 +93,8 @@ class MapboxNavigationActivity : AppCompatActivity() { // location puck integration private val navigationLocationProvider = NavigationLocationProvider() + private val waypoints = mutableListOf() + // camera private lateinit var navigationCamera: NavigationCamera private lateinit var viewportDataSource: MapboxNavigationViewportDataSource @@ -229,18 +235,17 @@ class MapboxNavigationActivity : AppCompatActivity() { } private val routesObserver = RoutesObserver { result -> - if (result.routes.isNotEmpty()) { + if (result.navigationRoutes.isNotEmpty()) { // generate route geometries asynchronously and render them - CoroutineScope(Dispatchers.Main).launch { - val result = routeLineAPI.setRoutes( - listOf(RouteLine(result.routes.first(), null)) - ) + routeLineAPI.setNavigationRoutes( + result.navigationRoutes, + mapboxNavigation.getAlternativeMetadataFor(result.navigationRoutes) + ) { val style = mapboxMap.getStyle() if (style != null) { - routeLineView.renderRouteDrawData(style, result) + routeLineView.renderRouteDrawData(style, it) } } - // update the camera position to account for the new route viewportDataSource.onRouteChanged(result.routes.first()) viewportDataSource.evaluate() @@ -263,11 +268,52 @@ class MapboxNavigationActivity : AppCompatActivity() { } } + private val routesPreviewObserver = RoutesPreviewObserver { update -> + val routePreview = update.routesPreview + if (routePreview != null) { + routeLineAPI.setNavigationRoutes( + routePreview.routesList, + routePreview.alternativesMetadata + ) { + val style = mapboxMap.getStyle() + if (style != null) { + routeLineView.renderRouteDrawData(style, it) + } + } + // update the camera position to account for the new route + viewportDataSource.onRouteChanged(routePreview.primaryRoute) + viewportDataSource.evaluate() + + binding.mapView.gestures.removeOnMapClickListener(previewMapClickListener) + binding.mapView.gestures.addOnMapClickListener(previewMapClickListener) + } else { + binding.mapView.gestures.removeOnMapClickListener(previewMapClickListener) + } + } + private val navigationSessionStateObserver = NavigationSessionStateObserver { logD("NavigationSessionState=$it", LOG_CATEGORY) logD("sessionId=${mapboxNavigation.getNavigationSessionState().sessionId}", LOG_CATEGORY) } + private val routeClickPadding = com.mapbox.android.gestures.Utils.dpToPx(30f) + + private val previewMapClickListener = OnMapClickListener { + CoroutineScope(Dispatchers.Main).launch { + val result = routeLineAPI.findClosestRoute( + it, + binding.mapView.getMapboxMap(), + routeClickPadding + ) + + val routeFound = result.value?.navigationRoute + if (routeFound != null && routeFound != routeLineAPI.getPrimaryNavigationRoute()) { + mapboxNavigation.changeRoutesPreviewPrimaryRoute(routeFound) + } + } + false + } + @SuppressLint("MissingPermission") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -438,6 +484,7 @@ class MapboxNavigationActivity : AppCompatActivity() { mapboxNavigation.registerRouteProgressObserver(routeProgressObserver) mapboxNavigation.registerLocationObserver(locationObserver) mapboxNavigation.registerVoiceInstructionsObserver(voiceInstructionsObserver) + mapboxNavigation.registerRoutesPreviewObserver(routesPreviewObserver) } override fun onStop() { @@ -447,6 +494,7 @@ class MapboxNavigationActivity : AppCompatActivity() { mapboxNavigation.unregisterRouteProgressObserver(routeProgressObserver) mapboxNavigation.unregisterLocationObserver(locationObserver) mapboxNavigation.unregisterVoiceInstructionsObserver(voiceInstructionsObserver) + mapboxNavigation.unregisterRoutesPreviewObserver(routesPreviewObserver) } override fun onDestroy() { @@ -464,19 +512,23 @@ class MapboxNavigationActivity : AppCompatActivity() { Point.fromLngLat(it.longitude, it.latitude) } ?: return + waypoints.add(destination) + val coordinates = listOf(origin) + waypoints + val layersList = listOf(mapboxNavigation.getZLevel()) + waypoints.map { null } mapboxNavigation.requestRoutes( RouteOptions.builder() .applyDefaultNavigationOptions() .applyLanguageAndVoiceUnitOptions(this) - .coordinatesList(listOf(origin, destination)) - .layersList(listOf(mapboxNavigation.getZLevel(), null)) + .alternatives(true) + .coordinatesList(coordinates) + .layersList(layersList) .build(), object : NavigationRouterCallback { override fun onRoutesReady( routes: List, routerOrigin: RouterOrigin ) { - setRouteAndStartNavigation(routes) + setRoutesPreview(routes) } override fun onFailure( @@ -493,6 +545,19 @@ class MapboxNavigationActivity : AppCompatActivity() { ) } + private fun setRoutesPreview(routes: List) { + binding.navigateButton.apply { + visibility = View.VISIBLE + setOnClickListener { + visibility = View.GONE + setRouteAndStartNavigation(mapboxNavigation.getRoutesPreview()!!.routesList) + mapboxNavigation.setRoutesPreview(emptyList()) + waypoints.clear() + } + } + mapboxNavigation.setRoutesPreview(routes) + } + private fun setRouteAndStartNavigation(route: List) { // set route mapboxNavigation.setNavigationRoutes(route) diff --git a/examples/src/main/res/layout/layout_activity_navigation.xml b/examples/src/main/res/layout/layout_activity_navigation.xml index 1a6f38c5d9e..85d34baa5bf 100644 --- a/examples/src/main/res/layout/layout_activity_navigation.xml +++ b/examples/src/main/res/layout/layout_activity_navigation.xml @@ -2,7 +2,9 @@ + android:layout_height="match_parent" + xmlns:tools="http://schemas.android.com/tools" + > +