Add AI disclaimer, 4 emulator screenshots, expanded feature sections (Android Auto, SomaFM, per-station quality, tabs, Media3 notification, now-playing logging, blurred art background), build.sh documentation, inline architecture diagram, and tech stack table. Made-with: Cursor
24/7 Radio
Android app for 24/7 internet radio streaming with minimum latency, aggressive reconnection, and full Icecast/Shoutcast metadata support.
Important
This project was built with the assistance of Cursor (Claude Opus/Sonnet 4.6).
Screenshots
| Station List | Now Playing | Mini-Player | Station Art Fallback |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
Features
Playback
- Custom raw audio pipeline — OkHttp → IcyParser → Mp3FrameSync → MediaCodec → AudioTrack for minimum latency (~26ms per MP3 frame)
- Stay Connected mode — aggressive reconnection with exponential backoff; never gives up until you say stop
- Configurable buffer — 0–500ms slider; 0ms keeps you as live as possible, higher values smooth out spotty connections
- Icecast/Shoutcast metadata — ICY protocol track title and artist extraction
- Album art — MusicBrainz/Cover Art Archive lookup with a fallback chain (ICY stream URL → station default art → station logo → placeholder)
Station Management
- SomaFM built-in — full SomaFM catalog pre-loaded, sorted by listener count
- Per-station stream quality — choose 128/256 kbps and SSL/non-SSL per station, or set a global preference order
- PLS/M3U import/export — with
#EXTIMGsupport for station artwork URLs - Tabs (playlists) — pin/unpin, rename, and drag-to-reorder tabs and stations within them
- Starring/favoriting — starred stations sort to the top of their tab
Now Playing Screen
- Blurred album art background — art fills the screen behind the player controls
- Session timer — total elapsed time since you hit play, not reset on reconnect
- Connection timer — elapsed time for the current TCP connection, resets on each reconnect
- Latency indicator — estimated stream-to-speaker latency in ms
- Track history — every track change is logged to the database with station and timestamp
- Now Playing file logging — optionally write track history to CSV, JSON Lines, or plain text
System Integration
- Media3 notification — system media notification with album art, transport controls (play/stop + seek-to-live), lockscreen and Bluetooth headset support
- Android Auto — app registers as a media source; browse your playlists and stations from the car display
Build Tooling
build.sh— interactive menu: release APK (signed), debug APK, keystore management, and clean
Requirements
- Android 9.0+ (API 28)
- Internet connection
Build
The recommended path is the interactive build script:
./build.sh
Menu options:
- Build Release APK — signed, ready to sideload
- Build Debug APK — quick, for testing
- Manage Keystore — create or inspect the signing key
- Clean — remove build artifacts
On first run, choose option 3 to create a keystore before building a release APK. Output lands in dist/.
Alternatively, build directly with Gradle:
./gradlew assembleDebug # debug
./gradlew assembleRelease # release (requires keystore configured in build.gradle.kts)
Importing Stations
- Open the app
- Tap the settings gear → Import
- Select a
.m3uor.plsfile - Stations are added to your library
Exported files include #EXTIMG tags for stations that have a default artwork URL set.
SomaFM
The SomaFM catalog is pre-loaded on first launch — no import needed. Stations are sorted by current listener count (refreshable with the sync button). You can set a global stream quality preference (256 kbps SSL recommended) or override it per station via long-press → Quality.
Architecture
Four layers:
┌─────────────────────────────────────────┐
│ UI Layer │
│ Compose screens, ViewModel, StateFlow │
├─────────────────────────────────────────┤
│ Service Layer │
│ RadioPlaybackService (MediaLibrary │
│ Service), RadioPlayerAdapter (Media3 │
│ Player facade), wake + wifi locks │
├─────────────────────────────────────────┤
│ Audio Engine │
│ StreamConnection → IcyParser → │
│ Mp3FrameSync → MediaCodec → AudioTrack │
├─────────────────────────────────────────┤
│ Data Layer │
│ Room DB, PLS/M3U import/export, │
│ DataStore preferences │
└─────────────────────────────────────────┘
The audio engine is a standalone component with no Android framework dependencies. The service owns reconnection policy, Android Auto browse tree, and the Media3 session. The UI observes state via StateFlow and never touches the engine directly.
For full detail see the design document and Media3/Android Auto design.
Tech Stack
| Layer | Library |
|---|---|
| Language | Kotlin |
| UI | Jetpack Compose, Material Design 3 |
| Media session / Auto | AndroidX Media3 (media3-session, media3-common) |
| HTTP streaming | OkHttp |
| Audio decoding | MediaCodec (hardware-accelerated) |
| Audio output | AudioTrack |
| Database | Room |
| Preferences | DataStore |
| Image loading | Coil |
| Album art lookup | MusicBrainz / Cover Art Archive (no API key required) |



