Building a navigation app
Navigation SDK for Android is only available upon request. Contact us to get started.
This tutorial guides you through building a navigation application using the TomTom Navigation SDK
for Android. You’ll learn to display a map, show the user’s location, calculate and display routes, and enable turn-by-turn navigation using built-in UI components.
Project setup
- Configure the project as described in the Project setup guide.
- Add the following dependencies to your app’s
build.gradle.kts
file and synchronize the project.1dependencies {2 val version = "0.40.0"3 implementation("com.tomtom.sdk.location:provider-android:$version")4 implementation("com.tomtom.sdk.location:provider-map-matched:$version")5 implementation("com.tomtom.sdk.location:provider-simulation:$version")6 implementation("com.tomtom.sdk.maps:map-display:$version")7 implementation("com.tomtom.sdk.navigation:navigation-online:$version")8 implementation("com.tomtom.sdk.navigation:route-replanner-online:$version")9 implementation("com.tomtom.sdk.navigation:ui:$version")10 implementation("com.tomtom.sdk.routing:route-planner-online:$version")11} - Retrieve the TomTom API key from the
BuildConfig
field and store in a variable:private val apiKey = BuildConfig.TOMTOM_API_KEY - In the
onCreate
function, the following SDK components will be enabled through the following steps:1override fun onCreate(savedInstanceState: Bundle?) {2 super.onCreate(savedInstanceState)3 setContentView(R.layout.activity_main)45 // if (!areLocationPermissionsGranted()) {6 // return7 // }89 // initLocationProvider()10 // initMap()11 // initRouting()12 // initNavigation()13}
With the dependencies and API key set up, you can proceed to the next step and start displaying the map.
Displaying a map
Step 1: Creating map UI
To host the map component, configure the FragmentContainerView
in your layout XML file, which can be either the activity layout or a fragment layout. The map content will be displayed within this designated container.
1<androidx.fragment.app.FragmentContainerView2 android:id="@+id/map_container"3 android:layout_width="match_parent"4 android:layout_height="match_parent" />
Now, you can initialize the MapFragment
and add it to the FragmentContainerView
programmatically.
Step 2: Displaying the map
To initialize the map, you’ll need the following components:
MapOptions
: allows you to customize the map’s behavior and appearance.MapFragment
: displays the map within theFragmentContainerView
.
Here’s the code snippet for the map initialization:
1private fun initMap() {2 val mapOptions = MapOptions(mapKey = apiKey)3 mapFragment = MapFragment.newInstance(mapOptions)4 supportFragmentManager.beginTransaction()5 .replace(R.id.map_container, mapFragment)6 .commit()7 mapFragment.getMapAsync { map ->8 tomTomMap = map9 // Place the code here to show user location and setup map listeners10 // as explained in the following sections.11 }12}
Once you have the TomTomMap
object, you can perform various actions such as adding markers or drawing routes. You can learn more about map configuration in the Map configuration guide.
Build and run your application. Upon execution, the application will display a globe map.

Showing user location
In this section, you learn to display the user’s location on the map and adjust the camera to focus on the user’s position. To enable this functionality, use the following steps:
Step 1: Enabling location services
Before proceeding with code changes, make sure to enable location services for your application. Follow the Android documentation to learn how to grant location permissions. Alternatively, you can manually configure permissions in the application settings.
You need to invoke areLocationPermissionsGranted()
method to ensure that location permissions granted.
1private fun areLocationPermissionsGranted() = ContextCompat.checkSelfPermission(2 this,3 Manifest.permission.ACCESS_FINE_LOCATION4) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(5 this,6 Manifest.permission.ACCESS_COARSE_LOCATION7) == PackageManager.PERMISSION_GRANTED
Step 2: Obtaining location updates
The SDK uses the LocationProvider
interface for location updates. In this use case, we use the AndroidLocationProvider
, which relies on Android’s system location services.
To retrieve location updates, you need to invoke the LocationProvider.enable() method. For more details, consult the Location quickstart guide.
1private fun initLocationProvider() {2 locationProvider = AndroidLocationProvider(context = this)3 locationProvider.enable()4}
Step 3: Displaying the user’s location on the map
The LocationProvider
only reports location changes. It does not interact internally with the map or navigation. Therefore, to show the user’s location on the map, you must set the LocationProvider
to the TomTomMap
. You also have to manually enable the location indicator. It can be configured using the LocationMarkerOptions
class. Read more about user location on the map in the Showing User Location document.
1private fun showUserLocation() {2 locationProvider.enable()3 // zoom to current location at city level4 onLocationUpdateListener = OnLocationUpdateListener { location ->5 tomTomMap.moveCamera(CameraOptions(location.position, zoom = 8.0))6 locationProvider.removeOnLocationUpdateListener(onLocationUpdateListener)7 }8 locationProvider.addOnLocationUpdateListener(onLocationUpdateListener)9 tomTomMap.setLocationProvider(locationProvider)10 val locationMarker = LocationMarkerOptions(type = LocationMarkerOptions.Type.Pointer)11 tomTomMap.enableLocationMarker(locationMarker)12}

Generating a route
This section describes how to calculate a route between two locations and display it on the map. You can find more details on adjusting planning criteria and accommodating vehicle profiles in the routing documentation.
Step 1: Setting up the route planner
The route planner is your access point to the routing service through the Routing API
. Initialize it with the default TomTom implementation based on the Routing API
:
1private fun initRouting() {2 routePlanner = OnlineRoutePlanner.create(context = this, apiKey = apiKey)3}
Step 2: Calculating a route
You can learn to plan a route using the RoutePlanningOptions
. This step specifies the origin, destination, guidance options, and vehicle profile. The calculated route is passed to a callback for further handling:
1private fun calculateRouteTo(destination: GeoPoint) {2 val userLocation =3 tomTomMap.currentLocation?.position ?: return4 val itinerary = Itinerary(origin = userLocation, destination = destination)5 routePlanningOptions = RoutePlanningOptions(6 itinerary = itinerary,7 guidanceOptions = GuidanceOptions(8 instructionType = InstructionType.Text,9 phoneticsType = InstructionPhoneticsType.Ipa,10 announcementPoints = AnnouncementPoints.All,11 extendedSections = ExtendedSections.All,12 progressPoints = ProgressPoints.All13 ),14 vehicle = Vehicle.Car()15 )16 routePlanner.planRoute(routePlanningOptions, routePlanningCallback)17}
Step 3: Displaying the route on the map
Now that you successfully planned a route, you can access the first route by processing the results of the RoutePlanningResponse
:
1private val routePlanningCallback = object : RoutePlanningCallback {2 override fun onSuccess(result: RoutePlanningResponse) {3 route = result.routes.first()4 route?.let { drawRoute(it) }5 }67 override fun onFailure(failure: RoutingFailure) {8 Toast.makeText(this@MainActivity, failure.message, Toast.LENGTH_SHORT).show()9 }1011 override fun onRoutePlanned(route: Route) = Unit12}
Displaying the route involves constructing a series of instructions from the route’s legs and then using the RouteOptions
to draw the route geometry and maneuver instructions. To keep the reference of the planned route to the route drawn on the map, the RouteOptions.tag
property is used. It also manages zoom levels for the route overview.
1private fun drawRoute(2 route: Route,3 color: Int = RouteOptions.DEFAULT_COLOR,4 withDepartureMarker: Boolean = true,5 withZoom: Boolean = true6) {7 val instructions = route.legs8 .flatMap { routeLeg -> routeLeg.instructions }9 .map {10 Instruction(11 routeOffset = it.routeOffset,12 combineWithNext = it.combineWithNext13 )14 }15 val routeOptions = RouteOptions(16 geometry = route.geometry,17 destinationMarkerVisible = true,18 departureMarkerVisible = withDepartureMarker,19 instructions = instructions,20 routeOffset = route.routePoints.map { it.routeOffset },21 color = color,22 tag = route.id.toString()23 )24 tomTomMap.addRoute(routeOptions)25 if (withZoom) {26 tomTomMap.zoomToRoutes(ZOOM_TO_ROUTE_PADDING)27 }28}29companion object {30 private const val ZOOM_TO_ROUTE_PADDING = 10031}
Step 4: User interaction
Learn how to interact with the map to set the destination and initiate route calculation. This step clears any existing map elements, calculates the route to the selected location, and sets up a MapLongClickListener
to handle destination selection:
1private fun clearMap() {2 tomTomMap.clear()3}45private val mapLongClickListener = MapLongClickListener { geoPoint ->6 clearMap()7 calculateRouteTo(geoPoint)8 true9}1011private fun setUpMapListeners() {12 tomTomMap.addMapLongClickListener(mapLongClickListener)13}
Finally, ensure these actions are executed when the map instance is ready to be used:
1private fun initMap() {2 val mapOptions = MapOptions(mapKey = apiKey)3 mapFragment = MapFragment.newInstance(mapOptions)4 supportFragmentManager.beginTransaction()5 .replace(R.id.map_container, mapFragment)6 .commit()7 mapFragment.getMapAsync { map ->8 tomTomMap = map9 showUserLocation()10 setUpMapListeners()11 }12}

Setting up turn-by-turn navigation
Step 1: Initializing navigation
Use the built-in navigation UI and location simulation engine to integrate turn-by-turn navigation into your application. The built-in UI displays essential information such as upcoming maneuvers, remaining distance, ETA, current speed, and speed limits. You can use default UI components or customize your own.
To start, create a TomTomNavigation
object:
1private fun initNavigation() {2 tomTomNavigation = OnlineTomTomNavigationFactory.create(3 Configuration(4 context = this,5 apiKey = apiKey,6 locationProvider = locationProvider,7 routePlanner = routePlanner8 )9 )10 tomTomNavigation.preferredLanguage = Locale.US11}
Step 2: Creating navigation UI
To host a navigation component, add another FragmentContainerView
to your layout XML file. If you want to position the navigation fragment at the bottom of the map container (assuming you have a map container with the ID "map_container"), you can use the app:layout_constraintBottom_toBottomOf
attribute and set its value to @+id/map_container
. This aligns the navigation fragment’s bottom edge with map container’s bottom edge.
1<androidx.fragment.app.FragmentContainerView2 android:id="@+id/navigation_fragment_container"3 android:layout_width="match_parent"4 android:layout_height="wrap_content"5 app:layout_constraintBottom_toBottomOf="@+id/map_container" />
Now, you can initialize the NavigationFragment
and add it to the FragmentContainerView
programmatically. Note that you must manually handle the disposal of the TomTomNavigation
instance.
1private fun initNavigationFragment() {2 val navigationUiOptions = NavigationUiOptions(3 keepInBackground = true4 )5 navigationFragment = NavigationFragment.newInstance(navigationUiOptions)6 supportFragmentManager.beginTransaction()7 .add(R.id.navigation_fragment_container, navigationFragment)8 .commitNow()9}
Step 3: Starting navigation
Initialize the navigation process by passing a Route
object and RoutePlanningOptions
. Before using it, ensure that you have associated the TomTomNavigation
object with the NavigationFragment
.
1private fun startNavigation(route: Route) {2 initNavigationFragment()3 navigationFragment.setTomTomNavigation(tomTomNavigation)4 val routePlan = RoutePlan(route, routePlanningOptions)5 navigationFragment.startNavigation(routePlan)6 navigationFragment.addNavigationListener(navigationListener)7 tomTomNavigation.addProgressUpdatedListener(progressUpdatedListener)8 tomTomNavigation.addRouteAddedListener(routeAddedListener)9 tomTomNavigation.addRouteRemovedListener(routeRemovedListener)10 tomTomNavigation.addActiveRouteChangedListener(activeRouteChangedListener)11}1213private val navigationListener = object : NavigationFragment.NavigationListener {14 override fun onStarted() {15 tomTomMap.addCameraChangeListener(cameraChangeListener)16 tomTomMap.cameraTrackingMode = CameraTrackingMode.FollowRoute17 tomTomMap.enableLocationMarker(LocationMarkerOptions(LocationMarkerOptions.Type.Chevron))18 setMapMatchedLocationProvider()19 route?.let { setSimulationLocationProviderToNavigation(it) }20 setMapNavigationPadding()21 }2223 override fun onStopped() {24 stopNavigation()25 }26}2728private val progressUpdatedListener = ProgressUpdatedListener {29 tomTomMap.routes.first().progress = it.distanceAlongRoute30}
Step 4: User interaction
Users can trigger navigation by tapping on a route, if navigation is not already running. Add a RouteClickListener
to the map view:
1private fun isNavigationRunning(): Boolean = tomTomNavigation.navigationSnapshot != null23private val routeClickListener = RouteClickListener {4 if (!isNavigationRunning()) {5 route?.let { route ->6 mapFragment.currentLocationButton.visibilityPolicy = VisibilityPolicy.Invisible7 startNavigation(route)8 }9 }10}1112private fun setUpMapListeners() {13 tomTomMap.addMapLongClickListener(mapLongClickListener)14 tomTomMap.addRouteClickListener(routeClickListener)15}
Step 5: Updating navigation states
Respond to navigation state updates by using the implemented listeners:
- The
RouteAddedListener
draws the route when recalculated by the navigation. The new route is drawn in gray since at this point it is not the active route yet. - The
RouteRemovedListener
removes the route from the map, when it is no longer used by the navigation. - The
ActiveRouteChangedListener
changes the color of the route on the map when the navigation active route is changed. - The
CameraChangeListener
adjusts UI elements based on camera tracking mode. - The
SimulationLocationProvider
simulates location updates to test the navigation UI.
1private val routeAddedListener by lazy {2 RouteAddedListener { route, routeAddedReason ->3 if (routeAddedReason !is RouteAddedReason.NavigationStarted) {4 drawRoute(5 route = route,6 color = Color.GRAY,7 withDepartureMarker = false,8 withZoom = false)9 }10 }11}1213private val routeRemovedListener by lazy {14 RouteRemovedListener { route, _ ->15 tomTomMap.routes.find { it.tag == route.id.toString() }?.remove()16 }17}1819private val activeRouteChangedListener by lazy {20 ActiveRouteChangedListener { route ->21 tomTomMap.routes.forEach {22 if (it.tag == route.id.toString()) {23 it.color = RouteOptions.DEFAULT_COLOR24 } else {25 it.color = Color.GRAY26 }27 }28 }29}30private val cameraChangeListener by lazy {31 CameraChangeListener {32 val cameraTrackingMode = tomTomMap.cameraTrackingMode33 if (cameraTrackingMode == CameraTrackingMode.FollowRoute) {34 navigationFragment.navigationView.showSpeedView()35 } else {36 navigationFragment.navigationView.hideSpeedView()37 }38 }39}40private fun setSimulationLocationProviderToNavigation(route: Route) {41 val routeGeoLocations = route.geometry.map { GeoLocation(it) }42 val simulationStrategy = InterpolationStrategy(routeGeoLocations)43 val oldLocationProvider = tomTomNavigation.locationProvider44 locationProvider = SimulationLocationProvider.create(strategy = simulationStrategy)45 tomTomNavigation.locationProvider = locationProvider46 oldLocationProvider.close()47 locationProvider.enable()48}
Step 6: Improving map-matching quality
To match raw location updates to the routes, use the MapMatchedLocationProvider
and set it to the TomTomMap
.
1private fun setMapMatchedLocationProvider() {2 val mapMatchedLocationProvider = MapMatchedLocationProvider(tomTomNavigation)3 tomTomMap.setLocationProvider(mapMatchedLocationProvider)4 mapMatchedLocationProvider.enable()5}
Step 6: Adjusting map display
You can set padding on the map to ensure proper visibility of the navigation UI elements:
1<dimen name="map_padding_bottom">263.0dp</dimen>23private fun setMapNavigationPadding() {4 val paddingBottom = resources.getDimensionPixelOffset(R.dimen.map_padding_bottom)5 val padding = Padding(0, 0, 0, paddingBottom)6 tomTomMap.setPadding(padding)7}
Step 7: Stopping navigation
Properly handle the end of the navigation process, including UI and resource clean-up:
1private fun stopNavigation() {2 navigationFragment.stopNavigation()3 mapFragment.currentLocationButton.visibilityPolicy =4 VisibilityPolicy.InvisibleWhenRecentered5 tomTomMap.removeCameraChangeListener(cameraChangeListener)6 tomTomMap.cameraTrackingMode = CameraTrackingMode.None7 tomTomMap.enableLocationMarker(LocationMarkerOptions(LocationMarkerOptions.Type.Pointer))8 tomTomMap.setPadding(Padding(0, 0, 0, 0))9 navigationFragment.removeNavigationListener(navigationListener)10 tomTomNavigation.removeProgressUpdatedListener(progressUpdatedListener)11 tomTomNavigation.removeRouteAddedListener(routeAddedListener)12 tomTomNavigation.removeRouteRemovedListener(routeRemovedListener)13 tomTomNavigation.removeActiveRouteChangedListener(activeRouteChangedListener)14 clearMap()15 initLocationProvider()16 showUserLocation()17}
Step 8: Clean Up
Remember to release resources when navigation is no longer needed:
1override fun onDestroy() {2 tomTomMap.setLocationProvider(null)3 tomTomNavigation.close()4 locationProvider.close()5 super.onDestroy()6}
Run the application. You should see a globe showing the user’s location. Set the destination point with a long press. If you want to start navigation along the drawn route, tap it. Navigation should start with a guidance panel and voice instructions.

Next steps
The TomTom Navigation
SDK allows you to customize the appearance of the map and its overlays, use your own Navigation UI components, or provide a custom implementation of certain navigation behaviors. See the following guides for more information: