From 8e752fa89285b55b11ad42aa6aed4715c21af11f Mon Sep 17 00:00:00 2001 From: Honey Date: Sat, 9 May 2026 15:00:18 +0200 Subject: [PATCH] fix: player bar instant update + stop music on app close MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit player.js: move NSBridge.setQueue() into requestAnimationFrame so the updatePlayerUI DOM changes are committed and painted before the blocking @JavascriptInterface call. setQueue serializes a large JSON payload and blocks the JS/Blink main thread while Java parses it — deferring it one frame ensures the player bar visually updates immediately on tap. MusicService: onTaskRemoved now calls player.stop() + stopSelf() so swiping the app from recents stops playback instead of leaving it running in the background. Co-Authored-By: Claude Sonnet 4.6 --- .../main/kotlin/com/novasonic/app/MusicService.kt | 4 ++-- src/player.js | 13 ++++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/android/app/src/main/kotlin/com/novasonic/app/MusicService.kt b/android/app/src/main/kotlin/com/novasonic/app/MusicService.kt index cb5a1f3..1a3fe22 100644 --- a/android/app/src/main/kotlin/com/novasonic/app/MusicService.kt +++ b/android/app/src/main/kotlin/com/novasonic/app/MusicService.kt @@ -101,8 +101,8 @@ class MusicService : MediaSessionService() { override fun onGetSession(controllerInfo: MediaSession.ControllerInfo) = mediaSession override fun onTaskRemoved(rootIntent: Intent?) { - // Keep playing when swiped from recents; only stop if idle/empty - if (!player.playWhenReady || player.mediaItemCount == 0) stopSelf() + player.stop() + stopSelf() } override fun onDestroy() { diff --git a/src/player.js b/src/player.js index 28192ab..702ac64 100644 --- a/src/player.js +++ b/src/player.js @@ -244,9 +244,16 @@ export function playQueue(songs, startIdx) { const s = S.queue[S.qIdx]; if (s) { updatePlayerUI(s, true); onNpSongChange(s.id); } renderQueue(); - try { - window.NSBridge.setQueue(JSON.stringify(buildTrackList()), startIdx, true); - } catch (e) { console.warn('NSBridge.setQueue failed', e); } + // Build the track list now (before rAF) while S.queue is current. + const trackListJson = JSON.stringify(buildTrackList()); + // Defer the NSBridge call to AFTER the current render frame so the + // updatePlayerUI DOM changes are committed and visible before the + // @JavascriptInterface call (which blocks the JS thread while Java + // parses the JSON and creates MediaItems). + requestAnimationFrame(() => { + try { window.NSBridge.setQueue(trackListJson, startIdx, true); } + catch (e) { console.warn('NSBridge.setQueue failed', e); } + }); if (S.radioMode) fillRadioQueue(); return; }