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 4b67fa68e..6b6f7ca24 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.formatDurationMs +import org.oxycblt.auxio.music.formatDurationMs /** * A dialog displayed when "View properties" is selected on a song, showing more information about 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 fed654348..16da37b57 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 @@ -34,7 +34,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.formatDurationMs +import org.oxycblt.auxio.music.formatDurationMs import org.oxycblt.auxio.util.getPlural import org.oxycblt.auxio.util.inflater 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 c3c3c78a8..331196ff3 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.formatDurationMs +import org.oxycblt.auxio.music.formatDurationMs import org.oxycblt.auxio.util.getPlural import org.oxycblt.auxio.util.inflater 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 82e1c8375..307e43bc8 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 @@ -34,8 +34,8 @@ import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.MenuItemListener import org.oxycblt.auxio.ui.recycler.SyncListDiffer import org.oxycblt.auxio.util.collectImmediately -import org.oxycblt.auxio.util.formatDurationMs -import org.oxycblt.auxio.util.secsToMs +import org.oxycblt.auxio.music.formatDurationMs +import org.oxycblt.auxio.music.secsToMs /** * A [HomeListFragment] for showing a list of [Album]s. 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 224baccd7..65abed109 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 @@ -32,7 +32,7 @@ import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.MenuItemListener import org.oxycblt.auxio.ui.recycler.SyncListDiffer import org.oxycblt.auxio.util.collectImmediately -import org.oxycblt.auxio.util.formatDurationMs +import org.oxycblt.auxio.music.formatDurationMs /** * A [HomeListFragment] for showing a list of [Artist]s. 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 640711ddd..e21f08496 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 @@ -32,7 +32,7 @@ import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.MenuItemListener import org.oxycblt.auxio.ui.recycler.SyncListDiffer import org.oxycblt.auxio.util.collectImmediately -import org.oxycblt.auxio.util.formatDurationMs +import org.oxycblt.auxio.music.formatDurationMs /** * A [HomeListFragment] for showing a list of [Genre]s. 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 78d578e10..22383810e 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 @@ -36,8 +36,8 @@ import org.oxycblt.auxio.ui.recycler.SongViewHolder import org.oxycblt.auxio.ui.recycler.SyncListDiffer import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.context -import org.oxycblt.auxio.util.formatDurationMs -import org.oxycblt.auxio.util.secsToMs +import org.oxycblt.auxio.music.formatDurationMs +import org.oxycblt.auxio.music.secsToMs /** * A [HomeListFragment] for showing a list of [Song]s. diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicUtil.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicUtil.kt index 6ccc54a6d..d67038c95 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicUtil.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicUtil.kt @@ -23,18 +23,12 @@ import android.content.Context import android.database.Cursor import android.net.Uri import android.provider.MediaStore +import android.text.format.DateUtils import androidx.core.text.isDigitsOnly import org.oxycblt.auxio.R -import org.oxycblt.auxio.util.nonZeroOrNull +import org.oxycblt.auxio.util.logD import java.util.UUID -/** Shortcut to resolve a year from a nullable date. Will return "No Date" if it is null. */ -fun Date?.resolveYear(context: Context) = - this?.resolveYear(context) ?: context.getString(R.string.def_date) - -/** Converts this string to a UUID, or returns null if it is not valid. */ -fun String.toUuid() = try { UUID.fromString(this) } catch (e: IllegalArgumentException) { null } - /** Shortcut for making a [ContentResolver] query with less superfluous arguments. */ fun ContentResolver.queryCursor( uri: Uri, @@ -65,3 +59,61 @@ val Long.audioUri: Uri /** Converts a [Long] Album ID into a URI pointing to MediaStore-cached album art. */ val Long.albumCoverUri: Uri get() = ContentUris.withAppendedId(EXTERNAL_ALBUM_ART_URI, this) + + +/** Shortcut to resolve a year from a nullable date. Will return "No Date" if it is null. */ +fun Date?.resolveYear(context: Context) = + this?.resolveYear(context) ?: context.getString(R.string.def_date) + +/** Converts this string to a UUID, or returns null if it is not valid. */ +fun String.toUuid() = try { UUID.fromString(this) } catch (e: IllegalArgumentException) { null } + +/** Converts a long in milliseconds to a long in deci-seconds */ +fun Long.msToDs() = floorDiv(100) + +/** Converts a long in milliseconds to a long in seconds */ +fun Long.msToSecs() = floorDiv(1000) + +/** Converts a long in deci-seconds to a long in milliseconds. */ +fun Long.dsToMs() = times(100) + +/** Converts a long in deci-seconds to a long in seconds. */ +fun Long.dsToSecs() = floorDiv(10) + +/** Converts a long in seconds to a long in milliseconds. */ +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.formatDurationSecs(isElapsed: Boolean): String { + if (!isElapsed && this == 0L) { + logD("Non-elapsed duration is zero, using --:--") + return "--:--" + } + + var durationString = DateUtils.formatElapsedTime(this) + + // If the duration begins with a excess zero [e.g 01:42], then cut it off. + if (durationString[0] == '0') { + durationString = durationString.slice(1 until durationString.length) + } + + return durationString +} \ No newline at end of file diff --git a/app/src/main/java/org/oxycblt/auxio/music/system/ExoPlayerBackend.kt b/app/src/main/java/org/oxycblt/auxio/music/system/ExoPlayerBackend.kt index aae3e529c..237aa095d 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/system/ExoPlayerBackend.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/system/ExoPlayerBackend.kt @@ -45,15 +45,15 @@ import org.oxycblt.auxio.util.logW * * @author OxygenCobalt */ -class ExoPlayerBackend(private val inner: MediaStoreBackend) : Indexer.Backend { +class ExoPlayerBackend(private val context: Context, private val inner: MediaStoreBackend) : Indexer.Backend { + private val settings = Settings(context) private val taskPool: Array = arrayOfNulls(TASK_CAPACITY) // No need to implement our own query logic, as this backend is still reliant on // MediaStore. - override fun query(context: Context) = inner.query(context) + override fun query() = inner.query() override fun buildSongs( - context: Context, cursor: Cursor, emitIndexing: (Indexer.Indexing) -> Unit ): List { @@ -82,11 +82,11 @@ class ExoPlayerBackend(private val inner: MediaStoreBackend) : Indexer.Backend { if (song != null) { songs.add(song) emitIndexing(Indexer.Indexing.Songs(songs.size, total)) - taskPool[i] = Task(context, raw) + taskPool[i] = Task(context, settings, raw) break@spin } } else { - taskPool[i] = Task(context, raw) + taskPool[i] = Task(context, settings, raw) break@spin } } @@ -122,8 +122,7 @@ class ExoPlayerBackend(private val inner: MediaStoreBackend) : Indexer.Backend { * Wraps an ExoPlayer metadata retrieval task in a safe abstraction. Access is done with [get]. * @author OxygenCobalt */ -class Task(context: Context, private val raw: Song.Raw) { - private val settings = Settings(context) +class Task(context: Context, private val settings: Settings, private val raw: Song.Raw) { private val future = MetadataRetriever.retrieveMetadata( context, diff --git a/app/src/main/java/org/oxycblt/auxio/music/system/Indexer.kt b/app/src/main/java/org/oxycblt/auxio/music/system/Indexer.kt index 8d9f3010d..b801c25d6 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/system/Indexer.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/system/Indexer.kt @@ -128,8 +128,6 @@ class Indexer { * complete, a new completion state will be pushed to each callback. */ suspend fun index(context: Context) { - requireBackgroundThread() - val handle = guard.newHandle() val notGranted = @@ -203,20 +201,20 @@ class Indexer { val mediaStoreBackend = when { - Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> Api30MediaStoreBackend() - Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> Api29MediaStoreBackend() - else -> Api21MediaStoreBackend() + Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> Api30MediaStoreBackend(context) + Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> Api29MediaStoreBackend(context) + else -> Api21MediaStoreBackend(context) } val settings = Settings(context) val backend = if (settings.useQualityTags) { - ExoPlayerBackend(mediaStoreBackend) + ExoPlayerBackend(context, mediaStoreBackend) } else { mediaStoreBackend } - val songs = buildSongs(context, backend, handle) + val songs = buildSongs(backend, handle) if (songs.isEmpty()) { return null } @@ -243,16 +241,16 @@ class Indexer { * [buildGenres] functions must be called with the returned list so that all songs are properly * linked up. */ - private fun buildSongs(context: Context, backend: Backend, handle: Long): List { + private fun buildSongs(backend: Backend, handle: Long): List { val start = System.currentTimeMillis() var songs = - backend.query(context).use { cursor -> + backend.query().use { cursor -> logD( "Successfully queried media database " + "in ${System.currentTimeMillis() - start}ms") - backend.buildSongs(context, cursor) { emitIndexing(it, handle) } + backend.buildSongs(cursor) { emitIndexing(it, handle) } } // Deduplicate songs to prevent (most) deformed music clones @@ -425,11 +423,10 @@ class Indexer { /** Represents a backend that metadata can be extracted from. */ interface Backend { /** Query the media database for a basic cursor. */ - fun query(context: Context): Cursor + fun query(): Cursor /** Create a list of songs from the [Cursor] queried in [query]. */ fun buildSongs( - context: Context, cursor: Cursor, emitIndexing: (Indexing) -> Unit ): List diff --git a/app/src/main/java/org/oxycblt/auxio/music/system/MediaStoreBackend.kt b/app/src/main/java/org/oxycblt/auxio/music/system/MediaStoreBackend.kt index 32826f4b2..2a50024f1 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/system/MediaStoreBackend.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/system/MediaStoreBackend.kt @@ -91,7 +91,6 @@ import org.oxycblt.auxio.util.logD * I wish I was born in the neolithic. */ -// TODO: Make context a member var to cache Settings // TODO: Move duration util to MusicUtil /** @@ -99,7 +98,7 @@ import org.oxycblt.auxio.util.logD * not a fully-featured class by itself, and it's API-specific derivatives should be used instead. * @author OxygenCobalt */ -abstract class MediaStoreBackend : Indexer.Backend { +abstract class MediaStoreBackend(private val context: Context) : Indexer.Backend { private var idIndex = -1 private var titleIndex = -1 private var displayNameIndex = -1 @@ -113,10 +112,10 @@ abstract class MediaStoreBackend : Indexer.Backend { private var artistIndex = -1 private var albumArtistIndex = -1 + private val settings = Settings(context) protected val volumes = mutableListOf() - override fun query(context: Context): Cursor { - val settings = Settings(context) + override fun query(): Cursor { val storageManager = context.getSystemServiceCompat(StorageManager::class) volumes.addAll(storageManager.storageVolumesCompat) val dirs = settings.getMusicDirs(storageManager) @@ -162,12 +161,9 @@ abstract class MediaStoreBackend : Indexer.Backend { } override fun buildSongs( - context: Context, cursor: Cursor, emitIndexing: (Indexer.Indexing) -> Unit ): List { - val settings = Settings(context) - val rawSongs = mutableListOf() while (cursor.moveToNext()) { rawSongs.add(buildRawSong(context, cursor)) @@ -255,8 +251,6 @@ abstract class MediaStoreBackend : Indexer.Backend { * outlined in [projection]. */ open fun buildRawSong(context: Context, cursor: Cursor): Song.Raw { - val settings = Settings(context) - // Initialize our cursor indices if we haven't already. if (idIndex == -1) { idIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns._ID) @@ -349,7 +343,7 @@ abstract class MediaStoreBackend : Indexer.Backend { * A [MediaStoreBackend] that completes the music loading process in a way compatible from * @author OxygenCobalt */ -class Api21MediaStoreBackend : MediaStoreBackend() { +class Api21MediaStoreBackend(context: Context) : MediaStoreBackend(context) { private var trackIndex = -1 private var dataIndex = -1 @@ -414,7 +408,7 @@ class Api21MediaStoreBackend : MediaStoreBackend() { * @author OxygenCobalt */ @RequiresApi(Build.VERSION_CODES.Q) -open class BaseApi29MediaStoreBackend : MediaStoreBackend() { +open class BaseApi29MediaStoreBackend(context: Context) : MediaStoreBackend(context) { private var volumeIndex = -1 private var relativePathIndex = -1 @@ -466,7 +460,7 @@ open class BaseApi29MediaStoreBackend : MediaStoreBackend() { * @author OxygenCobalt */ @RequiresApi(Build.VERSION_CODES.Q) -open class Api29MediaStoreBackend : BaseApi29MediaStoreBackend() { +open class Api29MediaStoreBackend(context: Context) : BaseApi29MediaStoreBackend(context) { private var trackIndex = -1 override val projection: Array @@ -497,7 +491,7 @@ open class Api29MediaStoreBackend : BaseApi29MediaStoreBackend() { * @author OxygenCobalt */ @RequiresApi(Build.VERSION_CODES.R) -class Api30MediaStoreBackend : BaseApi29MediaStoreBackend() { +class Api30MediaStoreBackend(context: Context) : BaseApi29MediaStoreBackend(context) { private var trackIndex: Int = -1 private var discIndex: Int = -1 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 1878ecd82..abbc4d930 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarFragment.kt @@ -34,7 +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 +import org.oxycblt.auxio.music.msToDs /** * A fragment showing the current playback state in a compact manner. Used as the bar for the 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 797ad7f74..06ec5d531 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt @@ -35,7 +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.music.msToDs import org.oxycblt.auxio.util.showToast import org.oxycblt.auxio.util.systemBarInsetsCompat 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 459f0155d..759e82855 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -36,9 +36,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.music.dsToMs import org.oxycblt.auxio.util.logE -import org.oxycblt.auxio.util.msToDs +import org.oxycblt.auxio.music.msToDs /** * The ViewModel that provides a UI frontend for [PlaybackStateManager]. 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 6b282efae..98f599de4 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.formatDurationDs +import org.oxycblt.auxio.music.formatDurationDs import org.oxycblt.auxio.util.inflater import org.oxycblt.auxio.util.logD 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 57c2735db..c196f91c8 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.formatDurationMs +import org.oxycblt.auxio.music.formatDurationMs import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.showToast import org.oxycblt.auxio.util.systemBarInsetsCompat 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 202d881f6..588e150ab 100644 --- a/app/src/main/java/org/oxycblt/auxio/util/PrimitiveUtil.kt +++ b/app/src/main/java/org/oxycblt/auxio/util/PrimitiveUtil.kt @@ -50,55 +50,6 @@ fun Int.nonZeroOrNull() = if (this > 0) this else null /** Returns null if this value is not in [range]. */ fun Int.inRangeOrNull(range: IntRange) = if (range.contains(this)) this else null -/** Converts a long in milliseconds to a long in deci-seconds */ -fun Long.msToDs() = floorDiv(100) - -/** Converts a long in milliseconds to a long in seconds */ -fun Long.msToSecs() = floorDiv(1000) - -/** Converts a long in deci-seconds to a long in milliseconds. */ -fun Long.dsToMs() = times(100) - -/** Converts a long in deci-seconds to a long in seconds. */ -fun Long.dsToSecs() = floorDiv(10) - -/** Converts a long in seconds to a long in milliseconds. */ -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.formatDurationSecs(isElapsed: Boolean): String { - if (!isElapsed && this == 0L) { - logD("Non-elapsed duration is zero, using --:--") - return "--:--" - } - - var durationString = DateUtils.formatElapsedTime(this) - - // If the duration begins with a excess zero [e.g 01:42], then cut it off. - if (durationString[0] == '0') { - durationString = durationString.slice(1 until durationString.length) - } - - return durationString -} /** Lazily reflect to retrieve a [Field]. */ fun lazyReflectedField(clazz: KClass<*>, field: String) = lazy {