Files
radiola-android/docs/superpowers/specs/2026-06-01-radio-record-design.md
2026-06-01 11:14:38 +03:00

6.9 KiB
Raw Blame History

radiOLA — Design Spec

Date: 2026-06-01 Author: brainstorming skill Status: Approved


Overview

Android-приложение для прослушивания онлайн-радио Radio Record (кодовое название проекта: radiOLA) с фокусом на:

  • Прослушивание станций с фильтрацией и поиском
  • Отображение текущего трека (now playing)
  • Deep links в музыкальные сервисы для быстрого поиска трека

Tech Stack

  • Language: Kotlin
  • UI: Jetpack Compose + Material 3
  • Navigation: Jetpack Navigation Compose
  • DI: Hilt
  • Network: Retrofit + Kotlinx Serialization
  • Database: Room
  • Preferences: DataStore
  • Player: ExoPlayer (Media3)
  • Background Service: MediaSessionService
  • Images: Coil
  • Testing: JUnit 5 + MockK + Turbine + Compose UI Test

Architecture

Clean Architecture + Domain Layer (feature-based packages inside single Gradle module).

📱 ui/
   ├── player/          # PlayerScreen, PlayerViewModel
   ├── stations/        # StationsScreen, StationsViewModel
   ├── favorites/       # FavoritesScreen
   ├── history/         # HistoryScreen
   └── settings/        # SettingsScreen
🧠 domain/
   ├── model/           # Station, Track, PlayerState, DeeplinkService
   ├── repository/      # Interfaces (StationRepository, NowPlayingRepository, etc.)
   └── usecase/         # PlayStation, GetNowPlaying, SearchTrackInService, etc.
💾 data/
   ├── remote/          # Retrofit API, DTOs, mappers
   ├── local/           # Room Entities, DAOs
   └── repository/      # Repository implementations
🎵 service/            # MediaSessionService (ExoPlayer + MediaSession)
🔗 deeplink/           # URL builders for music services
📱 widget/             # AppWidgetProvider

Screens & Navigation

Bottom Navigation (4 tabs) + Player BottomSheet.

Tab Screen Content
🎧 Радио StationsScreen Grid of stations, tag chips (filter), search bar
Избранное FavoritesScreen Favorite stations grid, quick play
🕐 История HistoryScreen List of last 200 tracks with timestamps, search, swipe actions
⚙️ Настройки SettingsScreen Sleep timer, equalizer, recording toggle

Player:

  • Collapsed: mini-bar at bottom (cover 48dp, station name, play/pause)
  • Expanded (tap): fullscreen with large cover, track info, deep link buttons row

Data Sources

Remote API (Radio Record)

Endpoint Description
GET https://www.radiorecord.ru/api/stations/ List of stations with metadata (id, name, prefix, genre, tags, covers)
GET https://air.radiorecord.ru/api/stations/now/ Current tracks for all stations (artist, song, iTunes covers)

Stream URL format: https://air.radiorecord.ru:805/{prefix}_128

Cache policy: stations list cached 1 hour; now playing polled every 10 seconds while player active.

Local (Room)

@Entity
class StationEntity(
    val id: Int,
    val name: String,
    val prefix: String,
    val streamUrl: String,
    val coverUrl: String,
    val sortOrder: Int
)

@Entity
class TrackHistoryEntity(
    val id: Int = 0,
    val artist: String,
    val song: String,
    val stationName: String,
    val coverUrl: String?,
    val timestamp: Long
)

Track history limit: 200 records, FIFO cleanup.

Preferences (DataStore)

  • last_station_id: last played station
  • sleep_timer_minutes: default timer value
  • enabled_deeplink_services: set of visible service IDs

Player & Background Playback

  • ExoPlayer inside MediaSessionService (Media3)
  • MediaSession for system integration: headphone buttons, Bluetooth, lock screen
  • Notification: MediaStyle with prev/pause/next buttons, cover art, now playing text
  • Audio Focus: auto-pause on call, duck on notifications
  • Now Playing Polling: GET /api/stations/now/ every 10s, match by station.id or prefix

Supported services: Яндекс Музыка, ВК Музыка, BOOM, Spotify, Apple Music, YouTube Music, Tidal, Deezer.

URL template: https://<service>/search?q={artist}+{track} (URLEncoded).

Opening: Intent(ACTION_VIEW, uri) — system handles app vs browser fallback.

UI:

  • Player: horizontal row of service icons below cover art
  • History: swipe left → "Find in..." → BottomSheet with service list
  • Settings: toggle visibility per service

Additional Features

Feature Implementation
Favorites Room + toggle in player/station card + drag-sort
History Auto-log on track change (deduplicate consecutive duplicates) + search + swipe-to-delete
Sleep Timer Handler postDelayed / WorkManager; stop service at timeout
Equalizer Android Equalizer AudioEffect; 5 bands + presets (Rock, Pop, Jazz, Flat, Bass); DataStore persistence
Stream Recording MediaMuxer on ExoPlayer stream; save to Music/RadioRecord/ via MediaStore; ⚠️ personal use only
Widget 4x1 AppWidgetProvider; cover + station name + play/pause; updates on track change

Error Handling

Scenario Behavior
No internet Snackbar "Offline mode"; show cached stations from Room; disable play
API down Exponential backoff retry (1s→2s→4s→8s, max 5); fallback to cache
Stream fails ExoPlayer retry x3; then suggest switching station
Recording unavailable Request WRITE_EXTERNAL_STORAGE; hide button if denied
Deep link fails Toast "Opening in browser" + force chooser
Bluetooth disconnect Auto-pause
Incoming call Auto-pause; resume after hangup

Testing Strategy

Type Scope Tools
Unit UseCases (filtering, deeplink URL building) JUnit 5 + MockK
Unit Repositories (cache merge, local/remote) Room in-memory + MockWebServer
UI Compose screens (search, filters, navigation) Compose UI Test + Hilt Test
Integration Player (play/pause, station switch) Espresso + IdlingResource
Manual Deep links on real device with all services

Out of Scope (Future)

  • Android Auto / Automotive OS
  • Crossfade between stations
  • Multiple stream qualities (64/128/320)
  • Social sharing (send track to friend)
  • Lyrics display

Risks & Assumptions

  1. Radio Record API stability — endpoints are unofficial/public; may change without notice. Mitigation: graceful degradation to cached data.
  2. Recording legality — stream recording is for personal use only. Mitigation: clear disclaimer in app; no sharing functionality.
  3. Deep link URL changes — music services may change search URL patterns. Mitigation: configurable URL templates in code; easy to update.