diff --git a/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt b/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt
index a5526f87..5d5c8923 100644
--- a/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt
+++ b/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt
@@ -5,12 +5,15 @@ import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.buildAnnotatedString
+import com.github.damontecres.wholphin.R
+import com.github.damontecres.wholphin.WholphinApplication
import com.github.damontecres.wholphin.ui.abbreviateNumber
import com.github.damontecres.wholphin.ui.detail.CardGridItem
import com.github.damontecres.wholphin.ui.detail.music.artistsString
import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisodeIds
import com.github.damontecres.wholphin.ui.dot
import com.github.damontecres.wholphin.ui.formatDateTime
+import com.github.damontecres.wholphin.ui.formatDuration
import com.github.damontecres.wholphin.ui.getDateFormatter
import com.github.damontecres.wholphin.ui.joinNotBlank
import com.github.damontecres.wholphin.ui.nav.Destination
@@ -161,10 +164,18 @@ data class BaseItem(
?.ticks
?.roundMinutes
?.takeIf { it > Duration.ZERO }
- ?.let { add(it.toString()) }
+ ?.let { add(WholphinApplication.instance.resources.formatDuration(it)) }
data.timeRemaining
?.roundMinutes
- ?.let { add("$it left") }
+ ?.let {
+ val resources = WholphinApplication.instance.resources
+ add(
+ resources.getString(
+ R.string.time_left,
+ resources.formatDuration(it),
+ ),
+ )
+ }
}
details.forEachIndexed { index, string ->
append(string)
diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/Formatting.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/Formatting.kt
index 2ea1bf87..a9ccd690 100644
--- a/app/src/main/java/com/github/damontecres/wholphin/ui/Formatting.kt
+++ b/app/src/main/java/com/github/damontecres/wholphin/ui/Formatting.kt
@@ -1,6 +1,7 @@
package com.github.damontecres.wholphin.ui
import android.content.Context
+import android.content.res.Resources
import android.text.format.DateFormat
import androidx.annotation.StringRes
import androidx.compose.foundation.text.appendInlineContent
@@ -21,6 +22,7 @@ import java.time.format.DateTimeParseException
import java.time.format.FormatStyle
import java.util.Date
import java.util.Locale
+import kotlin.time.Duration
/**
* Format the time-of-day of [time] honouring the device's 12/24-hour setting and the current
@@ -259,3 +261,18 @@ fun formatTypeName(type: BaseItemKind): Int =
BaseItemKind.LIVE_TV_PROGRAM -> TODO()
BaseItemKind.PLAYLISTS_FOLDER -> TODO()
}
+
+/**
+ * Format a [Duration] into a localized "Xh Ym Zs" string using string resources.
+ */
+fun Resources.formatDuration(duration: Duration): String =
+ duration.toComponents { hours, minutes, seconds, _ ->
+ when {
+ hours > 0 && minutes > 0 && seconds > 0 -> getString(R.string.duration_hours_minutes_seconds, hours.toInt(), minutes, seconds)
+ hours > 0 && minutes > 0 -> getString(R.string.duration_hours_minutes, hours.toInt(), minutes)
+ hours > 0 -> getString(R.string.duration_hours, hours.toInt())
+ minutes > 0 && seconds > 0 -> getString(R.string.duration_minutes_seconds, minutes, seconds)
+ minutes > 0 -> getString(R.string.duration_minutes, minutes)
+ else -> getString(R.string.duration_seconds, seconds)
+ }
+ }
diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/ChapterCard.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/ChapterCard.kt
index 54d1d2fc..d3c87124 100644
--- a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/ChapterCard.kt
+++ b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/ChapterCard.kt
@@ -9,15 +9,18 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.tv.material3.Card
import androidx.tv.material3.Text
import coil3.compose.AsyncImage
import com.github.damontecres.wholphin.ui.AppColors
+import com.github.damontecres.wholphin.ui.formatDuration
import com.github.damontecres.wholphin.ui.roundSeconds
import kotlin.time.Duration
@@ -62,8 +65,10 @@ fun ChapterCard(
text = it,
)
}
+ val resources = LocalResources.current
+ val positionText = remember(position) { resources.formatDuration(position.roundSeconds) }
Text(
- text = position.roundSeconds.toString(),
+ text = positionText,
)
}
}
diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderList.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderList.kt
index 3c69e5ce..e82d87a5 100644
--- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderList.kt
+++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderList.kt
@@ -38,6 +38,7 @@ import androidx.compose.ui.focus.focusRestorer
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.tv.material3.ListItemDefaults
@@ -58,6 +59,7 @@ import com.github.damontecres.wholphin.ui.cards.WatchedIcon
import com.github.damontecres.wholphin.ui.data.SortAndDirection
import com.github.damontecres.wholphin.ui.detail.AlphabetButtons
import com.github.damontecres.wholphin.ui.enableMarquee
+import com.github.damontecres.wholphin.ui.formatDuration
import com.github.damontecres.wholphin.ui.ifElse
import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.rememberInt
@@ -357,7 +359,7 @@ fun ListItemGeneric(
?.runTimeTicks
?.ticks
?.roundMinutes
- ?.toString() ?: "",
+ ?.let { LocalResources.current.formatDuration(it) } ?: "",
)
},
scale = scale,
@@ -419,7 +421,7 @@ fun ListItemMovie(
?.runTimeTicks
?.ticks
?.roundMinutes
- ?.toString() ?: "",
+ ?.let { LocalResources.current.formatDuration(it) } ?: "",
)
if (item?.played == true) {
WatchedIcon(padding = 0.dp)
@@ -546,7 +548,7 @@ fun ListItemAlbum(
.runTimeTicks
?.ticks
?.roundMinutes
- ?.toString() ?: "",
+ ?.let { LocalResources.current.formatDuration(it) } ?: "",
)
}
} else {
diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/data/ItemDetailsDialogInfo.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/data/ItemDetailsDialogInfo.kt
index f0591868..08bd0c55 100644
--- a/app/src/main/java/com/github/damontecres/wholphin/ui/data/ItemDetailsDialogInfo.kt
+++ b/app/src/main/java/com/github/damontecres/wholphin/ui/data/ItemDetailsDialogInfo.kt
@@ -25,6 +25,7 @@ import com.github.damontecres.wholphin.ui.components.ScrollableDialog
import com.github.damontecres.wholphin.ui.formatBitrate
import com.github.damontecres.wholphin.ui.formatBytes
import com.github.damontecres.wholphin.ui.formatDate
+import com.github.damontecres.wholphin.ui.formatDuration
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
import com.github.damontecres.wholphin.ui.letNotEmpty
import com.github.damontecres.wholphin.ui.util.StreamFormatting.formatAudioCodec
@@ -153,7 +154,7 @@ fun ItemDetailsDialog(
)
}
source.runTimeTicks?.let {
- add(runtimeLabel to it.ticks.toString())
+ add(runtimeLabel to resources.formatDuration(it.ticks))
}
},
)
diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PlaylistDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PlaylistDetails.kt
index ea1640ed..7d66c08b 100644
--- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PlaylistDetails.kt
+++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PlaylistDetails.kt
@@ -42,6 +42,7 @@ import androidx.compose.ui.focus.focusRestorer
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
@@ -94,6 +95,7 @@ import com.github.damontecres.wholphin.ui.detail.music.MusicViewModel
import com.github.damontecres.wholphin.ui.enableMarquee
import com.github.damontecres.wholphin.ui.equalsNotNull
import com.github.damontecres.wholphin.ui.formatDateTime
+import com.github.damontecres.wholphin.ui.formatDuration
import com.github.damontecres.wholphin.ui.formatTime
import com.github.damontecres.wholphin.ui.ifElse
import com.github.damontecres.wholphin.ui.launchDefault
@@ -809,9 +811,11 @@ fun PlaylistItem(
val endTime = now.toLocalTime().plusSeconds(duration.inWholeSeconds)
formatTime(context, endTime)
}
+ val resources = LocalResources.current
+ val durationText = remember(duration) { resources.formatDuration(duration) }
Column {
Text(
- text = duration.toString(),
+ text = durationText,
)
if (item.type != BaseItemKind.AUDIO) {
Text(
diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieDetailsHeader.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieDetailsHeader.kt
index 60d3806b..8b6f3e98 100644
--- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieDetailsHeader.kt
+++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverMovieDetailsHeader.kt
@@ -11,6 +11,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.style.TextOverflow
@@ -25,6 +26,7 @@ import com.github.damontecres.wholphin.preferences.UserPreferences
import com.github.damontecres.wholphin.ui.components.GenreText
import com.github.damontecres.wholphin.ui.components.OverviewText
import com.github.damontecres.wholphin.ui.components.QuickDetailsText
+import com.github.damontecres.wholphin.ui.formatDuration
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
import com.github.damontecres.wholphin.ui.letNotEmpty
import com.github.damontecres.wholphin.ui.listToDotString
@@ -64,16 +66,16 @@ fun DiscoverMovieDetailsHeader(
modifier = Modifier.fillMaxWidth(.60f),
) {
val padding = 4.dp
+ val resources = LocalResources.current
val details =
- remember(movie, rating) {
+ remember(movie, rating, resources) {
buildList {
movie.releaseDate?.let(::add)
movie.runtime
?.toDouble()
?.minutes
?.roundMinutes
- ?.toString()
- ?.let(::add)
+ ?.let { add(resources.formatDuration(it)) }
val release =
movie.releases
?.results
diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverSeriesDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverSeriesDetails.kt
index be9acc64..947dae96 100644
--- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverSeriesDetails.kt
+++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/discover/DiscoverSeriesDetails.kt
@@ -58,6 +58,7 @@ import com.github.damontecres.wholphin.ui.components.OverviewText
import com.github.damontecres.wholphin.ui.components.QuickDetailsText
import com.github.damontecres.wholphin.ui.data.ItemDetailsDialog
import com.github.damontecres.wholphin.ui.data.ItemDetailsDialogInfo
+import com.github.damontecres.wholphin.ui.formatDuration
import com.github.damontecres.wholphin.ui.letNotEmpty
import com.github.damontecres.wholphin.ui.listToDotString
import com.github.damontecres.wholphin.ui.nav.Destination
@@ -446,8 +447,9 @@ fun DiscoverSeriesDetailsHeader(
modifier = Modifier.fillMaxWidth(.60f),
) {
val padding = 4.dp
+ val resources = LocalResources.current
val details =
- remember(series, rating) {
+ remember(series, rating, resources) {
buildList {
series.firstAirDate?.let(::add)
series.episodeRunTime
@@ -455,8 +457,7 @@ fun DiscoverSeriesDetailsHeader(
?.takeIf { !it.isNaN() && it > 0 }
?.minutes
?.roundMinutes
- ?.toString()
- ?.let(::add)
+ ?.let { add(resources.formatDuration(it)) }
// TODO
}.let {
listToDotString(
diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewModel.kt
index 01279c97..a2a4bd01 100644
--- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewModel.kt
+++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewModel.kt
@@ -20,6 +20,7 @@ import com.github.damontecres.wholphin.ui.AppColors
import com.github.damontecres.wholphin.ui.data.RowColumn
import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisode
import com.github.damontecres.wholphin.ui.dot
+import com.github.damontecres.wholphin.ui.formatDuration
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
import com.github.damontecres.wholphin.ui.launchDefault
import com.github.damontecres.wholphin.ui.launchIO
@@ -643,18 +644,16 @@ data class TvProgram(
append(time)
if (!isFake) {
+ val resources = WholphinApplication.instance.resources
dot()
- duration
- .roundMinutes
- .toString()
- .let(::append)
+ append(resources.formatDuration(duration.roundMinutes))
if (now.isAfter(start) && now.isBefore(end)) {
dot()
java.time.Duration
.between(now, end)
.toKotlinDuration()
.roundMinutes
- .let { append("$it left") }
+ .let { append(resources.getString(R.string.time_left, resources.formatDuration(it))) }
}
seasonEpisode?.let { "S${it.season} E${it.episode}" }?.let {
dot()
diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/SongListDisplay.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/SongListDisplay.kt
index 011bb8ea..3879cca4 100644
--- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/SongListDisplay.kt
+++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/SongListDisplay.kt
@@ -20,6 +20,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
+import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.tv.material3.DenseListItem
@@ -32,6 +33,7 @@ import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.ui.PreviewTvSpec
import com.github.damontecres.wholphin.ui.components.Button
import com.github.damontecres.wholphin.ui.enableMarquee
+import com.github.damontecres.wholphin.ui.formatDuration
import com.github.damontecres.wholphin.ui.letNotEmpty
import com.github.damontecres.wholphin.ui.roundSeconds
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
@@ -115,7 +117,7 @@ fun SongListItem(
}
val trailingContent = @Composable {
Text(
- text = runtime.toString(),
+ text = runtime?.let { LocalResources.current.formatDuration(it) } ?: "",
)
}
diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/overlay/PlaybackController.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/overlay/PlaybackController.kt
index 86cd81b1..2909e666 100644
--- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/overlay/PlaybackController.kt
+++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/overlay/PlaybackController.kt
@@ -215,7 +215,7 @@ fun Controller(
}
}
Text(
- text = "Ends $endTimeStr",
+ text = stringResource(R.string.ends_at, endTimeStr),
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.labelLarge,
modifier =
diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/overlay/PlaybackControls.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/overlay/PlaybackControls.kt
index 820e634f..79f44929 100644
--- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/overlay/PlaybackControls.kt
+++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/overlay/PlaybackControls.kt
@@ -43,6 +43,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.isSpecified
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@@ -70,6 +71,7 @@ import com.github.damontecres.wholphin.ui.PreviewTvSpec
import com.github.damontecres.wholphin.ui.components.Button
import com.github.damontecres.wholphin.ui.components.SelectedLeadingContent
import com.github.damontecres.wholphin.ui.components.TextButton
+import com.github.damontecres.wholphin.ui.formatDuration
import com.github.damontecres.wholphin.ui.indexOfFirstOrNull
import com.github.damontecres.wholphin.ui.playback.ControllerViewState
import com.github.damontecres.wholphin.ui.playback.PlaybackDialogType
@@ -274,9 +276,13 @@ fun SeekBar(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
- val remaining = ((player.duration - position) / 1000).seconds
+ val resources = LocalResources.current
+ val positionSec = position / 1000
+ val remainingSec = (player.duration - position) / 1000
+ val positionText = remember(positionSec) { resources.formatDuration(positionSec.seconds) }
+ val remainingText = remember(remainingSec) { "-${resources.formatDuration(remainingSec.seconds)}" }
Text(
- text = (position / 1000).seconds.toString(),
+ text = positionText,
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.labelLarge,
modifier =
@@ -284,7 +290,7 @@ fun SeekBar(
.padding(8.dp),
)
Text(
- text = "-$remaining",
+ text = remainingText,
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.labelLarge,
modifier =
diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/overlay/PlaybackOverlay.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/overlay/PlaybackOverlay.kt
index 2c2aefe8..a6ade035 100644
--- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/overlay/PlaybackOverlay.kt
+++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/overlay/PlaybackOverlay.kt
@@ -41,6 +41,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
@@ -56,6 +57,7 @@ import com.github.damontecres.wholphin.ui.AppColors
import com.github.damontecres.wholphin.ui.AspectRatios
import com.github.damontecres.wholphin.ui.LocalImageUrlService
import com.github.damontecres.wholphin.ui.components.TimeDisplay
+import com.github.damontecres.wholphin.ui.formatDuration
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
import com.github.damontecres.wholphin.ui.playback.AnalyticsState
import com.github.damontecres.wholphin.ui.playback.ControllerViewState
@@ -304,8 +306,10 @@ fun PlaybackOverlay(
)
}
}
+ val resources = LocalResources.current
+ val seekText = remember(seekProgressMs / 1000L) { resources.formatDuration((seekProgressMs / 1000L).seconds) }
Text(
- text = (seekProgressMs / 1000L).seconds.toString(),
+ text = seekText,
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.labelLarge,
modifier =
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 312f78b2..0af5ea00 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -26,6 +26,12 @@
Continue watching
Critic Rating
%.2f seconds
+ %1$dh %2$dm %3$ds
+ %1$dh %2$dm
+ %1$dm %2$ds
+ %1$dh
+ %1$dm
+ %1$ds
Default
Delete
Died
@@ -144,6 +150,7 @@
Suggestions
Switch servers
Switch
+ %1$s left
Top Rated Unwatched
Trailers
DVR Schedule