mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-11-06 18:54:49 +01:00
Show duration & ends at time on next up card (#1690)
## Description Adds the duration & ends at time to the next up card ### Related issues Closes #1664 ### Testing <!-- Describe how this change was tested and on what device(s) --> ## Screenshots <img width="660" height="241" alt="image" src="https://github.com/user-attachments/assets/7551843e-4f53-43fc-af2a-3e503ba6d03c" /> ## AI or LLM usage None
This commit is contained in:
parent
f74306a12c
commit
077b7e4570
2 changed files with 73 additions and 27 deletions
|
|
@ -10,10 +10,8 @@ import androidx.compose.foundation.layout.aspectRatio
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
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.foundation.layout.width
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
|
@ -21,6 +19,7 @@ import androidx.compose.material.icons.filled.PlayArrow
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
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
|
||||||
|
|
@ -29,7 +28,10 @@ import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.graphics.toArgb
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
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.buildAnnotatedString
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
@ -47,10 +49,17 @@ import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.ui.AppColors
|
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.PreviewTvSpec
|
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||||
|
import com.github.damontecres.wholphin.ui.dot
|
||||||
|
import com.github.damontecres.wholphin.ui.formatDuration
|
||||||
|
import com.github.damontecres.wholphin.ui.formatTime
|
||||||
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
|
import com.github.damontecres.wholphin.ui.util.Clock
|
||||||
|
import com.github.damontecres.wholphin.ui.util.LocalClock
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.minutes
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
import kotlin.time.toJavaDuration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Layout for showing the next up episode during playback
|
* Layout for showing the next up episode during playback
|
||||||
|
|
@ -64,6 +73,7 @@ fun NextUpEpisode(
|
||||||
timeLeft: Duration?,
|
timeLeft: Duration?,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
aspectRatio: Float = AspectRatios.WIDE,
|
aspectRatio: Float = AspectRatios.WIDE,
|
||||||
|
runtime: Duration? = null,
|
||||||
) {
|
) {
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
|
@ -122,9 +132,36 @@ fun NextUpEpisode(
|
||||||
text = description ?: "",
|
text = description ?: "",
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
maxLines = 4,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
|
runtime?.let {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val resources = LocalResources.current
|
||||||
|
val now by LocalClock.current.now
|
||||||
|
val text =
|
||||||
|
remember(now, runtime) {
|
||||||
|
buildAnnotatedString {
|
||||||
|
append(resources.formatDuration(runtime))
|
||||||
|
dot()
|
||||||
|
val endsAt = now.plus(runtime.toJavaDuration())
|
||||||
|
val endTimeStr = formatTime(context, endsAt)
|
||||||
|
append(resources.getString(R.string.ends_at, endTimeStr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.padding(top = 4.dp)
|
||||||
|
.align(Alignment.Start),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -189,34 +226,41 @@ fun NextUpCard(
|
||||||
@Composable
|
@Composable
|
||||||
private fun NextUpEpisodePreview() {
|
private fun NextUpEpisodePreview() {
|
||||||
WholphinTheme(true) {
|
WholphinTheme(true) {
|
||||||
|
val theme = MaterialTheme.colorScheme
|
||||||
val previewHandler =
|
val previewHandler =
|
||||||
AsyncImagePreviewHandler {
|
AsyncImagePreviewHandler {
|
||||||
ColorImage(Color.Blue.toArgb())
|
ColorImage(theme.surfaceVariant.toArgb())
|
||||||
|
}
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize().background(Color.Black),
|
||||||
|
) {
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalAsyncImagePreviewHandler provides previewHandler,
|
||||||
|
LocalClock provides Clock(),
|
||||||
|
) {
|
||||||
|
NextUpEpisode(
|
||||||
|
title = "Episode Title",
|
||||||
|
description =
|
||||||
|
"This is the description of the episode. It might be long. " +
|
||||||
|
"It might be very, very long and overflow the available space. " +
|
||||||
|
"But it just keeps going and going.",
|
||||||
|
imageUrl = "",
|
||||||
|
onClick = {},
|
||||||
|
aspectRatio = AspectRatios.WIDE,
|
||||||
|
timeLeft = 30.seconds,
|
||||||
|
runtime = 32.minutes + 20.seconds,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.fillMaxHeight(.4f)
|
||||||
|
.fillMaxWidth(.66f)
|
||||||
|
.align(Alignment.BottomCenter)
|
||||||
|
.background(
|
||||||
|
MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp),
|
||||||
|
shape = RoundedCornerShape(8.dp),
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
CompositionLocalProvider(LocalAsyncImagePreviewHandler provides previewHandler) {
|
|
||||||
NextUpEpisode(
|
|
||||||
title = "Episode Title",
|
|
||||||
description =
|
|
||||||
"This is the description of the episode. It might be long. " +
|
|
||||||
"It might be very, very long and overflow the available space. " +
|
|
||||||
"But it just keeps going and going.",
|
|
||||||
imageUrl = "",
|
|
||||||
onClick = {},
|
|
||||||
aspectRatio = AspectRatios.FOUR_THREE,
|
|
||||||
timeLeft = 30.seconds,
|
|
||||||
modifier =
|
|
||||||
Modifier
|
|
||||||
.padding(16.dp)
|
|
||||||
.height(200.dp)
|
|
||||||
.width(400.dp)
|
|
||||||
// .fillMaxWidth(.4f)
|
|
||||||
// .align(Alignment.BottomCenter)
|
|
||||||
.background(
|
|
||||||
MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp),
|
|
||||||
shape = RoundedCornerShape(8.dp),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@ import com.github.damontecres.wholphin.util.Media3SubtitleOverride
|
||||||
import io.github.peerless2012.ass.media.widget.AssSubtitleView
|
import io.github.peerless2012.ass.media.widget.AssSubtitleView
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.jellyfin.sdk.model.extensions.ticks
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
@ -600,6 +601,7 @@ fun PlaybackPageContent(
|
||||||
viewModel.playNextUp()
|
viewModel.playNextUp()
|
||||||
},
|
},
|
||||||
timeLeft = if (autoPlayEnabled) timeLeft.seconds else null,
|
timeLeft = if (autoPlayEnabled) timeLeft.seconds else null,
|
||||||
|
runtime = it.data.runTimeTicks?.ticks,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue