Add seperate queue types
Add seperate queue types for different contexts, such as playing a song from an album.
This commit is contained in:
parent
96c30b3f93
commit
9f05ce6e52
7 changed files with 57 additions and 23 deletions
|
@ -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,
|
||||||
|
|
|
@ -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 ---
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
app/src/main/java/org/oxycblt/auxio/playback/PlaybackMode.kt
Normal file
10
app/src/main/java/org/oxycblt/auxio/playback/PlaybackMode.kt
Normal 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
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in a new issue