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; }