Skip to main content

Android Auto

The library registers the Media3 browse + playback service in its manifest, so Android Auto support is wired automatically. Your job is to supply the browse tree and resolve play requests.

Android Auto here is phone-projected Auto. Native Android Automotive OS (AAOS) is not supported.

1. Initialize on cold start

Android Auto can launch your app's JavaScript without any UI, so initialize the player and register your handlers from a bootstrap module imported before your app entry — see Setup & manual configuration.

2. Provide a browse tree

There are two complementary ways to feed the browser:

  • setBrowseSnapshot(snapshot) — push a full, pre-computed tree up front (fast, no per-node round trips).
  • onBrowseRequest(parentId) — resolve a node lazily on drilldown.
import { getTrackPlayer, registerPlaybackService } from 'react-native-queue-player';

// Push a top-level snapshot once you have data.
getTrackPlayer().setBrowseSnapshot({
rootId: 'root',
sections: [
{
id: 'playlists',
title: 'Playlists',
items: [
{ id: 'pl:chill', title: 'Chill', playable: false, hasChildren: true },
{ id: 'pl:focus', title: 'Focus', playable: false, hasChildren: true },
],
},
],
});

Register handlers for lazy browse, search, and playback resolution:

registerPlaybackService(() => ({
// Drill into a node (e.g. open a playlist).
onBrowseRequest: async (parentId) => {
const tracks = await fetchPlaylist(parentId);
return tracks.map((t) => ({
id: t.id, title: t.title, subtitle: t.artist, artworkUrl: t.artworkUrl, playable: true,
}));
},

// Live search box in the car UI.
onSearchRequest: async (query) => {
const results = await search(query);
return results.map((t) => ({ id: t.id, title: t.title, playable: true }));
},

// Resolve a browse item the user tapped into actual tracks to play.
onPlayFromIdRequest: async (mediaId) => {
const tracks = await resolveToTracks(mediaId);
return tracks; // TrackItem[]
},

// Optional connection lifecycle.
onCarConnect: () => {},
onCarDisconnect: () => {},
}));

When a request resolves to TrackItem[], the library sets the queue and starts playback for you.

Connection status

onCarConnect and onCarDisconnect are one-shot events — they fire when the first automotive controller connects and the last one drops. On a cold start the connect can fire before your bootstrap has registered the handler, so don't rely on the event alone.

isCarConnected() is the pull-based counterpart: it returns whether a controller is connected right now, so you can check at any time and gate car-only work — like pushing a fresh browse snapshot — on the current connection.

import { getTrackPlayer } from 'react-native-queue-player';

const player = getTrackPlayer();
if (player.isCarConnected()) {
player.setBrowseSnapshot(await buildSnapshot());
}

It holds the same state the events transition between (the headless Android Auto / Wear / Assistant controllers here; the CarPlay scene on iOS) and is process-scoped — a fresh app process starts disconnected until a controller connects within it.

Types

type BrowseSnapshot = { rootId: string; sections: BrowseSection[] };
type BrowseSection = { id: string; title: string; icon?: SectionIcon; artworkUrl?: string; items: BrowseItem[] };
type BrowseItem = { id: string; title: string; subtitle?: string; artworkUrl?: string; playable: boolean; hasChildren?: boolean };

SectionIcon is an exported enum of standard section glyphs (Home, Library, Playlists, Albums, Artists, Songs, RecentlyPlayed, …).

Runtime permission

Request POST_NOTIFICATIONS at runtime (Android 13+) so the media notification appears.

See also: CarPlay (the same handler model on iOS) and Voice (Google Assistant).