Commit 90850621 authored by Robert Stone's avatar Robert Stone
Browse files

Retain queue in Android Auto after playback has completed. Allow for...

Retain queue in Android Auto after playback has completed. Allow for resumption from queue item selection.
parent 647e8f83
Pipeline #118652 passed with stage
in 3 minutes and 52 seconds
......@@ -62,6 +62,12 @@ const val LIST_TITLE_ELLIPSIZE = "list_title_ellipsize"
// AudioPlayer
const val AUDIO_SHUFFLING = "audio_shuffling"
const val MEDIA_SHUFFLING = "media_shuffling"
const val POSITION_IN_SONG = "position_in_song"
const val POSITION_IN_MEDIA = "position_in_media"
const val POSITION_IN_AUDIO_LIST = "position_in_audio_list"
const val POSITION_IN_MEDIA_LIST = "position_in_media_list"
const val SHOW_REMAINING_TIME = "show_remaining_time"
const val PREF_PLAYLIST_TIPS_SHOWN = "playlist_tips_shown"
const val PREF_AUDIOPLAYER_TIPS_SHOWN = "audioplayer_tips_shown"
......
......@@ -277,5 +277,5 @@ internal class MediaSessionCallback(private val playbackService: PlaybackService
override fun onRewind() = playbackService.seek((playbackService.time - TEN_SECONDS).coerceAtLeast(0), fromUser = true)
override fun onSkipToQueueItem(id: Long) = playbackService.playIndex(id.toInt())
override fun onSkipToQueueItem(id: Long) = playbackService.playIndexOrLoadLastPlaylist(id.toInt())
}
\ No newline at end of file
......@@ -42,6 +42,7 @@ import androidx.annotation.RequiresApi
import androidx.annotation.StringRes
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.ServiceCompat
import androidx.core.content.edit
import androidx.core.content.getSystemService
import androidx.core.os.bundleOf
import androidx.lifecycle.Lifecycle
......@@ -65,10 +66,7 @@ import org.videolan.medialibrary.interfaces.media.MediaWrapper
import org.videolan.resources.*
import org.videolan.resources.util.getFromMl
import org.videolan.resources.util.launchForeground
import org.videolan.tools.Settings
import org.videolan.tools.WeakHandler
import org.videolan.tools.getContextWithLocale
import org.videolan.tools.safeOffer
import org.videolan.tools.*
import org.videolan.vlc.gui.helpers.AudioUtil
import org.videolan.vlc.gui.helpers.NotificationHelper
import org.videolan.vlc.gui.helpers.getBitmapFromDrawable
......@@ -924,21 +922,30 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner {
actions = actions or PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
if (isSeekable)
actions = actions or PlaybackStateCompat.ACTION_FAST_FORWARD or PlaybackStateCompat.ACTION_REWIND or PlaybackStateCompat.ACTION_SEEK_TO
actions = actions or PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM
if (playlistManager.canShuffle()) actions = actions or PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE
actions = actions or PlaybackStateCompat.ACTION_SET_REPEAT_MODE
if (playlistManager.canRepeat()) {
actions = actions or PlaybackStateCompat.ACTION_SET_REPEAT_MODE
val repeatResId = when (repeatType) {
PlaybackStateCompat.REPEAT_MODE_ALL -> R.drawable.ic_auto_repeat_pressed
PlaybackStateCompat.REPEAT_MODE_ONE -> R.drawable.ic_auto_repeat_one_pressed
else -> R.drawable.ic_auto_repeat_normal
}
pscb.addCustomAction("repeat", getString(R.string.repeat_title), repeatResId)
}
if (playlistManager.canShuffle()) {
actions = actions or PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE
val shuffleResId = when {
isShuffling -> R.drawable.ic_auto_shuffle_enabled
else -> R.drawable.ic_auto_shuffle_disabled
}
pscb.addCustomAction("shuffle", getString(R.string.shuffle_title), shuffleResId)
}
pscb.setActions(actions)
mediaSession.setRepeatMode(repeatType)
mediaSession.setShuffleMode(if (isShuffling) PlaybackStateCompat.SHUFFLE_MODE_ALL else PlaybackStateCompat.SHUFFLE_MODE_NONE)
val repeatResId = if (repeatType == PlaybackStateCompat.REPEAT_MODE_ALL) R.drawable.ic_auto_repeat_pressed else if (repeatType == PlaybackStateCompat.REPEAT_MODE_ONE) R.drawable.ic_auto_repeat_one_pressed else R.drawable.ic_auto_repeat_normal
if (playlistManager.canShuffle())
pscb.addCustomAction("shuffle", getString(R.string.shuffle_title), if (isShuffling) R.drawable.ic_auto_shuffle_enabled else R.drawable.ic_auto_shuffle_disabled)
pscb.addCustomAction("repeat", getString(R.string.repeat_title), repeatResId)
mediaSession.setExtras(Bundle().apply {
putBoolean(PLAYBACK_SLOT_RESERVATION_SKIP_TO_NEXT, true)
putBoolean(PLAYBACK_SLOT_RESERVATION_SKIP_TO_PREV, true)
})
val mediaIsActive = state != PlaybackStateCompat.STATE_STOPPED
val update = mediaSession.isActive != mediaIsActive
updateMediaQueueSlidingWindow()
......@@ -1144,7 +1151,8 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner {
toIndex = (songNum + halfWindowSize).coerceAtMost(mediaList.size)
fromIndex = (toIndex - windowSize).coerceAtLeast(0)
}
buildQueue(mediaList, fromIndex, toIndex)
//The on-screen queue icon will disappear if an empty queue is passed.
if (mediaList.isNotEmpty()) buildQueue(mediaList, fromIndex, toIndex)
prevUpdateInCarMode = true
} else if (mediaListChanged || prevUpdateInCarMode) {
buildQueue(playlistManager.getMediaList())
......@@ -1213,6 +1221,17 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner {
lifecycleScope.launch(start = CoroutineStart.UNDISPATCHED) { playlistManager.playIndex(index, flags) }
}
fun playIndexOrLoadLastPlaylist(index: Int) {
if (hasMedia()) playIndex(index)
else {
settings.edit {
putLong(POSITION_IN_SONG, 0L)
putInt(POSITION_IN_AUDIO_LIST, index)
}
loadLastPlaylist(PLAYLIST_TYPE_AUDIO)
}
}
@MainThread
fun flush() {
/* HACK: flush when activating a video track. This will force an
......@@ -1478,7 +1497,7 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner {
private const val PLAYBACK_BASE_ACTIONS = (PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH
or PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID or PlaybackStateCompat.ACTION_PLAY_FROM_URI
or PlaybackStateCompat.ACTION_PLAY_PAUSE)
or PlaybackStateCompat.ACTION_PLAY_PAUSE or PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM)
}
fun getTime(realTime: Long): Int {
......
......@@ -79,6 +79,8 @@ class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventList
fun hasCurrentMedia() = isValidPosition(currentIndex)
fun canRepeat() = mediaList.size() > 0
fun hasPlaylist() = mediaList.size() > 1
fun canShuffle() = mediaList.size() > 2
......@@ -186,9 +188,9 @@ class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventList
}
}
// load playlist
shuffling = settings.getBoolean(if (audio) "audio_shuffling" else "media_shuffling", false)
val position = max(0, settings.getInt(if (audio) "position_in_audio_list" else "position_in_media_list", 0))
savedTime = settings.getLong(if (audio) "position_in_song" else "position_in_media", -1)
shuffling = settings.getBoolean(if (audio) AUDIO_SHUFFLING else MEDIA_SHUFFLING, false)
val position = max(0, settings.getInt(if (audio) POSITION_IN_AUDIO_LIST else POSITION_IN_MEDIA_LIST, 0))
savedTime = settings.getLong(if (audio) POSITION_IN_SONG else POSITION_IN_MEDIA, -1)
if (!audio && position < playList.size && settings.getBoolean(VIDEO_PAUSED, false)) {
playList[position].addFlags(MediaWrapper.MEDIA_PAUSED)
}
......@@ -708,9 +710,9 @@ class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventList
if (!hasMedia()) return
settings.edit {
val audio = !video && isAudioList()
putBoolean(if (audio) "audio_shuffling" else "media_shuffling", shuffling)
putInt(if (audio) "position_in_audio_list" else "position_in_media_list", if (reset) 0 else currentIndex)
putLong(if (audio) "position_in_song" else "position_in_media", if (reset) 0L else player.getCurrentTime())
putBoolean(if (audio) AUDIO_SHUFFLING else MEDIA_SHUFFLING, shuffling)
putInt(if (audio) POSITION_IN_AUDIO_LIST else POSITION_IN_MEDIA_LIST, if (reset) 0 else currentIndex)
putLong(if (audio) POSITION_IN_SONG else POSITION_IN_MEDIA, if (reset) 0L else player.getCurrentTime())
if (!audio) {
putFloat(VIDEO_SPEED, player.getRate())
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment