docs: refresh README with screenshots, full feature list, and build guide

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
This commit is contained in:
cottongin
2026-03-18 14:06:00 -04:00
parent 639cb99d1f
commit a09c50c302
5 changed files with 128 additions and 15 deletions

143
README.md
View File

@@ -1,36 +1,149 @@
# 24/7 Radio
Personal-use Android app for 24/7 internet radio streaming.
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](https://cursor.com) (Claude Opus/Sonnet 4.6).
---
## Screenshots
| Station List | Now Playing | Mini-Player | Station Art Fallback |
|:---:|:---:|:---:|:---:|
| ![Station list showing SomaFM tab](docs/screenshots/01-station-list.jpg) | ![Now Playing with album art](docs/screenshots/02-now-playing-art.jpg) | ![Station list with mini-player bar](docs/screenshots/03-station-list-miniplayer.jpg) | ![Now Playing with station art fallback](docs/screenshots/04-now-playing-station-art.jpg) |
---
## Features
- **Custom Raw Audio Pipeline** — OkHttp → IcyParser → Mp3FrameSync → MediaCodec → AudioTrack for absolute minimum latency (~26ms per MP3 frame)
- **Stay Connected Mode** — Aggressive reconnection with exponential backoff, never gives up
- **Dual Timers** — Session elapsed time and connection elapsed time
- **Latency Indicator** — Estimated stream-to-speaker latency
- **Icecast/Shoutcast Metadata** — Track title, artist extraction from ICY protocol
- **Album Art** — MusicBrainz/Cover Art Archive lookup with fallback chain
- **Playlist Management** — PLS/M3U import/export with #EXTIMG support
- **Station Organization** — Playlists, starring/favoriting, manual reorder
### 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** — 0500ms 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 `#EXTIMG` support 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:
```bash
./gradlew assembleDebug
./build.sh
```
## Import Stations
Menu options:
1. **Build Release APK** — signed, ready to sideload
2. **Build Debug APK** — quick, for testing
3. **Manage Keystore** — create or inspect the signing key
4. **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:
```bash
./gradlew assembleDebug # debug
./gradlew assembleRelease # release (requires keystore configured in build.gradle.kts)
```
---
## Importing Stations
1. Open the app
2. Tap the import icon in the top bar
3. Select a .m3u or .pls file
4. Stations are added to your list
2. Tap the settings gear → Import
3. Select a `.m3u` or `.pls` file
4. 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
See [Design Document](docs/plans/2026-03-09-android-247-radio-design.md) and [Implementation Plan](docs/plans/2026-03-09-android-247-radio-implementation.md).
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](docs/plans/2026-03-09-android-247-radio-design.md) and [Media3/Android Auto design](docs/plans/2026-03-18-media3-android-auto-design.md).
---
## 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) |
---
## License
[MIT](LICENSE)

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB