Commit graph

27 commits

Author SHA1 Message Date
8919596ac2 New app icon and launch screen from "SyncWave Sv2.png"
- Adaptive icon: the glossy purple S logo, whole image at 94% so the S sits
  inside the 33dp safe zone (it reached 34.6dp untouched), edge blended into
  the new #0E0B14 background that matches the image's border.
- Themed (monochrome) icon: the S as a solid silhouette.
- Launch screen: only the S on #0E0B14, so the round splash crop shows no
  edge of the textured square.
- tools/make_icons.py regenerates all of these from the source PNG.

Verified on the emulator: icon in the app drawer and the launch screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 08:58:03 -04:00
5564ef2eb3 Skip songs that can't be played instead of stopping
Skipping through songs could leave the player stopped with Next/Previous
only changing the title. Cause: a song whose file is missing on the
server; Navidrome answers /rest/stream with a JSON error marked HTTP 200,
which was cached as the song, so the player failed to read it and stopped.

- Song downloads with a text/JSON/XML Content-Type are refused, so server
  error messages are never cached or played.
- SkipUnplayable: on a playback error, remove the song's cached copy,
  show a short note and skip to the next song (waits for the rest of the
  queue if it is still arriving; at most 3 in a row; not on network errors).
- Next/Previous re-prepare a stopped player.

Verified on the emulator against the owner's server: the broken song is
skipped with a note, the bad cached copy is removed and not re-saved,
10 rounds of rapid next/previous never left the player stopped, and after
an offline failure one tap on Next plays again. 84 unit tests pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 07:51:51 -04:00
1308fb67c7 Now Playing: star and menu, live lyrics, Up Next card
- Star and ⋮ beside the title. The star is the same one as in the queue
  view. The menu (also used by the queue view) has Add to playlist (own
  playlists or a new one), Go to album, Go to artist, Share, and a Sleep
  timer (15/30/45/60 minutes with a short fade, or end of song) that runs
  next to the player in the playback service.
- Live lyrics between the artist and the seek bar, from Navidrome
  (getLyricsBySongId, then getLyrics; .lrc text parsed too): the current
  line bright, the next one faded, sliding as the song moves. Tap for a
  full-screen view that follows the song and seeks when a line is tapped.
  Plain lyrics show a Lyrics button; no lyrics hides the area. Loaded in
  the background and cached.
- Up Next card at the bottom (next song, or Autoplay); tap opens the queue.
- AdaptiveColumn keeps everything below the cover and gives things up in
  order (lyrics to one line, Up Next card, live line, icon row) so nothing
  overlaps; on short screens the cover shrinks and centres.

Checked on the emulator at 1080x2400, 1080x1800 and sideways. 77 unit
tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:14:18 -04:00
bfc39d9334 Artwork colors follow the whole cover, not a small accent
Background colors for album pages and Now Playing used Palette's most
vibrant swatch even when it covered only a sliver of the cover, so
Recovery got red from the small cross in its logo. Now colors covering
under 6% of the cover are ignored and the rest are ranked by area, with a
bonus for colorfulness so a bold colored area still beats plain black.
The gradient's top and bottom use the same hue.

