Add skipping

Add the ability to skip fowards/backwards.
This commit is contained in:
OxygenCobalt 2020-10-13 12:40:16 -06:00
parent d8f40bfd27
commit 3376b57f8e
13 changed files with 149 additions and 7 deletions

View file

@ -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 ---

View file

@ -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
}

View file

@ -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]

View file

@ -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
}
}

View file

@ -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!!])
}
}

View file

@ -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)

View file

@ -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(

View file

@ -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>

View 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>

View 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>

View file

@ -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>

View file

@ -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>