From 9fad7042586ee2c19706e4cef616276c096fce82 Mon Sep 17 00:00:00 2001 From: "Kevin T. Coughlin" <706967+KevinTCoughlin@users.noreply.github.com> Date: Thu, 28 Nov 2024 19:20:24 -0800 Subject: [PATCH] Refactor --- .idea/modules.xml | 16 --- .../kevintcoughlin/smodr/views/TextView.kt | 108 +++++++++++++++++- .../smodr/views/activities/MainActivity.kt | 20 ++-- Smodr/src/main/res/values/strings.xml | 1 + 4 files changed, 114 insertions(+), 31 deletions(-) delete mode 100644 .idea/modules.xml diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 44e8d53f..00000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Smodr/src/main/java/com/kevintcoughlin/smodr/views/TextView.kt b/Smodr/src/main/java/com/kevintcoughlin/smodr/views/TextView.kt index 91310439..4fbdada3 100644 --- a/Smodr/src/main/java/com/kevintcoughlin/smodr/views/TextView.kt +++ b/Smodr/src/main/java/com/kevintcoughlin/smodr/views/TextView.kt @@ -1,8 +1,108 @@ package com.kevintcoughlin.smodr.views -import android.text.format.DateUtils +import android.content.Context +import android.util.Log import android.widget.TextView +import androidx.annotation.CheckResult +import androidx.annotation.StringRes +import com.kevintcoughlin.smodr.R +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.withContext +import kotlin.time.Duration +import kotlin.time.DurationUnit +import kotlin.time.toDuration +import java.util.Locale -fun TextView.setElapsedTime(milliseconds: Int) { - this.text = DateUtils.formatElapsedTime(milliseconds.toLong() / 1000) -} \ No newline at end of file +/** + * Sets the text of the TextView to display elapsed time in the format HH:mm:ss or MM:ss. + * + * @param milliseconds The elapsed time in milliseconds. Must be non-negative. + * @param locale The desired locale for formatting the elapsed time. Defaults to the system's default locale. + * @param fallbackTextResId The resource ID for the fallback text if the time is invalid. + * @param formatter A custom formatter function for elapsed time. + */ +inline fun TextView.setElapsedTime( + milliseconds: Long, + locale: Locale = Locale.getDefault(), + @StringRes fallbackTextResId: Int? = null, + formatter: (Duration, Locale) -> String = ::defaultElapsedTimeFormatter +) { + val fallbackText = fallbackTextResId?.let { context.getString(it) } + ?: context.getString(R.string.elapsed_time_fallback) + + this.text = if (milliseconds >= 0) { + formatter(milliseconds.toDuration(DurationUnit.MILLISECONDS), locale) + } else { + Log.w("setElapsedTime", "Invalid duration: $milliseconds") + fallbackText + } +} + +/** + * Default formatter for elapsed time. + * + * Formats elapsed time in the format HH:mm:ss or MM:ss. For durations exceeding 1 day, it includes days. + * + * @param duration The elapsed time as a [Duration]. + * @param locale The locale to use for formatting. + * @return A formatted elapsed time string. + */ +@CheckResult +fun defaultElapsedTimeFormatter(duration: Duration, locale: Locale): String { + val totalSeconds = duration.toLong(DurationUnit.SECONDS) + val days = totalSeconds / 86400 + val hours = (totalSeconds % 86400) / 3600 + val minutes = (totalSeconds % 3600) / 60 + val seconds = totalSeconds % 60 + + return when { + days > 0 -> String.format(locale, "%d days, %d:%02d:%02d", days, hours, minutes, seconds) + hours > 0 -> String.format(locale, "%d:%02d:%02d", hours, minutes, seconds) + else -> String.format(locale, "%d:%02d", minutes, seconds) + } +} + +/** + * Coroutine-based extension to update a TextView with elapsed time periodically. + * + * The update stops when the TextView is detached from the window. + * + * @param startTimeMillis The starting time in milliseconds since epoch. + * @param locale The desired locale for formatting the elapsed time. Defaults to the system's default locale. + * @param intervalMillis The interval between updates in milliseconds. + */ +suspend fun TextView.updateElapsedTime( + startTimeMillis: Long, + locale: Locale = Locale.getDefault(), + intervalMillis: Long = 1000L +) = withContext(Dispatchers.Main) { + while (isAttachedToWindow) { + val elapsedMillis = System.currentTimeMillis() - startTimeMillis + setElapsedTime(elapsedMillis, locale) + delay(intervalMillis) + } +} + +/** + * Formats elapsed time as a string without requiring a TextView. + * + * Useful for non-UI use cases such as logging or notifications. + * + * @param duration The elapsed time as a [Duration]. + * @param locale The desired locale for formatting the elapsed time. Defaults to the system's default locale. + * @return A formatted elapsed time string. + */ +@CheckResult +fun Duration.formatElapsedTime(locale: Locale = Locale.getDefault()): String { + return defaultElapsedTimeFormatter(this, locale) +} + +/** + * Utility to retrieve and cache fallback text for a given string resource ID. + */ +private val fallbackTextCache = mutableMapOf() + +fun Context.getFallbackText(@StringRes resId: Int): String { + return fallbackTextCache.getOrPut(resId) { getString(resId) } +} diff --git a/Smodr/src/main/java/com/kevintcoughlin/smodr/views/activities/MainActivity.kt b/Smodr/src/main/java/com/kevintcoughlin/smodr/views/activities/MainActivity.kt index c5afb25e..4356bb4b 100644 --- a/Smodr/src/main/java/com/kevintcoughlin/smodr/views/activities/MainActivity.kt +++ b/Smodr/src/main/java/com/kevintcoughlin/smodr/views/activities/MainActivity.kt @@ -14,7 +14,6 @@ import android.view.View import android.widget.SeekBar import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.content.res.AppCompatResources -import androidx.fragment.app.Fragment import com.google.android.gms.ads.AdRequest import com.google.android.gms.ads.MobileAds import com.google.android.gms.ads.RequestConfiguration @@ -115,7 +114,6 @@ class MainActivity : AppCompatActivity(), SeekBar.OnSeekBarChangeListener { updatePlayButtonState(R.drawable.ic_round_play_arrow_24) currentItem?.completed = true currentItem = null - logEvent("complete_playback", safeGetEventBundle(currentItem)) } } @@ -135,9 +133,15 @@ class MainActivity : AppCompatActivity(), SeekBar.OnSeekBarChangeListener { private fun updateSeekProgress() { mediaService?.let { service -> - binding.seekbar.progress = service.currentTime - binding.currentTime.setElapsedTime(service.currentTime) - binding.remainingTime.setElapsedTime(service.remainingTime) + with(binding) { + seekbar.progress = service.currentTime + listOf( + currentTime to service.currentTime, + remainingTime to service.remainingTime + ).forEach { (textView, time) -> + textView.setElapsedTime(time.toLong()) + } + } } } @@ -206,10 +210,4 @@ class MainActivity : AppCompatActivity(), SeekBar.OnSeekBarChangeListener { private const val FEEDBACK_URL = "https://github.com/cascadiacollections/SModr/issues/new" private val DEFAULT_CHANNEL = Channel("Tell 'Em Steve-Dave", "http://feeds.feedburner.com/TellEmSteveDave/") } - - fun onItemSelected(item: Item?) { - currentItem = item - mediaService?.startPlayback(item?.uri) - logEvent("selected_item", safeGetEventBundle(item)) - } } \ No newline at end of file diff --git a/Smodr/src/main/res/values/strings.xml b/Smodr/src/main/res/values/strings.xml index 2ba21202..687ba88f 100755 --- a/Smodr/src/main/res/values/strings.xml +++ b/Smodr/src/main/res/values/strings.xml @@ -7,4 +7,5 @@ Third-party notices Privacy policy Report an issue + --:--