Checked on the emulator: Recovery is slate grey, Hot Fuss steel blue,
Dark Sun still a dusky purple. 4 new unit tests (71 total).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:46:25 -04:00
0f26624e60 Now Playing: crisp cover edge instead of a fade
The cover keeps its full edge-to-edge size but no longer fades into the
background colour: it ends in a hard edge with a thin light line between
the art and the title area (bottom when upright, right side when
sideways). The darkening that keeps the white text readable now starts
below / beside the cover, so the art itself isn't dimmed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:42:43 -04:00
ca5ebd5b03 Play ALAC songs: FFmpeg audio decoder fallback
Android has no Apple Lossless (ALAC) decoder, so ALAC .m4a files (for
example Eminem's Recovery) "played" with the seek bar moving but no
sound: the player had no decoder for the audio track.

Adds Jellyfin's prebuilt Media3 FFmpeg decoder (GPL-3.0) and turns on
extension renderers in fallback mode, so Android's own decoders are still
used for FLAC, MP3, AAC and so on, and FFmpeg only steps in for formats
Android can't decode.

Checked on the emulator: "25 to Life" now produces a 44.1 kHz audio
track (FFmpeg), FLAC still uses c2.android.flac.decoder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:37:51 -04:00
ae6da9f407 Scrobbling with offline queue; Home tab with album rows
Scrobbling (the app never reported plays before):
- Scrobbler listens to the player inside PlaybackService, so plays from
  the app, notification, lock screen and with the screen off all count.
- "Now playing" (submission=false) when a song starts playing; a full
  scrobble (submission=true) once it has actually played half its length
  or 4 minutes, at most once per play (ListenTracker, unit-tested).
- Listens are saved on the phone first with their start time, sent in
  batches, and resent when the network returns or at the next app start.
- Deezer previews are never reported.
- Checked on the emulator: now playing sent, listen kept while offline
  with its original time, sent 7 s after reconnecting, preview silent.

Home tab (now the start tab):
- Recently Played, Recently Added, Most Played, Favorites (starred albums
  and songs) and Rediscover (random, fresh each time Home opens) rows,
  each with See all. Large shrinking title, pull-to-refresh, rows load in
  parallel behind placeholder cards and hide when empty.
- Rows are defined in HomeSections, so new ones (AI mixes) are one entry.

67 unit tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:30:20 -04:00
3c99e2f8c0 Search tab: library + Deezer results, previews, recent searches
- Search as you type (waits 350 ms after the last key), library via
  OpenSubsonic search3 and Deezer's public API at the same time.
- Empty field shows recent searches: tap to repeat, swipe or X to remove,
  Clear all. A search is saved when you press Search or tap a result.
- In your library: Top result card, Songs / Albums / Artists with See all
  (paged), tapping and long-press menus as elsewhere.
- Not in your library: Deezer songs and albums minus what you already own
  (MusicMatch: artist + title ignoring case, accents, punctuation and
  "feat." parts). Songs play their 30-second preview in the normal player
  with a "Preview · not in your library" label; previews can't be starred,
  don't trigger Autoplay and are left out of queue History. Album pages list
  each track as a preview.
- "Play full song" / "Download" on every outside result are disabled with
  a coming-soon note through OutsideMusicActions, so a later server feature
  can switch them on; Deezer track/album/artist ids and ISRC stay attached.
- Deezer unreachable: library results still show with a small note.
  Friendly "No results" state.
- 17 new unit tests (56 total).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 17:55:29 -04:00
f5b30cc8e6 New app icon and launch screen from syncwave-icon-1024.png
- Adaptive icon: the whole PNG, untrimmed, is the foreground (mipmap PNGs per
  density) on a solid #080812 background. The wave reaches 32.5dp from the
  centre of the 108dp layer, inside the 33dp safe zone, so no mask crops it.
- Themed (monochrome) icon: a white brightness mask of the wave.
- Launch screen: a splash-only copy where the grey spotlight behind the wave
  fades to #080812, so the round splash crop shows just the wave and its glow
  instead of a lit disc with hard edges.
- Removed the old SW logo (syncwave-logo.svg and its vector drawables).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:41:25 -04:00
521693b3b8 Queue view over Now Playing, Autoplay, favourites
- Swipe up on the full-art Now Playing (or tap the queue button) to slide
  up the queue view; swipe down or the queue button returns to the art.
  Swiping down from the art still closes Now Playing.
- Queue view, top to bottom: current song with star (favourite on the
  server) and more menu; Shuffle / Repeat / Autoplay pills; History of
  played songs with Clear; Up Next (drag handle to reorder, swipe left to
  remove); seek bar; previous / glass play pill / next; Lyrics, Cast and
  queue toggle. Background from the album art.
- Autoplay (on by default, remembered): when the last song in the queue
  starts, the playback service adds similar songs (getSimilarSongs) or,
  failing that, random ones. Works with the app closed.
- The full-art screen's bottom row is now Lyrics / Cast / Queue (shuffle
  and repeat moved to the queue view). Lyrics and Cast say "coming soon".
- Go to album / artist from a menu now closes Now Playing first.
- Queue entries are built by one shared MediaItemFactory (controller and
  service), carrying album, artist, cover and favourite details.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:32:29 -04:00
d974862745 Songs: fewer simultaneous requests, quiet retries, friendly errors
- Songs view sends at most two song requests at a time (scrolling, A–Z
  jumps and queue building share them), instead of bursts.
- All server requests that are safe to repeat (GET) are retried twice,
  after 0.6 s and 1.8 s, when the server answers 502/503/504 or the
  connection drops; Retry-After is honoured up to 3 s. Timeouts and
  cancelled requests are not retried.
