- Star icons now use distinct tint colors (primary vs faded) for clear state - Station switching no longer races — old playback job is cancelled before new - Skip-ahead drops ~1s of buffered audio per tap via atomic counter - Custom SocketFactory with SO_RCVBUF=16KB and TCP_NODELAY for minimal TCP buffering - Catch-up drain: discards pre-buffered frames until network reads block (live edge) - AudioTrack PERFORMANCE_MODE_LOW_LATENCY for smallest hardware buffer Made-with: Cursor
3.1 KiB
3.1 KiB
Full Implementation — Android 24/7 Radio
Date: 2026-03-09
Task Description
Brainstormed, designed, planned, and implemented a complete Android 24/7 internet radio streaming app from scratch using subagent-driven development.
What Was Built
Audio Engine (custom raw pipeline)
- StreamConnection — OkHttp HTTP client with ICY metadata header support
- IcyParser — Separates audio bytes from ICY metadata, parses StreamTitle into artist/title
- Mp3FrameSync — Finds MP3 frame boundaries with two-frame validation and re-sync
- AudioEngine — Wires pipeline: StreamConnection → IcyParser → RingBuffer → Mp3FrameSync → MediaCodec → AudioTrack
Android Service Layer
- RadioPlaybackService — Foreground service with wake lock, wifi lock, MediaSession
- Stay Connected — Exponential backoff reconnection (1s→30s cap), ConnectivityManager callback for instant retry
- NotificationHelper — Media-style notification with stop action
- RadioController — Shared state between service and UI via StateFlow
Data Layer
- Room Database — Station, Playlist, MetadataSnapshot, ListeningSession, ConnectionSpan entities with full DAOs
- DataStore Preferences — stayConnected, bufferMs, lastStationId
- PLS/M3U Import/Export — Full parsers with #EXTIMG support, round-trip tested
UI (Jetpack Compose + Material 3)
- Station List — Playlists as expandable groups, starring, tap-to-play, long-press menu, import
- Now Playing — Album art, dual timers (session + connection), latency indicator, stay connected toggle, buffer slider
- Settings — Playback prefs, playlist export, recently played, track history with search
- MiniPlayer — Bottom bar on station list when playing
Metadata
- AlbumArtResolver — MusicBrainz/Cover Art Archive → ICY StreamUrl → #EXTIMG → placeholder
- ArtCache — In-memory LRU cache (500 entries)
- Coil 3 — Image loading in Compose
Commit History (20 commits)
- Design document and implementation plan
- Project scaffolding (Gradle, manifest, dependencies)
- Room entities, DAOs, database, DataStore
- M3U/PLS import/export with tests
- ICY metadata parser with tests
- MP3 frame synchronizer with tests
- HTTP stream connection with tests
- Audio engine integration
- Foreground service with Stay Connected
- Material 3 theme and navigation
- Station List screen
- Now Playing screen with dual timers
- Settings screen with history
- Album art resolution with MusicBrainz
- Final integration and README
Test Coverage
- IcyParser: 10 tests
- Mp3FrameSync: 9 tests
- StreamConnection: 6 tests
- M3uParser: 6 tests
- PlsParser: 5 tests
- PlaylistExporter: 4 tests
- RingBuffer: 4 tests
- AlbumArtResolver: 9 tests (MockWebServer)
Follow-Up Items
- Test on actual Android 9 device with real Icecast/Shoutcast streams
- Add drag-to-reorder for stations and playlists
- Implement latency estimation from AudioTrack write/play head positions
- Add Bluetooth headset AUDIO_BECOMING_NOISY handling
- Add audio focus management
- Future: recording, clips, analytics (schema already supports it)