diff --git a/app/src/main/java/org/oxycblt/auxio/IntegerTable.kt b/app/src/main/java/org/oxycblt/auxio/IntegerTable.kt index d0bff5315..98f1b5739 100644 --- a/app/src/main/java/org/oxycblt/auxio/IntegerTable.kt +++ b/app/src/main/java/org/oxycblt/auxio/IntegerTable.kt @@ -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 } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt index b654902da..f2f3f0543 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -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) { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt index 6eebd76dd..973a524ce 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt @@ -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. diff --git a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt index 8d26371e7..f71fcb48d 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt @@ -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. diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt index aba66c5e0..9f2539c3f 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt @@ -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) { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt index f329e2a70..de2d08f2c 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -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) { - 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) { + 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, 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" } diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt index 9417f8a03..9156488cc 100644 --- a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt @@ -174,7 +174,7 @@ class SearchFragment : ListFragment() { 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)