- Plain error messages: e.g. "Your music server is busy or restarting.
  Please try again in a moment." instead of "HTTP 503".
- Failed responses are logged (tag SyncWaveHttp: method, path and code
  only, never credentials) to diagnose future errors.
- Unit tests for the retry behaviour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:18:27 -04:00
bac1965654 Now Playing: skip starts playback, big fitted title, glass play pill
- Skipping to the next or previous song while paused now starts playing
  it, from the app, mini player, notification and lock screen alike: the
  media session wraps the player in PlayOnSkipPlayer. Verified on the
  emulator with Now Playing, the mini player and system media keys.
- Title is much bigger and uses the full width: it shrinks from 32 sp to
  22 sp to fit in up to two lines, and only then scrolls slowly sideways
  on one line. Artist name one size larger.
- Play/pause is a purple frosted-glass pill: a blurred copy of the cover
  (Android 12+) under translucent brand purple, a soft light edge, a
  white icon, and a springy squish when pressed. Other buttons unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:06:00 -04:00
8733f8d6c7 3c: long-press menus, Up Next queue, shuffle and repeat
- Long-press a song, album or artist for Play next, Add to queue, Go to
  album, Go to artist, and Shuffle (albums, artists). A confirmation pill
  ("Added to queue") shows immediately; album/artist songs load behind it.
- Up Next over Now Playing: drag handle to reorder, swipe left to remove,
  tap to play. Reorders and removals go straight to the player.
- Shuffle (reorders upcoming songs; off restores their order) and repeat
  (off / all / one) buttons on Now Playing.
- Builds and unit tests pass; not yet tested on the emulator or a phone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:37:20 -04:00
1340f4dba0 3b: smoother scrolling in long lists
- Artist list rows request 160 px photos instead of 600 px (52 dp
  circles); the artist page still uses the large photo.
- Lists declare content types (song, artist, header, track, album) so
  rows scrolled off screen are reused instead of rebuilt.
- Measured while flinging 12 times on the emulator (debug build), janky
  frames before → after: Songs 2.1% → 0.8%, Artists 0.85% → 0.7%,
  Albums grid 0.85% → 0%.

