3.8 KiB
3.8 KiB
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
BounceMarqueeTextcomposable 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
rememberTextMeasurerfor unconstrained width measurement,rememberInfiniteTransitionwithkeyframesfor bounce animation - Both stroke and fill text layers share the same animated offset
- Replaced multi-line wrapping in
TrackInfoSectionwith single-line marquee
2. Timer/Latency Icons (NowPlayingScreen.kt)
- Replaced the concatenated text string in
TimerSectionwith aRowof icon+value pairs - Session elapsed:
Icons.Outlined.Scheduleicon - Connection elapsed:
Icons.Outlined.Wifiicon - Latency:
Icons.Outlined.Speedicon - Icons sized at 14dp, tinted with same dimmed color as text
3. Cross-Fade Metadata and Artwork (NowPlayingViewModel.kt, NowPlayingScreen.kt)
- Added
displayMetadataanddisplayArtworkUrlStateFlows 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
AnimatedContentwith 600ms fade transition aroundArtworkImage - Screen now reads
displayMetadatafor track info instead of rawplaybackState.metadata
4. Stream Quality Indicator (5 files)
StreamConnection.kt: AddedStreamInfodata class (bitrate, ssl, contentType); parsed from ICY response headers (icy-br,Content-Type, URL scheme)AudioEngineEvent.kt: AddedStreamInfoReceivedeventAudioEngine.kt: EmitsStreamInfoReceivedafter connection opensPlaybackState.kt: AddedstreamInfo: StreamInfo?toPlayingandPausedstatesRadioPlaybackService.kt: HandlesStreamInfoReceivedevent, updatesPlayingstateRadioController.kt: CarriesstreamInfotoPausedstate on pauseNowPlayingScreen.kt: AddedQualityBadgecomposable showing bitrate, codec (MP3/AAC/OGG/FLAC), and SSL lock icon
5. Dominant Color White Handling (NowPlayingScreen.kt)
- Added
.clearFilters()toPalette.Builderchain 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.ktapp/src/main/java/xyz/cottongin/radio247/ui/screens/nowplaying/NowPlayingViewModel.ktapp/src/main/java/xyz/cottongin/radio247/audio/StreamConnection.ktapp/src/main/java/xyz/cottongin/radio247/audio/AudioEngine.ktapp/src/main/java/xyz/cottongin/radio247/audio/AudioEngineEvent.ktapp/src/main/java/xyz/cottongin/radio247/service/PlaybackState.ktapp/src/main/java/xyz/cottongin/radio247/service/RadioPlaybackService.ktapp/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