docs: add chat summaries and plan documents
Made-with: Cursor
This commit is contained in:
20
chat-summaries/2026-03-09_23-30-live-listeners-and-hide.md
Normal file
20
chat-summaries/2026-03-09_23-30-live-listeners-and-hide.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Live Listener Polling and Station Hiding
|
||||
|
||||
## Task
|
||||
Implement live SomaFM listener count polling (triggered on "Listeners" chip tap with starred-first progressive sort) and a hide/unhide mechanism for stations.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### New Files
|
||||
- **`data/api/SomaFmApi.kt`** — Fetches `channels.json` from SomaFM, parses channel ID to listener count map. Includes `extractStreamId()` to derive the SomaFM channel ID from a station URL.
|
||||
|
||||
### Modified Files
|
||||
- **`Station.kt`** — Added `isHidden: Boolean = false` field.
|
||||
- **`StationDao.kt`** — Added `getHiddenStationsByPlaylist`, `getHiddenUnsortedStations`, `getHiddenCountByPlaylist`, `getHiddenUnsortedCount`, `toggleHidden`, and `updateListenerCount` queries. Existing `getStationsByPlaylist` and `getUnsortedStations` now filter out hidden stations (`AND isHidden = 0`).
|
||||
- **`RadioDatabase.kt`** — Bumped to version 3. Added `MIGRATION_2_3` that adds the `isHidden` column.
|
||||
- **`RadioApplication.kt`** — Registered `MIGRATION_2_3`.
|
||||
- **`StationListViewModel.kt`** — Added live polling logic (`pollListenerCounts()` coroutine that fetches from SomaFM API, updates starred stations first, then the rest). Added `_showHidden`, `_isPollingListeners` state flows, `hiddenCountFlow`, `toggleHidden()`, `toggleShowHidden()`. `TabInfo.hasListenerData` replaced with `isBuiltIn` flag. Sort mode `LISTENERS_DESC` now groups starred first, then unstarred, each sorted by listener count desc.
|
||||
- **`StationListScreen.kt`** — "Listeners" chip now shown on all built-in tabs with a `CircularProgressIndicator` while polling. "Show N hidden" / "Show visible" toggle appears when hidden stations exist. Long-press context menu on all stations now includes "Hide" (or "Unhide" in hidden view). Hidden stations render at 50% opacity.
|
||||
|
||||
## Follow-up Items
|
||||
- None identified.
|
||||
26
chat-summaries/2026-03-09_tabbed-station-libraries.md
Normal file
26
chat-summaries/2026-03-09_tabbed-station-libraries.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Tabbed Station Libraries
|
||||
|
||||
## Task
|
||||
Replace the single station list with a tabbed interface where every playlist gets its own tab, SomaFM is a built-in tab with listener-count sorting, and imported playlists become their own tabs automatically.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### Data Model
|
||||
- **Station.kt** -- Added `listenerCount: Int = 0` field for sort-by-listeners feature
|
||||
- **Playlist.kt** -- Added `isBuiltIn: Boolean = false` to protect built-in playlists (SomaFM) from deletion
|
||||
|
||||
### Database
|
||||
- **RadioDatabase.kt** -- Bumped to version 2, added `MIGRATION_1_2` that adds the new columns and seeds SomaFM data for existing installs
|
||||
- **SomaFmSeedData.kt** -- Added listener counts to all 44 stations, set `isBuiltIn = true` on SomaFM playlist, extracted shared `seedStations()` function used by both `onCreate` and the migration
|
||||
- **RadioApplication.kt** -- Registered `MIGRATION_1_2`
|
||||
|
||||
### UI
|
||||
- **StationListScreen.kt** -- Complete restructure: `ScrollableTabRow` for tabs, `SortChipRow` with FilterChips (Default, A-Z, Z-A, Listeners), per-tab station list. Built-in tabs hide add/import/delete actions. Listener count shown inline on SomaFM stations. Long-press edit/delete disabled on built-in tab stations.
|
||||
- **StationListViewModel.kt** -- New state model with `TabInfo`, `SortMode`, tab selection, and in-memory sorting. Import now creates a new playlist (tab) from the file name and assigns all parsed stations to it, then switches to the new tab.
|
||||
- **AddStationDialog.kt** -- Simplified: removed playlist picker (stations go to current tab's playlist automatically)
|
||||
- **EditStationDialog.kt** -- Simplified: removed playlist picker
|
||||
|
||||
## Follow-up Items
|
||||
- Drag-and-drop reordering within tabs
|
||||
- Tab reordering
|
||||
- Renaming tabs/playlists
|
||||
@@ -0,0 +1,26 @@
|
||||
# Dominant Color Extraction for Now Playing Metadata Background
|
||||
|
||||
## Task
|
||||
Detect the dominant color of the album artwork and use it as the background color for the metadata section of the Now Playing screen. Text colors are automatically inverted for contrast.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### New dependency
|
||||
- **`gradle/libs.versions.toml`**: Added `androidx.palette:palette-ktx:1.0.0`
|
||||
- **`app/build.gradle.kts`**: Added `implementation(libs.palette)`
|
||||
|
||||
### `RadioApplication.kt`
|
||||
- Added a shared `ImageLoader` instance (Coil 3) for use in palette extraction.
|
||||
|
||||
### `NowPlayingScreen.kt`
|
||||
- **Palette extraction**: Uses `LaunchedEffect(artworkUrl)` to load a 64x64 thumbnail via Coil's `ImageLoader`, then runs `Palette.from(bitmap).generate()` on `Dispatchers.IO`.
|
||||
- **`DominantColors` data class**: Holds `background`, `onBackground`, and `accent` colors extracted from the artwork.
|
||||
- **Contrast logic**: `contrastingTextColor()` checks luminance and picks black or white text. Accent color is picked from light/dark vibrant or muted swatches depending on background brightness.
|
||||
- **All child composables** (`TrackInfoSection`, `TimerSection`, `TransportControls`, `SettingsSection`) now accept explicit color parameters instead of reading from `MaterialTheme.colorScheme`.
|
||||
- **Metadata panel backgrounds**: Both `PortraitContent` (bottom half) and `LandscapeContent` (right half) apply `Modifier.background(bgColor)` using the extracted dominant color.
|
||||
- **Slider and Switch colors**: Themed to match the extracted accent color.
|
||||
- **Landscape TopAppBar**: Also uses the dominant background color for consistency.
|
||||
|
||||
## Follow-up Items
|
||||
- Consider animating the color transition when artwork changes (e.g., `animateColorAsState`).
|
||||
- May want to add a subtle gradient at the seam between artwork panel and metadata panel.
|
||||
44
chat-summaries/2026-03-10_11-55-now-playing-ui-overhaul.md
Normal file
44
chat-summaries/2026-03-10_11-55-now-playing-ui-overhaul.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Now Playing UI Overhaul — Dominant Color, Typography, Modern Controls
|
||||
|
||||
## Task
|
||||
Address four issues: (1) swap station name / artist-title text hierarchy, (2) fix dominant color extraction not applying, (3) invert back button in portrait mode, (4) make the UI feel cohesive and modern to match the blur aesthetic.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### `NowPlayingScreen.kt` — full rewrite
|
||||
|
||||
**Typography swap**
|
||||
- Station name → `labelLarge`, uppercase, 3sp letter-spacing, 50% alpha (diminished label)
|
||||
- Artist/title → `headlineLarge`, bold (hero text)
|
||||
- "No track info" replaced with `♪` music note when no metadata
|
||||
- Artist-title separator changed from `-` to `—` (em dash)
|
||||
|
||||
**Palette extraction fix**
|
||||
- Replaced Coil 3 `ImageLoader.execute()` + `toBitmap()` (which was silently failing) with direct `OkHttp` + `BitmapFactory.decodeStream()` for reliable bitmap creation
|
||||
- Palette swatch fallback chain: dominant → vibrant → muted
|
||||
- Accent color picks light variants on dark backgrounds, dark variants on light
|
||||
- Added `animateColorAsState` with 600ms tween for smooth color transitions when artwork/tracks change
|
||||
- Bitmap properly recycled after palette extraction
|
||||
|
||||
**Portrait back button**
|
||||
- Added gradient scrim (45% black → transparent, 100dp tall) at top of artwork panel
|
||||
- Back icon uses `Color.White` for visibility over any artwork
|
||||
|
||||
**Modern UI refresh**
|
||||
- Transport controls: hero button (play/pause) is a filled accent circle (80dp) with contrasting icon; secondary buttons (skip, stop) are smaller plain icons
|
||||
- Settings section wrapped in a translucent `Surface` card with 16dp rounded corners
|
||||
- Timer section condensed to a single compact line with `·` separators, `bodyMedium` weight `Light`
|
||||
- Settings labels use `labelLarge`/`labelMedium` for clean hierarchy
|
||||
- Switch and slider colors fully themed to accent/text palette
|
||||
- Increased breathing room between sections (20–28dp spacers)
|
||||
- Stop button toned down to 50% alpha (not a bright red error color)
|
||||
|
||||
### `RadioApplication.kt`
|
||||
- Removed unused `ImageLoader` (palette now uses OkHttp directly)
|
||||
|
||||
### `gradle/libs.versions.toml` / `app/build.gradle.kts`
|
||||
- `palette-ktx` dependency added (previous commit, retained)
|
||||
|
||||
## Follow-up Items
|
||||
- Consider adding a gradient seam between artwork panel and metadata panel for smoother visual transition
|
||||
- May want to add haptic feedback on transport button taps
|
||||
38
chat-summaries/2026-03-10_12-20-blurred-bg-system-bars.md
Normal file
38
chat-summaries/2026-03-10_12-20-blurred-bg-system-bars.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Full-Screen Blurred Background, Larger Controls, Dynamic System Bars
|
||||
|
||||
## Task
|
||||
1. Larger transport buttons with more spacing
|
||||
2. Narrower settings widget with larger text
|
||||
3. Full-screen blurred album art background (replacing split artwork/metadata layout)
|
||||
4. Dynamic status bar and navigation bar coloring
|
||||
|
||||
## Changes Made
|
||||
|
||||
### `NowPlayingScreen.kt`
|
||||
|
||||
**Full-screen blurred background (#3)**
|
||||
- New `BlurredBackground` composable renders behind all content — loads artwork at 10x10 resolution via Coil, applies `cloudy(radius = 25)`, then overlays a 15% black dim
|
||||
- Falls back to `bgColor` (palette-extracted dominant color) when no artwork
|
||||
- Removed `ArtworkPanel` (which had its own blur + sharp art). Replaced with `ArtworkImage` (sharp art only) layered on top of the shared blurred background
|
||||
- Portrait: top half shows sharp artwork, bottom half shows metadata/controls — both over the shared blur
|
||||
- Landscape: left half shows sharp artwork, right half shows metadata/controls — both over the shared blur
|
||||
- Metadata section no longer has its own `background(bgColor)` since the blur is now the background
|
||||
|
||||
**Larger transport buttons + more spacing (#1)**
|
||||
- Hero button (play/pause): 80dp → 96dp, icon 48dp → 56dp
|
||||
- Secondary buttons (skip, stop): 56dp → 64dp, icon 30dp → 36dp
|
||||
- Spacing between buttons: 16dp → 28dp
|
||||
|
||||
**Narrower settings widget + larger text (#2)**
|
||||
- Settings card width capped at `widthIn(max = 320.dp)` instead of `fillMaxWidth()`
|
||||
- Label text style: `labelLarge` → `bodyLarge` (portrait), `labelMedium` → `bodyMedium` (landscape)
|
||||
|
||||
**Dynamic system bars (#4)**
|
||||
- `DisposableEffect` saves original status/nav bar colors and restores them on exit
|
||||
- `SideEffect` applies the animated `bgColor` (at 85% opacity) to both `window.statusBarColor` and `window.navigationBarColor`
|
||||
- `WindowInsetsControllerCompat` flips light/dark icon appearance based on background luminance
|
||||
- Works on API 28+ via the deprecated-but-functional Window color APIs
|
||||
|
||||
## Follow-up Items
|
||||
- The 15% dim on the blurred background is configurable — user may want to adjust
|
||||
- Could add edge-to-edge mode if targeting API 35+
|
||||
33
chat-summaries/2026-03-10_13-00-settings-panel.md
Normal file
33
chat-summaries/2026-03-10_13-00-settings-panel.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Settings Panel Implementation
|
||||
|
||||
## Task
|
||||
Added four new feature sections to the Settings screen: SomaFM quality preference, now-playing history file logging, reset customizations, and restart app. Also fixed a crash-on-launch bug on physical devices caused by missing `isHidden` column in seed data INSERT.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### Bug Fix
|
||||
- `SomaFmSeedData.kt`: Added `isHidden` column to the station INSERT for fresh installs (v4+ schema) to fix `SQLiteConstraintException` crash on real devices.
|
||||
|
||||
### New Files
|
||||
- `data/model/StationStream.kt` — Room entity for multi-stream quality options per station
|
||||
- `data/db/StationStreamDao.kt` — DAO for station stream queries
|
||||
- `service/StreamResolver.kt` — Resolves ordered list of stream URLs based on quality preference (per-station override or global)
|
||||
- `data/logging/NowPlayingHistoryWriter.kt` — Appends track changes to a file in CSV, JSON lines, or plain text format
|
||||
|
||||
### Modified Files
|
||||
- `data/model/Station.kt` — Added `qualityOverride: String?` column
|
||||
- `data/db/RadioDatabase.kt` — Added `StationStream` entity, `StationStreamDao`, bumped to v4, added `MIGRATION_3_4`
|
||||
- `data/db/SomaFmSeedData.kt` — Seeds `station_streams` table (2-4 rows per station: SSL/non-SSL x 128/256kbps), added `includeStreams` parameter, `seedStreamsForExistingStations()` for migration
|
||||
- `data/prefs/RadioPreferences.kt` — Added `qualityPreference`, `historyEnabled`, `historyFormat`, `historyDirUri` keys + setters
|
||||
- `RadioApplication.kt` — Exposed `streamResolver` and `historyWriter`, registered `MIGRATION_3_4`
|
||||
- `service/RadioPlaybackService.kt` — Uses `StreamResolver` for URL resolution, calls `historyWriter.append()` after metadata persist
|
||||
- `ui/screens/settings/SettingsScreen.kt` — Added SomaFM Quality (reorderable list), Now Playing History (toggle/format/dir picker), Reset (with optional station deletion), Restart App sections
|
||||
- `ui/screens/settings/SettingsViewModel.kt` — Added quality/history preference management, `resetCustomizations()` (DB transaction), `restartApp()` (graceful then forced)
|
||||
|
||||
### Design Doc
|
||||
- `docs/plans/2026-03-10-settings-panel-design.md`
|
||||
|
||||
## Follow-up Items
|
||||
- Per-station quality override UI (accessible from station edit dialog or long-press menu)
|
||||
- Stream fallback chain retry (currently uses first resolved URL; could try subsequent URLs on connection failure)
|
||||
- History file rotation/cleanup for large files
|
||||
@@ -0,0 +1,67 @@
|
||||
# Now Playing UX Improvements
|
||||
|
||||
**Date:** 2026-03-10
|
||||
|
||||
## Task Description
|
||||
|
||||
Five UX enhancements to the Now Playing screen in the Android 247 Radio app.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. Bouncing Marquee Text (`NowPlayingScreen.kt`)
|
||||
- Created `BounceMarqueeText` composable that measures text width vs container
|
||||
- If text fits: displays centered, stationary (single line)
|
||||
- If text overflows: animates horizontal translation with a bounce pattern (scroll to end, pause 1.5s, scroll back, pause 1.5s, repeat)
|
||||
- Scroll speed scales proportionally with overflow distance (18ms per dp)
|
||||
- Uses `rememberTextMeasurer` for unconstrained width measurement, `rememberInfiniteTransition` with `keyframes` for bounce animation
|
||||
- Both stroke and fill text layers share the same animated offset
|
||||
- Replaced multi-line wrapping in `TrackInfoSection` with single-line marquee
|
||||
|
||||
### 2. Timer/Latency Icons (`NowPlayingScreen.kt`)
|
||||
- Replaced the concatenated text string in `TimerSection` with a `Row` of icon+value pairs
|
||||
- Session elapsed: `Icons.Outlined.Schedule` icon
|
||||
- Connection elapsed: `Icons.Outlined.Wifi` icon
|
||||
- Latency: `Icons.Outlined.Speed` icon
|
||||
- Icons sized at 14dp, tinted with same dimmed color as text
|
||||
|
||||
### 3. Cross-Fade Metadata and Artwork (`NowPlayingViewModel.kt`, `NowPlayingScreen.kt`)
|
||||
- Added `displayMetadata` and `displayArtworkUrl` StateFlows to ViewModel
|
||||
- These are "committed" values that only update after artwork resolution completes
|
||||
- When metadata changes, artwork resolve runs in background while old metadata+art remains displayed
|
||||
- Once resolve completes, both metadata and artwork URL update atomically
|
||||
- Station changes trigger immediate display reset
|
||||
- Added `AnimatedContent` with 600ms fade transition around `ArtworkImage`
|
||||
- Screen now reads `displayMetadata` for track info instead of raw `playbackState.metadata`
|
||||
|
||||
### 4. Stream Quality Indicator (5 files)
|
||||
- `StreamConnection.kt`: Added `StreamInfo` data class (bitrate, ssl, contentType); parsed from ICY response headers (`icy-br`, `Content-Type`, URL scheme)
|
||||
- `AudioEngineEvent.kt`: Added `StreamInfoReceived` event
|
||||
- `AudioEngine.kt`: Emits `StreamInfoReceived` after connection opens
|
||||
- `PlaybackState.kt`: Added `streamInfo: StreamInfo?` to `Playing` and `Paused` states
|
||||
- `RadioPlaybackService.kt`: Handles `StreamInfoReceived` event, updates `Playing` state
|
||||
- `RadioController.kt`: Carries `streamInfo` to `Paused` state on pause
|
||||
- `NowPlayingScreen.kt`: Added `QualityBadge` composable showing bitrate, codec (MP3/AAC/OGG/FLAC), and SSL lock icon
|
||||
|
||||
### 5. Dominant Color White Handling (`NowPlayingScreen.kt`)
|
||||
- Added `.clearFilters()` to `Palette.Builder` chain before `.generate()`
|
||||
- Removes the default Palette filter that excludes near-white and near-black colors
|
||||
- White-dominant album art now correctly produces a white background with dark text
|
||||
|
||||
## Files Modified
|
||||
- `app/src/main/java/xyz/cottongin/radio247/ui/screens/nowplaying/NowPlayingScreen.kt`
|
||||
- `app/src/main/java/xyz/cottongin/radio247/ui/screens/nowplaying/NowPlayingViewModel.kt`
|
||||
- `app/src/main/java/xyz/cottongin/radio247/audio/StreamConnection.kt`
|
||||
- `app/src/main/java/xyz/cottongin/radio247/audio/AudioEngine.kt`
|
||||
- `app/src/main/java/xyz/cottongin/radio247/audio/AudioEngineEvent.kt`
|
||||
- `app/src/main/java/xyz/cottongin/radio247/service/PlaybackState.kt`
|
||||
- `app/src/main/java/xyz/cottongin/radio247/service/RadioPlaybackService.kt`
|
||||
- `app/src/main/java/xyz/cottongin/radio247/service/RadioController.kt`
|
||||
|
||||
## Verification
|
||||
- Build: `./gradlew compileDebugKotlin` -- exit 0 (only pre-existing deprecation warnings)
|
||||
- Tests: `./gradlew testDebugUnitTest` -- 56/56 pass, 0 failures
|
||||
|
||||
## Follow-up Items
|
||||
- Test on device: marquee animation with various track name lengths
|
||||
- Test on device: cross-fade timing with slow MusicBrainz lookups
|
||||
- Test on device: verify ICY headers (`icy-br`) are present for non-SomaFM stations
|
||||
Reference in New Issue
Block a user