Images already load at their display size and come from Coil's memory
and 250 MB disk caches. Large titles and the album-open transition were
done in earlier 3b work. The Now Playing pause shrink stays off: it
doesn't fit the full-screen artwork layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:06:44 -04:00
4264b0c910 Faster playback start: small start buffer, raw files, cache, preload
Measured tap → first sound on the emulator against the Navidrome demo
(see PlaybackTrace, `adb logcat -s SWTiming`):

                               before      after
  album, same song replayed    1.15-1.8 s  0.19-0.24 s  (now from cache)
  Next button                  -           0.24 s       (next song preloaded)
  Songs view: wait before      0.65-0.8 s  0 ms         (queue fills in background)
  the audio request even went
  album, never-played FLAC     1.7 s       1.76 s       (unchanged: download of
                                                        the file's start)

- ExoPlayer starts after 250 ms of audio (default 2.5 s) and keeps a
  30-120 s buffer while playing; 1.5 s after a stall.
- Stream URLs ask for format=raw (no on-the-fly transcoding).
- Tapped song is sent to the player first; the rest of the queue follows
  (songs before it are inserted in front). Songs view fetches the next 99
  in the background instead of before playing.
- Next song preloads (10 s) for instant, gapless track changes.
- 512 MB on-phone LRU audio cache (Media3 SimpleCache); notification
  artwork now comes from Coil's memory/disk cache instead of a download.
- Mini player and Now Playing switch to the tapped song immediately; a
  spinner ring appears around play/pause only if audio takes > 0.4 s.
- PlaybackTrace logs each step (tap, service, HTTP, buffering, decoder,
  first sound) for future measurements.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:03:10 -04:00
9cefca9c8f SyncWave logo as app icon and launch screen
- syncwave-logo.svg converted to vector drawables: a full-colour adaptive
  icon foreground (the three strokes, scaled so the farthest stroke edge
  sits inside the 66dp safe zone) on a solid #15132a background.
- Monochrome layer for Android themed icons: both strokes in one colour,
  without the background-coloured gap stroke.
- Removed the template's green Android icon and per-density webp files;
  minSdk 28 uses the adaptive icon everywhere it's supported.
- Launch screen via androidx.core:core-splashscreen 1.2.0: the logo on
  #15132a on every Android version, held until the saved login is read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 19:21:03 -04:00
885ee47d77 Album page details from file tags
- Detail line under title and artist: genre · year · LOSSLESS badge for
  FLAC/ALAC (or format · bitrate for lossy files).
- Shuffle, large Play and Download buttons. Download shows a "coming soon"
  toast until offline downloads exist.
- Disc headings (with disc titles when tagged) for multi-disc albums.
- Info block after the tracks: release date (day, month or year as
  tagged), songs and running time, record label, and file quality
  (format, bit depth / sample rate when available).
- Everything comes from getAlbum tags; anything missing is hidden. ALAC vs
  AAC inside .m4a is told apart by bitrate, since the codec isn't reported.
- Long durations now round to the nearest minute (a 4:02 album said 5 min).
- AlbumFacts unit tests; CLAUDE.md updated with today's changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:48:49 -04:00
928cecfe9d Library tab redesign: Downloaded, larger rows, Recently Added
- Categories are now Playlists, Artists, Albums, Songs and Downloaded.
  Genres is removed from the list; its screens stay in the code, unlinked.
- Downloaded opens an empty state until offline downloads are built.
- Larger category rows: 32dp icons, title-size labels, 68dp rows.
- Recently Added below the list: a two-column grid of the 20 newest
  albums (getAlbumList2 "newest") with large covers, name and artist;
  tapping one opens its album page with the flying-cover transition.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:41:32 -04:00
aee2c28d66 Now Playing: full-screen album art behind everything
- The cover fills the screen edge to edge behind all controls: full width
  from the very top when upright, full height on the left when sideways.
  Never cropped; a soft shade at the top keeps the status bar and grabber
  readable.
- A dark gradient over the lower part (right side when sideways) keeps
  the white title, artist, seek bar and controls readable.
- Cover-color tint fills everything the art doesn't cover. It matches the
  art's faded edge exactly and only deepens beyond it, so there's no seam.
- Dropped the pause/play shrink and the blurred backdrop: a shrinking
  full-screen background would break the edge-to-edge look.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:39:20 -04:00
8dcd6f2ebf Library upgrade: Playlists, Artists, Albums, Songs and Genres
- Library tab opens on a list of categories, each opening its own view.
- Artists: A–Z under the server's letter headings, with artist photos
  (initial on a purple circle as fallback). Artist page: photo, Play all,
  albums in a horizontal row (covers fly to the album page), and every song
  on their albums.
- Albums: the grid, now with a Sort menu (Title, Artist, Year newest
  first, Recently added). Also reused for a genre's albums.
- Songs: every song A–Z, fetched 100 at a time wherever you scroll, with
  same-size placeholders and an A–Z fast-scroll bar (letter bubble,
  haptic ticks). Letter jumps binary-search single positions instead of
  downloading the whole list, remembering what they've seen. Tapping a
  song queues it plus the next 99.
- Playlists: Navidrome playlists, each with cover, length, Play and its
  songs in playlist order. Genres: genres with album/song counts, each
  opening its album grid.
- Data: new OpenSubsonic calls (getArtists, getArtist, getPlaylists,
  getPlaylist, getGenres, more getAlbumList2 options). The title-sorted song
  list uses Navidrome's own REST API (OpenSubsonic can't sort songs), kept
  in its own client with JWT login and automatic re-login.
- Shared UI pieces: large title with back button, song rows, album cards,
  load/retry handling, cover-URL and playback hooks for screens.
- Tests for the letter index, new API parsing and the Navidrome login flow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:39:38 -04:00
49319832e5 Fix landscape: Now Playing controls off screen, album cover too tall
- Now Playing switches to a side-by-side layout when the screen is wider
  than tall: cover on the left sized to the height, title, seek bar and
  previous/play/next on the right. Previously the cover filled the height
  and pushed every control below the screen edge.
- Upright, the cover now shrinks when space is short (split-screen, small
  phones) so the controls always fit; unchanged on normal phones.
- Album page cover is capped at 55% of the screen height (cropped to a
  wide band when sideways) so the title and tracks stay visible; the
  collapsing top bar starts fully hidden on these shorter headers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:10:22 -04:00
49aa680ada Full-width album header with fading cover and parallax
- Album pages open with the cover full-width and edge to edge (up behind
  the status bar, no border or rounded corners). Its bottom fades into the
  album's background color, with title, artist and year over the fade.
- Gentle parallax: the cover scrolls at 65% of the list's speed. The fade
  stays fixed while the cover slides under it, so no hard edge appears.
- Floating round back button over the cover; a solid album-colored bar
  with the album name fades in, fully opaque by the time the big title
  reaches it.
- Page background stays the solid album color down to the cover's bottom
  and only deepens below it, safe on any screen height.
- Header loads a 1000px cover and shows the grid's cached 400px cover until
  it arrives, so the flying cover never lands on a blank square.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:02:57 -04:00
4bbe0b2d70 Smooth collapsing titles, flying album covers, softer Now Playing art
- Large titles on Home, Library and Search are one title that scales from
  34sp to 20sp and glides into the top bar while scrolling (replacing
  Material's two-title cross-fade); the bar still settles fully open or
  collapsed. Actions stay in the top row.
- Opening an album: the tapped cover flies from the grid to the album page
  (shared-element transition on a spring) while the pages cross-fade, and
  flies back on return. The album page shows its title and cover at once
  (passed from the grid) while tracks load; the top bar title only fades
  in after the header scrolls away.
- Tab switches fade in 150 ms instead of the 700 ms default.
- Now Playing art: shrinks to 84% when paused and springs back with a
  single soft overshoot; its shadow lowers and lifts with it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:53:10 -04:00
fdb79673cf Make purple the brand color; color album pages and Now Playing from cover art
- BrandPurple (#6A4DF4, #A693FF on dark backgrounds) is the official accent
  for all menus, tabs and lists, also applied to the Android window theme
  (launch window, system-drawn accents) in light and dark. Template colors
  removed. Dynamic (wallpaper) color stays off.
- Album pages and Now Playing take their background gradient from the
  album cover via AndroidX Palette. Colors are darkened only as needed for
  white text to meet WCAG 4.5:1, cached per cover, and fade in. Those
  screens use ArtworkTheme: white text, light brand purple accents.
- Status bar icons turn light on artwork-colored album pages.
- Unit tests for the color/contrast math.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:41:25 -04:00
9d8ee6d717 Add tab layout, floating mini player and Now Playing sheet
- Bottom tab bar (Home, Library, Search) with custom icons and a springy
  press effect. Each tab keeps its own history; tapping the current tab
  returns to its top screen. Library (album grid) is the start tab; Home
  and Search are placeholders. Sign out moves to Home.
- SyncWave theme: neutral surfaces with an electric-violet accent in light
  and dark, bold large titles that collapse on scroll. Wallpaper-based
  dynamic color is off.
- Floating mini player just above the tab bar on every screen; content
  scrolls underneath both bars.
- Now Playing sheet slides up on a spring from the mini player: large art
  (shrinks with a bounce when paused), title, artist, custom seek bar with
  elapsed/remaining time, previous/play-pause/next. Swipe down (follows the
  finger, closes on distance or flick) or Back to close; the screen behind
  recedes into a rounded card. Blurred-art backdrop on Android 12+.
- PlaybackController gains seek, previous, position and duration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:27:00 -04:00
560ef40624 Add album page, Media3 playback and mini player
- Tapping an album opens its page (cover, details, track list in disc/track
  order); tapping a track plays it with the rest of the album queued after.
- PlaybackService (Media3 MediaLibraryService + ExoPlayer) plays in the
  background and with the screen off, with notification and lock-screen
  controls, audio focus, and pause on headphone unplug. Exported with the
  MediaLibraryService/MediaBrowserService filters for Android Auto later;
  library browsing returns an empty root until then.
- Streams and notification artwork load through the authenticated OkHttp
  client, so credentials never appear in URLs. The session attaches stream
  URLs from media ids.
- Mini player (artwork, title, play/pause, next) above the navigation bar
  on every screen; the playing track is highlighted on the album page.
- Signing out stops playback and clears the queue.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:08:03 -04:00
99b57f0162 Add Navidrome login and album grid
- Login screen (server URL, username, password) that verifies against the
  server with an OpenSubsonic ping before saving.
- Credentials stored encrypted with an Android Keystore AES-GCM key in
  DataStore, excluded from backups. Subsonic token auth (salted md5), so the
  password is never sent.
- Album grid with cover art (Coil), A-Z, paged 100 at a time, pull to
  refresh, sign out.
- Layered packages (model, data/auth, data/subsonic, data/library, ui) with
  a manual AppContainer, ready for Media3, Android Auto and the brain service.
- Kotlin 2.4.10 and current AndroidX/OkHttp/Retrofit; fix stray "cla" typo
  in the template MainActivity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:50:38 -04:00