Improve Progress Bars on Playback Fragments

Change the Progress Bars on PlaybackFragment/CompactFragment to reflect the total seconds in a song, instead of the percentage of completion.
This commit is contained in:
OxygenCobalt 2020-10-12 19:36:32 -06:00
parent 3bafc17d0c
commit a72ab10007
9 changed files with 30 additions and 32 deletions

View file

@ -135,14 +135,16 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
// Update the adapter with the new data // Update the adapter with the new data
libraryAdapter.updateData( libraryAdapter.updateData(
mode.getSortedBaseModelList(
when (libraryModel.showMode.value) { when (libraryModel.showMode.value) {
ShowMode.SHOW_GENRES -> mode.getSortedGenreList(musicModel.genres.value!!) ShowMode.SHOW_GENRES -> musicModel.genres.value!!
ShowMode.SHOW_ARTISTS -> mode.getSortedArtistList(musicModel.artists.value!!) ShowMode.SHOW_ARTISTS -> musicModel.artists.value!!
ShowMode.SHOW_ALBUMS -> mode.getSortedAlbumList(musicModel.albums.value!!) ShowMode.SHOW_ALBUMS -> musicModel.albums.value!!
else -> mode.getSortedArtistList(musicModel.artists.value!!) else -> musicModel.artists.value!!
} }
) )
)
// Then update the menu item in the toolbar to reflect the new mode // Then update the menu item in the toolbar to reflect the new mode
binding.libraryToolbar.menu.forEach { binding.libraryToolbar.menu.forEach {

View file

@ -56,14 +56,14 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
LoadingFragmentDirections.actionToMain() LoadingFragmentDirections.actionToMain()
) )
} else { } else {
// If the response wasn't a success, then show the specific error message
// depending on which error response was given, along with a retry button
binding.loadingErrorText.text = binding.loadingErrorText.text =
if (it == MusicLoaderResponse.NO_MUSIC) if (it == MusicLoaderResponse.NO_MUSIC)
getString(R.string.error_no_music) getString(R.string.error_no_music)
else else
getString(R.string.error_music_load_failed) getString(R.string.error_music_load_failed)
// If the response wasn't a success, then show the specific error message
// depending on which error response was given, along with a retry button
showError(binding) showError(binding)
binding.loadingRetryButton.visibility = View.VISIBLE binding.loadingRetryButton.visibility = View.VISIBLE
} }

View file

