Navigation, point of interest (POI), and weather apps using the following
templates can draw maps by accessing a Surface.
To use the following templates, your app must declare one of these
corresponding permissions in a <uses-permission> element in the
AndroidManifest.xml file.
| Template | Permission | Guidance |
|---|---|---|
NavigationTemplate |
androidx.car.app.NAVIGATION_TEMPLATES |
Navigation |
MapWithContentTemplate |
or,
|
Navigation, POI, Weather |
|
(deprecated) |
androidx.car.app.NAVIGATION_TEMPLATES |
Navigation |
|
(deprecated) |
androidx.car.app.NAVIGATION_TEMPLATES |
Navigation |
(deprecated) |
androidx.car.app.NAVIGATION_TEMPLATES |
Navigation |
See the reference implementation
To view a complete reference implementation, see Navigation Sample.
Declare the surface permission
In addition to the permission required for the template that your app is using,
your app must declare the androidx.car.app.ACCESS_SURFACE permission in its
AndroidManifest.xml file to get access to the surface:
<manifest ...>
...
<uses-permission android:name="androidx.car.app.ACCESS_SURFACE" />
...
</manifest>
Access the surface
To access the Surface that the host provides, you must implement a
SurfaceCallback and provide that implementation to the AppManager
car service. The current Surface is passed to your SurfaceCallback in the
SurfaceContainer parameter of the onSurfaceAvailable() and
onSurfaceDestroyed() callbacks.
Kotlin
carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)
Java
carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);
Use a virtual display to render content
In addition to rendering directly into the Surface using the Canvas API,
you can also render Views into the Surface using the VirtualDisplay and
Presentation APIs, as this example shows:
class HelloWorldSurfaceCallback(context: Context) : SurfaceCallback {
lateinit var virtualDisplay: VirtualDisplay
lateinit var presentation: Presentation
override fun onSurfaceAvailable(surfaceContainer: SurfaceContainer) {
virtualDisplay = context
.getSystemService(DisplayManager::class.java)
.createVirtualDisplay(
VIRTUAL_DISPLAY_NAME ,
surfaceContainer.width,
surfaceContainer.height,
surfaceContainer.dpi,
surfaceContainer.surface,
0
)
presentation = Presentation(context, virtualDisplay.display)
// Instantiate the view to be used as the content view
val view = ...
presentation.setContentView(view)
presentation.show()
}
override fun onSurfaceDestroyed(surfaceContainer: SurfaceContainer) {
presentation.dismiss()
// This handles releasing the Surface provided when creating the VirtualDisplay
virtualDisplay.release()
}
}
Use Compose to render to the virtual display
You can use a ComposeView as the content view of the Presentation. Because
ComposeView is used outside an activity, confirm that it or a parent view
propagates a LifecycleOwner and SavedStateRegistryOwner. To do this, use
setViewTreeLifecycleOwner and setViewTreeSavedStateRegistryOwner.
Session already implements LifecycleOwner. To serve both roles, your
implementation can additionally implement SavedStateRegistryOwner.
class HelloWorldSession() : Session(), SavedStateRegistryOwner { ... }
class HelloWorldSurfaceCallback(session: HelloWorldSession) : SurfaceCallback {
...
override fun onSurfaceAvailable(surfaceContainer: SurfaceContainer) {
...
val view = ComposeView(session.carContext)
view.setViewTreeLifecycleOwner(session)
view.setViewTreeSavedStateRegistryOwner(session)
view.setContent {
// Composable content
}
presentation.setContentView(view)
presentation.show()
}
...
}
Understand the surface visible area
The host can draw user interface elements for the templates on top of the map.
The host calls the SurfaceCallback.onVisibleAreaChanged method
to communicate the area of the surface most likely to be unobstructed and
visible to the user.
To minimize the number of changes, the host calls the
SurfaceCallback.onStableAreaChanged method with the smallest rectangle,
which is always visible according to the current template.
For example, when a navigation app uses the NavigationTemplate with an
action strip on top, to make more space on the screen the action strip can
be concealed when the user hasn't interacted with the screen. This case results
in a callback to onStableAreaChanged and onVisibleAreaChanged with the same
rectangle.
When the action strip is concealed, only onVisibleAreaChanged is called with
the larger area. If the user interacts with the screen, only
onVisibleAreaChanged is called with the first rectangle.
Support dark theme
Apps must redraw their map onto the Surface instance with the proper dark
colors when the host determines conditions warrant it, as described in
Android app quality for cars.
To draw a dark map, use the CarContext.isDarkMode method. When dark
theme status changes, you receive a call to
Session.onCarConfigurationChanged.
Draw maps on the cluster display
In addition to drawing maps on the main display, navigation apps can also support drawing maps on the cluster display behind the steering wheel. To learn more, see Drawing to the cluster display.