Migrate to v2
Mapbox Navigation SDK for Android v2 is a major new version of the SDK. To install the latest version of the SDK, follow the installation instructions and use this guide to update your application from v1 to v2.
Requirements
Like in v1, the Mapbox Navigation SDK for Android v2 is compatible with applications that:
- Use the Android SDK 21 or higher.
- Use Java 8 for
sourceCompatibility
andtargetCompatibility
, as shown in the installation guide.
Artifacts
v1
In v1 there were two artifacts to choose from:
com.mapbox.navigation:core:{version}
: To use the Navigation SDK without pre-built UI components.com.mapbox.navigation:ui:{version}
: To use the Navigation SDK with pre-built UI components, which also includesnavigation:core
as a dependency.
v2
In Navigation SDK v2, all features are available under one Maven artifact: com.mapbox.navigation:android:{version}
.
Besides the one grouping artifact, there are also multiple granular ones that you can pick-and-choose if you’re not planning to use all the features and want to reduce the binary size of your application:
com.mapbox.navigation:core
offers all the core localization features from v1.com.mapbox.navigation:ui-maps
offers the route line and arrow APIs, navigation camera, building highlight and extrusion, extended visual guidance, and other tools and features that integrate with the Mapbox Maps SDK.com.mapbox.navigation:ui-maneuver
offers the maneuver view and its APIs that replace v1'sInstructionView
.com.mapbox.navigation:ui-tripprogress
offers the trip progress view and APIs that replace v1'sSummaryBottomSheet
.com.mapbox.navigation:ui-voice
offers all necessary APIs to play voice instructions.com.mapbox.navigation:ui-speedlimit
offers the speed limit view and its APIs that replace v1'sSpeedLimitView
.
Read more about leveraging the Navigation SDK v2's modular architecture.
Dependencies
Maps SDK
In v2, the Navigation SDK upgrades to Mapbox Maps SDK v10. Maps SDK v10 offers 3D maps and improved performance.
Maps SDK v10 is also a SEMVER major release with breaking API changes. If the application you are migrating uses a map, make sure you read the Maps SDK migration guide before reading the navigation-specific content below.
Upgrade your application from the Mapbox Maps SDK for Android v9 to v10.
Kotlin
The Navigation SDK v2 is written 100% in Kotlin, the official language recommended by Google for Android development. This is a change from how the previous versions were a mix of Java and Kotlin.
Java support
While the Mapbox Navigation SDK for Android is built with Kotlin, Kotlin is 100% interoperable with Java. Applications with a Java codebase can interact properly with the public APIs exposed by Mapbox SDKs for Android.
For information on Java and Kotlin interoperability see the offical Kotlin docs:
If you experience any issues using the Mapbox Navigation SDK for Android with Java, contact us.
Core components in v2
Core localization components in the Navigation SDK v2 are mostly unchanged architecturally, but introduce a couple breaking changes that improve the experience or clarify the behavior of the SDK to streamline the integration.
The following symbols were removed, renamed, or changed:
- Requesting a route with
MapboxNavigation#requestRoutes
no longer automatically sets the result as the primary route. MapboxNavigation#defaultNavigationOptionsBuilder
has been removed.OnboardRouterOptions
was renamed toRoutingTilesOptions
.- The default directory for caches changed from
APP_FOLDER/Offline/api.mapbox.com/$tilesVersion/tiles
toAPP_FOLDER/mbx_nav/tiles/api.mapbox.com
.
Request a route
MapboxNavigation#requestRoutes
no longer automatically sets the result as the primary route for the navigation experience. This means that you should call MapboxNavigation#setRoutes
with a result when calling MapboxNavigation#requestRoutes
.
This change allows for dispatching and managing multiple route requests concurrently, including canceling with MapboxNavigation#cancelRouteRequest
.
v1
mapboxNavigation?.requestRoutes(
RouteOptions.builder().applyDefaultParams()
.accessToken(Utils.getMapboxAccessToken(applicationContext))
.coordinates(originLocation.toPoint(), null, latLng.toPoint())
.profile(DirectionsCriteria.PROFILE_DRIVING_TRAFFIC)
.build(),
routesReqCallback
)
private val routesReqCallback = object : RoutesRequestCallback {
override fun onRoutesReady(routes: List<DirectionsRoute>) {
if (routes.isNotEmpty()) {
directionRoute = routes[0]
navigationMapboxMap?.drawRoute(routes[0])
startNavigation.visibility = View.VISIBLE
} else {
startNavigation.visibility = View.GONE
}
}
override fun onRoutesRequestFailure(throwable: Throwable, routeOptions: RouteOptions) {
// Handle error
}
override fun onRoutesRequestCanceled(routeOptions: RouteOptions) {
// Handle cancelation
}
}
v2
val routeOptions = RouteOptions.builder()
.applyDefaultNavigationOptions()
.applyLanguageAndVoiceUnitOptions(this)
.coordinates(
origin = origin,
destination = destination
)
.layersList(listOf(mapboxNavigation.getZLevel(), null))
.build()
mapboxNavigation.requestRoutes(
routeOptions,
object : RouterCallback {
override fun onRoutesReady(
routes: List<DirectionsRoute>,
routerOrigin: RouterOrigin
) {
mapboxNavigation.setRoutes(routes)
}
override fun onFailure(
reasons: List<RouterFailure>,
routeOptions: RouteOptions
) {
// no impl
}
override fun onCanceled(routeOptions: RouteOptions, routerOrigin: RouterOrigin) {
// no impl
}
}
)
Read more about requesting routes using the Navigation SDK v2.
Navigation options
MapboxNavigation#defaultNavigationOptionsBuilder
has been removed. In v2, the NavigationOptions
class now contains all the right defaults out of the box.
The OnboardRouterOptions
class was renamed to RoutingTilesOptions
to better reflect the purpose. If you are using a custom base URI, dataset, or version of the tiles, those are now separate fields in the builder rather than a single URI path.
Predictive caching
The default directory for caching navigation tiles changed:
- v1:
APP_FOLDER/Offline/api.mapbox.com/$tilesVersion/tiles
- v2:
APP_FOLDER/mbx_nav/tiles/api.mapbox.com
Caches from previous versions of the Navigation SDK are not compatible with v2.
When migrating make sure you have cleaned up the old navigation tiles cache folder to reclaim disk space:
- Delete any folders used for caching with previous versions of the SDK including the default one.
OnboardRouterOptions
enables you to specify a path where navigation tiles will be saved. If a custom directory was used, you should remove it, too.- Be sure to configure the custom path before starting a free drive or an active guidance session if you don't want to use the default location.
Read more about how predictive caching works in the Navigation SDK v2.
UI components in v2
The general approach to UI components changed in v2. It exposes direct access to the data that powers pre-built UI components (View
s) so you can either render that data in a Mapbox-designed UI component or a custom one. Read more in the Approach to UI components section below.
The following v1 symbols were removed in v2:
- The drop-in UI provided by
NavigationView
is not available in v2. - The
MapRouteLineInitializedCallback
is not available in v2.
The following v1 symbols were replaced in v2:
InstructionView
was replaced by the maneuvers interface.SummaryBottomSheet
was replaced by the trip progress interface.NavigationMapRoute
was replaced by the route line and route arrow interfaces.NavigationSpeechPlayer
was replaced by the voice interface.BuildingExtrusionHighlightLayer
andBuildingFootprintHighlightLayer
were replaced by the buildings interface.GuidanceViewImageProvider
andGuidanceViewListener
were replaced by the junction view interface.SpeedLimitView
was replaced by the speed limit interface.
Approach to UI components
Unlike in the Navigation SDK v1, where the views were exposed with no access to data, the v2 UI components now focus on separating the data transformation logic (preparing for presentation) from the actual presentation layer that updates the app’s UI elements. You can now access APIs specifically catered to give you access to the data, prepared using raw data points from core modules. Based on the data retrieved from these APIs, you can choose to render it either on a Mapbox-designed View
or your on custom view.
This separation requires additional synchronization on the app level, but gives you greater control over the user experience as your app scales. In v2 you now have control over the UI components' lifecycle to better fit them into more complex setups. This is also true for UI components that interact directly with the Mapbox Map
. The presentation data is prepared separately from the Map
itself, and you can dispatch updates at the correct time, depending on the Map
’s placement in the view hierarchy and its lifecycle.
NavigationView
was removed
The NavigationView
class, which provided a complete drop-in UI for turn-by-turn navigation, was removed in v2.
In v2 there are more opportunities for customization. You can now build your own UI for turn-by-turn navigation using our pre-built UI components, your own custom UI components, or some combination of the two.
v1
XML layout:
<com.mapbox.navigation.ui.NavigationView
android:id="@+id/navigationView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
Activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
navigationView.onCreate(savedInstanceState)
navigationView.initialize(this)
}
v2
In v2, you will need to add specific views to your layout file individually and pass data to those components manually. See the Turn-by-turn navigation: User interface guide and the Add a complete turn-by-turn experience example to see all the available Mapbox-designed UI components and how to fit them together.
Build a user interface for a turn-by-turn navigation experience using the Mapbox Navigation SDK for Android's pre-built UI components.
Render a complete turn-by-turn experience using all relevant Navigation SDK APIs and pre-built UI components.
InstructionView
was replaced
In v2, the maneuvers interface replaces v1's InstructionView
.
The maneuvers interface has several components that are responsible for updating the current maneuver instructions with banner-related data. MapboxManeuverView
allows you to quickly integrate the maneuver instructions in your app by passing it data from MapboxManeuverApi
. But, you can also access the component parts of MapboxManeuverView
individually to create a custom experience.
You can also instantiate and invoke MapboxManeuverApi
independent of MapboxManeuverView
to retrieve the data and render it on a custom maneuver view.
v1
Drop-in UI: This UI component was included in v1's pre-built UI (NavigationView
). In v2 you will have to add this UI component manually.
InstructionView
component: If you used the InstructionView
directly in v1, your implementation likely looks something like this:
XML layout:
<com.mapbox.navigation.ui.instruction.InstructionView
android:id="@+id/instructionView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="top"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
Activity:
// Update the instruction view when route progress changes.
private val routeProgressObserver = object : RouteProgressObserver {
override fun onRouteProgressChanged(routeProgress: RouteProgress) {
instructionView.updateDistanceWith(routeProgress)
}
}
// Update the instruction view when banner instructions change.
private val bannerInstructionObserver = object : BannerInstructionsObserver {
override fun onNewBannerInstructions(bannerInstructions: BannerInstructions) {
instructionView.updateBannerInstructionsWith(bannerInstructions)
}
}
v2
To replace InstructionView
, add MapboxManeuverView
to your layout and update data using MapboxManeuverApi
:
XML layout:
<com.mapbox.navigation.ui.maneuver.view.MapboxManeuverView
android:id="@+id/maneuverView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
Activity:
// Define distance formatter options
val distanceFormatterOptions = mapboxNavigation.navigationOptions.distanceFormatterOptions
// Create an instance of the MapboxManeuverApi
private val maneuverApi = MapboxManeuverApi(
MapboxDistanceFormatter(distanceFormatterOptions)
)
// Get a list of maneuvers every time route progress changes and
// display the updated maneuver data the ManeuverView
private val routeProgressObserver = RouteProgressObserver { progress ->
val maneuvers = maneuverApi.getManeuvers(progress)
maneuvers.fold(
{ error ->
// handle error
},
{
maneuverView.renderManeuvers(maneuvers)
}
)
}
For more details on adding the maneuvers UI component to your application, follow the instructions in the Maneuver instructions guide.
Read more about how maneuver instructions work in the Navigation SDK v2.
Draw maneuver instructions using the Maneuver API and MapboxManeuverView
.
SummaryBottomSheet
was replaced
In v2, the trip progress interface replaces v1's SummaryBottomSheet
.
The trip progress interface has several components that are responsible for updating trip related data on to the screen. MapboxTripProgressView
allows you to quickly display trip-related data in your app by passing it data from MapboxTripProgressApi
.
For more customized requirements you can use the MapboxTripProgressApi
to retrieve the data and render it on your custom trip progress view.
v1
Drop-in UI: This UI component was included in v1's pre-built UI (NavigationView
). In v2 you will have to add this UI component manually.
SummaryBottomSheet
component: If you used the SummaryBottomSheet
directly in v1, your implementation likely looks something like this:
XML layout:
<com.mapbox.navigation.ui.summary.SummaryBottomSheet
android:id="@+id/summaryBottomSheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior" />
Activity:
private val routeProgressObserver = object : RouteProgressObserver {
override fun onRouteProgressChanged(routeProgress: RouteProgress) {
summaryBottomSheet.update(routeProgress)
}
}
v2
To replace SummaryBottomSheet
, add MapboxTripProgressView
to your layout and update data using MapboxTripProgressApi
:
XML layout:
<com.mapbox.navigation.ui.tripprogress.view.MapboxTripProgressView
android:id="@+id/tripProgressView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
Activity:
// Get notified with progress along the currently active route.
private val routeProgressObserver = RouteProgressObserver { progress ->
val tripProgress = tripProgressApi.getTripProgress(progress)
binding.tripProgressView.render(tripProgress)
}
For more details on adding the trip progress UI component to your application, follow the instructions in the Trip progress guide.
Read more about how the trip progress component works in the Navigation SDK v2.
Draw trip progress information using the Trip Progress API and MapboxTripProgressView
.
NavigationMapRoute
was replaced
In v1, the NavigationMapRoute
class included both a route line and route arrow indicating the next maneuver. In v2, you can interact with the route line and route arrow interfaces directly and independently. The MapRouteLineInitializedCallback
is not available in v2.
In v2, MapboxRouteLineView
allows you to quickly integrate a route line into your app by passing it data from MapboxRouteLineApi
and rendering it on the MapView
. You can also instantiate and invoke MapboxRouteLineApi
independent of MapboxRouteLineView
to retrieve relevant data and render a custom route line.
In v2, MapboxRouteArrowView
allows you to quickly integrate a route arrow into your app by passing it data from MapboxRouteArrowApi
and rendering it on the MapView
. You can also instantiate and invoke MapboxRouteArrowApi
independent of MapboxRouteArrowView
to retrieve relevant data and render a custom arrow.
v1
Drop-in UI: These UI components were included in v1's pre-built UI (NavigationView
). In v2 you will have to add these UI components manually.
NavigationMapRoute
class: If you used the NavigationMapRoute
class directly in v1, your implementation likely looks something like this:
XML layout:
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
Activity:
navigationMapRoute = NavigationMapRoute.Builder(mapView, mapboxMap, this)
.withVanishRouteLineEnabled(true)
.withMapboxNavigation(mapboxNavigation)
.build()
fun onMapReady(mapboxMap: MapboxMap) {
if (activeRoute != null) {
final List<DirectionsRoute> routes = listOf(activeRoute)
navigationMapRoute.addRoutes(routes)
}
}
private val routesReqCallback = object : RoutesRequestCallback {
override fun onRoutesReady(routes: List<DirectionsRoute>) {
if (!routes.isEmpty()) {
activeRoute = routes.get[0]
navigationMapRoute.addRoutes(routes)
}
}
}
v2
To replace NavigationMapRoute
, add MapboxRouteLineView
to your layout and update data using MapboxRouteLineApi
:
XML layout:
<com.mapbox.maps.MapView
android:id="@+id/mapView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
Activity:
// Initialize route line view to draw arrows on the map
val mapboxRouteLineOptions = MapboxRouteLineOptions.Builder(this)
.withRouteLineBelowLayerId("road-label")
.build()
routeLineApi = MapboxRouteLineApi(mapboxRouteLineOptions)
routeLineView = MapboxRouteLineView(mapboxRouteLineOptions)
// Initialize maneuver arrow view to draw arrows on the map
val routeArrowOptions = RouteArrowOptions.Builder(this)
.withAboveLayerId(TOP_LEVEL_ROUTE_LINE_LAYER_ID)
.build()
}
routeArrowApi = MapboxRouteArrowApi()
routeArrowView = MapboxRouteArrowView(routeArrowOptions)
// Whenever route progress is updated, generate the route arrow to
// represent the next maneuver, update data, and pass it to the view
// class to visualize the updates on the map.
private val routeProgressObserver = RouteProgressObserver { routeProgress ->
val arrowUpdate = routeArrowApi.addUpcomingManeuverArrow(routeProgress)
mapboxMap.getStyle()?.apply {
// Render the result to update the map.
routeArrowView.renderManeuverUpdate(this, arrowUpdate)
}
}
// Whenever the route data changes, draw the route line on the map.
private val routesObserver: RoutesObserver = RoutesObserver { routeUpdateResult ->
// Wrap the DirectionRoute objects and pass them to the MapboxRouteLineApi
// to generate the data necessary to draw the route(s) on the map.
val routeLines = routeUpdateResult.routes.map { RouteLine(it, null) }
routeLineApi.setRoutes(
routeLines
) { value ->
// The MapboxRouteLineView expects a non-null reference to
// the map style. The data generated by the call to the
// MapboxRouteLineApi above must be rendered by the
// MapboxRouteLineView in order to visualize the changes
// on the map.
mapboxMap.getStyle()?.apply {
routeLineView.renderRouteDrawData(this, value)
}
}
}
MapRouteLineInitializedCallback
is not available in v2.As soon as you call MapboxRouteLineView#render
, the layers are added to the map synchronously, so this is the moment after which you can reference them for z-positioning other runtime layers. You can check RouteLayerConstants
for layer IDs. Also, you may call MapboxRouteLineView#initializeLayers
in advance to initialize the layers manually.For more details on adding the route line and route arrow UI components to your application including alternative routes and vanishing route lines, follow the instructions in the Route line and Route arrow guides.
Read more about how route lines work in the Navigation SDK v2.
Read more about how the new route arrow component works in the Navigation SDK v2.
Draw route lines on a map using the route line and route arrow APIs and the related MapboxRouteLineView
and MapboxRouteArrowView
.
Building*HighlightLayer
was replaced
In v1, there were two separate classes, BuildingExtrusionHighlightLayer
and BuildingFootprintHighlightLayer
, for highlighting a specific building footprint Polygon
or extruding a footprint as a 3D building shape. In v2, the functionality associated with those classes is now handled by a single buildings interface.
In v2, MapboxBuildingView
allows you to quickly integrate building-related data in your app by passing it data from MapboxBuildingsApi
and rendering it on the MapView
.
You can also instantiate and invoke MapboxBuildingsApi
independent of MapboxBuildingView
to retrieve the data and render it on your custom building view.
v1
Drop-in UI: Building highlighting was included in v1's pre-built UI (NavigationView
). In v2 you will have to add these UI components manually.
BuildingExtrusionHighlightLayer
or BuildingFootprintHighlightLayer
class: If you used the BuildingExtrusionHighlightLayer
or BuildingFootprintHighlightLayer
class directly in v1, your implementation likely looks something like this:
XML layout:
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
Activity:
override fun onRouteProgressChanged(routeProgress: RouteProgress) {
// Show the building extrusion when the device is a certain
// number of meters away from the end of the DirectionsRoute.
if (routeProgress.distanceRemaining < 250f) {
mapboxMap.getStyle {
// Initialize the Nav UI SDK's BuildingExtrusionHighlightLayer class.
val buildingExtrusionHighlightLayer = BuildingExtrusionHighlightLayer(mapboxMap)
buildingExtrusionHighlightLayer.queryLatLng = highlightQueryLatLng
buildingExtrusionHighlightLayer.updateVisibility(true)
}
}
}
v2
To replace BuildingExtrusionHighlightLayer
or BuildingFootprintHighlightLayer
, add MapView
to your layout and update data using MapboxBuildingsApi
:
XML layout:
<com.mapbox.maps.MapView
android:id="@+id/mapView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
Activity:
val buildingView = MapboxBuildingView()
val buildingApi = MapboxBuildingsApi(mapboxMap)
// The callback contains a list of buildings returned as
// a result of querying the MapboxMap. If no buildings
// are available, the list is empty.
private val callback = MapboxNavigationConsumer<Expected<BuildingError, BuildingValue>> { expected ->
expected.fold(
{
// Handle error
},
{ value ->
mapboxMap.getStyle { style ->
buildingView.highlightBuilding(style, value.buildings)
}
}
)
}
private val arrivalObserver: ArrivalObserver = object : ArrivalObserver {
override fun onFinalDestinationArrival(routeProgress: RouteProgress) {
buildingApi.queryBuildingOnFinalDestination(routeProgress, callback)
}
override fun onNextRouteLegStart(routeLegProgress: RouteLegProgress) {
mapboxMap.getStyle { style ->
buildingView.removeBuildingHighlight(style)
}
}
override fun onWaypointArrival(routeProgress: RouteProgress) {
buildingApi.queryBuildingOnWaypoint(routeProgress, callback)
}
}
For more details on adding highlighted buildings to your application, follow the instructions in the Building highlights guide.
Read more about how building highlights work in the Navigation SDK v2.
Use the example to render building extrusions on arrival using building API and MapboxBuildingView.
SpeedLimitView
was replaced
In v2, the speed limit interface replaces v1's SpeedLimitView
.
The speed limit interface includes several components that are responsible for rendering and updating speed limit information on to the screen. MapboxSpeedLimitView
allows you to quickly display speed limit information in your app by passing it data from MapboxSpeedLimitApi
.
You can also instantiate and invoke MapboxSpeedLimitApi
independent of MapboxSpeedLimitView
to retrieve relevant data and render a custom speed limit component.
v1
Drop-in UI: This UI component was included in v1's pre-built UI (NavigationView
). In v2 you will have to add this UI component manually.
SpeedLimitView
component: If you used the SpeedLimitView
class directly in v1, your implementation likely looks something like this:
XML layout:
<com.mapbox.navigation.ui.speed.SpeedLimitView
android:id="@+id/speedLimitView"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
Activity:
private val mapMatcherResultObserver = object : MapMatcherResultObserver {
override fun onNewMapMatcherResult(mapMatcherResult: MapMatcherResult) {
ifNonNull(mapMatcherResult.speedLimit) {
speedLimitView.setSpeedLimit(it)
} ?: speedLimitView.hide()
}
}
v2
To replace SpeedLimitView
, add MapboxSpeedLimitView
to your layout and update data using MapboxSpeedLimitApi
:
XML layout:
<com.mapbox.navigation.ui.speedlimit.view.MapboxSpeedLimitView
android:id="@+id/speedLimitView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
Activity:
// The data in the MapboxSpeedLimitView is formatted by different
// formatting implementations. Below is the default formatter using
// default options but you can use your own formatting classes.
val speedLimitFormatter = SpeedLimitFormatter(this)
// API used for formatting speed limit related data.
val speedLimitApi = MapboxSpeedLimitApi(speedLimitFormatter)
// Gets notified with location updates.
private val locationObserver = object : LocationObserver {
override fun onNewLocationMatcherResult(locationMatcherResult: LocationMatcherResult) {
val speedLimitValue = speedLimitApi.updateSpeedLimit(locationMatcherResult.speedLimit)
binding.speedLimitView.render(speedLimitValue)
}
}
For more details on adding the speed limit UI component to your application, follow the instructions in the Speed limit guide.
Read more about how the speed limit component works in the Navigation SDK v2.
Render the speed limit of the current road using the Speed Limit API and MapboxSpeedLimitView.
GuidanceViewImageProvider
and GuidanceViewListener
were replaced
In v2, the junction view interface replaces v1's GuidanceViewImageProvider
and GuidanceViewListener
.
The junction view interface has several components that are responsible for updating the junction data and displaying it on the screen. MapboxJunctionView
allows you to quickly integrate junction views in your app.
For more customized requirements you can use the MapboxJunctionApi
to retrieve the data and render it on your own.
NavigationSpeechPlayer
was replaced
In v2, the voice interface replaces v1's NavigationSpeechPlayer
. There's no replacement for v1's VoiceInstructionLoader
in v2.
The voice interface involves the a few components for playing prompt and detailed voice instructions. Use the MapboxSpeechApi
to generate an announcement based on VoiceInstructions
objects. Then pass the resulting object to theMapboxVoiceInstructionsPlayer
, a text-to-speech engine implementation, to be played. It will use either the VoiceInstructionsFilePlayer
or VoiceInstructionsTextPlayer
speech player depending on if a synthesized speech mp3 is provided or not.
v1
Drop-in UI: This interface was included in v1's pre-built UI (NavigationView
). In v2 you will have to add it to your application manually.
NavigationSpeechPlayer
component: If you used NavigationSpeechPlayer
directly in v1, your implementation likely looks something like this:
Activity:
private lateinit var speechPlayer: NavigationSpeechPlayer
override fun onMapReady(mapboxMap: MapboxMap) {
...
initializeSpeechPlayer()
}
private fun initializeSpeechPlayer() {
val cache = Cache(File(application.cacheDir, VOICE_INSTRUCTION_CACHE), 10 * 1024 * 1024)
val voiceInstructionLoader =
VoiceInstructionLoader(application, Mapbox.getAccessToken(), cache)
val speechPlayerProvider =
SpeechPlayerProvider.Builder(
application, Locale.US.language, true, voiceInstructionLoader
).build()
speechPlayer = NavigationSpeechPlayer(speechPlayerProvider)
}
override fun onNewVoiceInstructions(voiceInstructions: VoiceInstructions) {
speechPlayer.play(voiceInstructions)
}
v2
To replace NavigationSpeechPlayer
:
Activity:
private lateinit var speechApi: MapboxSpeechApi
private lateinit var voiceInstructionsPlayer: MapboxVoiceInstructionsPlayer
speechApi = MapboxSpeechApi(
this,
getString(R.string.mapbox_access_token),
Locale.US.language
)
voiceInstructionsPlayer = MapboxVoiceInstructionsPlayer(
this,
getString(R.string.mapbox_access_token),
Locale.US.language
)
private val voiceInstructionsObserver =
VoiceInstructionsObserver { voiceInstructions ->
speechApi.generate(voiceInstructions, speechCallback)
}
private val speechCallback =
MapboxNavigationConsumer<Expected<SpeechError, SpeechValue>> { expected ->
expected.fold(
{ error ->
// play the instruction via fallback text-to-speech engine
voiceInstructionsPlayer.play(
error.fallback,
voiceInstructionsPlayerCallback
)
},
{ value ->
// play the sound file from the external generator
voiceInstructionsPlayer.play(
value.announcement,
voiceInstructionsPlayerCallback
)
}
)
}
private val voiceInstructionsPlayerCallback =
MapboxNavigationConsumer<SpeechAnnouncement> { value ->
// remove already consumed file to free-up space
speechApi.clean(value)
}
For more details on adding voice instructions to your application, follow the instructions in the Voice instructions guide.
Read more about how voice instructions work in the Navigation SDK v2.
Use the example to play voice instructions using Speech API and Voice Instruction Player.
Navigation camera
The Mapbox Maps SDK v10 removes the CameraMode
s that were part of the LocationComponent
and used to order the map camera to follow the location puck. In v2, the concepts of updating the puck’s position and updating the camera’s position are completely decoupled.
With Maps SDK v10, all camera properties can be manipulated independently and in parallel, so there is no limit to how you can build a custom system that tracks the puck’s position in navigation scenarios. This wasn’t the case with Maps SDK v9 and earlier that was the cause for the introduction of CameraMode
s that aren’t needed anymore.
The Navigation SDK v2 still allows you to integrate a camera system that tracks the puck’s location and this is where NavigationCamera
and the concept of the ViewportDataSource
are introduced.
NavigationCamera
is a class that tries to simplify management of the Map's camera object in typical navigation scenarios, maintains a state (IDLE
, FOLLOWING
, or OVERVIEW
), and executes camera transitions. NavigationCamera
does not produce any camera position values, the positions that the camera should transition to are generated by the ViewportDataSource
interface. Mapbox also provides a default MapboxNavigationViewportDataSource
implementation that produces opinionated camera positions based on the inputted data.
The new NavigationCamera
also replaces the legacy Navigation SDK implementations of DynamicCamera
and SimpleCamera
that were wrappers on top of the Maps SDK CameraMode
s.
Below, see a few common uses of v1 LocationComponent
camera usage examples and how they can be recreated or improved with the NavigationCamera
and the MapboxNavigationViewportDataSource
.
Initialize the camera
v2
viewportDataSource = MapboxNavigationViewportDataSource(mapView.getMapboxMap())
navigationCamera = NavigationCamera(
mapView.getMapboxMap(),
mapView.getCameraAnimationsPlugin(),
viewportDataSource
)
Start tracking user location
v1
// Transition to the last known location sample that the
// LocationComponent knows (or do nothing if not available)
locationComponent.setCameraMode(cameraMode)
v2
// Provide the MapboxNavigationViewportDataSource with
// location data
viewportDataSource.onLocationChanged(location)
viewportDataSource.evaluate()
// Then initialize the `FOLLOWING` state
navigationCamera.requestNavigationCameraToFollowing()
In v2, the location can be obtained from the navigation's LocationObserver
if the trip session has already been started (MapboxNavigation#startTripSession
), or from any LocationEngine
instance. You can also fetch the initial data directly from the LocationComponentPlugin
's LocationProvider
.
Track user location with bearing-matching location
v1
Any location update pushed to the LocationComponent
after setting the CameraMode
would update the camera.
v2
// Update the viewport data source to trigger a camera
// update when NavigationCamera is in a FOLLOWING state.
viewportDataSource.onLocationChanged(location)
viewportDataSource.evaluate()
Track user location with locked bearing
v1
// Tracking user location while having the camera
// pointing to the north was a separate state.
locationComponent.setCameraMode(CameraMode.TRACKING_GPS_NORTH)
v2
// To detach the camera's bearing from the location
// use the property overrides.
viewportDataSource.followingBearingPropertyOverride(0.0)
viewportDataSource.evaluate()
In v2, when followingBearingPropertyOverride
is initialized, the MapboxNavigationViewportDataSource
will not change the value of the overridden property. This means that now you can not only lock the camera's bearing to the north while tracking but also to any other value.
To reset back to location bearing matching, clear the override.
viewportDataSource.followingBearingPropertyOverride(null)
viewportDataSource.evaluate()
Change the zoom level, pitch, or padding
Zoom level, pitch, and padding manipulation operations have the same before
and now
changes, only the property name changes. Let's take zoom as an example.
v1
The zoom level could be changed via the locationComponent.zoomWhileTracking
tracking method. The method only worked if CameraMode
was already set to one of the tracking modes, otherwise, the request failed.
The zoom level could also be changed by overriding the com.mapbox.navigation.ui.camera.Camera#zoom
method and recomputing the value that Navigation SDK requested.
v2
Now, the zoom level control can either be controlled by the MapboxNavigationViewportDataSource
which will have a constant zoom level when in a free-drive mode (when there is no route present) and a dynamic zoom level when we're in an active guidance session, or overridden and controlled manually.
To let the default viewport data source manage the zoom when in an active guidance session, provide an active route via the:
viewportDataSource.onRouteChanged(route)
This is often used with the RoutesObserver
:
private val routesObserver = object : RoutesObserver {
override fun onRoutesChanged(routes: List<DirectionsRoute>) {
if (routes.isNotEmpty()) {
viewportDataSource.onRouteChanged(routes.first())
viewportDataSource.evaluate()
} else {
viewportDataSource.clearRouteData()
viewportDataSource.evaluate()
}
}
}
And provide the RouteProgress
via:
viewportDataSource.onRouteProgressChanged(routeProgress)
This is often used with the RouteProgressObserver
:
private val routeProgressObserver = object : RouteProgressObserver {
override fun onRouteProgressChanged(routeProgress: RouteProgress) {
viewportDataSource.onRouteProgressChanged(routeProgress)
viewportDataSource.evaluate()
}
}
When a route and progress are available, the viewport data source will always try to produce camera positions that best frame the upcoming maneuvers together with the user's location from viewportDataSource.onLocationChanged(location)
.
But you can also always manually control the zoom level by overriding the zoom property:
viewportDataSource.followingZoomPropertyOverride(16.5)
viewportDataSource.evaluate()
Clear route data
Remember to call viewportDataSource.clearRouteData()
whenever the active route is cleared to prevent the default viewport data source from trying to frame the route's geometry that's not visible anymore. For example:
val routesObserver = object : RoutesObserver {
override fun onRoutesChanged(routes: List<DirectionsRoute>) {
if (routes.isNotEmpty()) {
viewportDataSource.onRouteChanged(routes.first())
viewportDataSource.evaluate()
} else {
viewportDataSource.clearRouteData()
viewportDataSource.evaluate()
}
}
}
mapboxNavigation.registerRoutesObserver(routesObserver)
Add padding
The default MapboxNavigationViewportDataSource
does not apply any padding. You can apply the padding for framed geometries with:
viewportDataSource.followingPadding(padding)
viewportDataSource.evaluate()
and
viewportDataSource.overviewPadding(padding)
viewportDataSource.evaluate()
Control puck's position on screen
When in the FOLLOWING
state, the first point of the framed geometry list will be placed at the bottom edge of this padding, centered horizontally. This typically refers to the user location provided by the viewportDataSource.onLocationChanged(location)
, if available.
Controlling the position of the puck on screen does not happen via the map's padding value anymore, but by the padding provided to the viewportDataSource.followingPadding
. Make sure to read the inline documentation of the property.
Camera transition collisions and navigation gestures management
Both the legacy CameraMode
s and the new NavigationCamera
assume full ownership of the camera object while not idle. This means that by design, any outside transition (like MapboxMap#easeTo
and others) would compete with the internal transitions of the navigation camera features.
v1
Whenever any other transition was scheduled CameraMode
would fallback to NONE
but you could use the LocationComponentOptions#trackingGesturesManagement
to prevent gestures like double-tap to zoom in, two-tap to zoom out, or quick sale from breaking the tracking state.
v2
By default, the NavigationCamera
only falls back to the IDLE
state when another transition interrupts the navigation camera's scheduled transition. But, in practice, developers should use the CameraAnimationsLifecycleListener
to avoid competing transitions.
Mapbox provides two default implementations of that interface:
NavigationBasicGesturesHandler
, which requests theNavigationCamera
toIDLE
whenever any outside transition or gesture interaction happens.NavigationScaleGestureHandler
, which requests theNavigationCamera
toIDLE
whenever any outside transition happens but allows for executing navigation gestures described above, same asLocationComponentOptions#trackingGesturesManagement
did in the past.
You can enable those implementations by registering the lifecycle listeners:
mapView.getCameraAnimationsPlugin().addCameraAnimationsLifecycleListener(
NavigationScaleGestureHandler(
context,
navigationCamera,
mapView.getMapboxMap(),
mapView.gestures,
mapView.location,
object : NavigationScaleGestureActionListener {
override fun onNavigationScaleGestureAction() {
viewportDataSource.options.followingFrameOptions.zoomUpdatesAllowed = false
}
}
).apply { initialize() }
)
Manage zoom level updates after gesture interaction
When an allowed scale gesture interaction happens and the zoom level changes, the navigation camera would try to reset that zoom level back to the opinionated value computed via the default viewport data source provider as soon as another MapboxNavigationViewportDataSource#evaluate
is called. To prevent that, notice the:
object : NavigationScaleGestureActionListener {
override fun onNavigationScaleGestureAction() {
viewportDataSource.options.followingFrameOptions.zoomUpdatesAllowed = false
}
}
above. This prevents the viewport data source from producing any zoom values after the gesture interaction happens. Reset the followingFrameOptions.zoomUpdatesAllowed
value whenever appropriate to restore the dynamic (or overridden) zoom level updates.
Route overview
v1
The CameraMode
s did not offer any means of showing the overview of the route in the context of the position of the puck. The legacy Navigation SDK APIs did have a concept of an overview, but it also did not take into account the current user's location.
v2
NavigationCamera
offers an OVERVIEW
state available via navigationCamera.requestNavigationCameraToOverview()
. When paired with the MapboxNavigationViewportDataSource
, the overview state will always frame the whole route, or if the RouteProgress
is also being provided it will frame the part of the route that is remaining. Here's how an overview with padding can be requested:
private val routesObserver = object : RoutesObserver {
override fun onRoutesChanged(routes: List<DirectionsRoute>) {
if (routes.isNotEmpty()) {
viewportDataSource.onRouteChanged(routes.first())
viewportDataSource.overviewPadding = overviewEdgeInsets
viewportDataSource.evaluate()
navigationCamera.requestNavigationCameraToOverview()
} else {
viewportDataSource.clearRouteData()
viewportDataSource.evaluate()
}
}
}
Then, on each RouteProgress
update, the camera position will be recalculated to only frame the rest of the route:
private val routeProgressObserver = object : RouteProgressObserver {
override fun onRouteProgressChanged(routeProgress: RouteProgress) {
viewportDataSource.onRouteProgressChanged(routeProgress)
viewportDataSource.evaluate()
}
}
Frame other points
v1
CameraMode
s did not allow for framing any arbitrary points in the camera's frame. We had to rely on the location and zoom level to manipulate what's visible on the map.
v2
The MapboxNavigationViewportDataSource
allows for passing an arbitrary list of points and the API will make sure that all points are visible in the frame together with the provided location and route (if present), as long as the zoom level property is not overridden. Additional points that you'd like to be captured in the camera's frame can be passed via:
viewportDataSource.additionalPointsToFrameForFollowing(points)
viewportDataSource.evaluate()
This can be powerful when paired with bearing manipulation, like:
val center = mapboxMap.cameraState.center
viewportDataSource.additionalPointsToFrameForFollowing(listOf(lookAtPoint))
viewportDataSource.followingBearingPropertyOverride(
TurfMeasurement.bearing(center, lookAtPoint)
)
viewportDataSource.evaluate()
In active guidance mode, the above operation would frame the current location, the upcoming maneuver, the lookAtPoint
but also changes the bearing of the camera to head towards that new point.
Options
Make sure to browse through the MapboxNavigationViewportDataSource.options
and read about customization to the generated camera positions and how you can tweak the algorithms and change the user experience.
Mapbox Electronic Horizon
The electronic horizon API was changed. You can use both callbacks
and direct calls
to get the electronic horizon data.
With callbacks you get only the most important data, which can be more efficient and save resources. If you need additional data (for example: edge metadata
, edge shape
, road object metadata
, etc.) you can make a direct call.
Read more about how electronic horizon works in the Navigation SDK v2.