Add seperate queue types

Add seperate queue types for different contexts, such as playing a song from an album.
This commit is contained in:
OxygenCobalt 2020-10-14 08:09:45 -06:00
parent 96c30b3f93
commit 9f05ce6e52
7 changed files with 57 additions and 23 deletions

View file

@ -7,14 +7,12 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentStateAdapter
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import org.oxycblt.auxio.databinding.FragmentMainBinding import org.oxycblt.auxio.databinding.FragmentMainBinding
import org.oxycblt.auxio.library.LibraryFragment import org.oxycblt.auxio.library.LibraryFragment
import org.oxycblt.auxio.loading.LoadingViewModel
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.songs.SongsFragment import org.oxycblt.auxio.songs.SongsFragment
import org.oxycblt.auxio.theme.accent import org.oxycblt.auxio.theme.accent
@ -23,10 +21,6 @@ import org.oxycblt.auxio.theme.getTransparentAccent
import org.oxycblt.auxio.theme.toColor import org.oxycblt.auxio.theme.toColor
class MainFragment : Fragment() { class MainFragment : Fragment() {
private val loadingModel: LoadingViewModel by activityViewModels {
LoadingViewModel.Factory(requireActivity().application)
}
private val shownFragments = listOf(0, 1) private val shownFragments = listOf(0, 1)
private val tabIcons = listOf( private val tabIcons = listOf(
R.drawable.ic_library, R.drawable.ic_library,

View file

@ -13,6 +13,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentAlbumDetailBinding import org.oxycblt.auxio.databinding.FragmentAlbumDetailBinding
import org.oxycblt.auxio.detail.adapters.DetailSongAdapter import org.oxycblt.auxio.detail.adapters.DetailSongAdapter
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.playback.PlaybackMode
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
import org.oxycblt.auxio.theme.disable import org.oxycblt.auxio.theme.disable
@ -44,7 +45,7 @@ class AlbumDetailFragment : Fragment() {
} }
val songAdapter = DetailSongAdapter { val songAdapter = DetailSongAdapter {
playbackModel.update(it) playbackModel.update(it, PlaybackMode.IN_ALBUM)
} }
// --- UI SETUP --- // --- UI SETUP ---

View file

@ -25,6 +25,7 @@ import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackMode
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.theme.applyColor import org.oxycblt.auxio.theme.applyColor
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
@ -181,7 +182,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
// If the item is a song [That was selected through search], then update the playback // If the item is a song [That was selected through search], then update the playback
// to that song instead of doing any navigation // to that song instead of doing any navigation
if (baseModel is Song) { if (baseModel is Song) {
playbackModel.update(baseModel) playbackModel.update(baseModel, PlaybackMode.ALL_SONGS)
return return
} }

View file

@ -0,0 +1,10 @@
package org.oxycblt.auxio.playback
// Enum for instruction how the queue should function.
// ALL SONGS -> Play from all songs
// IN_GENRE -> Play from the genre
// IN_ARTIST -> Play from the songs of the artist
// IN_ALBUM -> Play from the songs of the album
enum class PlaybackMode {
ALL_SONGS, IN_GENRE, IN_ARTIST, IN_ALBUM
}

View file

@ -1,5 +1,6 @@
package org.oxycblt.auxio.playback package org.oxycblt.auxio.playback
import android.util.Log
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.Transformations
@ -9,6 +10,7 @@ import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.toDuration import org.oxycblt.auxio.music.toDuration
// TODO: Implement media controls // TODO: Implement media controls
// TODO: Implement persistence
// TODO: Add the playback service itself // TODO: Add the playback service itself
// TODO: Possibly add some swipe-to-next-track function, could require a ViewPager. // TODO: Possibly add some swipe-to-next-track function, could require a ViewPager.
// A ViewModel that acts as an intermediary between PlaybackService and the Playback Fragments. // A ViewModel that acts as an intermediary between PlaybackService and the Playback Fragments.
@ -16,14 +18,15 @@ class PlaybackViewModel : ViewModel() {
private val mCurrentSong = MutableLiveData<Song>() private val mCurrentSong = MutableLiveData<Song>()
val currentSong: LiveData<Song> get() = mCurrentSong val currentSong: LiveData<Song> get() = mCurrentSong
private val mCurrentIndex = MutableLiveData(0)
val currentIndex: LiveData<Int> get() = mCurrentIndex
private val mQueue = MutableLiveData(mutableListOf<Song>()) private val mQueue = MutableLiveData(mutableListOf<Song>())
val queue: LiveData<MutableList<Song>> get() = mQueue val queue: LiveData<MutableList<Song>> get() = mQueue
private val mCurrentIndex = MutableLiveData(0)
val currentIndex: LiveData<Int> get() = mCurrentIndex
private val mCurrentMode = MutableLiveData(PlaybackMode.ALL_SONGS)
private val mCurrentDuration = MutableLiveData(0L) private val mCurrentDuration = MutableLiveData(0L)
val currentDuration: LiveData<Long> get() = mCurrentDuration
private val mIsPlaying = MutableLiveData(false) private val mIsPlaying = MutableLiveData(false)
val isPlaying: LiveData<Boolean> get() = mIsPlaying val isPlaying: LiveData<Boolean> get() = mIsPlaying
@ -32,22 +35,43 @@ class PlaybackViewModel : ViewModel() {
val isSeeking: LiveData<Boolean> get() = mIsSeeking val isSeeking: LiveData<Boolean> get() = mIsSeeking
// Formatted variants of the duration // Formatted variants of the duration
val formattedCurrentDuration = Transformations.map(currentDuration) { val formattedCurrentDuration = Transformations.map(mCurrentDuration) {
it.toDuration() it.toDuration()
} }
val formattedSeekBarProgress = Transformations.map(currentDuration) { val formattedSeekBarProgress = Transformations.map(mCurrentDuration) {
if (mCurrentSong.value != null) it.toInt() else 0 if (mCurrentSong.value != null) it.toInt() else 0
} }
// Update the current song while changing the queue to All Songs. // Update the current song while changing the queue mode.
fun update(song: Song) { fun update(song: Song, mode: PlaybackMode) {
Log.d(this::class.simpleName, "Updating song to ${song.name} and mode to $mode")
val musicStore = MusicStore.getInstance() val musicStore = MusicStore.getInstance()
updatePlayback(song) updatePlayback(song)
mQueue.value = musicStore.songs.toMutableList() mQueue.value = when (mode) {
mCurrentIndex.value = musicStore.songs.indexOf(song) PlaybackMode.ALL_SONGS -> musicStore.songs.toMutableList()
PlaybackMode.IN_ARTIST -> song.album.artist.songs
PlaybackMode.IN_ALBUM -> song.album.songs
// Warning: Calling update() with a mode of IN_GENRE Will cause Auxio to play
// from the artist's most prominent genre instead of the song's genre.
// FIXME: This could be fixed by moving genre loading to songs
PlaybackMode.IN_GENRE -> {
Log.d(
this::class.simpleName,
"update() was called with IN_GENRES, using " +
"most prominent genre instead of the song's genre."
)
song.album.artist.genres[0].songs
}
}
mCurrentMode.value = mode
mCurrentIndex.value = mQueue.value!!.indexOf(song)
} }
private fun updatePlayback(song: Song) { private fun updatePlayback(song: Song) {

View file

@ -9,6 +9,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import org.oxycblt.auxio.databinding.FragmentSongsBinding import org.oxycblt.auxio.databinding.FragmentSongsBinding
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.playback.PlaybackMode
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
@ -31,7 +32,7 @@ class SongsFragment : Fragment() {
binding.songRecycler.apply { binding.songRecycler.apply {
adapter = SongAdapter(musicStore.songs) { adapter = SongAdapter(musicStore.songs) {
playbackModel.update(it) playbackModel.update(it, PlaybackMode.ALL_SONGS)
} }
applyDivider() applyDivider()
setHasFixedSize(true) setHasFixedSize(true)

View file

@ -96,13 +96,16 @@ fun MenuItem.applyColor(@ColorInt color: Int) {
// Disable an ImageButton // Disable an ImageButton
fun ImageButton.disable(context: Context) { fun ImageButton.disable(context: Context) {
imageTintList = ColorStateList.valueOf( if (isEnabled) {
R.color.inactive_color.toColor(context) imageTintList = ColorStateList.valueOf(
) R.color.inactive_color.toColor(context)
)
isEnabled = false isEnabled = false
}
} }
// Enable an ImageButton
fun ImageButton.enable(context: Context) { fun ImageButton.enable(context: Context) {
if (!isEnabled) { if (!isEnabled) {
imageTintList = ColorStateList.valueOf( imageTintList = ColorStateList.valueOf(