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.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
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.
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
From Car App API level 2 onwards, you can add zooming and panning to your map in
the NavigationTemplate
to allow users to see different parts of the map.
SurfaceCallback methods
The SurfaceCallback
has three callback methods that enable you to add map interactivity to your
NavigationTemplate
: onScale
,
onScroll
, and
onFling
.
See the table below for how these callbacks relate to user interactions.
Interaction | SurfaceCallback method |
---|---|
Pinch (zoom) | onScale |
Single-touch drag | onScroll |
Single-touch fling | onFling |
Double-tap | onScale (with a scale factor determined by the template host) |
Rotary nudge in Pan mode | onScroll (with a distance factor determined by the template host) |
Map action strip
The NavigationTemplate
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.