Add localized duration formatting and related string resources (#1571)

## Description
Instead of using English-only duration format (e.g. `HHh MMm SSs`) in
various places, I've added a format function that is based on several
new localized resources keys.

That includes:
1. Media runtime in Item view
2. Time remaining in Item view
3. End time in Player view
4. Time passed and time remained in Player view

### Related issues
None

### Testing
I installed the app on my Google TV Streamer, switched to my language,
browsed all item views that I could think of to see that the duration
labels are all localized in my language. I didn't commit those strings,
I will do it through the Weblate after it will get merged. I also played
a few items and saw that the labels there are localized as well.

## Screenshots
It looks the same in English as before, currently no other language has
custom resources for it yet.

## AI or LLM usage
Used Claude Sonnet to detect places that I missed that are related to
this localization change. Also it helped me to get access to my format
function from various places in the code.

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
This commit is contained in:
Tal Sarid 2026-07-06 15:43:06 +03:00 • committed by GitHub
parent 1d7ddd713b
commit f6e00c7820
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 86 additions and 25 deletions

View file

@ -5,12 +5,15 @@ import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.buildAnnotatedString 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.abbreviateNumber
import com.github.damontecres.wholphin.ui.detail.CardGridItem import com.github.damontecres.wholphin.ui.detail.CardGridItem
import com.github.damontecres.wholphin.ui.detail.music.artistsString import com.github.damontecres.wholphin.ui.detail.music.artistsString
import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisodeIds import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisodeIds
import com.github.damontecres.wholphin.ui.dot import com.github.damontecres.wholphin.ui.dot
import com.github.damontecres.wholphin.ui.formatDateTime 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.getDateFormatter
import com.github.damontecres.wholphin.ui.joinNotBlank import com.github.damontecres.wholphin.ui.joinNotBlank
import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.nav.Destination
@ -161,10 +164,18 @@ data class BaseItem(
?.ticks ?.ticks
?.roundMinutes ?.roundMinutes
?.takeIf { it > Duration.ZERO } ?.takeIf { it > Duration.ZERO }
?.let { add(it.toString()) } ?.let { add(WholphinApplication.instance.resources.formatDuration(it)) }
data.timeRemaining data.timeRemaining
?.roundMinutes ?.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 -> details.forEachIndexed { index, string ->
append(string) append(string)

View file

@ -1,6 +1,7 @@
package com.github.damontecres.wholphin.ui package com.github.damontecres.wholphin.ui
import android.content.Context import android.content.Context
import android.content.res.Resources
import android.text.format.DateFormat import android.text.format.DateFormat
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.foundation.text.appendInlineContent import androidx.compose.foundation.text.appendInlineContent
@ -21,6 +22,7 @@ import java.time.format.DateTimeParseException
import java.time.format.FormatStyle import java.time.format.FormatStyle
import java.util.Date import java.util.Date
import java.util.Locale 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 * 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.LIVE_TV_PROGRAM -> TODO()
BaseItemKind.PLAYLISTS_FOLDER -> 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)
}
}

View file

@ -9,15 +9,18 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale 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.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.tv.material3.Card import androidx.tv.material3.Card
import androidx.tv.material3.Text import androidx.tv.material3.Text
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
import com.github.damontecres.wholphin.ui.AppColors import com.github.damontecres.wholphin.ui.AppColors
import com.github.damontecres.wholphin.ui.formatDuration
import com.github.damontecres.wholphin.ui.roundSeconds import com.github.damontecres.wholphin.ui.roundSeconds
import kotlin.time.Duration import kotlin.time.Duration
@ -62,8 +65,10 @@ fun ChapterCard(
text = it, text = it,
) )
} }
val resources = LocalResources.current
val positionText = remember(position) { resources.formatDuration(position.roundSeconds) }
Text( Text(
text = position.roundSeconds.toString(), text = positionText,
) )
} }
} }

View file

