playback: add scaffold for new playback mode
Add the scaffold for PlaySong, a new version of playback modes that - Supports playback of a song by itself, requested by #424. - Will make direct playback from the song menu feasible (given additional reworks) - Prevents the invalid state of playing a song by it's playlist, as the sealed interface implementation of PlaySong requires a Playlist to be provided to it's respective variant.
This commit is contained in:
parent
7d42d016f1
commit
7495a59ab1
7 changed files with 157 additions and 105 deletions
|
@ -101,8 +101,6 @@ object IntegerTable {
|
|||
const val SORT_BY_TRACK = 0xA117
|
||||
/** Sort.Mode.ByDateAdded */
|
||||
const val SORT_BY_DATE_ADDED = 0xA118
|
||||
/** Sort.Mode.None */
|
||||
const val SORT_BY_NONE = 0xA11F
|
||||
/** ReplayGainMode.Off (No longer used but still reserved) */
|
||||
// const val REPLAY_GAIN_MODE_OFF = 0xA110
|
||||
/** ReplayGainMode.Track */
|
||||
|
@ -123,4 +121,16 @@ object IntegerTable {
|
|||
const val COVER_MODE_MEDIA_STORE = 0xA11D
|
||||
/** CoverMode.Quality */
|
||||
const val COVER_MODE_QUALITY = 0xA11E
|
||||
/** PlaySong.Itself */
|
||||
const val PLAY_SONG_ITSELF = 0xA11F
|
||||
/** PlaySong.FromAll */
|
||||
const val PLAY_SONG_FROM_ALL = 0xA120
|
||||
/** PlaySong.FromAlbum */
|
||||
const val PLAY_SONG_FROM_ALBUM = 0xA121
|
||||
/** PlaySong.FromArtist */
|
||||
const val PLAY_SONG_FROM_ARTIST = 0xA122
|
||||
/** PlaySong.FromGenre */
|
||||
const val PLAY_SONG_FROM_GENRE = 0xA123
|
||||
/** PlaySong.FromPlaylist */
|
||||
const val PLAY_SONG_FROM_PLAYLIST = 0xA124
|
||||
}
|
||||
|
|
|
@ -180,8 +180,12 @@ class AlbumDetailFragment :
|
|||
}
|
||||
|
||||
override fun onRealClick(item: Song) {
|
||||
// There can only be one album, so a null mode and an ALBUMS mode will function the same.
|
||||
playbackModel.playFrom(item, detailModel.playbackMode ?: MusicMode.ALBUMS)
|
||||
val mode = detailModel.playbackMode
|
||||
if (mode != null) {
|
||||
playbackModel.play(item, detailModel.playbackMode ?: MusicMode.ALBUMS)
|
||||
} else {
|
||||
playbackModel.playFromAlbum(item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOpenMenu(item: Song, anchor: View) {
|
||||
|
|
|
@ -182,7 +182,7 @@ class ArtistDetailFragment :
|
|||
is Song -> {
|
||||
val playbackMode = detailModel.playbackMode
|
||||
if (playbackMode != null) {
|
||||
playbackModel.playFrom(item, playbackMode)
|
||||
playbackModel.play(item, playbackMode)
|
||||
} else {
|
||||
// When configured to play from the selected item, we already have an Artist
|
||||
// to play from.
|
||||
|
|
|
@ -180,7 +180,7 @@ class GenreDetailFragment :
|
|||
is Song -> {
|
||||
val playbackMode = detailModel.playbackMode
|
||||
if (playbackMode != null) {
|
||||
playbackModel.playFrom(item, playbackMode)
|
||||
playbackModel.play(item, playbackMode)
|
||||
} else {
|
||||
// When configured to play from the selected item, we already have an Genre
|
||||
// to play from.
|
||||
|
|
|
@ -137,7 +137,7 @@ class SongListFragment :
|
|||
}
|
||||
|
||||
override fun onRealClick(item: Song) {
|
||||
playbackModel.playFrom(item, homeModel.playbackMode)
|
||||
playbackModel.play(item, homeModel.playbackMode)
|
||||
}
|
||||
|
||||
override fun onOpenMenu(item: Song, anchor: View) {
|
||||
|
|
|
@ -180,32 +180,31 @@ constructor(
|
|||
|
||||
// --- PLAYING FUNCTIONS ---
|
||||
|
||||
fun play(song: Song, playbackMode: MusicMode) {
|
||||
play(song, PlaySong.fromPlaybackModeTemporary(playbackMode))
|
||||
}
|
||||
|
||||
fun play(song: Song, with: PlaySong) {
|
||||
logD("Playing $song with $with")
|
||||
playWithImpl(song, with, isImplicitlyShuffled())
|
||||
}
|
||||
|
||||
// fun playExplicit(song: Song, with: PlaySong) {
|
||||
// playWithImpl(song, with, false)
|
||||
// }
|
||||
//
|
||||
// fun shuffleExplicit(song: Song, with: PlaySong) {
|
||||
// playWithImpl(song, with, true)
|
||||
// }
|
||||
|
||||
/** Shuffle all songs in the music library. */
|
||||
fun shuffleAll() {
|
||||
logD("Shuffling all songs")
|
||||
playImpl(null, null, true)
|
||||
playFromAllImpl(null, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Play a [Song] from the [MusicParent] outlined by the given [MusicMode].
|
||||
* - If [MusicMode.SONGS], the [Song] is played from all songs.
|
||||
* - If [MusicMode.ALBUMS], the [Song] is played from it's [Album].
|
||||
* - If [MusicMode.ARTISTS], the [Song] is played from one of it's [Artist]s.
|
||||
* - If [MusicMode.GENRES], the [Song] is played from one of it's [Genre]s.
|
||||
* [MusicMode.PLAYLISTS] is disallowed here.
|
||||
*
|
||||
* @param song The [Song] to play.
|
||||
* @param playbackMode The [MusicMode] to play from.
|
||||
*/
|
||||
fun playFrom(song: Song, playbackMode: MusicMode) {
|
||||
logD("Playing $song from $playbackMode")
|
||||
when (playbackMode) {
|
||||
MusicMode.SONGS -> playImpl(song, null)
|
||||
MusicMode.ALBUMS -> playImpl(song, song.album)
|
||||
MusicMode.ARTISTS -> playFromArtist(song)
|
||||
MusicMode.GENRES -> playFromGenre(song)
|
||||
MusicMode.PLAYLISTS -> error("Playing from a playlist is not supported.")
|
||||
}
|
||||
fun playFromAlbum(song: Song) {
|
||||
playFromAlbumImpl(song, isImplicitlyShuffled())
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -216,16 +215,7 @@ constructor(
|
|||
* be prompted on what artist to play. Defaults to null.
|
||||
*/
|
||||
fun playFromArtist(song: Song, artist: Artist? = null) {
|
||||
if (artist != null) {
|
||||
logD("Playing $song from $artist")
|
||||
playImpl(song, artist)
|
||||
} else if (song.artists.size == 1) {
|
||||
logD("$song has one artist, playing from it")
|
||||
playImpl(song, song.artists[0])
|
||||
} else {
|
||||
logD("$song has multiple artists, showing choice dialog")
|
||||
startPlaybackDecisionImpl(PlaybackDecision.PlayFromArtist(song))
|
||||
}
|
||||
playFromArtistImpl(song, artist, isImplicitlyShuffled())
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -236,19 +226,77 @@ constructor(
|
|||
* be prompted on what artist to play. Defaults to null.
|
||||
*/
|
||||
fun playFromGenre(song: Song, genre: Genre? = null) {
|
||||
if (genre != null) {
|
||||
logD("Playing $song from $genre")
|
||||
playImpl(song, genre)
|
||||
} else if (song.genres.size == 1) {
|
||||
logD("$song has one genre, playing from it")
|
||||
playImpl(song, song.genres[0])
|
||||
} else {
|
||||
logD("$song has multiple genres, showing choice dialog")
|
||||
startPlaybackDecisionImpl(PlaybackDecision.PlayFromGenre(song))
|
||||
playFromGenreImpl(song, genre, isImplicitlyShuffled())
|
||||
}
|
||||
|
||||
/**
|
||||
* Play a [Song] from one of it's [Playlist]s.
|
||||
*
|
||||
* @param song The [Song] to play.
|
||||
* @param playlist The [Playlist] to play from. Must be linked to the [Song].
|
||||
*/
|
||||
fun playFromPlaylist(song: Song, playlist: Playlist) {
|
||||
playFromPlaylistImpl(song, playlist, isImplicitlyShuffled())
|
||||
}
|
||||
|
||||
private fun isImplicitlyShuffled() =
|
||||
playbackManager.queue.isShuffled && playbackSettings.keepShuffle
|
||||
|
||||
private fun playWithImpl(song: Song, with: PlaySong, shuffled: Boolean) {
|
||||
when (with) {
|
||||
is PlaySong.ByItself -> playItselfImpl(song, shuffled)
|
||||
is PlaySong.FromAll -> playFromAllImpl(song, shuffled)
|
||||
is PlaySong.FromAlbum -> playFromAlbumImpl(song, shuffled)
|
||||
is PlaySong.FromArtist -> playFromArtistImpl(song, with.which, shuffled)
|
||||
is PlaySong.FromGenre -> playFromGenreImpl(song, with.which, shuffled)
|
||||
is PlaySong.FromPlaylist -> playFromPlaylistImpl(song, with.which, shuffled)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startPlaybackDecisionImpl(decision: PlaybackDecision) {
|
||||
private fun playItselfImpl(song: Song, shuffled: Boolean) {
|
||||
playImpl(song, listOf(song), shuffled)
|
||||
}
|
||||
|
||||
private fun playFromAllImpl(song: Song?, shuffled: Boolean) {
|
||||
playImpl(song, null, shuffled)
|
||||
}
|
||||
|
||||
private fun playFromAlbumImpl(song: Song, shuffled: Boolean) {
|
||||
playImpl(song, song.album, shuffled)
|
||||
}
|
||||
|
||||
private fun playFromArtistImpl(song: Song, artist: Artist?, shuffled: Boolean) {
|
||||
if (artist != null) {
|
||||
logD("Playing $song from $artist")
|
||||
playImpl(song, artist, shuffled)
|
||||
} else if (song.artists.size == 1) {
|
||||
logD("$song has one artist, playing from it")
|
||||
playImpl(song, song.artists[0], shuffled)
|
||||
} else {
|
||||
logD("$song has multiple artists, showing choice dialog")
|
||||
startPlaybackDecision(PlaybackDecision.PlayFromArtist(song))
|
||||
}
|
||||
}
|
||||
|
||||
private fun playFromGenreImpl(song: Song, genre: Genre?, shuffled: Boolean) {
|
||||
if (genre != null) {
|
||||
logD("Playing $song from $genre")
|
||||
playImpl(song, genre, shuffled)
|
||||
} else if (song.genres.size == 1) {
|
||||
logD("$song has one genre, playing from it")
|
||||
playImpl(song, song.genres[0], shuffled)
|
||||
} else {
|
||||
logD("$song has multiple genres, showing choice dialog")
|
||||
startPlaybackDecision(PlaybackDecision.PlayFromGenre(song))
|
||||
}
|
||||
}
|
||||
|
||||
private fun playFromPlaylistImpl(song: Song, playlist: Playlist, shuffled: Boolean) {
|
||||
logD("Playing $song from $playlist")
|
||||
playImpl(song, playlist, shuffled)
|
||||
}
|
||||
|
||||
private fun startPlaybackDecision(decision: PlaybackDecision) {
|
||||
val existing = _playbackDecision.flow.value
|
||||
if (existing != null) {
|
||||
logD("Already handling decision $existing, ignoring $decision")
|
||||
|
@ -257,17 +305,6 @@ constructor(
|
|||
_playbackDecision.put(decision)
|
||||
}
|
||||
|
||||
/**
|
||||
* PLay a [Song] from one of it's [Playlist]s.
|
||||
*
|
||||
* @param song The [Song] to play.
|
||||
* @param playlist The [Playlist] to play from. Must be linked to the [Song].
|
||||
*/
|
||||
fun playFromPlaylist(song: Song, playlist: Playlist) {
|
||||
logD("Playing $song from $playlist")
|
||||
playImpl(song, playlist)
|
||||
}
|
||||
|
||||
/**
|
||||
* Play an [Album].
|
||||
*
|
||||
|
@ -278,46 +315,6 @@ constructor(
|
|||
playImpl(null, album, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Play an [Artist].
|
||||
*
|
||||
* @param artist The [Artist] to play.
|
||||
*/
|
||||
fun play(artist: Artist) {
|
||||
logD("Playing $artist")
|
||||
playImpl(null, artist, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Play a [Genre].
|
||||
*
|
||||
* @param genre The [Genre] to play.
|
||||
*/
|
||||
fun play(genre: Genre) {
|
||||
logD("Playing $genre")
|
||||
playImpl(null, genre, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Play a [Playlist].
|
||||
*
|
||||
* @param playlist The [Playlist] to play.
|
||||
*/
|
||||
fun play(playlist: Playlist) {
|
||||
logD("Playing $playlist")
|
||||
playImpl(null, playlist, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Play a list of [Song]s.
|
||||
*
|
||||
* @param songs The [Song]s to play.
|
||||
*/
|
||||
fun play(songs: List<Song>) {
|
||||
logD("Playing ${songs.size} songs")
|
||||
playbackManager.play(null, null, songs, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffle an [Album].
|
||||
*
|
||||
|
@ -328,6 +325,16 @@ constructor(
|
|||
playImpl(null, album, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Play an [Artist].
|
||||
*
|
||||
* @param artist The [Artist] to play.
|
||||
*/
|
||||
fun play(artist: Artist) {
|
||||
logD("Playing $artist")
|
||||
playImpl(null, artist, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffle an [Artist].
|
||||
*
|
||||
|
@ -338,6 +345,16 @@ constructor(
|
|||
playImpl(null, artist, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Play a [Genre].
|
||||
*
|
||||
* @param genre The [Genre] to play.
|
||||
*/
|
||||
fun play(genre: Genre) {
|
||||
logD("Playing $genre")
|
||||
playImpl(null, genre, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffle a [Genre].
|
||||
*
|
||||
|
@ -348,6 +365,16 @@ constructor(
|
|||
playImpl(null, genre, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Play a [Playlist].
|
||||
*
|
||||
* @param playlist The [Playlist] to play.
|
||||
*/
|
||||
fun play(playlist: Playlist) {
|
||||
logD("Playing $playlist")
|
||||
playImpl(null, playlist, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffle a [Playlist].
|
||||
*
|
||||
|
@ -358,6 +385,16 @@ constructor(
|
|||
playImpl(null, playlist, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Play a list of [Song]s.
|
||||
*
|
||||
* @param songs The [Song]s to play.
|
||||
*/
|
||||
fun play(songs: List<Song>) {
|
||||
logD("Playing ${songs.size} songs")
|
||||
playbackManager.play(null, null, songs, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffle a list of [Song]s.
|
||||
*
|
||||
|
@ -368,11 +405,12 @@ constructor(
|
|||
playbackManager.play(null, null, songs, true)
|
||||
}
|
||||
|
||||
private fun playImpl(
|
||||
song: Song?,
|
||||
parent: MusicParent?,
|
||||
shuffled: Boolean = playbackManager.queue.isShuffled && playbackSettings.keepShuffle
|
||||
) {
|
||||
private fun playImpl(song: Song?, queue: List<Song>, shuffled: Boolean) {
|
||||
check(song == null || queue.contains(song)) { "Song to play not in queue" }
|
||||
playbackManager.play(song, null, queue, shuffled)
|
||||
}
|
||||
|
||||
private fun playImpl(song: Song?, parent: MusicParent?, shuffled: Boolean) {
|
||||
check(song == null || parent == null || parent.songs.contains(song)) {
|
||||
"Song to play not in parent"
|
||||
}
|
||||
|
|
|
@ -174,7 +174,7 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
|
|||
|
||||
override fun onRealClick(item: Music) {
|
||||
when (item) {
|
||||
is Song -> playbackModel.playFrom(item, searchModel.playbackMode)
|
||||
is Song -> playbackModel.play(item, searchModel.playbackMode)
|
||||
is Album -> detailModel.showAlbum(item)
|
||||
is Artist -> detailModel.showArtist(item)
|
||||
is Genre -> detailModel.showGenre(item)
|
||||
|
|
Loading…
Reference in a new issue