Tighten detail action buttons for readability on backdrops.
Use compact solid buttons and a quieter back control so labels stay clear over hero art. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
6c96b0ca01
commit
57280ef470
2 changed files with 96 additions and 15 deletions
|
|
@ -12,8 +12,8 @@ android {
|
||||||
applicationId = "nl.vonas.mediacluster.tv"
|
applicationId = "nl.vonas.mediacluster.tv"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 16
|
versionCode = 17
|
||||||
versionName = "0.7.9"
|
versionName = "0.8.0"
|
||||||
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")
|
||||||
|
|
|
||||||
|
|
@ -45,15 +45,16 @@ import androidx.compose.ui.text.font.FontWeight
|
||||||
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
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.tv.material3.Border
|
||||||
import androidx.tv.material3.ClickableSurfaceDefaults
|
import androidx.tv.material3.ClickableSurfaceDefaults
|
||||||
import androidx.tv.material3.ExperimentalTvMaterial3Api
|
import androidx.tv.material3.ExperimentalTvMaterial3Api
|
||||||
|
import androidx.tv.material3.Glow
|
||||||
import androidx.tv.material3.Surface
|
import androidx.tv.material3.Surface
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import nl.vonas.mediacluster.tv.data.ApiClient
|
import nl.vonas.mediacluster.tv.data.ApiClient
|
||||||
import nl.vonas.mediacluster.tv.ui.components.McButton
|
|
||||||
import nl.vonas.mediacluster.tv.ui.theme.McColors
|
import nl.vonas.mediacluster.tv.ui.theme.McColors
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
@ -198,6 +199,86 @@ private fun MetaChips(parts: List<String>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTvMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun DetailBackButton(onClick: () -> Unit) {
|
||||||
|
Surface(
|
||||||
|
onClick = onClick,
|
||||||
|
shape = ClickableSurfaceDefaults.shape(shape = RoundedCornerShape(4.dp)),
|
||||||
|
scale = ClickableSurfaceDefaults.scale(focusedScale = 1.05f),
|
||||||
|
glow = ClickableSurfaceDefaults.glow(focusedGlow = Glow(elevationColor = Color.Transparent, elevation = 0.dp)),
|
||||||
|
colors = ClickableSurfaceDefaults.colors(
|
||||||
|
containerColor = Color.Transparent,
|
||||||
|
focusedContainerColor = Color(0x33FFFFFF),
|
||||||
|
contentColor = Color(0xFFCFCFCF),
|
||||||
|
focusedContentColor = Color.White,
|
||||||
|
),
|
||||||
|
border = ClickableSurfaceDefaults.border(
|
||||||
|
focusedBorder = Border(
|
||||||
|
border = androidx.compose.foundation.BorderStroke(1.dp, Color.White),
|
||||||
|
shape = RoundedCornerShape(4.dp),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
"Terug",
|
||||||
|
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
|
||||||
|
color = Color.Unspecified,
|
||||||
|
fontSize = 13.sp,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTvMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun DetailActionButton(
|
||||||
|
label: String,
|
||||||
|
primary: Boolean = false,
|
||||||
|
focusRequester: FocusRequester? = null,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
val bg = if (primary) Color.White else Color(0xE6181818)
|
||||||
|
val bgFocus = if (primary) Color(0xFFE8E8E8) else Color.White
|
||||||
|
val fg = if (primary) Color.Black else Color.White
|
||||||
|
Surface(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = if (focusRequester != null) Modifier.focusRequester(focusRequester) else Modifier,
|
||||||
|
shape = ClickableSurfaceDefaults.shape(shape = RoundedCornerShape(4.dp)),
|
||||||
|
scale = ClickableSurfaceDefaults.scale(focusedScale = 1.05f),
|
||||||
|
glow = ClickableSurfaceDefaults.glow(focusedGlow = Glow(elevationColor = Color.Transparent, elevation = 0.dp)),
|
||||||
|
colors = ClickableSurfaceDefaults.colors(
|
||||||
|
containerColor = bg,
|
||||||
|
focusedContainerColor = bgFocus,
|
||||||
|
contentColor = fg,
|
||||||
|
focusedContentColor = Color.Black,
|
||||||
|
),
|
||||||
|
border = if (primary) {
|
||||||
|
ClickableSurfaceDefaults.border()
|
||||||
|
} else {
|
||||||
|
ClickableSurfaceDefaults.border(
|
||||||
|
border = Border(
|
||||||
|
border = androidx.compose.foundation.BorderStroke(1.dp, Color(0x55FFFFFF)),
|
||||||
|
shape = RoundedCornerShape(4.dp),
|
||||||
|
),
|
||||||
|
focusedBorder = Border(
|
||||||
|
border = androidx.compose.foundation.BorderStroke(1.dp, Color.White),
|
||||||
|
shape = RoundedCornerShape(4.dp),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = label,
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||||
|
color = Color.Unspecified,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
maxLines = 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalTvMaterial3Api::class)
|
@OptIn(ExperimentalTvMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MovieDetailScreen(
|
fun MovieDetailScreen(
|
||||||
|
|
@ -286,7 +367,7 @@ fun MovieDetailScreen(
|
||||||
.padding(start = 44.dp, end = 20.dp, top = 28.dp, bottom = 28.dp),
|
.padding(start = 44.dp, end = 20.dp, top = 28.dp, bottom = 28.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
) {
|
) {
|
||||||
McButton("← Terug", onClick = onBack)
|
DetailBackButton(onClick = onBack)
|
||||||
Spacer(Modifier.height(8.dp))
|
Spacer(Modifier.height(8.dp))
|
||||||
DetailLogoOrTitle(logoUrl, title)
|
DetailLogoOrTitle(logoUrl, title)
|
||||||
MetaChips(metaParts)
|
MetaChips(metaParts)
|
||||||
|
|
@ -320,18 +401,18 @@ fun MovieDetailScreen(
|
||||||
}
|
}
|
||||||
if (error != null) Text(error!!, color = McColors.Danger)
|
if (error != null) Text(error!!, color = McColors.Danger)
|
||||||
Spacer(Modifier.height(4.dp))
|
Spacer(Modifier.height(4.dp))
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
files.firstOrNull()?.let { (id, _) ->
|
files.firstOrNull()?.let { (id, _) ->
|
||||||
McButton("▶ Afspelen", primary = true, focusRequester = playFocus) {
|
DetailActionButton("Afspelen", primary = true, focusRequester = playFocus) {
|
||||||
onPlay(id, title, 0)
|
onPlay(id, title, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!trailerKey.isNullOrBlank()) {
|
if (!trailerKey.isNullOrBlank()) {
|
||||||
McButton("Trailer") {
|
DetailActionButton("Trailer") {
|
||||||
openYoutubeTrailer(context, trailerKey!!)
|
openYoutubeTrailer(context, trailerKey!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
McButton("Als gekeken") {
|
DetailActionButton("Als gekeken") {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
runCatching { api.markWatched(movieId = movieId) }
|
runCatching { api.markWatched(movieId = movieId) }
|
||||||
onBack()
|
onBack()
|
||||||
|
|
@ -341,9 +422,9 @@ fun MovieDetailScreen(
|
||||||
if (files.size > 1) {
|
if (files.size > 1) {
|
||||||
Spacer(Modifier.height(4.dp))
|
Spacer(Modifier.height(4.dp))
|
||||||
Text("Versies", color = Color.White, fontSize = 15.sp, fontWeight = FontWeight.SemiBold)
|
Text("Versies", color = Color.White, fontSize = 15.sp, fontWeight = FontWeight.SemiBold)
|
||||||
LazyRow(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
items(files) { (id, label) ->
|
items(files) { (id, label) ->
|
||||||
McButton(label.take(42), primary = id == files.first().first) {
|
DetailActionButton(label.take(36), primary = id == files.first().first) {
|
||||||
onPlay(id, title, 0)
|
onPlay(id, title, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -458,7 +539,7 @@ fun SeriesDetailScreen(
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
McButton("← Terug", onClick = onBack)
|
DetailBackButton(onClick = onBack)
|
||||||
Spacer(Modifier.height(10.dp))
|
Spacer(Modifier.height(10.dp))
|
||||||
DetailLogoOrTitle(logoUrl, title)
|
DetailLogoOrTitle(logoUrl, title)
|
||||||
MetaChips(metaParts)
|
MetaChips(metaParts)
|
||||||
|
|
@ -475,14 +556,14 @@ fun SeriesDetailScreen(
|
||||||
}
|
}
|
||||||
if (error != null) Text(error!!, color = McColors.Danger)
|
if (error != null) Text(error!!, color = McColors.Danger)
|
||||||
Spacer(Modifier.height(6.dp))
|
Spacer(Modifier.height(6.dp))
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
episodes.firstOrNull()?.let { ep ->
|
episodes.firstOrNull()?.let { ep ->
|
||||||
McButton("▶ Afspelen", primary = true, focusRequester = firstEpFocus) {
|
DetailActionButton("Afspelen", primary = true, focusRequester = firstEpFocus) {
|
||||||
onPlay(ep.fileId, ep.id, "$title · S${ep.season}E${ep.number}")
|
onPlay(ep.fileId, ep.id, "$title · S${ep.season}E${ep.number}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!trailerKey.isNullOrBlank()) {
|
if (!trailerKey.isNullOrBlank()) {
|
||||||
McButton("Trailer") { openYoutubeTrailer(context, trailerKey!!) }
|
DetailActionButton("Trailer") { openYoutubeTrailer(context, trailerKey!!) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (seasons.size > 1) {
|
if (seasons.size > 1) {
|
||||||
|
|
@ -492,7 +573,7 @@ fun SeriesDetailScreen(
|
||||||
LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
items(seasons) { s ->
|
items(seasons) { s ->
|
||||||
val n = s.optInt("seasonNumber")
|
val n = s.optInt("seasonNumber")
|
||||||
McButton(
|
DetailActionButton(
|
||||||
"Seizoen $n",
|
"Seizoen $n",
|
||||||
primary = n == selectedSeason,
|
primary = n == selectedSeason,
|
||||||
) { selectedSeason = n }
|
) { selectedSeason = n }
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue