As vehicles evolve into digital experiences, the need for glanceable, fast, and distraction-free interfaces becomes paramount. In Android Automotive OS (AAOS), this demand has led to the emergence of the Jetpack Glance framework — a powerful tool for creating UI surfaces that are lightweight, fast to load, and safe for drivers to interact with.
In this blog post, we’ll explore how Jetpack Glance can be used to build a media playback card for Android Automotive OS. From setting up dependencies to implementing a full-featured glanceable media widget with play/pause/skip functionality — we’ll walk through the full picture with code, context, and best practices.
What is Jetpack Glance?
Jetpack Glance is a declarative UI library designed for building remote user interfaces, including:
- App widgets (for Android homescreens)
- Glanceable UIs for wearables (e.g., Tiles)
- Future-facing vehicle dashboards and clusters in Android Automotive
Think of Glance as the Compose-inspired sibling of RemoteViews, but tailored for rendering quickly, efficiently, and safely on surfaces with strict interaction rules — like a car’s infotainment screen.
Why Use Glance in Android Automotive?
Using Glance in AAOS allows developers to:
- Create lightweight UIs for media, navigation, or vehicle info
- Ensure low distraction by adhering to system-level constraints
- Maintain fast rendering even on constrained hardware
- Leverage Jetpack Compose-like syntax without full Compose overhead
Key Use Cases in AAOS
Use Case | Description |
---|---|
Media Cards | Display now-playing info and basic playback controls |
Navigation Previews | Show turn-by-turn summaries or route cards |
Vehicle Status | Fuel, tire pressure, battery charge level |
Contextual Alerts | Door open, low fuel, safety notifications |
Setting Up Jetpack Glance in Your Project
Add Required Dependencies
Update your build.gradle
with the latest Glance libraries:
dependencies {
implementation "androidx.glance:glance:1.0.0"
implementation "androidx.glance:glance-appwidget:1.0.0"
implementation "androidx.glance:glance-wear-tiles:1.0.0" // optional
implementation "androidx.core:core-ktx:1.12.0"
}
Tip: Glance is backward-compatible with Android 12 and above, making it suitable for most AAOS setups.
Creating a Glanceable Media Widget for AAOS
Let’s walk through a full example where we build a media playback widget that can be shown in a center display or cluster (with OEM support).
Define the Glance Widget
class MediaGlanceWidget : GlanceAppWidget() {
@Composable
override fun Content() {
val title = "Song Title"
val artist = "Artist Name"
Column(
modifier = GlanceModifier
.fillMaxSize()
.padding(16.dp)
.background(Color.DarkGray),
verticalAlignment = Alignment.CenterVertically,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Now Playing", style = TextStyle(fontWeight = FontWeight.Bold, color = Color.White))
Spacer(Modifier.height(8.dp))
Text(title, style = TextStyle(color = Color.White))
Text(artist, style = TextStyle(color = Color.LightGray))
Spacer(Modifier.height(16.dp))
Row(horizontalAlignment = Alignment.CenterHorizontally) {
Image(
provider = ImageProvider(R.drawable.ic_previous),
contentDescription = "Previous",
modifier = GlanceModifier.size(32.dp).clickable {
actionStartService<MediaControlService>("ACTION_PREVIOUS")
}
)
Spacer(Modifier.width(16.dp))
Image(
provider = ImageProvider(R.drawable.ic_play),
contentDescription = "Play",
modifier = GlanceModifier.size(32.dp).clickable {
actionStartService<MediaControlService>("ACTION_PLAY_PAUSE")
}
)
Spacer(Modifier.width(16.dp))
Image(
provider = ImageProvider(R.drawable.ic_next),
contentDescription = "Next",
modifier = GlanceModifier.size(32.dp).clickable {
actionStartService<MediaControlService>("ACTION_NEXT")
}
)
}
}
}
}
Handling Playback Actions: MediaControlService
Since Glance doesn’t support direct onClick
behavior like Compose, we use a Service
to act on UI interactions.
class MediaControlService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when (intent?.action) {
"ACTION_PLAY_PAUSE" -> togglePlayPause()
"ACTION_NEXT" -> skipToNext()
"ACTION_PREVIOUS" -> skipToPrevious()
}
return START_NOT_STICKY
}
private fun togglePlayPause() {
// Hook into MediaSession or ExoPlayer
}
private fun skipToNext() {
// Forward playback command
}
private fun skipToPrevious() {
// Rewind playback
}
override fun onBind(intent: Intent?): IBinder? = null
}
Integrating with AndroidManifest.xml
To register the widget and service:
<receiver
android:name=".MediaGlanceWidgetReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/media_widget_info" />
</receiver>
<service
android:name=".MediaControlService"
android:exported="false" />
Widget Configuration XML
In res/xml/media_widget_info.xml
:
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="180dp"
android:minHeight="100dp"
android:updatePeriodMillis="60000"
android:widgetCategory="home_screen" />
Best Practices for Automotive Glance UI
- Keep UI distraction-optimized
- Use readable font sizes and sufficient contrast
- Avoid overloading the interface — 2–3 actions max
- Make controls large and touch-friendly
- Always test on real AAOS hardware or emulator
Conclusion
Jetpack Glance is quickly becoming a go-to tool for developers looking to build safe, fast, and flexible UI surfaces across Android form factors. In the automotive space, it shines by helping deliver minimalist, glanceable media controls that respect both performance and safety constraints.
As AAOS continues to evolve, expect more OEM support for Glance in clusters, dashboards, and center displays — especially with the push toward custom car launchers and immersive media experiences