This section details the different features of the library that you can make use of to implement the functionality of your turn-by-turn navigation app.
Declare navigation support in your manifest
Your navigation app needs to declare the androidx.car.app.category.NAVIGATION
car app category in the intent
filter of its CarAppService
:
<application>
...
<service
...
android:name=".MyNavigationCarAppService"
android:exported="true">
<intent-filter>
<action android:name="androidx.car.app.CarAppService" />
<category android:name="androidx.car.app.category.NAVIGATION"/>
</intent-filter>
</service>
...
<application>
Support navigation intents
In order to support navigation Intents to your app, including those coming from
the Google Assistant using a voice query, your app needs to handle the
CarContext.ACTION_NAVIGATE
intent in its
Session.onCreateScreen
and
Session.onNewIntent
.
See the documentation of
CarContext.startCarApp
for details on the format of the intent.
Access the navigation templates
Navigation apps can access the following templates specifically designed for navigation apps. All these templates display a surface in the background that your app can access in order to draw your map, alongside other information provided by your app which varies per template.
NavigationTemplate
: displays the map along with an optional informational message or routing directions and travel estimates during active navigation.MapTemplate
: Presents a compact version of a list (as in theListTemplate
) or of a pane (detailed information with prominent actions, as in thePaneTemplate
) next to a map.PlaceListNavigationTemplate
: displays a list of places that can have corresponding markers drawn in the map.RoutePreviewNavigationTemplate
: displays a list of routes one of which can be selected and highlighted in the map.
For more details on how to design your navigation app’s user interface using those templates, see the Android for Cars App Library Design Guidelines.
In order to get access to the navigation templates, your app needs to declare
the androidx.car.app.NAVIGATION_TEMPLATES
permission in its
AndroidManifest.xml
:
<uses-permission android:name="androidx.car.app.NAVIGATION_TEMPLATES"/>
Drawing the Map
Navigation applications can access a Surface
to draw the map on relevant templates.
A SurfaceContainer
object can
then be accessed by setting a SurfaceCallback
instance to the AppManager
car
service:
Kotlin
carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)
Java
carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);
The SurfaceCallback
provides a
callback when the SurfaceContainer
is available along with other callbacks when the properties of the Surface
change.
In order to get access to the surface, your app needs to declare the
androidx.car.app.ACCESS_SURFACE
permission in its AndroidManifest.xml
:
<uses-permission android:name="androidx.car.app.ACCESS_SURFACE"/>
The map’s visible area
The host may draw user interface elements for the different templates on top of
the map. The host will communicate the area that is guaranteed to be unoccluded
and fully visible to the user by calling the
SurfaceCallback.onVisibleAreaChanged
method. Also, in order to minimize the number of changes, the host will also
call the
SurfaceCallback.onStableAreaChanged
method with the smallest rectangle, which will always be visible based on the
current template.
For example, when a navigation app is using the
NavigationTemplate
with an action strip on top, the action strip may hide itself when the user has
not interacted with the screen for a while to make more space for the map. In
this case, there will be a callback to onStableAreaChanged
and
onVisibleAreaChanged
with the same rectangle. When the action strip is hidden,
only onVisibleAreaChanged
will be called with the larger area. If the user
interacts with the screen, then again only onVisibleAreaChanged
is called with
the first rectangle.
Dark mode
Navigation applications must redraw their map onto the Surface
instance with
the proper dark colors when the host determines that conditions warrant it, as
described in the
Android Auto app quality guidelines.
In order to decide on whether you should draw a dark map you can use the
CarContext.isDarkMode
method. Whenever the dark mode status changes, you will receive a call to
Session.onCarConfigurationChanged
.
Navigation metadata
Navigation applications must communicate additional navigation metadata with the host. The host makes use of the information to provide information to the vehicle head unit and to prevent navigation applications from clashing over shared resources.
Navigation metadata is provided through the
NavigationManager
car service accessible from the
CarContext
:
Kotlin
val navigationManager = carContext.getCarService(NavigationManager::class.java)
Java
NavigationManager navigationManager = carContext.getCarService(NavigationManager.class);
Starting, ending, and stopping navigation
In order for the host to manage multiple navigation apps, routing notifications,
and vehicle cluster data, it needs to be aware of the current state of
navigation. When a user starts navigation, the app should call
NavigationManager.navigationStarted
.
Similarly, when navigation ends, for example when the user arrives at their
destination or the user cancels navigation, the app should call
NavigationManager.navigationEnded
.
You should only call
NavigationManager.navigationEnded
when the user is finished navigating. For example, if you need to recalculate
the route in the middle of a trip, use
Trip.Builder.setLoading(true)
instead.
Occasionally, the host will need an app to stop navigation and will call
stopNavigation
in a
NavigationManagerListener
object provided by your app through
NavigationManager.setListener
.
The app must then stop issuing next-turn information in the cluster display,
navigation notifications, and voice guidance.
Trip information
During active navigation, the app should call
NavigationManager.updateTrip
.
The information provided in this call will be used in the vehicle’s cluster and
heads-up displays. Not all information may be displayed to the user depending on
the particular vehicle being driven. For example, the Desktop Head Unit shows
the Step
added to the
Trip
, but does not show
the Destination
information.
In order to test that the information is reaching the cluster the Desktop Head
Unit (DHU) tool can be configured to show a simple cluster display. Create an
cluster.ini
file with the following contents:
[general]
instrumentcluster = true
You can then invoke the DHU with an additional command line parameter:
dhu -c cluster.ini
Customize TravelEstimate with text and/or an icon
To customize the travel estimate with text and/or an icon, use the
TravelEstimate.Builder
's
setTripIcon
and/or
setTripText
methods. The
NavigationTemplate
uses
TravelEstimate
to optionally set text and icons alongside, or in place of, the estimated time
of arrival, remaining time, and remaining distance.

