From fd260f2709c07e798e3813cd283f7f3d48169c8f Mon Sep 17 00:00:00 2001 From: Amar Sandhu Date: Thu, 18 Dec 2025 13:52:27 -0700 Subject: [PATCH] Add "Ends at" time display. (#475) Addresses #441 . --------- Co-authored-by: Damontecres --- .../wholphin/ui/components/MovieComponents.kt | 35 +++++++++++++++---- .../ui/components/SeriesComponents.kt | 22 +++++------- .../wholphin/ui/detail/PlaylistDetails.kt | 22 +++++++++--- app/src/main/res/values/strings.xml | 1 + 4 files changed, 56 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/MovieComponents.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/MovieComponents.kt index 30e0a902..e25f50be 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/MovieComponents.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/MovieComponents.kt @@ -1,29 +1,32 @@ package com.github.damontecres.wholphin.ui.components +import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.tv.material3.MaterialTheme +import com.github.damontecres.wholphin.R +import com.github.damontecres.wholphin.ui.TimeFormatter import com.github.damontecres.wholphin.ui.roundMinutes import com.github.damontecres.wholphin.ui.timeRemaining +import com.github.damontecres.wholphin.ui.util.LocalClock import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.extensions.ticks +import java.time.LocalDateTime @Composable fun MovieQuickDetails( dto: BaseItemDto?, modifier: Modifier = Modifier, ) { + val context = LocalContext.current + val now = LocalClock.current.now val details = - remember(dto) { + remember(dto, now) { buildList { dto?.productionYear?.let { add(it.toString()) } - dto?.runTimeTicks?.ticks?.roundMinutes?.let { - add(it.toString()) - } - dto?.timeRemaining?.roundMinutes?.let { - add("$it left") - } + addRuntimeDetails(context, now, dto) dto?.officialRating?.let(::add) } } @@ -36,3 +39,21 @@ fun MovieQuickDetails( modifier = modifier, ) } + +fun MutableList.addRuntimeDetails( + context: Context, + now: LocalDateTime, + dto: BaseItemDto?, +) { + val runtime = dto?.runTimeTicks?.ticks + runtime?.let { duration -> + add(duration.roundMinutes.toString()) + } + dto?.timeRemaining?.roundMinutes?.let { + add("$it left") + } + (dto?.timeRemaining ?: runtime)?.let { remaining -> + val endTimeStr = TimeFormatter.format(now.plusSeconds(remaining.inWholeSeconds)) + add(context.getString(R.string.ends_at, endTimeStr)) + } +} diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/SeriesComponents.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/SeriesComponents.kt index bdb94ba5..de79bd5a 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/SeriesComponents.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/SeriesComponents.kt @@ -3,6 +3,7 @@ package com.github.damontecres.wholphin.ui.components import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.tv.material3.MaterialTheme @@ -11,7 +12,7 @@ import com.github.damontecres.wholphin.ui.formatDateTime import com.github.damontecres.wholphin.ui.roundMinutes import com.github.damontecres.wholphin.ui.seasonEpisode import com.github.damontecres.wholphin.ui.seriesProductionYears -import com.github.damontecres.wholphin.ui.timeRemaining +import com.github.damontecres.wholphin.ui.util.LocalClock import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.extensions.ticks @@ -57,17 +58,14 @@ fun EpisodeQuickDetails( dto: BaseItemDto?, modifier: Modifier = Modifier, ) { + val context = LocalContext.current + val now = LocalClock.current.now val details = - remember(dto) { + remember(dto, now) { buildList { dto?.seasonEpisode?.let(::add) dto?.premiereDate?.let { add(formatDateTime(it)) } - val duration = dto?.runTimeTicks?.ticks - duration - ?.roundMinutes - ?.toString() - ?.let(::add) - dto?.timeRemaining?.roundMinutes?.let { add("$it left") } + addRuntimeDetails(context, now, dto) dto?.officialRating?.let(::add) } } @@ -89,11 +87,9 @@ fun SeriesQuickDetails( remember(dto) { buildList { dto?.seriesProductionYears?.let(::add) - val duration = dto?.runTimeTicks?.ticks - duration - ?.roundMinutes - ?.toString() - ?.let(::add) + dto?.runTimeTicks?.ticks?.roundMinutes?.let { + add(it.toString()) + } dto?.officialRating?.let(::add) } } 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 d27edfff..150228a5 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 @@ -53,6 +53,7 @@ import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.services.BackdropService import com.github.damontecres.wholphin.services.NavigationManager import com.github.damontecres.wholphin.ui.DefaultItemFields +import com.github.damontecres.wholphin.ui.TimeFormatter import com.github.damontecres.wholphin.ui.cards.ItemCardImage import com.github.damontecres.wholphin.ui.components.DialogItem import com.github.damontecres.wholphin.ui.components.DialogParams @@ -68,6 +69,7 @@ import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.roundMinutes import com.github.damontecres.wholphin.ui.tryRequestFocus +import com.github.damontecres.wholphin.ui.util.LocalClock import com.github.damontecres.wholphin.util.ApiRequestPager import com.github.damontecres.wholphin.util.GetPlaylistItemsRequestHandler import com.github.damontecres.wholphin.util.LoadingExceptionHandler @@ -407,10 +409,22 @@ fun PlaylistItem( ) }, trailingContent = { - item?.data?.runTimeTicks?.ticks?.roundMinutes?.let { - Text( - text = it.toString(), - ) + item?.data?.runTimeTicks?.ticks?.roundMinutes?.let { duration -> + val now = LocalClock.current.now + val endTimeStr = + remember(item, now) { + val endTime = now.toLocalTime().plusSeconds(duration.inWholeSeconds) + TimeFormatter.format(endTime) + } + Column { + Text( + text = duration.toString(), + ) + Text( + text = stringResource(R.string.ends_at, endTimeStr), + style = MaterialTheme.typography.bodySmall, + ) + } } }, leadingContent = { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d399631a..47122cca 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -36,6 +36,7 @@ Downloading… Enabled + Ends at %1$s Enter Server IP or URL Enter server address Episodes