playback: add ability to play/shuffle selection
Add the ability to play or shuffle a selection. This finally allows "arbitrary" playback to be created from any combination of songs/albums/artist/genres, rather than just from pre-defined options. Resolves #313.
This commit is contained in:
parent
692839e8fe
commit
5988908b56
8 changed files with 50 additions and 42 deletions
|
@ -71,6 +71,14 @@ abstract class SelectionFragment<VB : ViewBinding> :
|
|||
requireContext().showToast(R.string.lng_queue_added)
|
||||
true
|
||||
}
|
||||
R.id.action_selection_play -> {
|
||||
playbackModel.play(selectionModel.consume())
|
||||
true
|
||||
}
|
||||
R.id.action_selection_shuffle -> {
|
||||
playbackModel.shuffle(selectionModel.consume())
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ class MusicStore private constructor() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Remove a [Listener] from this instance, preventing it from recieving any further updates.
|
||||
* Remove a [Listener] from this instance, preventing it from receiving any further updates.
|
||||
* @param listener The [Listener] to remove. Does nothing if the [Listener] was never added in
|
||||
* the first place.
|
||||
* @see Listener
|
||||
|
|
|
@ -129,7 +129,6 @@ class Directory private constructor(val volume: StorageVolume, val relativePath:
|
|||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
data class MusicDirectories(val dirs: List<Directory>, val shouldInclude: Boolean)
|
||||
// TODO: Unify include + exclude
|
||||
|
||||
/**
|
||||
* A mime type of a file. Only intended for display.
|
||||
|
|
|
@ -196,7 +196,7 @@ val StorageVolume.isInternalCompat: Boolean
|
|||
get() = isPrimaryCompat && isEmulatedCompat
|
||||
|
||||
/**
|
||||
* The unique identifier for this [StorageVolume], obtained in a version compatible manner Can be
|
||||
* The unique identifier for this [StorageVolume], obtained in a version compatible manner. Can be
|
||||
* null.
|
||||
* @see StorageVolume.getUuid
|
||||
*/
|
||||
|
|
|
@ -38,6 +38,7 @@ class PlaybackViewModel(application: Application) :
|
|||
private val musicSettings = MusicSettings.from(application)
|
||||
private val playbackSettings = PlaybackSettings.from(application)
|
||||
private val playbackManager = PlaybackStateManager.getInstance()
|
||||
private val musicStore = MusicStore.getInstance()
|
||||
private var lastPositionJob: Job? = null
|
||||
|
||||
private val _song = MutableStateFlow<Song?>(null)
|
||||
|
@ -161,27 +162,13 @@ class PlaybackViewModel(application: Application) :
|
|||
*/
|
||||
fun playFrom(song: Song, playbackMode: MusicMode) {
|
||||
when (playbackMode) {
|
||||
MusicMode.SONGS -> playFromAll(song)
|
||||
MusicMode.ALBUMS -> playFromAlbum(song)
|
||||
MusicMode.SONGS -> playImpl(song, null)
|
||||
MusicMode.ALBUMS -> playImpl(song, song.album)
|
||||
MusicMode.ARTISTS -> playFromArtist(song)
|
||||
MusicMode.GENRES -> playFromGenre(song)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Play the given [Song] from all songs in the music library.
|
||||
* @param song The [Song] to play.
|
||||
*/
|
||||
fun playFromAll(song: Song) {
|
||||
playImpl(song, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Play a [Song] from it's [Album].
|
||||
* @param song The [Song] to play.
|
||||
*/
|
||||
fun playFromAlbum(song: Song) = playImpl(song, song.album)
|
||||
|
||||
/**
|
||||
* Play a [Song] from one of it's [Artist]s.
|
||||
* @param song The [Song] to play.
|
||||
|
@ -250,6 +237,13 @@ class PlaybackViewModel(application: Application) :
|
|||
*/
|
||||
fun play(genre: Genre) = playImpl(null, genre, false)
|
||||
|
||||
/**
|
||||
* Play a [Music] selection.
|
||||
* @param selection The selection to play.
|
||||
*/
|
||||
fun play(selection: List<Music>) =
|
||||
playbackManager.play(null, selectionToSongs(selection), false)
|
||||
|
||||
/**
|
||||
* Shuffle an [Album].
|
||||
* @param album The [Album] to shuffle.
|
||||
|
@ -269,13 +263,11 @@ class PlaybackViewModel(application: Application) :
|
|||
fun shuffle(genre: Genre) = playImpl(null, genre, true)
|
||||
|
||||
/**
|
||||
* Start the given [InternalPlayer.Action] to be completed eventually. This can be used to
|
||||
* enqueue a playback action at startup to then occur when the music library is fully loaded.
|
||||
* @param action The [InternalPlayer.Action] to perform eventually.
|
||||
* Shuffle a [Music] selection.
|
||||
* @param selection The selection to shuffle.
|
||||
*/
|
||||
fun startAction(action: InternalPlayer.Action) {
|
||||
playbackManager.startAction(action)
|
||||
}
|
||||
fun shuffle(selection: List<Music>) =
|
||||
playbackManager.play(null, selectionToSongs(selection), true)
|
||||
|
||||
private fun playImpl(
|
||||
song: Song?,
|
||||
|
@ -285,6 +277,7 @@ class PlaybackViewModel(application: Application) :
|
|||
check(song == null || parent == null || parent.songs.contains(song)) {
|
||||
"Song to play not in parent"
|
||||
}
|
||||
val library = musicStore.library ?: return
|
||||
val sort =
|
||||
when (parent) {
|
||||
is Genre -> musicSettings.genreSongSort
|
||||
|
@ -292,7 +285,17 @@ class PlaybackViewModel(application: Application) :
|
|||
is Album -> musicSettings.albumSongSort
|
||||
null -> musicSettings.songSort
|
||||
}
|
||||
playbackManager.play(song, parent, sort, shuffled)
|
||||
val queue = sort.songs(parent?.songs ?: library.songs)
|
||||
playbackManager.play(song, queue, shuffled)
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the given [InternalPlayer.Action] to be completed eventually. This can be used to
|
||||
* enqueue a playback action at startup to then occur when the music library is fully loaded.
|
||||
* @param action The [InternalPlayer.Action] to perform eventually.
|
||||
*/
|
||||
fun startAction(action: InternalPlayer.Action) {
|
||||
playbackManager.startAction(action)
|
||||
}
|
||||
|
||||
// --- PLAYER FUNCTIONS ---
|
||||
|
|
|
@ -155,20 +155,21 @@ class PlaybackStateManager private constructor() {
|
|||
/**
|
||||
* Start new playback.
|
||||
* @param song A particular [Song] to play, or null to play the first [Song] in the new queue.
|
||||
* @param queue The queue of [Song]s to play from.
|
||||
* @param parent The [MusicParent] to play from, or null if to play from the entire [Library].
|
||||
* @param sort [Sort] to initially sort an ordered queue with.
|
||||
* @param shuffled Whether to shuffle or not.
|
||||
*/
|
||||
@Synchronized
|
||||
fun play(song: Song?, parent: MusicParent?, sort: Sort, shuffled: Boolean) {
|
||||
fun play(song: Song?, queue: List<Song>, shuffled: Boolean) {
|
||||
val internalPlayer = internalPlayer ?: return
|
||||
val library = musicStore.library ?: return
|
||||
// Set up parent and queue
|
||||
this.parent = parent
|
||||
queue.start(song, sort.songs(parent?.songs ?: library.songs), shuffled)
|
||||
this.queue.start(song, queue, shuffled)
|
||||
// Notify components of changes
|
||||
notifyNewPlayback()
|
||||
internalPlayer.loadSong(queue.currentSong, true)
|
||||
internalPlayer.loadSong(this.queue.currentSong, true)
|
||||
// Played something, so we are initialized now
|
||||
isInitialized = true
|
||||
}
|
||||
|
|
|
@ -355,15 +355,14 @@ class PlaybackService :
|
|||
}
|
||||
// Shuffle all -> Start new playback from all songs
|
||||
is InternalPlayer.Action.ShuffleAll -> {
|
||||
playbackManager.play(null, null, musicSettings.songSort, true)
|
||||
playbackManager.play(null, musicSettings.songSort.songs(library.songs), true)
|
||||
}
|
||||
// Open -> Try to find the Song for the given file and then play it from all songs
|
||||
is InternalPlayer.Action.Open -> {
|
||||
library.findSongForUri(application, action.uri)?.let { song ->
|
||||
playbackManager.play(
|
||||
song,
|
||||
null,
|
||||
musicSettings.songSort,
|
||||
musicSettings.songSort.songs(library.songs),
|
||||
playbackManager.queue.isShuffled && playbackSettings.keepShuffle)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,16 +8,14 @@
|
|||
app:showAsAction="ifRoom"/>
|
||||
<item
|
||||
android:id="@+id/action_selection_queue_add"
|
||||
android:icon="@drawable/ic_play_next"
|
||||
android:title="@string/lbl_queue_add"
|
||||
app:showAsAction="never" />
|
||||
<!-- TOOD: Disabled until able to get queue system into shape -->
|
||||
<!-- <item-->
|
||||
<!-- android:id="@+id/action_play_selection"-->
|
||||
<!-- android:title="@string/lbl_play_selected"-->
|
||||
<!-- app:showAsAction="never"/>-->
|
||||
<!-- <item-->
|
||||
<!-- android:id="@+id/action_shuffle_selection"-->
|
||||
<!-- android:title="@string/lbl_shuffle_selected"-->
|
||||
<!-- app:showAsAction="never"/>-->
|
||||
<item
|
||||
android:id="@+id/action_selection_play"
|
||||
android:title="@string/lbl_play_selected"
|
||||
app:showAsAction="never"/>
|
||||
<item
|
||||
android:id="@+id/action_selection_shuffle"
|
||||
android:title="@string/lbl_shuffle_selected"
|
||||
app:showAsAction="never"/>
|
||||
</menu>
|
Loading…
Reference in a new issue