@ -4,7 +4,7 @@ import android.net.Uri
// --- MUSIC MODELS --- // --- MUSIC MODELS ---
// TODO: Remove parent/child references so that they can be parcelable [Would require genre rework] // TODO: Remove parent/child references so that they can be parcelable [Would require genre rework]
// TODO: Dont determine artist/album/song counts on the fly [If possible] // TODO: Don't determine artist/album/song counts on the fly [If possible]
// The base model for all music // The base model for all music
// This is used in a lot of general functions in order to cut down on code // This is used in a lot of general functions in order to cut down on code
@ -99,5 +99,5 @@ data class Genre(
// Header [Used for search, nothing else] // Header [Used for search, nothing else]
data class Header( data class Header(
override val id: Long = -1, override val id: Long = -1,
override var name: String = "" override var name: String = "",
) : BaseModel() ) : BaseModel()

View file

@ -12,7 +12,7 @@ import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
// Get the cover art for a song or album // Get the cover art for a song
@BindingAdapter("coverArt") @BindingAdapter("coverArt")
fun ImageView.bindCoverArt(song: Song) { fun ImageView.bindCoverArt(song: Song) {
val request = getDefaultRequest(context, this) val request = getDefaultRequest(context, this)
@ -23,6 +23,7 @@ fun ImageView.bindCoverArt(song: Song) {
Coil.imageLoader(context).enqueue(request) Coil.imageLoader(context).enqueue(request)
} }
// Get the cover art for an album
@BindingAdapter("coverArt") @BindingAdapter("coverArt")
fun ImageView.bindCoverArt(album: Album) { fun ImageView.bindCoverArt(album: Album) {
val request = getDefaultRequest(context, this) val request = getDefaultRequest(context, this)
@ -38,7 +39,7 @@ fun ImageView.bindCoverArt(album: Album) {
fun ImageView.bindArtistImage(artist: Artist) { fun ImageView.bindArtistImage(artist: Artist) {
val request: ImageRequest val request: ImageRequest
// If there are more than one albums, then create a mosaic of them. // If there is more than one album, then create a mosaic of them.
if (artist.numAlbums >= 4) { if (artist.numAlbums >= 4) {
val uris = mutableListOf<Uri>() val uris = mutableListOf<Uri>()
@ -114,7 +115,7 @@ fun ImageView.bindGenreImage(genre: Genre) {
Coil.imageLoader(context).enqueue(request) Coil.imageLoader(context).enqueue(request)
} }
// Get the base request used across the app. // Get the base request used across the other functions.
private fun getDefaultRequest(context: Context, imageView: ImageView): ImageRequest.Builder { private fun getDefaultRequest(context: Context, imageView: ImageView): ImageRequest.Builder {
return ImageRequest.Builder(context) return ImageRequest.Builder(context)
.crossfade(true) .crossfade(true)

View file

@ -14,6 +14,7 @@ import org.oxycblt.auxio.MainFragmentDirections
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentCompactPlaybackBinding import org.oxycblt.auxio.databinding.FragmentCompactPlaybackBinding
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import kotlin.time.seconds
class CompactPlaybackFragment : Fragment() { class CompactPlaybackFragment : Fragment() {
private val musicModel: MusicViewModel by activityViewModels { private val musicModel: MusicViewModel by activityViewModels {
@ -65,7 +66,7 @@ class CompactPlaybackFragment : Fragment() {
Log.d(this::class.simpleName, "Updating song display to ${it.name}") Log.d(this::class.simpleName, "Updating song display to ${it.name}")
binding.song = it binding.song = it
binding.playbackProgress.max = it.seconds.toInt()
binding.root.visibility = View.VISIBLE binding.root.visibility = View.VISIBLE
} }
} }
@ -81,6 +82,10 @@ class CompactPlaybackFragment : Fragment() {
} }
} }
playbackModel.formattedSeekBarProgress.observe(viewLifecycleOwner) {
binding.playbackProgress.progress = it
}
Log.d(this::class.simpleName, "Fragment Created") Log.d(this::class.simpleName, "Fragment Created")
return binding.root return binding.root

View file

@ -59,6 +59,7 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
playbackModel.currentSong.observe(viewLifecycleOwner) { playbackModel.currentSong.observe(viewLifecycleOwner) {
binding.song = it binding.song = it
binding.playbackSeekBar.max = it.seconds.toInt()
} }
playbackModel.isPlaying.observe(viewLifecycleOwner) { playbackModel.isPlaying.observe(viewLifecycleOwner) {

View file

@ -24,16 +24,18 @@ class PlaybackViewModel : ViewModel() {
private val mIsSeeking = MutableLiveData(false) private val mIsSeeking = MutableLiveData(false)
val isSeeking: LiveData<Boolean> get() = mIsSeeking val isSeeking: LiveData<Boolean> get() = mIsSeeking
// Formatted variants of the duration
val formattedCurrentDuration = Transformations.map(currentDuration) { val formattedCurrentDuration = Transformations.map(currentDuration) {
it.toDuration() it.toDuration()
} }
val formattedSeekBarProgress = Transformations.map(currentDuration) { val formattedSeekBarProgress = Transformations.map(currentDuration) {
((it.toDouble() / mCurrentSong.value!!.seconds) * 100).toInt() if (mCurrentSong.value != null) it.toInt() else 0
} }
fun updateSong(song: Song) { fun updateSong(song: Song) {
mCurrentSong.value = song mCurrentSong.value = song
mCurrentDuration.value = 0
if (!mIsPlaying.value!!) { if (!mIsPlaying.value!!) {
mIsPlaying.value = true mIsPlaying.value = true
@ -45,12 +47,13 @@ class PlaybackViewModel : ViewModel() {
mIsPlaying.value = !mIsPlaying.value!! mIsPlaying.value = !mIsPlaying.value!!
} }
// Set the seeking status
fun setSeekingStatus(status: Boolean) { fun setSeekingStatus(status: Boolean) {
mIsSeeking.value = status mIsSeeking.value = status
} }
// Update the current duration using a SeekBar progress
fun updateCurrentDurationWithProgress(progress: Int) { fun updateCurrentDurationWithProgress(progress: Int) {
mCurrentDuration.value = mCurrentDuration.value = progress.toLong()
((progress.toDouble() / 100) * mCurrentSong.value!!.seconds).toLong()
} }
} }

View file

@ -1,5 +1,6 @@
package org.oxycblt.auxio.recycler package org.oxycblt.auxio.recycler
// TODO: Swap these temp values for actual constants
enum class ShowMode(val constant: Long) { enum class ShowMode(val constant: Long) {
SHOW_GENRES(0), SHOW_ARTISTS(1), SHOW_ALBUMS(2), SHOW_SONGS(3); SHOW_GENRES(0), SHOW_ARTISTS(1), SHOW_ALBUMS(2), SHOW_SONGS(3);

View file

@ -4,7 +4,6 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
// Sorting modes // Sorting modes
@ -16,20 +15,6 @@ enum class SortMode(val iconRes: Int) {
NUMERIC_UP(R.drawable.ic_sort_numeric_up), NUMERIC_UP(R.drawable.ic_sort_numeric_up),
NUMERIC_DOWN(R.drawable.ic_sort_numeric_down); NUMERIC_DOWN(R.drawable.ic_sort_numeric_down);
fun getSortedGenreList(list: List<Genre>): List<Genre> {
return when (this) {
ALPHA_UP -> list.sortedWith(
compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name }
)
ALPHA_DOWN -> list.sortedWith(
compareBy(String.CASE_INSENSITIVE_ORDER) { it.name }
)
else -> list
}
}
fun getSortedArtistList(list: List<Artist>): List<Artist> { fun getSortedArtistList(list: List<Artist>): List<Artist> {
return when (this) { return when (this) {
ALPHA_UP -> list.sortedWith( ALPHA_UP -> list.sortedWith(