@ -38,6 +38,7 @@ import androidx.compose.ui.focus.focusRestorer
import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.tv.material3.ListItemDefaults 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.data.SortAndDirection
import com.github.damontecres.wholphin.ui.detail.AlphabetButtons import com.github.damontecres.wholphin.ui.detail.AlphabetButtons
import com.github.damontecres.wholphin.ui.enableMarquee 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.ifElse
import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.rememberInt import com.github.damontecres.wholphin.ui.rememberInt
@ -357,7 +359,7 @@ fun ListItemGeneric(
?.runTimeTicks ?.runTimeTicks
?.ticks ?.ticks
?.roundMinutes ?.roundMinutes
?.toString() ?: "", ?.let { LocalResources.current.formatDuration(it) } ?: "",
) )
}, },
scale = scale, scale = scale,
@ -419,7 +421,7 @@ fun ListItemMovie(
?.runTimeTicks ?.runTimeTicks
?.ticks ?.ticks
?.roundMinutes ?.roundMinutes
?.toString() ?: "", ?.let { LocalResources.current.formatDuration(it) } ?: "",
) )
if (item?.played == true) { if (item?.played == true) {
WatchedIcon(padding = 0.dp) WatchedIcon(padding = 0.dp)
@ -546,7 +548,7 @@ fun ListItemAlbum(
.runTimeTicks .runTimeTicks
?.ticks ?.ticks
?.roundMinutes ?.roundMinutes
?.toString() ?: "", ?.let { LocalResources.current.formatDuration(it) } ?: "",
) )
} }
} else { } else {

View file

@ -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.formatBitrate
import com.github.damontecres.wholphin.ui.formatBytes import com.github.damontecres.wholphin.ui.formatBytes
import com.github.damontecres.wholphin.ui.formatDate 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.isNotNullOrBlank
import com.github.damontecres.wholphin.ui.letNotEmpty import com.github.damontecres.wholphin.ui.letNotEmpty
import com.github.damontecres.wholphin.ui.util.StreamFormatting.formatAudioCodec import com.github.damontecres.wholphin.ui.util.StreamFormatting.formatAudioCodec
@ -153,7 +154,7 @@ fun ItemDetailsDialog(
) )
} }
source.runTimeTicks?.let { source.runTimeTicks?.let {
add(runtimeLabel to it.ticks.toString()) add(runtimeLabel to resources.formatDuration(it.ticks))
} }
}, },
) )

View file

