Prefer series poster in continue watching with episode overlay.
Use the series poster instead of episode stills, and show SxE plus title on Verder kijken cards. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
388962e6d1
commit
4cd4347daf
3 changed files with 96 additions and 9 deletions
|
|
@ -20,8 +20,8 @@ android {
|
||||||
applicationId = "nl.vonas.mediacluster.tv"
|
applicationId = "nl.vonas.mediacluster.tv"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 43
|
versionCode = 68
|
||||||
versionName = "0.12.1"
|
versionName = "0.14.5"
|
||||||
buildConfigField("String", "DEFAULT_API_BASE", "\"https://master.vonas.nl\"")
|
buildConfigField("String", "DEFAULT_API_BASE", "\"https://master.vonas.nl\"")
|
||||||
ndk {
|
ndk {
|
||||||
abiFilters += listOf("arm64-v8a", "armeabi-v7a")
|
abiFilters += listOf("arm64-v8a", "armeabi-v7a")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
package nl.vonas.mediacluster.tv.ui.components
|
package nl.vonas.mediacluster.tv.ui.components
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
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.height
|
||||||
|
|
@ -17,7 +20,9 @@ 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.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusProperties
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.input.key.Key
|
import androidx.compose.ui.input.key.Key
|
||||||
import androidx.compose.ui.input.key.KeyEventType
|
import androidx.compose.ui.input.key.KeyEventType
|
||||||
|
|
@ -47,6 +52,9 @@ fun McButton(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
primary: Boolean = false,
|
primary: Boolean = false,
|
||||||
focusRequester: FocusRequester? = null,
|
focusRequester: FocusRequester? = null,
|
||||||
|
focusUp: FocusRequester? = null,
|
||||||
|
focusDown: FocusRequester? = null,
|
||||||
|
onFocused: (() -> Unit)? = null,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val bg = if (primary) Color.White else Color(0x33FFFFFF)
|
val bg = if (primary) Color.White else Color(0x33FFFFFF)
|
||||||
|
|
@ -55,9 +63,21 @@ fun McButton(
|
||||||
val fgFocus = Color.Black
|
val fgFocus = Color.Black
|
||||||
Surface(
|
Surface(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = modifier.then(
|
modifier = modifier
|
||||||
if (focusRequester != null) Modifier.focusRequester(focusRequester) else Modifier,
|
.then(if (focusRequester != null) Modifier.focusRequester(focusRequester) else Modifier)
|
||||||
),
|
.then(
|
||||||
|
if (focusUp != null || focusDown != null) {
|
||||||
|
Modifier.focusProperties {
|
||||||
|
focusUp?.let { up = it }
|
||||||
|
focusDown?.let { down = it }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Modifier
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.onFocusChanged { state ->
|
||||||
|
if (state.isFocused) onFocused?.invoke()
|
||||||
|
},
|
||||||
shape = ClickableSurfaceDefaults.shape(shape = RoundedCornerShape(4.dp)),
|
shape = ClickableSurfaceDefaults.shape(shape = RoundedCornerShape(4.dp)),
|
||||||
scale = ClickableSurfaceDefaults.scale(focusedScale = 1.06f),
|
scale = ClickableSurfaceDefaults.scale(focusedScale = 1.06f),
|
||||||
glow = ClickableSurfaceDefaults.glow(
|
glow = ClickableSurfaceDefaults.glow(
|
||||||
|
|
@ -116,6 +136,13 @@ fun PosterCard(
|
||||||
width: Dp = 140.dp,
|
width: Dp = 140.dp,
|
||||||
height: Dp = 210.dp,
|
height: Dp = 210.dp,
|
||||||
onLongClick: (() -> Unit)? = null,
|
onLongClick: (() -> Unit)? = null,
|
||||||
|
/** Toon episode/subtitle overlay (o.a. Verder kijken). */
|
||||||
|
showCaption: Boolean = false,
|
||||||
|
/**
|
||||||
|
* Grid-modus (Films/Series): vul celbreedte met vaste poster-ratio 2:3
|
||||||
|
* (zelfde framing als Home), i.p.v. vaste dp die in bredere cellen verkeerd cropt.
|
||||||
|
*/
|
||||||
|
fillWidth: Boolean = false,
|
||||||
) {
|
) {
|
||||||
val progress = if (item.durationMs > 0) {
|
val progress = if (item.durationMs > 0) {
|
||||||
(item.positionMs.toFloat() / item.durationMs.toFloat()).coerceIn(0f, 1f)
|
(item.positionMs.toFloat() / item.durationMs.toFloat()).coerceIn(0f, 1f)
|
||||||
|
|
@ -128,7 +155,7 @@ fun PosterCard(
|
||||||
onClick = if (useKeyHandler) ({}) else onClick,
|
onClick = if (useKeyHandler) ({}) else onClick,
|
||||||
onLongClick = null,
|
onLongClick = null,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.width(width)
|
.then(if (fillWidth) Modifier.fillMaxWidth() else Modifier.width(width))
|
||||||
.then(
|
.then(
|
||||||
if (useKeyHandler) {
|
if (useKeyHandler) {
|
||||||
Modifier.tvShortOrLongPress(onClick = onClick, onLongPress = onLongClick!!)
|
Modifier.tvShortOrLongPress(onClick = onClick, onLongPress = onLongClick!!)
|
||||||
|
|
@ -153,7 +180,10 @@ fun PosterCard(
|
||||||
Box(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(height)
|
.then(
|
||||||
|
if (fillWidth) Modifier.aspectRatio(2f / 3f)
|
||||||
|
else Modifier.height(height)
|
||||||
|
)
|
||||||
.clip(RoundedCornerShape(4.dp))
|
.clip(RoundedCornerShape(4.dp))
|
||||||
.background(Color(0xFF1A1A1A)),
|
.background(Color(0xFF1A1A1A)),
|
||||||
) {
|
) {
|
||||||
|
|
@ -162,13 +192,70 @@ fun PosterCard(
|
||||||
contentDescription = item.title,
|
contentDescription = item.title,
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
contentScale = ContentScale.Crop,
|
contentScale = ContentScale.Crop,
|
||||||
|
alignment = Alignment.Center,
|
||||||
)
|
)
|
||||||
|
if (showCaption && !item.subtitle.isNullOrBlank()) {
|
||||||
|
val parts = item.subtitle!!.split(" · ", limit = 2)
|
||||||
|
val epCode = parts.getOrNull(0)
|
||||||
|
val epTitle = parts.getOrNull(1)
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.align(Alignment.BottomCenter)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(
|
||||||
|
androidx.compose.ui.graphics.Brush.verticalGradient(
|
||||||
|
listOf(Color.Transparent, Color(0xEE000000)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.padding(horizontal = 8.dp, vertical = 8.dp),
|
||||||
|
) {
|
||||||
|
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||||
|
if (!epCode.isNullOrBlank()) {
|
||||||
|
Text(
|
||||||
|
epCode,
|
||||||
|
color = Color.White,
|
||||||
|
fontSize = 11.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (!epTitle.isNullOrBlank()) {
|
||||||
|
Text(
|
||||||
|
epTitle,
|
||||||
|
color = Color(0xFFD0D0D0),
|
||||||
|
fontSize = 10.sp,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
} else if (epCode.isNullOrBlank()) {
|
||||||
|
Text(
|
||||||
|
item.subtitle!!,
|
||||||
|
color = Color.White,
|
||||||
|
fontSize = 11.sp,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (progress > 0.02f) {
|
if (progress > 0.02f) {
|
||||||
|
val barH = if (showCaption) 5.dp else 4.dp
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.align(Alignment.BottomCenter)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(barH)
|
||||||
|
.background(Color(0x66FFFFFF)),
|
||||||
|
)
|
||||||
Box(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
.align(Alignment.BottomStart)
|
.align(Alignment.BottomStart)
|
||||||
.fillMaxWidth(progress)
|
.fillMaxWidth(progress)
|
||||||
.height(4.dp)
|
.height(barH)
|
||||||
.background(McColors.Brand),
|
.background(McColors.Brand),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -816,7 +816,7 @@ export class ViewerService {
|
||||||
episode.title ? ` · ${episode.title}` : ""
|
episode.title ? ` · ${episode.title}` : ""
|
||||||
}`,
|
}`,
|
||||||
year: episode.series.year,
|
year: episode.series.year,
|
||||||
posterUrl: episode.stillUrl || episode.series.posterUrl,
|
posterUrl: episode.series.posterUrl || episode.stillUrl,
|
||||||
backdropUrl: episode.series.backdropUrl,
|
backdropUrl: episode.series.backdropUrl,
|
||||||
mediaFileId: file?.id ?? null,
|
mediaFileId: file?.id ?? null,
|
||||||
positionMs: p.positionMs,
|
positionMs: p.positionMs,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue