refactor: restructure src into player/views/ui subfolders; fix notification skip buttons
- Move player.js + nowplaying.js → src/player/ - Move views.js + spotify/soundcloud/youtube/abs → src/views/ - Move ui.js + components.js → src/ui/ - Update all import paths (static + lazy) across every file - MusicService: wrap ExoPlayer in ForwardingPlayer that always advertises next/prev commands so the notification skip buttons stay visible in radio mode even when the JS queue has only one native track Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
d8908c69e9
commit
308a27d293
11 files changed
+78
-54
No files matched your search
@@ -6,6 +6,7 @@ import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.media3.common.AudioAttributes
|
||||
import androidx.media3.common.C
|
||||
import androidx.media3.common.ForwardingPlayer
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.PlaybackException
|
||||
import androidx.media3.common.Player
|
||||
@@ -97,6 +98,28 @@ class MusicService : MediaSessionService() {
|
||||
.setWakeMode(C.WAKE_MODE_NETWORK) // holds WakeLock + WifiLock through Doze
|
||||
.build()
|
||||
|
||||
// ForwardingPlayer that always reports next/prev as available commands.
|
||||
// ExoPlayer drops those commands when its queue has only one item (single-track model),
|
||||
// which makes Media3 hide the skip buttons in the notification. JS still owns queue
|
||||
// logic — the MediaSession callback intercepts the commands and delegates to JS.
|
||||
val sessionPlayer = object : ForwardingPlayer(player) {
|
||||
override fun getAvailableCommands(): Player.Commands =
|
||||
super.getAvailableCommands().buildUpon()
|
||||
.add(Player.COMMAND_SEEK_TO_NEXT)
|
||||
.add(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)
|
||||
.add(Player.COMMAND_SEEK_TO_PREVIOUS)
|
||||
.add(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
|
||||
.build()
|
||||
|
||||
override fun isCommandAvailable(command: Int): Boolean = when (command) {
|
||||
Player.COMMAND_SEEK_TO_NEXT,
|
||||
Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM,
|
||||
Player.COMMAND_SEEK_TO_PREVIOUS,
|
||||
Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM -> true
|
||||
else -> super.isCommandAvailable(command)
|
||||
}
|
||||
}
|
||||
|
||||
// Tapping the notification opens the app directly to the Now Playing screen.
|
||||
val notifTapIntent = Intent(this, MainActivity::class.java).apply {
|
||||
putExtra("open_now_playing", true)
|
||||
@@ -107,7 +130,7 @@ class MusicService : MediaSessionService() {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
mediaSession = MediaSession.Builder(this, player)
|
||||
mediaSession = MediaSession.Builder(this, sessionPlayer)
|
||||
.setBitmapLoader(bitmapLoader)
|
||||
.setSessionActivity(notifTapPending)
|
||||
.setCallback(object : MediaSession.Callback {
|
||||
@@ -148,6 +171,7 @@ class MusicService : MediaSessionService() {
|
||||
// No play() — wait for JS to decide whether to auto-play
|
||||
}
|
||||
} catch (_: Exception) {}
|
||||
|
||||
}
|
||||
|
||||
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo) = mediaSession
|
||||
|
||||
Reference in new issue
Block a user