mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-11-06 18:54:49 +01:00
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:
parent
1d7ddd713b
commit
f6e00c7820
14 changed files with 86 additions and 25 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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) } ?: "",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue