Skip to content

13.14.1

Choose a tag to compare

@github-actions github-actions released this 15 Sep 15:06

Changelog

Changes in v13.14.0..HEAD:

Highlights

  • Android 8–11 Startup Crash Fixed — v13.14.0 could not start at all on Android 8 through 11 (including Android Automotive head units): a Class.getPackageName() call (API 31) sat in the startup preference flow and killed the process before any UI appeared. The namespace is now derived with Class.getName() (API 1). (Hush)
  • Download Crash Fixed on Android 8–12 — Intent.getParcelableExtra(name, Class) (API 33) was called unguarded at the start of every download. Now version-guarded, so downloading no longer throws on older devices. (Hush)
  • API-Level Regressions Are Now Release-Blocking — NewApi is fatal and abortOnError = true in both the app and the Waze shim, so lintVital (part of every release assemble) fails the build on any above-minSdk call instead of letting it reach a phone. (Hush)
  • Blocked Artists Enforced in the Playback Queue — blocking an artist now also removes their tracks from the queues the player actually builds (fresh queue, restored queue, radio/infinite, Together), not just the browse screens. (Hush)
  • Artwork Out-Of-Memory Fixed — notification, player and widget artwork is decoded at a bounded resolution with a bounds-only first pass, so a large upload no longer allocates tens of megabytes (and no longer allocates a second full-size copy). (Hush)

Crash Fixes (Android 8–11 / Automotive)

  • Class.getPackageName() (API 31) removed from the startup path — IconUtils/AppIconRepository now derive the manifest namespace from MainActivity::class.java.name and keep using context.packageName for the applicationId, so the dynamic icon alias resolves in debug (.debug suffix) and on Android 8–11. Bundle/dex verified: no getPackageName()Ljava/lang/String; remains and MainActivity survives R8. (Hush)
  • Typed getParcelableExtra (API 33) guarded — ExoDownloadService reads the Media3 download request with the typed overload on API 33+ and the single-argument overload below it. (Hush)
  • Receiver flags unified through ContextCompat — every registerReceiver call that passed RECEIVER_EXPORTED/RECEIVER_NOT_EXPORTED (API 33 constants, enforced from API 34) now goes through ContextCompat.registerReceiver, which masks the flags below API 33 and forwards them above it. Covers the app's Waze command receiver, the Bluetooth receiver, the canvas-unlock receiver, and the shim's WAZE_METADATA_UPDATE receiver. (Hush)

API-Level Guard (Build / CI)

  • NewApi is fatal — declared in app/lint.xml and the new waze-shim/lint.xml. Verified by injecting the exact defect that shipped (Uri::class.java.packageName): lintVitalGmsMobileUniversalRelease fails with Call requires API level 31, and passes once removed. (Hush)
  • abortOnError = true — with the previous false, even a fatal lint issue was only reported, which is how the API-31 call reached users. warningsAsErrors stays off, so warnings do not fail a build. (Hush)

Playback and Queues

  • Blocked artists filtered where the queue is built — restorePersistentQueue (app restart), startQueue, radio/infinite queue creation and refill, and the Together playback queue all apply the blocked-artist set, read from the database with an empty-set fallback that cannot break queue start. (Hush)
  • Index-correct filtering — Queue.Status.filterBlockedArtists goes through the same filterItems path as explicit/video filtering, so dropping a track before the selected one re-counts mediaItemIndex instead of starting playback on a different song. Covered by QueueBlockedArtistFilterTest (drop before, drop after, drop selection, drop everything, out-of-range index, empty no-op). (Hush)
  • Hide music videos unchanged — the existing filterVideo enforcement is untouched; blocked artists join the same chain rather than replacing it. (Hush)

Performance and Memory

  • Bounded artwork decoding — new BoundedBitmapDecode helper: bounds-only pass → inSampleSize → decode straight to software ARGB_8888. The full-resolution bitmap is never allocated, and the extra full-size copy the old code made is gone. Wired into NotificationArtworkLoader, CoilBitmapLoader (Media3 BitmapLoader) and MusicServiceWidgetUpdater's dominant-colour extraction (now sampled at 256 px and recycled). (Hush)

Equalizer

  • Active-profile recomposition fix — AxionEqScreen read activeProfileId.value inside composition (lint StateFlowValueCalledInComposition), so the selected profile did not update the UI. It now collects the flow as state. (Hush)

Build / CI

  • TV release build fixed — the Waze shim archive was produced into app/src/main/assets, which every variant merges, so assembleGmsTvUniversalRelease failed Gradle's implicit-dependency validation when built alongside the mobile variants. The copy now targets only the mobile flavor's asset directory (the runtime reads the shims from this archive, and WAZE_SUPPORTED is false on TV). Verified with all three CI release variants in one invocation. (Hush)

SpotiFLAC

  • Provider stall watchdog no longer kills healthy downloads — the 25s attempt ceiling was measured from the attempt start, so a provider that was streaming a large lossless file was abandoned mid-transfer and then recorded as stalled and demoted for the full 2-minute cooldown. On device it reported itself as timed out without progress for 29ms — 29 ms after bytes had arrived. The ceiling is now counted from the last real byte transfer (only a growing byte count counts, so a provider re-emitting its size still cannot look alive), the abort reason is captured when it fires, and the reported silence matches the trigger that fired. (Hush)
  • Playback cache identity is stable across call sites and queue restores — MediaMetadata.durationMs reads 0 when an item carries no duration (the norm for a restored queue), and a plain fallback chain never reached the database behind it, so 0 became part of the track's identity. Measured on device for one song: the file was downloaded under Africa|Toto||0 and the next lookup computed Africa|Toto||272000, missed the cache, and re-downloaded a track already on disk. A single bestDurationMs helper now takes the first known duration across the queue item, what playback recorded, and the database. (Hush)
  • Second-chance lookup by media id — when the identity key has no entry, the media-id index still knows which file this queue item resolved to, so a drifted identity serves the existing file instead of re-downloading it. Verified on device: cache miss key=00d73932… followed by cache hit by mediaId index key=df62ab79… and no download. (Hush)
  • Quality bucket guard on that lookup — it has no quality component in its key, so entries now record the bucket they were fetched for: a hi-res request is never answered with the lossless copy on disk, while an entry written before the field existed still satisfies the default lossless request. (Hush)

Housekeeping

  • Version bumped to 13.14.1 (versionCode 171, app + waze-shim). (Hush)
  • SpotiFLAC verified end-to-end on a real device: six provider extensions installed, four gateway sessions self-renewing without a manual check, lossless FLAC files served from deezer, qobuz-web and tidal-web (codec=flac bits=16 rate=44100), correct SpotiFLAC • <source> • cached labels, and cached replay served ahead of the Media3 cache with no re-download. (Hush)
  • Unit tests pass (328 tests, all green), including new suites for the blocked-artist queue filter, the provider stall policy, and the playback cache identity. The device hashes behind the cache drift are pinned in the cache key test, so the evidence stays attached to the code that has to avoid it. (Hush)
  • Lint: 0 errors and 0 NewApi findings in app and waze-shim; all three CI release variants assemble successfully with the configuration cache enabled. (Hush)

Upstream credits

Hush is built on ArchiveTune and combines features, fixes, and UI from several open-source YouTube Music clients—including Metrolist, Vivi Music, and Echo Music. Those projects are credited below; their licenses and copyright notices are preserved in source.

Project Repository
ArchiveTune ArchiveTuneApp/ArchiveTune
Metrolist metrolistgroup/metrolist
Vivi Music vivizzz007/vivi-music
Echo Music EchoMusicApp/Echo-Music
SpotiFLAC spotiflacapp/SpotiFLAC-Mobile
Zemer Cipher ZemerTeam/zemer-cipher

Thank you to the maintainers and contributors of every project listed above.