@ -42,6 +42,7 @@ import androidx.compose.ui.focus.focusRestorer
import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp 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.enableMarquee
import com.github.damontecres.wholphin.ui.equalsNotNull import com.github.damontecres.wholphin.ui.equalsNotNull
import com.github.damontecres.wholphin.ui.formatDateTime 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.formatTime
import com.github.damontecres.wholphin.ui.ifElse import com.github.damontecres.wholphin.ui.ifElse
import com.github.damontecres.wholphin.ui.launchDefault import com.github.damontecres.wholphin.ui.launchDefault
@ -809,9 +811,11 @@ fun PlaylistItem(
val endTime = now.toLocalTime().plusSeconds(duration.inWholeSeconds) val endTime = now.toLocalTime().plusSeconds(duration.inWholeSeconds)
formatTime(context, endTime) formatTime(context, endTime)
} }
val resources = LocalResources.current
val durationText = remember(duration) { resources.formatDuration(duration) }
Column { Column {
Text( Text(
text = duration.toString(), text = durationText,
) )
if (item.type != BaseItemKind.AUDIO) { if (item.type != BaseItemKind.AUDIO) {
Text( Text(

View file

@ -11,6 +11,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.style.TextOverflow 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.GenreText
import com.github.damontecres.wholphin.ui.components.OverviewText import com.github.damontecres.wholphin.ui.components.OverviewText
import com.github.damontecres.wholphin.ui.components.QuickDetailsText 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.isNotNullOrBlank
import com.github.damontecres.wholphin.ui.letNotEmpty import com.github.damontecres.wholphin.ui.letNotEmpty
import com.github.damontecres.wholphin.ui.listToDotString import com.github.damontecres.wholphin.ui.listToDotString
@ -64,16 +66,16 @@ fun DiscoverMovieDetailsHeader(
modifier = Modifier.fillMaxWidth(.60f), modifier = Modifier.fillMaxWidth(.60f),
) { ) {
val padding = 4.dp val padding = 4.dp
val resources = LocalResources.current
val details = val details =
remember(movie, rating) { remember(movie, rating, resources) {
buildList { buildList {
movie.releaseDate?.let(::add) movie.releaseDate?.let(::add)
movie.runtime movie.runtime
?.toDouble() ?.toDouble()
?.minutes ?.minutes
?.roundMinutes ?.roundMinutes
?.toString() ?.let { add(resources.formatDuration(it)) }
?.let(::add)
val release = val release =
movie.releases movie.releases
?.results ?.results

View file

@ -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.components.QuickDetailsText
import com.github.damontecres.wholphin.ui.data.ItemDetailsDialog import com.github.damontecres.wholphin.ui.data.ItemDetailsDialog
import com.github.damontecres.wholphin.ui.data.ItemDetailsDialogInfo 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.letNotEmpty
import com.github.damontecres.wholphin.ui.listToDotString import com.github.damontecres.wholphin.ui.listToDotString
import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.nav.Destination
@ -446,8 +447,9 @@ fun DiscoverSeriesDetailsHeader(
modifier = Modifier.fillMaxWidth(.60f), modifier = Modifier.fillMaxWidth(.60f),
) { ) {
val padding = 4.dp val padding = 4.dp
val resources = LocalResources.current
val details = val details =
remember(series, rating) { remember(series, rating, resources) {
buildList { buildList {
series.firstAirDate?.let(::add) series.firstAirDate?.let(::add)
series.episodeRunTime series.episodeRunTime
@ -455,8 +457,7 @@ fun DiscoverSeriesDetailsHeader(
?.takeIf { !it.isNaN() && it > 0 } ?.takeIf { !it.isNaN() && it > 0 }
?.minutes ?.minutes
?.roundMinutes ?.roundMinutes
?.toString() ?.let { add(resources.formatDuration(it)) }
?.let(::add)
// TODO // TODO
}.let { }.let {
listToDotString( listToDotString(

View file

@ -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.data.RowColumn
import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisode import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisode
import com.github.damontecres.wholphin.ui.dot 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.isNotNullOrBlank
import com.github.damontecres.wholphin.ui.launchDefault import com.github.damontecres.wholphin.ui.launchDefault
import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.launchIO
@ -643,18 +644,16 @@ data class TvProgram(
append(time) append(time)
if (!isFake) { if (!isFake) {
val resources = WholphinApplication.instance.resources
dot() dot()
duration append(resources.formatDuration(duration.roundMinutes))
.roundMinutes
.toString()
.let(::append)
if (now.isAfter(start) && now.isBefore(end)) { if (now.isAfter(start) && now.isBefore(end)) {
dot() dot()
java.time.Duration java.time.Duration
.between(now, end) .between(now, end)
.toKotlinDuration() .toKotlinDuration()
.roundMinutes .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 { seasonEpisode?.let { "S${it.season} E${it.episode}" }?.let {
dot() dot()

View file

@ -20,6 +20,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.tv.material3.DenseListItem 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.PreviewTvSpec
import com.github.damontecres.wholphin.ui.components.Button import com.github.damontecres.wholphin.ui.components.Button
import com.github.damontecres.wholphin.ui.enableMarquee 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.letNotEmpty
import com.github.damontecres.wholphin.ui.roundSeconds import com.github.damontecres.wholphin.ui.roundSeconds
import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.theme.WholphinTheme
@ -115,7 +117,7 @@ fun SongListItem(
} }
val trailingContent = @Composable { val trailingContent = @Composable {
Text( Text(
text = runtime.toString(), text = runtime?.let { LocalResources.current.formatDuration(it) } ?: "",
) )
} }

View file

@ -215,7 +215,7 @@ fun Controller(
} }
} }
Text( Text(
text = "Ends $endTimeStr", text = stringResource(R.string.ends_at, endTimeStr),
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.labelLarge, style = MaterialTheme.typography.labelLarge,
modifier = modifier =

View file

@ -43,6 +43,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.isSpecified import androidx.compose.ui.graphics.isSpecified
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource 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.Button
import com.github.damontecres.wholphin.ui.components.SelectedLeadingContent import com.github.damontecres.wholphin.ui.components.SelectedLeadingContent
import com.github.damontecres.wholphin.ui.components.TextButton 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.indexOfFirstOrNull
import com.github.damontecres.wholphin.ui.playback.ControllerViewState import com.github.damontecres.wholphin.ui.playback.ControllerViewState
import com.github.damontecres.wholphin.ui.playback.PlaybackDialogType import com.github.damontecres.wholphin.ui.playback.PlaybackDialogType
@ -274,9 +276,13 @@ fun SeekBar(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween, 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(
text = (position / 1000).seconds.toString(), text = positionText,
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.labelLarge, style = MaterialTheme.typography.labelLarge,
modifier = modifier =
@ -284,7 +290,7 @@ fun SeekBar(
.padding(8.dp), .padding(8.dp),
) )
Text( Text(
text = "-$remaining", text = remainingText,
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.labelLarge, style = MaterialTheme.typography.labelLarge,
modifier = modifier =

View file

@ -41,6 +41,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp 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.AspectRatios
import com.github.damontecres.wholphin.ui.LocalImageUrlService import com.github.damontecres.wholphin.ui.LocalImageUrlService
import com.github.damontecres.wholphin.ui.components.TimeDisplay 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.isNotNullOrBlank
import com.github.damontecres.wholphin.ui.playback.AnalyticsState import com.github.damontecres.wholphin.ui.playback.AnalyticsState
import com.github.damontecres.wholphin.ui.playback.ControllerViewState 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(
text = (seekProgressMs / 1000L).seconds.toString(), text = seekText,
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.labelLarge, style = MaterialTheme.typography.labelLarge,
modifier = modifier =

View file

@ -26,6 +26,12 @@
<string name="continue_watching">Continue watching</string> <string name="continue_watching">Continue watching</string>
<string name="critic_rating">Critic Rating</string> <string name="critic_rating">Critic Rating</string>
<string name="decimal_seconds">%.2f seconds</string> <string name="decimal_seconds">%.2f seconds</string>
<string name="duration_hours_minutes_seconds">%1$dh %2$dm %3$ds</string>
<string name="duration_hours_minutes">%1$dh %2$dm</string>
<string name="duration_minutes_seconds">%1$dm %2$ds</string>
<string name="duration_hours">%1$dh</string>
<string name="duration_minutes">%1$dm</string>
<string name="duration_seconds">%1$ds</string>
<string name="default_track">Default</string> <string name="default_track">Default</string>
<string name="delete">Delete</string> <string name="delete">Delete</string>
<string name="died">Died</string> <string name="died">Died</string>
@ -144,6 +150,7 @@
<string name="suggestions">Suggestions</string> <string name="suggestions">Suggestions</string>
<string name="switch_servers">Switch servers</string> <string name="switch_servers">Switch servers</string>
<string name="switch_user">Switch</string> <string name="switch_user">Switch</string>
<string name="time_left">%1$s left</string>
<string name="top_unwatched">Top Rated Unwatched</string> <string name="top_unwatched">Top Rated Unwatched</string>
<string name="trailers_title">Trailers</string> <string name="trailers_title">Trailers</string>
<string name="tv_dvr_schedule">DVR Schedule</string> <string name="tv_dvr_schedule">DVR Schedule</string>