Add skipping
Add the ability to skip fowards/backwards.
This commit is contained in:
parent
d8f40bfd27
commit
3376b57f8e
13 changed files with 149 additions and 7 deletions
|
@ -22,6 +22,7 @@ class AlbumDetailFragment : Fragment() {
|
|||
private val args: AlbumDetailFragmentArgs by navArgs()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
private val musicModel: MusicViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
|
@ -45,7 +46,7 @@ class AlbumDetailFragment : Fragment() {
|
|||
}
|
||||
|
||||
val songAdapter = DetailSongAdapter {
|
||||
playbackModel.updateSong(it)
|
||||
playbackModel.update(it, musicModel.songs.value!!)
|
||||
}
|
||||
|
||||
// --- UI SETUP ---
|
||||
|
|
|
@ -190,7 +190,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
|||
// If the item is a song [That was selected through search], then update the playback
|
||||
// to that song instead of doing any navigation
|
||||
if (baseModel is Song) {
|
||||
playbackModel.updateSong(baseModel)
|
||||
playbackModel.update(baseModel, musicModel.songs.value!!)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -67,6 +67,22 @@ data class Artist(
|
|||
}
|
||||
return num
|
||||
}
|
||||
val songs: MutableList<Song>
|
||||
get() {
|
||||
val songs = mutableListOf<Song>()
|
||||
albums.forEach {
|
||||
songs.addAll(it.songs)
|
||||
}
|
||||
return songs
|
||||
}
|
||||
val genreSongs: MutableList<Song>
|
||||
get() {
|
||||
val songs = mutableListOf<Song>()
|
||||
genres.forEach {
|
||||
songs.addAll(it.songs)
|
||||
}
|
||||
return songs
|
||||
}
|
||||
}
|
||||
|
||||
// Genre
|
||||
|
@ -93,6 +109,14 @@ data class Genre(
|
|||
}
|
||||
return num
|
||||
}
|
||||
val songs: MutableList<Song>
|
||||
get() {
|
||||
val songs = mutableListOf<Song>()
|
||||
artists.forEach {
|
||||
songs.addAll(it.songs)
|
||||
}
|
||||
return songs
|
||||
}
|
||||
}
|
||||
|
||||
// Header [Used for search, nothing else]
|
||||
|
|
|
@ -15,6 +15,8 @@ import androidx.navigation.fragment.findNavController
|
|||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentPlaybackBinding
|
||||
import org.oxycblt.auxio.theme.accent
|
||||
import org.oxycblt.auxio.theme.disable
|
||||
import org.oxycblt.auxio.theme.enable
|
||||
import org.oxycblt.auxio.theme.toColor
|
||||
|
||||
class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||
|
@ -33,8 +35,11 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
|||
|
||||
// Create accents & icons to use
|
||||
val accentColor = ColorStateList.valueOf(accent.first.toColor(requireContext()))
|
||||
val inactiveColor = ColorStateList.valueOf(R.color.control_color.toColor(requireContext()))
|
||||
val controlColor = ColorStateList.valueOf(R.color.control_color.toColor(requireContext()))
|
||||
val normalTextColor = binding.playbackDurationCurrent.currentTextColor
|
||||
val disabledColor = ColorStateList.valueOf(
|
||||
R.color.inactive_color.toColor(requireContext())
|
||||
)
|
||||
|
||||
val iconPauseToPlay = ContextCompat.getDrawable(
|
||||
requireContext(), R.drawable.ic_pause_to_play
|
||||
|
@ -60,10 +65,29 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
|||
// --- VIEWMODEL SETUP --
|
||||
|
||||
playbackModel.currentSong.observe(viewLifecycleOwner) {
|
||||
Log.d(this::class.simpleName, "Updating song display to ${it.name}.")
|
||||
|
||||
binding.song = it
|
||||
binding.playbackSeekBar.max = it.seconds.toInt()
|
||||
}
|
||||
|
||||
playbackModel.currentIndex.observe(viewLifecycleOwner) {
|
||||
if (it > 0) {
|
||||
binding.playbackSkipPrev.enable(requireContext())
|
||||
} else {
|
||||
binding.playbackSkipPrev.disable(requireContext())
|
||||
}
|
||||
|
||||
Log.d(this::class.simpleName, it.toString())
|
||||
|
||||
if (it < playbackModel.queue.value!!.lastIndex) {
|
||||
binding.playbackSkipNext.enable(requireContext())
|
||||
} else {
|
||||
Log.d(this::class.simpleName, "Fucking stupid retard.")
|
||||
binding.playbackSkipNext.disable(requireContext())
|
||||
}
|
||||
}
|
||||
|
||||
playbackModel.isPlaying.observe(viewLifecycleOwner) {
|
||||
if (it) {
|
||||
// Animate the playing status and switch the button to the accent color
|
||||
|
@ -76,7 +100,7 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
|||
binding.playbackPlayPause.setImageDrawable(iconPlayToPause)
|
||||
iconPlayToPause.start()
|
||||
|
||||
binding.playbackPlayPause.backgroundTintList = inactiveColor
|
||||
binding.playbackPlayPause.backgroundTintList = controlColor
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,12 @@ class PlaybackViewModel : ViewModel() {
|
|||
private val mCurrentSong = MutableLiveData<Song>()
|
||||
val currentSong: LiveData<Song> get() = mCurrentSong
|
||||
|
||||
private val mCurrentIndex = MutableLiveData(0)
|
||||
val currentIndex: LiveData<Int> get() = mCurrentIndex
|
||||
|
||||
private val mQueue = MutableLiveData(mutableListOf<Song>())
|
||||
val queue: LiveData<MutableList<Song>> get() = mQueue
|
||||
|
||||
private val mCurrentDuration = MutableLiveData(0L)
|
||||
val currentDuration: LiveData<Long> get() = mCurrentDuration
|
||||
|
||||
|
@ -33,7 +39,15 @@ class PlaybackViewModel : ViewModel() {
|
|||
if (mCurrentSong.value != null) it.toInt() else 0
|
||||
}
|
||||
|
||||
fun updateSong(song: Song) {
|
||||
// Update the current song while changing the queue to All Songs.
|
||||
fun update(song: Song, allSongs: List<Song>) {
|
||||
updatePlayback(song)
|
||||
|
||||
mQueue.value = allSongs.toMutableList()
|
||||
mCurrentIndex.value = allSongs.indexOf(song)
|
||||
}
|
||||
|
||||
private fun updatePlayback(song: Song) {
|
||||
mCurrentSong.value = song
|
||||
mCurrentDuration.value = 0
|
||||
|
||||
|
@ -56,4 +70,20 @@ class PlaybackViewModel : ViewModel() {
|
|||
fun updateCurrentDurationWithProgress(progress: Int) {
|
||||
mCurrentDuration.value = progress.toLong()
|
||||
}
|
||||
|
||||
fun skipNext() {
|
||||
if (mCurrentIndex.value!! < mQueue.value!!.size) {
|
||||
mCurrentIndex.value = mCurrentIndex.value!!.inc()
|
||||
}
|
||||
|
||||
updatePlayback(mQueue.value!![mCurrentIndex.value!!])
|
||||
}
|
||||
|
||||
fun skipPrev() {
|
||||
if (mCurrentIndex.value!! > 0) {
|
||||
mCurrentIndex.value = mCurrentIndex.value!!.dec()
|
||||
}
|
||||
|
||||
updatePlayback(mQueue.value!![mCurrentIndex.value!!])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ class SongsFragment : Fragment() {
|
|||
|
||||
binding.songRecycler.apply {
|
||||
adapter = SongAdapter(musicModel.songs.value!!) {
|
||||
playbackModel.updateSong(it)
|
||||
playbackModel.update(it, musicModel.songs.value!!)
|
||||
}
|
||||
applyDivider()
|
||||
setHasFixedSize(true)
|
||||
|
|
|
@ -103,6 +103,16 @@ fun ImageButton.disable(context: Context) {
|
|||
isEnabled = false
|
||||
}
|
||||
|
||||
fun ImageButton.enable(context: Context) {
|
||||
if (!isEnabled) {
|
||||
imageTintList = ColorStateList.valueOf(
|
||||
R.color.control_color.toColor(context)
|
||||
)
|
||||
|
||||
isEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
// Apply a custom vertical divider
|
||||
fun RecyclerView.applyDivider() {
|
||||
val div = DividerItemDecoration(
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
android:tint="@color/control_color">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z" />
|
||||
android:pathData="M 5.88,7.06 12,13.166667 18.12,7.06 20,8.94 l -8,8 -8,-8 z" />
|
||||
</vector>
|
11
app/src/main/res/drawable/ic_skip_next.xml
Normal file
11
app/src/main/res/drawable/ic_skip_next.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="@color/control_color">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z" />
|
||||
</vector>
|
11
app/src/main/res/drawable/ic_skip_prev.xml
Normal file
11
app/src/main/res/drawable/ic_skip_prev.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="@color/control_color">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M6,6h2v12L6,18zM9.5,12l8.5,6L18,6z" />
|
||||
</vector>
|
|
@ -19,6 +19,7 @@
|
|||
android:id="@+id/playback_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true"
|
||||
android:background="@color/background">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
|
@ -157,5 +158,33 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:src="@drawable/ic_play_to_pause" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/playback_skip_next"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="@dimen/size_play_pause_compact"
|
||||
android:layout_height="@dimen/size_play_pause_compact"
|
||||
android:layout_marginStart="@dimen/margin_mid_large"
|
||||
android:contentDescription="@string/description_skip_next"
|
||||
android:background="@drawable/ui_unbounded_ripple"
|
||||
android:src="@drawable/ic_skip_next"
|
||||
android:onClick="@{() -> playbackModel.skipNext()}"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/playback_play_pause"
|
||||
app:layout_constraintStart_toEndOf="@+id/playback_play_pause"
|
||||
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/playback_skip_prev"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="@dimen/size_play_pause_compact"
|
||||
android:layout_height="@dimen/size_play_pause_compact"
|
||||
android:src="@drawable/ic_skip_prev"
|
||||
android:contentDescription="@string/description_skip_prev"
|
||||
android:background="@drawable/ui_unbounded_ripple"
|
||||
android:layout_marginEnd="@dimen/margin_mid_large"
|
||||
android:onClick="@{() -> playbackModel.skipPrev()}"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/playback_play_pause"
|
||||
app:layout_constraintEnd_toStartOf="@+id/playback_play_pause"
|
||||
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
|
@ -39,6 +39,8 @@
|
|||
<string name="description_sort_alpha_up">Sort from Z to A</string>
|
||||
<string name="description_play">Play</string>
|
||||
<string name="description_pause">Pause</string>
|
||||
<string name="description_skip_next">Skip to next song</string>
|
||||
<string name="description_skip_prev">Skip to last song</string>
|
||||
|
||||
<!-- Placeholder Namespace | Placeholder values -->
|
||||
<string name="placeholder_genre">Unknown Genre</string>
|
||||
|
|
Loading…
Reference in a new issue