feat(listening): local listening history + Last.fm scrobbler (v0.3.5-beta.2)
Lays the foundation for Spotify-independent recommendations. Every play
that crosses the Last.fm convention threshold (≥30s OR ≥50% of duration,
floored at 30s and capped at 4min) writes a ListeningEventEntity to
Room. Events feed the (optional) Last.fm scrobbler and the (future)
Stash Mixes engine.
Last.fm integration (web-auth flow):
- LastFmApiClient (core/data/lastfm): auth.getToken, auth.getSession,
track.scrobble. Every write signed per Last.fm's md5-sorted-params
convention. - LastFmSessionPreference: DataStore-backed (username, sessionKey).
- LastFmScrobbler: @singleton, app-scoped; drains unscrobbled events
whenever a new listening event is recorded. Skips gracefully when the
API key isn't configured at build time. - ListeningRecorder (core/media/listening): observes PlayerRepository,
writes an event once the threshold delay elapses without a track
change. Cancelled + restarted on track transitions.
Settings UI:
- New "Last.fm" section (between Audio Effects and Storage).
- NotConfigured: explains the developer setup step.
- Disconnected: Connect button → fetches token → opens browser to
last.fm/api/auth with the user's default handler. - AwaitingAuth: "Finish connecting" button → exchanges token for
session key once user has clicked Allow on Last.fm's website. - Connected: shows username + pending-scrobble count + Disconnect.
Build setup:
- BuildConfig fields LASTFM_API_KEY / LASTFM_API_SECRET sourced from
local.properties (gitignored) or env vars. - release.yml passes the same env vars through, sourced from optional
GitHub Secrets (LASTFM_API_KEY / LASTFM_API_SECRET). Missing secrets
ship an APK that builds fine but shows "Not configured."
Schema: Room v7→v8 adds listening_events(id, track_id, started_at,
scrobbled) with FK to tracks + indexes on track_id, started_at,
scrobbled. Migration is additive; no data loss.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Install
Download the .apk below and open it on your Android device. If you've got a previous Stash release installed, this upgrades in place — your library, sync history, and settings are preserved.
Requires: Android 8.0 (API 26) or newer.
See the README for first-time setup.
Full Changelog: v0.3.5-beta.1...v0.3.5-beta.2