
Security News
Suno Breached via Shai-Hulud Worm, Leaked Code Exposes AI Music Scraping
A Shai-Hulud infection exposed Suno's source code, which shows the AI music startup stream-ripped tracks to train its models.
react-native-sportscar
Advanced tools
React Native Android Auto media player module with customizable UI (New Architecture/TurboModules)
A React Native module for Android Auto integration with customizable media player support for both audio and video content.
MediaBrowserServiceCompat implementation⚠️ Important: Video content only plays in parked mode due to Android Auto platform restrictions, not module limitations. While driving, only audio from videos will play.
🧪 Experimental: Video support is currently experimental and may have limitations or issues. Audio playback is fully stable and recommended for production use.
npm install react-native-sportscar react-native-nitro-modules
# or
yarn add react-native-sportscar react-native-nitro-modules
Dependencies:
android/build.gradle (project level)buildscript {
ext {
minSdkVersion = 29 // Required for Android Auto
compileSdkVersion = 34
targetSdkVersion = 34
}
}
android/app/src/main/AndroidManifest.xml<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- Required Permissions -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Android Auto Features -->
<uses-feature android:name="android.hardware.screen.landscape" />
<uses-feature android:name="android.software.leanback" />
<application>
<!-- Android Auto Media Service -->
<service
android:name="com.sportscar.service.AndroidAutoMediaService"
android:exported="true"
android:foregroundServiceType="mediaPlayback">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
<!-- Android Auto App Descriptor -->
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" />
<!-- Handle potential conflicts with other AndroidX Car App dependencies -->
<meta-data
android:name="androidx.car.app.CarAppMetadataHolderService.CAR_HARDWARE_MANAGER"
android:value="androidx.car.app.hardware.ProjectedCarHardwareManager"
tools:replace="android:value" />
</application>
</manifest>
android/app/src/main/res/xml/automotive_app_desc.xml<?xml version="1.0" encoding="utf-8"?>
<automotiveApp>
<uses name="media" />
<uses name="video" />
</automotiveApp>
The library provides custom React hooks for easier state management:
A simple hook for tracking Android Auto playback state:
import { usePlaybackStateChange } from 'react-native-sportscar';
const MyComponent = () => {
const {
playbackInfo,
isLoading,
isPlaying,
isStopped,
isBuffering,
refresh,
} = usePlaybackStateChange({
fetchInitialState: true,
onStateChange: (info) => {
console.log('Playback state changed:', info.state);
},
});
return (
<View>
<Text>State: {playbackInfo?.state}</Text>
<Text>Playing: {isPlaying ? 'Yes' : 'No'}</Text>
<Text>Stopped: {isStopped ? 'Yes' : 'No'}</Text>
<Text>Buffering: {isBuffering ? 'Yes' : 'No'}</Text>
</View>
);
};
import AndroidAuto from 'react-native-sports-car';
import type {
MediaLibrary,
AndroidAutoMediaItem,
} from 'react-native-sports-car';
// Define your media library
const mediaLibrary: MediaLibrary = {
layoutType: 'GRID',
items: [
{
id: 'song1',
title: 'Bohemian Rhapsody',
artist: 'Queen',
album: 'A Night at the Opera',
duration: 355000, // in milliseconds
mediaType: 'AUDIO',
mediaUrl: 'https://example.com/bohemian-rhapsody.mp3',
artworkUrl: 'https://example.com/queen-album-art.jpg',
genre: 'Rock',
year: 1975,
},
{
id: 'video1',
title: 'Music Video',
artist: 'Artist Name',
duration: 240000,
mediaType: 'VIDEO',
mediaUrl: 'https://example.com/music-video.mp4',
artworkUrl: 'https://example.com/video-thumbnail.jpg',
},
],
};
// Initialize the media library
await AndroidAuto.initializeMediaLibrary(JSON.stringify(mediaLibrary));
// Play media
await AndroidAuto.playMedia('song1');
// Control playback
await AndroidAuto.pause();
await AndroidAuto.resume();
await AndroidAuto.stop();
// Next/Previous tracks
await AndroidAuto.skipToNext();
await AndroidAuto.skipToPrevious();
// Seek to position (in milliseconds)
await AndroidAuto.seekTo(120000); // Seek to 2 minutes
// Set playback speed
await AndroidAuto.setPlaybackSpeed(1.5); // 1.5x speed
// Get current playback state
const playbackInfo = await AndroidAuto.getPlaybackState();
console.log('Current position:', playbackInfo.currentPosition);
console.log('Is playing:', playbackInfo.isPlaying);
Since Nitro Modules don't support event listeners, the library uses callback-based event handling:
import { useEffect } from 'react';
useEffect(() => {
// Set up playback state callback
AndroidAuto.setPlaybackStateCallback((playbackInfo) => {
console.log('Playback state:', playbackInfo.state);
console.log('Current position:', playbackInfo.positionMs);
console.log('Is playing:', playbackInfo.state === 'playing');
});
// Set up media player event callback
AndroidAuto.setMediaPlayerEventCallback((eventType, data) => {
switch (eventType) {
case 'mediaItemChanged':
console.log('Now playing:', data.mediaItem.title);
break;
case 'error':
console.error('Playback error:', data.error);
break;
case 'playbackStateChanged':
console.log('Playback state changed:', data.state);
break;
}
});
return () => {
// Clean up callbacks
AndroidAuto.setPlaybackStateCallback(null);
AndroidAuto.setMediaPlayerEventCallback(null);
};
}, []);
import { createPlaylist, getNextTrack, getPreviousTrack } from 'react-native-sportscar';
// Create playlist from all playable items
const playlist = createPlaylist(mediaLibrary, { includeAllItems: true });
// Navigate tracks manually
const nextTrackId = getNextTrack(playlist, currentTrackId, true); // with repeat
const prevTrackId = getPreviousTrack(playlist, currentTrackId, true);
if (nextTrackId) {
await AndroidAuto.playMedia(nextTrackId);
}
// Update media library with new content
const updatedLibrary: MediaLibrary = {
layoutType: 'LIST',
items: [...existingItems, ...newItems],
};
await AndroidAuto.updateMediaLibrary(JSON.stringify(updatedLibrary));
// Grid layout (default)
const gridLibrary: MediaLibrary = {
layoutType: 'GRID',
items: mediaItems,
};
// List layout
const listLibrary: MediaLibrary = {
layoutType: 'LIST',
items: mediaItems,
};
// Set layout type separately
await AndroidAuto.setLayoutType('GRID');
| Method | Parameters | Return Type | Description |
|---|---|---|---|
initializeMediaLibrary | jsonString: string | Promise<boolean> | Initialize media library |
updateMediaLibrary | jsonString: string | Promise<boolean> | Update media library |
getMediaLibrary | - | Promise<MediaLibrary> | Get current media library |
playMedia | mediaId: string | Promise<boolean> | Play specific media item |
pause | - | Promise<boolean> | Pause playback |
resume | - | Promise<boolean> | Resume playback |
stop | - | Promise<boolean> | Stop playback |
skipToNext | - | Promise<boolean> | Skip to next track |
skipToPrevious | - | Promise<boolean> | Skip to previous track |
seekTo | positionMs: number | Promise<boolean> | Seek to position |
setPlaybackSpeed | speed: number | Promise<boolean> | Set playback speed |
getPlaybackState | - | Promise<PlaybackInfo> | Get current state |
setLayoutType | layoutType: string | Promise<boolean> | Set UI layout |
refreshAndroidAutoUI | - | Promise<boolean> | Refresh Android Auto UI |
isCurrentlyPlaying | - | Promise<boolean> | Check if currently playing |
getLastPlayedMediaInfo | - | Promise<LastPlayedMediaInfo | null> | Get last played media info |
handleAppStateChange | appState: string | Promise<boolean> | Handle app state change |
getCurrentAppState | - | Promise<AppState> | Get current app state |
setPlaybackStateCallback | callback: PlaybackStateCallback | null | void | Set playback state callback |
setMediaPlayerEventCallback | callback: MediaPlayerEventCallback | null | void | Set media player event callback |
| Callback | Parameters | Description |
|---|---|---|
PlaybackStateCallback | (playbackInfo: PlaybackInfo) => void | Called when playback state changes |
MediaPlayerEventCallback | (eventType: MediaPlayerEventType, data?: any) => void | Called for media player events |
| Event Type | Description |
|---|---|
playbackStateChanged | Playback state updated |
mediaItemChanged | Media item changed |
error | Error occurred |
// Media Types
export type LayoutType = 'grid' | 'list';
export type MediaType = 'audio' | 'video' | 'folder';
export type PlaybackState =
| 'playing'
| 'paused'
| 'stopped'
| 'buffering'
| 'error';
export type AppState = 'foreground' | 'background' | 'destroyed';
export type RepeatMode = 'none' | 'one' | 'all';
// Media Item Interface
interface MediaItem {
id: string;
title: string;
artist?: string;
album?: string;
duration?: number; // milliseconds
mediaType: MediaType;
mediaUrl: string;
artworkUrl?: string;
genre?: string;
year?: number;
children?: MediaItem[]; // for folders
}
// Media Library Interface
interface MediaLibrary {
layoutType: LayoutType;
items: MediaItem[];
}
// Playback Information
interface PlaybackInfo {
state: PlaybackState;
currentMediaId?: string;
positionMs: number; // milliseconds
durationMs: number; // milliseconds
playbackSpeed: number;
shuffleEnabled: boolean;
repeatMode: RepeatMode;
}
// Last Played Media Info
interface LastPlayedMediaInfo {
mediaId: string;
positionMs: number; // milliseconds
}
// Callback Types
type PlaybackStateCallback = (playbackInfo: PlaybackInfo) => void;
type MediaPlayerEventCallback = (
eventType: MediaPlayerEventType,
data?: any
) => void;
type MediaPlayerEventType =
| 'playbackStateChanged'
| 'mediaItemChanged'
| 'error';
Install Android Auto DHU:
# Download from Android Developer site
# Extract and run
./desktop-head-unit
Enable Developer Mode:
Connect via USB:
Enable Parked Mode (REQUIRED for video playback):
adb shell settings put global android_auto_parked 1🚨 Critical: Video content ONLY plays in parked mode due to Android Auto platform safety restrictions.
🧪 Experimental Feature: Video support is currently experimental and may not work reliably in all scenarios.
Desktop Head Unit (DHU):
# Method 1: DHU Settings
# Go to Settings → Developer settings → Enable "Simulate parked mode"
# Method 2: ADB Command
adb shell settings put global android_auto_parked 1
Physical Car:
Build Errors:
# Clean and rebuild
cd android && ./gradlew clean
cd .. && npx react-native run-android
AndroidX Car App Dependency Conflicts:
Error: Attribute meta-data#androidx.car.app.CarAppMetadataHolderService.CAR_HARDWARE_MANAGER@value
is also present at [androidx.car.app:app-automotive:1.4.0]
Solution:
xmlns:tools="http://schemas.android.com/tools" to your manifesttools:replace="android:value" as shown in step 2 aboveandroidx.car.app:app-automotive in your dependencies, consider removing it if not neededService Not Found:
Video Not Playing While Driving:
adb shell settings put global android_auto_parked 1Video Issues (Experimental Feature):
Playback Issues:
Android Auto Not Detecting:
# Check Android Auto service
adb shell dumpsys activity service CarService
# Monitor logs
adb logcat | grep -E "(AndroidAuto|MediaService|CAR\.)"
# Check parked mode status
adb shell settings get global android_auto_parked
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.
MIT License - see LICENSE file for details.
For issues and questions:
Made with ❤️ for the React Native community
FAQs
React Native Android Auto media player module with customizable UI (New Architecture/TurboModules)
The npm package react-native-sportscar receives a total of 3 weekly downloads. As such, react-native-sportscar popularity was classified as not popular.
We found that react-native-sportscar demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
A Shai-Hulud infection exposed Suno's source code, which shows the AI music startup stream-ripped tracks to train its models.

Security News
Vercel is formalizing a monthly release program for Next.js. The change follows React2Shell and a sharp rise in AI-assisted vulnerability discovery.

Research
/Security News
11 malicious NuGet tools pose as game cheats to deploy Windows payloads, track hosts, and use Google Sheets for telemetry and control.