From 13793fdfe267bfee81e0f082b8edb5bb25cc52a2 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Mon, 29 Aug 2022 09:44:31 -0600 Subject: [PATCH] playback: add smooth seeking Switch position math to rely on deciseconds (1/10th of a second) instead of full seconds. This makes seeking and position management much smoother, with minimal performance cost. In the future I may try to migrate the playback state so that the position calculations are done on the UI end, but this works for now. --- CHANGELOG.md | 6 +++++ .../oxycblt/auxio/detail/SongDetailDialog.kt | 4 +-- .../detail/recycler/AlbumDetailAdapter.kt | 8 +++--- .../detail/recycler/GenreDetailAdapter.kt | 6 ++--- .../auxio/home/list/AlbumListFragment.kt | 7 ++--- .../auxio/home/list/ArtistListFragment.kt | 4 +-- .../auxio/home/list/GenreListFragment.kt | 4 +-- .../auxio/home/list/SongListFragment.kt | 7 ++--- .../java/org/oxycblt/auxio/music/Music.kt | 16 +++++++----- .../auxio/playback/PlaybackBarFragment.kt | 10 ++++--- .../auxio/playback/PlaybackPanelFragment.kt | 13 +++++----- .../auxio/playback/PlaybackViewModel.kt | 16 +++++++----- .../oxycblt/auxio/playback/StyledSeekBar.kt | 20 +++++++------- .../oxycblt/auxio/settings/AboutFragment.kt | 4 +-- .../main/java/org/oxycblt/auxio/ui/Sort.kt | 11 +++----- .../org/oxycblt/auxio/util/PrimitiveUtil.kt | 26 ++++++++++++++++++- 16 files changed, 99 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae954ceba..c63b34c89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## dev +#### What's New +- Made the SeekBar much smoother and better to use + +#### What's Fixed +- Fixed issue where fast scroller popup would not appear + ## 2.6.2 #### What's New diff --git a/app/src/main/java/org/oxycblt/auxio/detail/SongDetailDialog.kt b/app/src/main/java/org/oxycblt/auxio/detail/SongDetailDialog.kt index dfccbb22d..a49d94374 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/SongDetailDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/SongDetailDialog.kt @@ -29,7 +29,7 @@ import org.oxycblt.auxio.databinding.DialogSongDetailBinding import org.oxycblt.auxio.ui.fragment.ViewBindingDialogFragment import org.oxycblt.auxio.util.androidActivityViewModels import org.oxycblt.auxio.util.collectImmediately -import org.oxycblt.auxio.util.formatDuration +import org.oxycblt.auxio.util.formatDurationMs /** * A dialog displayed when "View properties" is selected on a song, showing more information about @@ -70,7 +70,7 @@ class SongDetailDialog : ViewBindingDialogFragment() { binding.detailRelativeDir.setText(song.song.path.parent.resolveName(context)) binding.detailFormat.setText(song.info.resolvedMimeType.resolveName(context)) binding.detailSize.setText(Formatter.formatFileSize(context, song.song.size)) - binding.detailDuration.setText(song.song.durationSecs.formatDuration(true)) + binding.detailDuration.setText(song.song.durationMs.formatDurationMs(true)) if (song.info.bitrateKbps != null) { binding.detailBitrate.setText( diff --git a/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt index 8faf31bdc..1177502ca 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt @@ -33,7 +33,7 @@ import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.MenuItemListener import org.oxycblt.auxio.ui.recycler.SimpleItemCallback import org.oxycblt.auxio.util.context -import org.oxycblt.auxio.util.formatDuration +import org.oxycblt.auxio.util.formatDurationMs import org.oxycblt.auxio.util.getPlural import org.oxycblt.auxio.util.inflater @@ -131,7 +131,7 @@ private class AlbumDetailViewHolder private constructor(private val binding: Ite val songCount = context.getPlural(R.plurals.fmt_song_count, item.songs.size) - val duration = item.durationSecs.formatDuration(true) + val duration = item.durationMs.formatDurationMs(true) text = context.getString(R.string.fmt_three, date, songCount, duration) } @@ -157,7 +157,7 @@ private class AlbumDetailViewHolder private constructor(private val binding: Ite oldItem.artist.rawName == newItem.artist.rawName && oldItem.date == newItem.date && oldItem.songs.size == newItem.songs.size && - oldItem.durationSecs == newItem.durationSecs && + oldItem.durationMs == newItem.durationMs && oldItem.releaseType == newItem.releaseType } } @@ -207,7 +207,7 @@ private class AlbumSongViewHolder private constructor(private val binding: ItemA } binding.songName.text = item.resolveName(binding.context) - binding.songDuration.text = item.durationSecs.formatDuration(false) + binding.songDuration.text = item.durationMs.formatDurationMs(false) // binding.songMenu.setOnClickListener { listener.onOpenMenu(item, it) } binding.root.setOnLongClickListener { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt index 6a1591706..1dda090e1 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt @@ -29,7 +29,7 @@ import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.SimpleItemCallback import org.oxycblt.auxio.ui.recycler.SongViewHolder import org.oxycblt.auxio.util.context -import org.oxycblt.auxio.util.formatDuration +import org.oxycblt.auxio.util.formatDurationMs import org.oxycblt.auxio.util.getPlural import org.oxycblt.auxio.util.inflater @@ -106,7 +106,7 @@ private class GenreDetailViewHolder private constructor(private val binding: Ite binding.detailName.text = item.resolveName(binding.context) binding.detailSubhead.text = binding.context.getPlural(R.plurals.fmt_song_count, item.songs.size) - binding.detailInfo.text = item.durationSecs.formatDuration(false) + binding.detailInfo.text = item.durationMs.formatDurationMs(false) binding.detailPlayButton.setOnClickListener { listener.onPlayParent() } binding.detailShuffleButton.setOnClickListener { listener.onShuffleParent() } } @@ -126,7 +126,7 @@ private class GenreDetailViewHolder private constructor(private val binding: Ite override fun areItemsTheSame(oldItem: Genre, newItem: Genre) = oldItem.rawName == newItem.rawName && oldItem.songs.size == newItem.songs.size && - oldItem.durationSecs == newItem.durationSecs + oldItem.durationMs == newItem.durationMs } } } diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt index 025aefe3f..e3a374db1 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt @@ -33,8 +33,9 @@ import org.oxycblt.auxio.ui.recycler.MenuItemListener import org.oxycblt.auxio.ui.recycler.MonoAdapter import org.oxycblt.auxio.ui.recycler.SyncBackingData import org.oxycblt.auxio.util.collectImmediately -import org.oxycblt.auxio.util.formatDuration +import org.oxycblt.auxio.util.formatDurationMs import org.oxycblt.auxio.util.logEOrThrow +import org.oxycblt.auxio.util.secsToMs /** * A [HomeListFragment] for showing a list of [Album]s. @@ -71,14 +72,14 @@ class AlbumListFragment : HomeListFragment() { is Sort.Mode.ByYear -> album.date?.resolveYear(requireContext()) // Duration -> Use formatted duration - is Sort.Mode.ByDuration -> album.durationSecs.formatDuration(false) + is Sort.Mode.ByDuration -> album.durationMs.formatDurationMs(false) // Count -> Use song count is Sort.Mode.ByCount -> album.songs.size.toString() // Last added -> Format as date is Sort.Mode.ByDateAdded -> { - val dateAddedMillis = album.dateAdded * 1000 + val dateAddedMillis = album.dateAdded.secsToMs() formatterSb.setLength(0) DateUtils.formatDateRange( context, diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/ArtistListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/ArtistListFragment.kt index 1c6190709..bacbf2154 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/ArtistListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/ArtistListFragment.kt @@ -31,7 +31,7 @@ import org.oxycblt.auxio.ui.recycler.MenuItemListener import org.oxycblt.auxio.ui.recycler.MonoAdapter import org.oxycblt.auxio.ui.recycler.SyncBackingData import org.oxycblt.auxio.util.collectImmediately -import org.oxycblt.auxio.util.formatDuration +import org.oxycblt.auxio.util.formatDurationMs import org.oxycblt.auxio.util.logEOrThrow /** @@ -61,7 +61,7 @@ class ArtistListFragment : HomeListFragment() { is Sort.Mode.ByName -> artist.sortName?.run { first().uppercase() } // Duration -> Use formatted duration - is Sort.Mode.ByDuration -> artist.durationSecs.formatDuration(false) + is Sort.Mode.ByDuration -> artist.durationMs.formatDurationMs(false) // Count -> Use song count is Sort.Mode.ByCount -> artist.songs.size.toString() diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/GenreListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/GenreListFragment.kt index d61ea89f0..8c0e77b12 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/GenreListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/GenreListFragment.kt @@ -31,7 +31,7 @@ import org.oxycblt.auxio.ui.recycler.MenuItemListener import org.oxycblt.auxio.ui.recycler.MonoAdapter import org.oxycblt.auxio.ui.recycler.SyncBackingData import org.oxycblt.auxio.util.collectImmediately -import org.oxycblt.auxio.util.formatDuration +import org.oxycblt.auxio.util.formatDurationMs import org.oxycblt.auxio.util.logEOrThrow /** @@ -61,7 +61,7 @@ class GenreListFragment : HomeListFragment() { is Sort.Mode.ByName -> genre.sortName?.run { first().uppercase() } // Duration -> Use formatted duration - is Sort.Mode.ByDuration -> genre.durationSecs.formatDuration(false) + is Sort.Mode.ByDuration -> genre.durationMs.formatDurationMs(false) // Count -> Use song count is Sort.Mode.ByCount -> genre.songs.size.toString() diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt index 30293180e..095e4d11b 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt @@ -34,8 +34,9 @@ import org.oxycblt.auxio.ui.recycler.SongViewHolder import org.oxycblt.auxio.ui.recycler.SyncBackingData import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.context -import org.oxycblt.auxio.util.formatDuration +import org.oxycblt.auxio.util.formatDurationMs import org.oxycblt.auxio.util.logEOrThrow +import org.oxycblt.auxio.util.secsToMs /** * A [HomeListFragment] for showing a list of [Song]s. @@ -78,11 +79,11 @@ class SongListFragment : HomeListFragment() { is Sort.Mode.ByYear -> song.album.date?.resolveYear(requireContext()) // Duration -> Use formatted duration - is Sort.Mode.ByDuration -> song.durationSecs.formatDuration(false) + is Sort.Mode.ByDuration -> song.durationMs.formatDurationMs(false) // Last added -> Format as date is Sort.Mode.ByDateAdded -> { - val dateAddedMillis = song.dateAdded * 1000 + val dateAddedMillis = song.dateAdded.secsToMs() formatterSb.setLength(0) DateUtils.formatDateRange( context, diff --git a/app/src/main/java/org/oxycblt/auxio/music/Music.kt b/app/src/main/java/org/oxycblt/auxio/music/Music.kt index b3969a83c..af0770036 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Music.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Music.kt @@ -62,10 +62,6 @@ sealed class Music : Item() { sealed class MusicParent : Music() { /** The songs that this parent owns. */ abstract val songs: List - - /** The formatted total duration of this genre */ - val durationSecs: Long - get() = songs.sumOf { it.durationSecs } } /** The data object for a song. */ @@ -122,9 +118,6 @@ data class Song( override fun resolveName(context: Context) = rawName - /** The duration of this song, in seconds (rounded down) */ - val durationSecs = durationMs / 1000 - private var _album: Album? = null /** The album of this song. */ val album: Album @@ -236,6 +229,9 @@ data class Album( /** The earliest date a song in this album was added. */ val dateAdded = songs.minOf { it.dateAdded } + val durationMs: Long + get() = songs.sumOf { it.durationMs } + /** Internal field. Do not use. */ val _artistGroupingId: Long get() = _artistGroupingName.toMusicId() @@ -273,6 +269,9 @@ data class Artist( /** The songs of this artist. */ override val songs = albums.flatMap { it.songs } + + val durationMs: Long + get() = songs.sumOf { it.durationMs } } /** The data object for a genre. */ @@ -291,6 +290,9 @@ data class Genre(override val rawName: String?, override val songs: List) get() = rawName.toMusicId() override fun resolveName(context: Context) = rawName ?: context.getString(R.string.def_genre) + + val durationMs: Long + get() = songs.sumOf { it.durationMs } } private fun String?.toMusicId(): Long { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarFragment.kt index cc40b22df..1878ecd82 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarFragment.kt @@ -20,6 +20,7 @@ package org.oxycblt.auxio.playback import android.os.Bundle import android.view.LayoutInflater import androidx.fragment.app.activityViewModels +import kotlin.math.max import org.oxycblt.auxio.IntegerTable import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentPlaybackBarBinding @@ -33,6 +34,7 @@ import org.oxycblt.auxio.util.androidActivityViewModels import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.getAttrColorCompat import org.oxycblt.auxio.util.getColorCompat +import org.oxycblt.auxio.util.msToDs /** * A fragment showing the current playback state in a compact manner. Used as the bar for the @@ -104,7 +106,7 @@ class PlaybackBarFragment : ViewBindingFragment() { collectImmediately(playbackModel.song, ::updateSong) collectImmediately(playbackModel.isPlaying, ::updateIsPlaying) - collectImmediately(playbackModel.positionSecs, ::updatePosition) + collectImmediately(playbackModel.positionDs, ::updatePosition) } override fun onDestroyBinding(binding: FragmentPlaybackBarBinding) { @@ -119,7 +121,7 @@ class PlaybackBarFragment : ViewBindingFragment() { binding.playbackCover.bind(song) binding.playbackSong.text = song.resolveName(context) binding.playbackInfo.text = song.resolveIndividualArtistName(context) - binding.playbackProgressBar.max = song.durationSecs.toInt() + binding.playbackProgressBar.max = song.durationMs.msToDs().toInt() } } @@ -138,8 +140,8 @@ class PlaybackBarFragment : ViewBindingFragment() { requireBinding().playbackSecondaryAction.isActivated = isShuffled } - private fun updatePosition(position: Long) { - requireBinding().playbackProgressBar.progress = position.toInt() + private fun updatePosition(positionDs: Long) { + requireBinding().playbackProgressBar.progress = positionDs.toInt() } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt index 5c52323c5..534b403ab 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt @@ -35,6 +35,7 @@ import org.oxycblt.auxio.playback.state.RepeatMode import org.oxycblt.auxio.ui.MainNavigationAction import org.oxycblt.auxio.ui.fragment.MenuFragment import org.oxycblt.auxio.util.collectImmediately +import org.oxycblt.auxio.util.msToDs import org.oxycblt.auxio.util.showToast import org.oxycblt.auxio.util.systemBarInsetsCompat @@ -109,7 +110,7 @@ class PlaybackPanelFragment : collectImmediately(playbackModel.song, ::updateSong) collectImmediately(playbackModel.parent, ::updateParent) - collectImmediately(playbackModel.positionSecs, ::updatePosition) + collectImmediately(playbackModel.positionDs, ::updatePosition) collectImmediately(playbackModel.repeatMode, ::updateRepeat) collectImmediately(playbackModel.isPlaying, ::updatePlaying) collectImmediately(playbackModel.isShuffled, ::updateShuffled) @@ -142,8 +143,8 @@ class PlaybackPanelFragment : } } - override fun seekTo(positionSecs: Long) { - playbackModel.seekTo(positionSecs) + override fun seekTo(positionDs: Long) { + playbackModel.seekTo(positionDs) } private fun updateSong(song: Song?) { @@ -155,7 +156,7 @@ class PlaybackPanelFragment : binding.playbackSong.text = song.resolveName(context) binding.playbackArtist.text = song.resolveIndividualArtistName(context) binding.playbackAlbum.text = song.album.resolveName(context) - binding.playbackSeekBar.durationSecs = song.durationSecs + binding.playbackSeekBar.durationDs = song.durationMs.msToDs() } private fun updateParent(parent: MusicParent?) { @@ -166,8 +167,8 @@ class PlaybackPanelFragment : parent?.resolveName(context) ?: context.getString(R.string.lbl_all_songs) } - private fun updatePosition(positionSecs: Long) { - requireBinding().playbackSeekBar.positionSecs = positionSecs + private fun updatePosition(positionDs: Long) { + requireBinding().playbackSeekBar.positionDs = positionDs } private fun updateRepeat(repeatMode: RepeatMode) { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt index 694f3bd0b..c4399bcdf 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -35,7 +35,9 @@ import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.playback.state.RepeatMode import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.util.application +import org.oxycblt.auxio.util.dsToMs import org.oxycblt.auxio.util.logE +import org.oxycblt.auxio.util.msToDs /** * The ViewModel that provides a UI frontend for [PlaybackStateManager]. @@ -60,10 +62,10 @@ class PlaybackViewModel(application: Application) : private val _isPlaying = MutableStateFlow(false) val isPlaying: StateFlow get() = _isPlaying - private val _positionSecs = MutableStateFlow(0L) - /** The current playback position, in seconds */ - val positionSecs: StateFlow - get() = _positionSecs + private val _positionDs = MutableStateFlow(0L) + /** The current playback position, in *deci-seconds* */ + val positionDs: StateFlow + get() = _positionDs private val _repeatMode = MutableStateFlow(RepeatMode.NONE) /** The current repeat mode, see [RepeatMode] for more information */ @@ -147,8 +149,8 @@ class PlaybackViewModel(application: Application) : // --- PLAYER FUNCTIONS --- /** Update the position and push it to [PlaybackStateManager] */ - fun seekTo(positionSecs: Long) { - playbackManager.seekTo(positionSecs * 1000) + fun seekTo(positionDs: Long) { + playbackManager.seekTo(positionDs.dsToMs()) } // --- QUEUE FUNCTIONS --- @@ -267,7 +269,7 @@ class PlaybackViewModel(application: Application) : } override fun onPositionChanged(positionMs: Long) { - _positionSecs.value = positionMs / 1000 + _positionDs.value = positionMs.msToDs() } override fun onPlayingChanged(isPlaying: Boolean) { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/StyledSeekBar.kt b/app/src/main/java/org/oxycblt/auxio/playback/StyledSeekBar.kt index bd8fa9997..657c120d0 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/StyledSeekBar.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/StyledSeekBar.kt @@ -22,7 +22,7 @@ import android.util.AttributeSet import com.google.android.material.slider.Slider import kotlin.math.max import org.oxycblt.auxio.databinding.ViewSeekBarBinding -import org.oxycblt.auxio.util.formatDuration +import org.oxycblt.auxio.util.formatDurationDs import org.oxycblt.auxio.util.inflater import org.oxycblt.auxio.util.logD @@ -66,18 +66,18 @@ constructor( * The current position, in seconds. This is the current value of the SeekBar and is indicated * by the start TextView in the layout. */ - var positionSecs: Long + var positionDs: Long get() = binding.seekBarSlider.value.toLong() set(value) { // Sanity check: Ensure that this value is within the duration and will not crash // the app, and that the user is not currently seeking (which would cause the SeekBar // to jump around). - if (value <= durationSecs && !isActivated) { + if (value <= durationDs && !isActivated) { binding.seekBarSlider.value = value.toFloat() // We would want to keep this in the callback, but the callback only fires when // a value changes completely, and sometimes that does not happen with this view. - binding.seekBarPosition.text = value.formatDuration(true) + binding.seekBarPosition.text = value.formatDurationDs(true) } } @@ -85,7 +85,7 @@ constructor( * The current duration, in seconds. This is the end value of the SeekBar and is indicated by * the end TextView in the layout. */ - var durationSecs: Long + var durationDs: Long get() = binding.seekBarSlider.valueTo.toLong() set(value) { // Sanity check 1: If this is a value so low that it effectively rounds down to @@ -95,13 +95,13 @@ constructor( // Sanity check 2: If the current value exceeds the new duration value, clamp it // down so that we don't crash and instead have an annoying visual flicker. - if (positionSecs > to) { - logD("Clamping invalid position [current: $positionSecs new max: $to]") + if (positionDs > to) { + logD("Clamping invalid position [current: $positionDs new max: $to]") binding.seekBarSlider.value = to.toFloat() } binding.seekBarSlider.valueTo = to.toFloat() - binding.seekBarDuration.text = value.formatDuration(false) + binding.seekBarDuration.text = value.formatDurationDs(false) } override fun onStartTrackingTouch(slider: Slider) { @@ -119,13 +119,13 @@ constructor( } override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) { - binding.seekBarPosition.text = value.toLong().formatDuration(true) + binding.seekBarPosition.text = value.toLong().formatDurationDs(true) } interface Callback { /** * Called when a seek event was completed and the new position must be seeked to by the app. */ - fun seekTo(positionSecs: Long) + fun seekTo(positionDs: Long) } } diff --git a/app/src/main/java/org/oxycblt/auxio/settings/AboutFragment.kt b/app/src/main/java/org/oxycblt/auxio/settings/AboutFragment.kt index eafa08110..57c2735db 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/AboutFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/AboutFragment.kt @@ -39,7 +39,7 @@ import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.ui.fragment.ViewBindingFragment import org.oxycblt.auxio.util.androidActivityViewModels import org.oxycblt.auxio.util.collectImmediately -import org.oxycblt.auxio.util.formatDuration +import org.oxycblt.auxio.util.formatDurationMs import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.showToast import org.oxycblt.auxio.util.systemBarInsetsCompat @@ -84,7 +84,7 @@ class AboutFragment : ViewBindingFragment() { binding.aboutTotalDuration.text = getString( R.string.fmt_lib_total_duration, - songs.sumOf { it.durationSecs }.formatDuration(false)) + songs.sumOf { it.durationMs }.formatDurationMs(false)) } private fun updateAlbumCount(albums: List) { diff --git a/app/src/main/java/org/oxycblt/auxio/ui/Sort.kt b/app/src/main/java/org/oxycblt/auxio/ui/Sort.kt index 32091dde2..892d34c93 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/Sort.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/Sort.kt @@ -205,23 +205,20 @@ data class Sort(val mode: Mode, val isAscending: Boolean) { override fun getSongComparator(ascending: Boolean): Comparator = MultiComparator( - compareByDynamic(ascending) { it.durationSecs }, - compareBy(BasicComparator.SONG)) + compareByDynamic(ascending) { it.durationMs }, compareBy(BasicComparator.SONG)) override fun getAlbumComparator(ascending: Boolean): Comparator = MultiComparator( - compareByDynamic(ascending) { it.durationSecs }, - compareBy(BasicComparator.ALBUM)) + compareByDynamic(ascending) { it.durationMs }, compareBy(BasicComparator.ALBUM)) override fun getArtistComparator(ascending: Boolean): Comparator = MultiComparator( - compareByDynamic(ascending) { it.durationSecs }, + compareByDynamic(ascending) { it.durationMs }, compareBy(BasicComparator.ARTIST)) override fun getGenreComparator(ascending: Boolean): Comparator = MultiComparator( - compareByDynamic(ascending) { it.durationSecs }, - compareBy(BasicComparator.GENRE)) + compareByDynamic(ascending) { it.durationMs }, compareBy(BasicComparator.GENRE)) } /** Sort by the amount of songs. Only applicable to music parents. */ diff --git a/app/src/main/java/org/oxycblt/auxio/util/PrimitiveUtil.kt b/app/src/main/java/org/oxycblt/auxio/util/PrimitiveUtil.kt index 2c1459b00..23c11a4ee 100644 --- a/app/src/main/java/org/oxycblt/auxio/util/PrimitiveUtil.kt +++ b/app/src/main/java/org/oxycblt/auxio/util/PrimitiveUtil.kt @@ -47,12 +47,36 @@ fun Int.nonZeroOrNull() = if (this > 0) this else null fun Int.inRangeOrNull(range: IntRange) = if (range.contains(this)) this else null +fun Long.msToDs() = floorDiv(100) + +fun Long.msToSecs() = floorDiv(1000) + +fun Long.dsToMs() = times(100) + +fun Long.dsToSecs() = floorDiv(10) + +fun Long.secsToMs() = times(1000) + +/** + * Convert a [Long] of milliseconds into a string duration. + * @param isElapsed Whether this duration is represents elapsed time. If this is false, then --:-- + * will be returned if the second value is 0. + */ +fun Long.formatDurationMs(isElapsed: Boolean) = msToSecs().formatDurationSecs(isElapsed) + +/** + * Convert a [Long] of deci-seconds into a string duration. + * @param isElapsed Whether this duration is represents elapsed time. If this is false, then --:-- + * will be returned if the second value is 0. + */ +fun Long.formatDurationDs(isElapsed: Boolean) = dsToSecs().formatDurationSecs(isElapsed) + /** * Convert a [Long] of seconds into a string duration. * @param isElapsed Whether this duration is represents elapsed time. If this is false, then --:-- * will be returned if the second value is 0. */ -fun Long.formatDuration(isElapsed: Boolean): String { +fun Long.formatDurationSecs(isElapsed: Boolean): String { if (!isElapsed && this == 0L) { logD("Non-elapsed duration is zero, using --:--") return "--:--"