diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainActivity.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainActivity.kt index 24d63de..13fc7a0 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainActivity.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainActivity.kt @@ -122,9 +122,7 @@ class MainActivity : ComponentActivity() { LocalColorPalette provides colorPalette, LocalShimmerTheme provides shimmerTheme, LocalTypography provides rememberTypography(colorPalette.text), - LocalYoutubePlayer provides rememberYoutubePlayer(mediaControllerFuture) { - it.repeatMode = preferences.repeatMode - }, + LocalYoutubePlayer provides rememberYoutubePlayer(mediaControllerFuture), LocalMenuState provides rememberMenuState(), LocalHapticFeedback provides rememberHapticFeedback() ) { diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/services/PlayerService.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/services/PlayerService.kt index 2639574..a4dc1fe 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/services/PlayerService.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/services/PlayerService.kt @@ -56,6 +56,8 @@ val GetCacheSizeCommand = SessionCommand("GetCacheSizeCommand", Bundle.EMPTY) val DeleteSongCacheCommand = SessionCommand("DeleteSongCacheCommand", Bundle.EMPTY) +val SetSkipSilenceCommand = SessionCommand("SetSkipSilenceCommand", Bundle.EMPTY) + @ExperimentalAnimationApi @ExperimentalFoundationApi class PlayerService : MediaSessionService(), MediaSession.MediaItemFiller, @@ -70,6 +72,8 @@ class PlayerService : MediaSessionService(), MediaSession.MediaItemFiller, private lateinit var cache: SimpleCache + private lateinit var player: ExoPlayer + private lateinit var mediaSession: MediaSession private lateinit var notificationManager: NotificationManager @@ -90,7 +94,7 @@ class PlayerService : MediaSessionService(), MediaSession.MediaItemFiller, val cacheEvictor = LeastRecentlyUsedCacheEvictor(preferences.exoPlayerDiskCacheMaxSizeBytes) cache = SimpleCache(cacheDir, cacheEvictor, StandaloneDatabaseProvider(this)) - val player = ExoPlayer.Builder(this) + player = ExoPlayer.Builder(this) .setHandleAudioBecomingNoisy(true) .setWakeMode(C.WAKE_MODE_LOCAL) .setMediaSourceFactory(DefaultMediaSourceFactory(createDataSourceFactory())) @@ -102,10 +106,11 @@ class PlayerService : MediaSessionService(), MediaSession.MediaItemFiller, true ) .build() - .also { player -> - player.playWhenReady = true - player.addAnalyticsListener(PlaybackStatsListener(false, this)) - } + + player.repeatMode = preferences.repeatMode + player.skipSilenceEnabled = preferences.skipSilence + player.playWhenReady = true + player.addAnalyticsListener(PlaybackStatsListener(false, this)) mediaSession = MediaSession.Builder(this, player) .withSessionActivity() @@ -146,9 +151,9 @@ class PlayerService : MediaSessionService(), MediaSession.MediaItemFiller, override fun onDestroy() { if (preferences.persistentQueue) { - val mediaItems = mediaSession.player.currentTimeline.mediaItems - val mediaItemIndex = mediaSession.player.currentMediaItemIndex - val mediaItemPosition = mediaSession.player.currentPosition + val mediaItems = player.currentTimeline.mediaItems + val mediaItemIndex = player.currentMediaItemIndex + val mediaItemPosition = player.currentPosition Database.internal.queryExecutor.execute { Database.clearQueuedMediaItems() @@ -163,7 +168,7 @@ class PlayerService : MediaSessionService(), MediaSession.MediaItemFiller, } } - mediaSession.player.release() + player.release() mediaSession.release() cache.release() super.onDestroy() @@ -183,6 +188,7 @@ class PlayerService : MediaSessionService(), MediaSession.MediaItemFiller, .add(StopRadioCommand) .add(GetCacheSizeCommand) .add(DeleteSongCacheCommand) + .add(SetSkipSilenceCommand) .build() val playerCommands = Player.Commands.Builder().addAllCommands().build() return MediaSession.ConnectionResult.accept(sessionCommands, playerCommands) @@ -205,8 +211,8 @@ class PlayerService : MediaSessionService(), MediaSession.MediaItemFiller, ).let { coroutineScope.launch(Dispatchers.Main) { when (customCommand) { - StartRadioCommand -> mediaSession.player.addMediaItems(it.process().drop(1)) - StartArtistRadioCommand -> mediaSession.player.forcePlayFromBeginning(it.process()) + StartRadioCommand -> player.addMediaItems(it.process().drop(1)) + StartArtistRadioCommand -> player.forcePlayFromBeginning(it.process()) } radio = it } @@ -221,6 +227,9 @@ class PlayerService : MediaSessionService(), MediaSession.MediaItemFiller, cache.removeResource(videoId) } } + SetSkipSilenceCommand -> { + player.skipSilenceEnabled = args.getBoolean("skipSilence") + } } return super.onCustomCommand(session, controller, customCommand, args) @@ -241,9 +250,9 @@ class PlayerService : MediaSessionService(), MediaSession.MediaItemFiller, override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) { radio?.let { radio -> - if (mediaSession.player.mediaItemCount - mediaSession.player.currentMediaItemIndex <= 3) { + if (player.mediaItemCount - player.currentMediaItemIndex <= 3) { coroutineScope.launch(Dispatchers.Main) { - mediaSession.player.addMediaItems(radio.process()) + player.addMediaItems(radio.process()) } } } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/PlayerSettingsScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/PlayerSettingsScreen.kt index 4bce1a2..2dcbdcf 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/PlayerSettingsScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/PlayerSettingsScreen.kt @@ -9,13 +9,16 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp +import androidx.core.os.bundleOf import it.vfsfitvnm.route.RouteHandler import it.vfsfitvnm.vimusic.R +import it.vfsfitvnm.vimusic.services.SetSkipSilenceCommand import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.screens.* import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalTypography import it.vfsfitvnm.vimusic.utils.LocalPreferences +import it.vfsfitvnm.vimusic.utils.LocalYoutubePlayer import it.vfsfitvnm.vimusic.utils.semiBold @ExperimentalAnimationApi @@ -43,6 +46,7 @@ fun PlayerSettingsScreen() { val colorPalette = LocalColorPalette.current val typography = LocalTypography.current val preferences = LocalPreferences.current + val mediaController = LocalYoutubePlayer.current?.mediaController Column( modifier = Modifier @@ -86,6 +90,16 @@ fun PlayerSettingsScreen() { }, isEnabled = false ) + + SwitchSettingEntry( + title = "Skip silence", + text = "Skip silent parts during playback", + isChecked = preferences.skipSilence, + onCheckedChange = { + mediaController?.sendCustomCommand(SetSkipSilenceCommand, bundleOf("skipSilence" to it)) + preferences.skipSilence = it + } + ) } } } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt index 4acc123..6f341db 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt @@ -26,6 +26,7 @@ class Preferences(holder: SharedPreferences) : SharedPreferences by holder { var exoPlayerDiskCacheMaxSizeBytes by preference("exoPlayerDiskCacheMaxSizeBytes", 512L * 1024 * 1024) var displayLikeButtonInNotification by preference("displayLikeButtonInNotification", false) var persistentQueue by preference("persistentQueue", false) + var skipSilence by preference("skipSilence", false) } val Context.preferences: Preferences diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/YoutubePlayer.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/YoutubePlayer.kt index 84eb92b..91e3851 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/YoutubePlayer.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/YoutubePlayer.kt @@ -50,11 +50,10 @@ val LocalYoutubePlayer = compositionLocalOf { null } @Composable fun rememberYoutubePlayer( - mediaControllerFuture: ListenableFuture, - block: (MediaController) -> Unit, + mediaControllerFuture: ListenableFuture ): YoutubePlayer? { val mediaController by produceState(initialValue = null) { - value = mediaControllerFuture.await().also(block) + value = mediaControllerFuture.await() } val playerState = remember(mediaController) {