The following snippet uses
TravelEstimate.Builder
's
setTripIcon
and
setTripText
methods to customize the travel estimate:
Kotlin
TravelEstimate.Builder(Distance.create(...), DateTimeWithZone.create(...)) ... .setTripIcon(CarIcon.Builder(...).build()) .setTripText(CarText.create(...)) .build()
Java
new TravelEstimate.Builder(Distance.create(...), DateTimeWithZone.create(...)) ... .setTripIcon(CarIcon.Builder(...).build()) .setTripText(CarText.create(...)) .build();
Turn-by-turn notifications
The turn-by-turn (TBT) navigation instructions can be given with a frequently updated navigation notification. In order to be treated as a navigation notification in the car screen, your notification's builder must do the following:
- Mark the notification as ongoing with the
NotificationCompat.Builder.setOngoing
method. - Set the notification’s category to
Notification.CATEGORY_NAVIGATION
. - Extend the notification with a
CarAppExtender
.
A navigation notification will be displayed in the rail widget at the bottom of
the car screen. If the notification's importance level is set to
IMPORTANCE_HIGH
, it will also be displayed as a heads-up notification (HUN).
If the importance is not set with the
CarAppExtender.Builder.setImportance
method, the
notification channel's importance
will be used.
The app can set a PendingIntent
in the
CarAppExtender
that
will be sent to the app when the user taps on the HUN or the rail widget.
If
NotificationCompat.Builder.setOnlyAlertOnce
is called with a value of true
, a high-importance notification will alert only
once in the HUN.
The following snippet shows how to build a navigation notification:
Kotlin
NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) ... .setOnlyAlertOnce(true) .setOngoing(true) .setCategory(NotificationCompat.CATEGORY_NAVIGATION) .extend( CarAppExtender.Builder() .setContentTitle(carScreenTitle) ... .setContentIntent( PendingIntent.getBroadcast( context, ACTION_OPEN_APP.hashCode(), Intent(ACTION_OPEN_APP).setComponent( ComponentName(context, MyNotificationReceiver::class.java)), 0)) .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH) .build()) .build()
Java
new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) ... .setOnlyAlertOnce(true) .setOngoing(true) .setCategory(NotificationCompat.CATEGORY_NAVIGATION) .extend( new CarAppExtender.Builder() .setContentTitle(carScreenTitle) ... .setContentIntent( PendingIntent.getBroadcast( context, ACTION_OPEN_APP.hashCode(), new Intent(ACTION_OPEN_APP).setComponent( new ComponentName(context, MyNotificationReceiver.class)), 0)) .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH) .build()) .build();
Guidelines for turn-by-turn notifications
Navigation apps should update the TBT notification regularly on distance
changes, which updates the rail widget, and only show the notification as a HUN.
Apps can control the HUN behavior by setting the notification's importance with
the
CarAppExtender.Builder.setImportance
method. Setting the importance to IMPORTANCE_HIGH
will show a HUN, and setting
it to any other value will only update the rail widget.
Refresh PlaceListNavigationTemplate content
You can allow drivers to refresh content with the tap of a button while browsing
lists of places built with
PlaceListNavigationTemplate
.
Implement the
OnContentRefreshListener
interface's
onContentRefreshRequested
method and use
PlaceListNavigationTemplate.Builder.setOnContentRefreshListener
to set the listener on the template to enable list refresh.
The following snippet shows setting the listener on the template:
Kotlin
PlaceListNavigationTemplate.Builder() ... .setOnContentRefreshListener { // Execute any desired logic ... // Then call invalidate() so onGetTemplate() is called again invalidate() } .build()
Java
new PlaceListNavigationTemplate.Builder() ... .setOnContentRefreshListener(() -> { // Execute any desired logic ... // Then call invalidate() so onGetTemplate() is called again invalidate(); }) .build();
The refresh button is only shown in the header of the
PlaceListNavigationTemplate
if the listener has a value.
When the driver clicks the refresh button, the
onContentRefreshRequested
method of your
OnContentRefreshListener
implementation is called. Within
onContentRefreshRequested
,
call the
Screen.invalidate
method.
The host subsequently calls back into your app’s
Screen.onGetTemplate
method to retrieve the template with the refreshed content. See
Refresh the contents of a template for
more information about refreshing templates. As long as the next template
returned by
onGetTemplate
is of
the same type, it is counted as a refresh and is not counted towards the
template quota.
Voice guidance
To play navigation guidance over the car speakers your app must request
audio focus. As a part of your
AudioFocusRequest
you should set
the usage as AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE
. You should
also set the focus gain as AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
.
Simulating navigation
In order to verify your app's navigation functionality when submitting it to the
Google Play Store, your app must implement the
NavigationManagerCallback.onAutoDriveEnabled
callback. When this callback is called, your app should simulate navigation to
the chosen destination when the user begins navigation. Your app can exit this
mode whenever the current Session
's
lifecycle reaches the
Lifecycle.Event.ON_DESTROY
state.
You can test that your implementation of onAutoDriveEnabled
is called by
executing the following from a command line:
adb shell dumpsys activity service CAR_APP_SERVICE_NAME AUTO_DRIVE
For example:
adb shell dumpsys activity service androidx.car.app.samples.navigation.car.NavigationCarAppService AUTO_DRIVE
Default navigation car app
In Android Auto, the default navigation car app corresponds to the last navigation app that the user launched. This is the app that, for example, will receive navigation intents when the user invokes navigation commands through the assistant or when another app sends an intent to start navigation.
Allow users to interact with your map
You can add support for users to interact with maps, for example, zooming and panning of maps, allowing users to see different parts of a map. Each template has a different minimum Car App API level requirement. See the table below for the minimum level for the template that you want to implement.
Template | Interactivity supported since Car App API Level |
---|---|
NavigationTemplate | 2 |
PlaceListNavigationTemplate | 4 |
RoutePreviewNavigationTemplate | 4 |
MapTemplate | 5 |
SurfaceCallback methods
The SurfaceCallback
has several
callback methods that enable you to add map interactivity to your maps built with the
NavigationTemplate
, PlaceListNavigationTemplate
, RoutePreviewNavigationTemplate
, or MapTemplate
templates:
onClick
,
onScroll
,
onScale
,
and
onFling
.
See the table below for how these callbacks relate to user interactions.
Interaction | SurfaceCallback method |
Supported since Car App API level |
---|---|---|
Tap | onClick |
5 |
Pinch (zoom) | onScale |
2 |
Single-touch drag | onScroll |
2 |
Single-touch fling | onFling |
2 |
Double-tap | onScale (with a scale factor determined by the template host) |
2 |
Rotary nudge in Pan mode | onScroll (with a distance factor determined by the template host) |
2 |
Map action strip
The
NavigationTemplate
,
PlaceListNavigationTemplate
,
RoutePreviewNavigationTemplate
,
and MapTemplate
templates can have a map action strip for map-related actions such as zoom in
and out, recenter, display a compass, or any other actions your app may choose
to display. The map action strip can have up to four icon-only buttons that can
be refreshed without impacting task depth. Similar to the Action Strip, the map
action strip will hide during idle state and reappear on active state.
To receive map interactivity callbacks, you must add an
Action.PAN
button in the map action strip. If your app omits the Action.PAN
button in the map action strip, you will not receive user input from the
SurfaceCallback
methods and the host will exit any previously activated
pan mode. When the user presses the pan button, the host enters
pan mode. On a touchscreen, the pan button will not be displayed.
Pan mode
In pan mode, the template host translates user input from non-touch input
devices, such as rotary controllers and touchpads, into the appropriate
SurfaceCallback
methods. Respond to the user action to enter or exit pan mode
with the
setPanModeListener
method in the NavigationTemplate
Builder
. The host may hide other UI
components in the template while the user is in pan mode.
Stable area
The stable area is updated between idle and active state. Your app should draw driving-related information such as speed, speed limit, or road warnings depending on the size of the stable area so that important information on the map doesn’t get occluded by the map action strip.
In-context navigation alerts
Alert
displays important
information to the driver, with optional actions, without leaving the context of
the navigation screen. To provide the best experience to the driver,
Alert
works within the
NavigationTemplate
to avoid blocking the navigation route and to minimize driver distraction.
Alert
is only available within the
NavigationTemplate
.
To notify a user outside of the
NavigationTemplate
,
consider using a heads-up notification (HUN) as explained in
Display notifications.
For example, use Alert
to:
- Inform the driver of an update relevant to the current navigation, such as a change in traffic conditions.
- Ask the driver for an update related to the current navigation, such as the existence of a speed trap.
- Propose an upcoming task and ask if the driver is going to accept or not, such as if the driver is willing to pick up someone on their way.
In its basic form, an Alert
consists of a title and the Alert
duration time. The duration time is represented by a progress bar. Optionally,
you can add a subtitle, an icon, and up to two
Action
s.

Once an Alert
is shown, it does not
carry over to another template if a driver interaction results in leaving the
NavigationTemplate
.
It stays in the original
NavigationTemplate
until Alert
times out, the user
takes an action, or the app dismisses the
Alert
.
Configure alert duration
Choose an Alert
duration that
matches your app’s needs. The recommended duration for a navigation
Alert
is 10 seconds. Refer to the
Android for Cars Design Guidelines
for guidelines.
Create an alert
Use the Alert.Builder
to create an Alert
instance.
Kotlin
Alert.Builder( /*alertId*/ 1, /*title*/ CarText.create("Hello"), /*durationMillis*/ 5000 ) // The fields below are optional .addAction(firstAction) .addAction(secondAction) .setSubtitle(CarText.create(...)) .setIcon(CarIcon.APP_ICON) .setCallback(...) .build()
Java
new Alert.Builder( /*alertId*/ 1, /*title*/ CarText.create("Hello"), /*durationMillis*/ 5000 ) // The fields below are optional .addAction(firstAction) .addAction(secondAction) .setSubtitle(CarText.create(...)) .setIcon(CarIcon.APP_ICON) .setCallback(...) .build();
If you want to listen for Alert
cancellation or dismissal, create an implementation of the
AlertCallback
interface.
The AlertCallback
call
paths are:
If the
Alert
times out, the host calls theAlertCallback.onCancel
method with theAlertCallback.REASON_TIMEOUT
value. It then callsAlertCallback.onDismiss
method.If the driver clicks one of the action buttons, the host calls
Action.OnClickListener
, and then the host callsAlertCallback.onDismiss
.If the
Alert
is not supported, the host callsAlertCallback.onCancel
with theAlertCallback.REASON_NOT_SUPPORTED
value. (The host does not callAlertCallback.onDismiss
asAlert
was not shown.)
Show an alert
To show an Alert
, call the
AppManager.showAlert
method available through your app’s
CarContext
.
// Show an alert
carContext.getCarService(AppManager.class).showAlert(alert)
- Calling
showAlert
with anAlert
that has analertId
that is the same as theAlert
that is currently on display does nothing. (TheAlert
doesn’t update. To updateAlert
, you must recreate theAlert
with a newalertId
.) - Calling
showAlert
with anAlert
that has a differentalertId
than theAlert
that is currently on display will dismiss theAlert
that is currently displayed.
Dismiss an alert
While Alert
s automatically dismiss
due to timeout or driver interaction, you can also manually dismiss an
Alert
. For example, if you want to
dismiss an Alert
because its
information is now stale. To dismiss an
Alert
call the
dismissAlert
method with the
alertId
of the Alert
.
// Dismiss the same alert
carContext.getCarService(AppManager.class).dismissAlert(alert.getId())
Calling
dismissAlert
with an
alertId
that doesn't match the currently displayed
Alert
(if there is one) does
nothing. (It doesn't throw an exception).