Fix Android TV scrub: left/right always seek like Netflix, polish player controls.
This commit is contained in:
parent
276467f09b
commit
d9785975e0
2 changed files with 178 additions and 88 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 = 109
|
versionCode = 110
|
||||||
versionName = "0.14.46"
|
versionName = "0.14.47"
|
||||||
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")
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,10 @@ fun PlayerScreen(
|
||||||
var showNextOverlay by remember { mutableStateOf(false) }
|
var showNextOverlay by remember { mutableStateOf(false) }
|
||||||
var nextCountdown by remember { mutableIntStateOf(10) }
|
var nextCountdown by remember { mutableIntStateOf(10) }
|
||||||
var nextOverlayConsumed by remember { mutableStateOf(false) }
|
var nextOverlayConsumed by remember { mutableStateOf(false) }
|
||||||
|
/** true = knoppenrij heeft focus (↓); false = scrub-modus (←/→ seeken). */
|
||||||
|
var controlsNavMode by remember { mutableStateOf(false) }
|
||||||
|
var scrubHint by remember { mutableStateOf<String?>(null) }
|
||||||
|
var scrubToken by remember { mutableIntStateOf(0) }
|
||||||
|
|
||||||
BindCastPlayerRemote(
|
BindCastPlayerRemote(
|
||||||
api = api,
|
api = api,
|
||||||
|
|
@ -133,12 +137,29 @@ fun PlayerScreen(
|
||||||
onStop = onBack,
|
onStop = onBack,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun showControls() {
|
fun showControls(navMode: Boolean = controlsNavMode) {
|
||||||
controls = true
|
controls = true
|
||||||
|
controlsNavMode = navMode
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hideControls() {
|
fun hideControls() {
|
||||||
if (menu == null && !showNextOverlay) controls = false
|
if (menu == null && !showNextOverlay) {
|
||||||
|
controls = false
|
||||||
|
controlsNavMode = false
|
||||||
|
scrubHint = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun scrubBy(deltaMs: Long) {
|
||||||
|
val dur = player.duration.takeIf { it > 0 } ?: Long.MAX_VALUE
|
||||||
|
val target = (player.currentPosition + deltaMs).coerceIn(0L, dur)
|
||||||
|
player.seekTo(target)
|
||||||
|
positionMs = target
|
||||||
|
val sec = kotlin.math.abs(deltaMs) / 1000
|
||||||
|
scrubHint = if (deltaMs < 0) "−${sec}s" else "+${sec}s"
|
||||||
|
scrubToken++
|
||||||
|
// Netflix: scrub houdt bediening zichtbaar, maar ←/→ blijven seeken (geen knop-focus).
|
||||||
|
showControls(navMode = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun goNext() {
|
fun goNext() {
|
||||||
|
|
@ -156,7 +177,7 @@ fun PlayerScreen(
|
||||||
|
|
||||||
fun togglePlay() {
|
fun togglePlay() {
|
||||||
if (player.isPlaying) player.pause() else player.play()
|
if (player.isPlaying) player.pause() else player.play()
|
||||||
showControls()
|
showControls(navMode = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openOpenSubtitles() {
|
fun openOpenSubtitles() {
|
||||||
|
|
@ -383,20 +404,29 @@ fun PlayerScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(controls, menu, playing) {
|
LaunchedEffect(controls, menu, playing, scrubToken) {
|
||||||
if (controls && menu == null && playing) {
|
if (controls && menu == null && playing && scrubHint == null) {
|
||||||
delay(4_500)
|
delay(4_500)
|
||||||
hideControls()
|
hideControls()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(controls, menu) {
|
LaunchedEffect(scrubToken) {
|
||||||
|
if (scrubHint == null) return@LaunchedEffect
|
||||||
|
delay(900)
|
||||||
|
scrubHint = null
|
||||||
|
if (controls && menu == null && playing) {
|
||||||
|
delay(3_200)
|
||||||
|
hideControls()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(controls, menu, controlsNavMode) {
|
||||||
delay(40)
|
delay(40)
|
||||||
if (menu != null) return@LaunchedEffect
|
if (menu != null) return@LaunchedEffect
|
||||||
if (controls) {
|
when {
|
||||||
runCatching { controlsFocus.requestFocus() }
|
controls && controlsNavMode -> runCatching { controlsFocus.requestFocus() }
|
||||||
} else {
|
else -> runCatching { catcherFocus.requestFocus() }
|
||||||
runCatching { catcherFocus.requestFocus() }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -409,7 +439,7 @@ fun PlayerScreen(
|
||||||
when (ev.key) {
|
when (ev.key) {
|
||||||
Key.DirectionCenter, Key.Enter, Key.NumPadEnter -> {
|
Key.DirectionCenter, Key.Enter, Key.NumPadEnter -> {
|
||||||
if (menu != null) return@onPreviewKeyEvent false
|
if (menu != null) return@onPreviewKeyEvent false
|
||||||
if (!controls) {
|
if (!controls || !controlsNavMode) {
|
||||||
// Netflix: OK toont bediening én pauzeert/hervat
|
// Netflix: OK toont bediening én pauzeert/hervat
|
||||||
togglePlay()
|
togglePlay()
|
||||||
true
|
true
|
||||||
|
|
@ -425,53 +455,59 @@ fun PlayerScreen(
|
||||||
Key.MediaPlay -> {
|
Key.MediaPlay -> {
|
||||||
if (menu != null) return@onPreviewKeyEvent false
|
if (menu != null) return@onPreviewKeyEvent false
|
||||||
player.play()
|
player.play()
|
||||||
showControls()
|
showControls(navMode = true)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Key.MediaPause -> {
|
Key.MediaPause -> {
|
||||||
if (menu != null) return@onPreviewKeyEvent false
|
if (menu != null) return@onPreviewKeyEvent false
|
||||||
player.pause()
|
player.pause()
|
||||||
showControls()
|
showControls(navMode = true)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Key.MediaRewind -> {
|
Key.MediaRewind -> {
|
||||||
if (menu != null) return@onPreviewKeyEvent false
|
if (menu != null) return@onPreviewKeyEvent false
|
||||||
player.seekBack()
|
scrubBy(-10_000)
|
||||||
showControls()
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Key.MediaFastForward -> {
|
Key.MediaFastForward -> {
|
||||||
if (menu != null) return@onPreviewKeyEvent false
|
if (menu != null) return@onPreviewKeyEvent false
|
||||||
player.seekForward()
|
scrubBy(10_000)
|
||||||
showControls()
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Key.DirectionLeft -> {
|
Key.DirectionLeft -> {
|
||||||
// Alleen seeken zonder controls; mét controls = navigatie in menu
|
// Altijd scrubben (ook mét controls zichtbaar) — nooit door knoppen navigeren.
|
||||||
if (menu != null || controls) return@onPreviewKeyEvent false
|
if (menu != null) return@onPreviewKeyEvent false
|
||||||
player.seekBack()
|
scrubBy(-10_000)
|
||||||
showControls()
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Key.DirectionRight -> {
|
Key.DirectionRight -> {
|
||||||
if (menu != null || controls) return@onPreviewKeyEvent false
|
if (menu != null) return@onPreviewKeyEvent false
|
||||||
player.seekForward()
|
scrubBy(10_000)
|
||||||
showControls()
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Key.DirectionDown -> {
|
Key.DirectionDown -> {
|
||||||
if (menu != null) return@onPreviewKeyEvent false
|
if (menu != null) return@onPreviewKeyEvent false
|
||||||
if (!controls) {
|
when {
|
||||||
showControls()
|
!controls -> {
|
||||||
true
|
showControls(navMode = true)
|
||||||
} else {
|
true
|
||||||
false
|
}
|
||||||
|
!controlsNavMode -> {
|
||||||
|
controlsNavMode = true
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Key.DirectionUp, Key.Menu -> {
|
Key.DirectionUp, Key.Menu -> {
|
||||||
if (menu != null) return@onPreviewKeyEvent false
|
if (menu != null) return@onPreviewKeyEvent false
|
||||||
showControls()
|
if (controls && controlsNavMode) {
|
||||||
true
|
controlsNavMode = false
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
showControls(navMode = false)
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
@ -496,8 +532,8 @@ fun PlayerScreen(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Houd altijd focus wanneer controls weg zijn, anders bereiken D-pad/OK Compose niet
|
// Focus-catcher: actief in scrub-modus of zonder controls
|
||||||
if (!controls && menu == null && error == null) {
|
if ((!controls || !controlsNavMode) && menu == null && error == null) {
|
||||||
Box(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
@ -526,48 +562,66 @@ fun PlayerScreen(
|
||||||
exit = fadeOut(),
|
exit = fadeOut(),
|
||||||
) {
|
) {
|
||||||
Box(Modifier.fillMaxSize()) {
|
Box(Modifier.fillMaxSize()) {
|
||||||
// Top / bottom scrim zoals Netflix
|
|
||||||
Box(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(
|
.background(
|
||||||
Brush.verticalGradient(
|
Brush.verticalGradient(
|
||||||
listOf(
|
0f to Color(0xB3000000),
|
||||||
Color(0xCC000000),
|
0.28f to Color.Transparent,
|
||||||
Color.Transparent,
|
0.55f to Color.Transparent,
|
||||||
Color.Transparent,
|
1f to Color(0xF2000000),
|
||||||
Color(0xF2000000),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Terug linksboven
|
|
||||||
Row(
|
Row(
|
||||||
Modifier
|
Modifier
|
||||||
.align(Alignment.TopStart)
|
.align(Alignment.TopStart)
|
||||||
.padding(28.dp),
|
.padding(horizontal = 36.dp, vertical = 28.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
NetflixTextButton("← Terug", onClick = onBack)
|
NetflixTextButton("← Terug", onClick = onBack)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grote play/pause in het midden als gepauzeerd
|
// Scrub-hint midden (Netflix-stijl)
|
||||||
if (!playing) {
|
AnimatedVisibility(
|
||||||
|
visible = scrubHint != null,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut(),
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.clip(RoundedCornerShape(40.dp))
|
||||||
|
.background(Color(0xCC000000))
|
||||||
|
.padding(horizontal = 28.dp, vertical = 16.dp),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
scrubHint.orEmpty(),
|
||||||
|
color = Color.White,
|
||||||
|
fontSize = 28.sp,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!playing && scrubHint == null) {
|
||||||
Surface(
|
Surface(
|
||||||
onClick = { togglePlay() },
|
onClick = { togglePlay() },
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center),
|
||||||
shape = ClickableSurfaceDefaults.shape(shape = CircleShape),
|
shape = ClickableSurfaceDefaults.shape(shape = CircleShape),
|
||||||
scale = ClickableSurfaceDefaults.scale(focusedScale = 1.12f),
|
scale = ClickableSurfaceDefaults.scale(focusedScale = 1.12f),
|
||||||
colors = ClickableSurfaceDefaults.colors(
|
colors = ClickableSurfaceDefaults.colors(
|
||||||
containerColor = Color(0x55FFFFFF),
|
containerColor = Color(0x66FFFFFF),
|
||||||
focusedContainerColor = Color.White,
|
focusedContainerColor = Color.White,
|
||||||
contentColor = Color.White,
|
contentColor = Color.White,
|
||||||
focusedContentColor = Color.Black,
|
focusedContentColor = Color.Black,
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
Box(Modifier.size(88.dp), contentAlignment = Alignment.Center) {
|
Box(Modifier.size(92.dp), contentAlignment = Alignment.Center) {
|
||||||
Text("▶", color = Color.Unspecified, fontSize = 36.sp)
|
Text("▶", color = Color.Unspecified, fontSize = 38.sp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -576,68 +630,92 @@ fun PlayerScreen(
|
||||||
Modifier
|
Modifier
|
||||||
.align(Alignment.BottomStart)
|
.align(Alignment.BottomStart)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 48.dp, vertical = 28.dp),
|
.padding(horizontal = 48.dp, vertical = 32.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
verticalArrangement = Arrangement.spacedBy(14.dp),
|
||||||
) {
|
) {
|
||||||
Box(
|
val pct = if (durationMs > 0) {
|
||||||
Modifier
|
(positionMs.toFloat() / durationMs.toFloat()).coerceIn(0f, 1f)
|
||||||
.fillMaxWidth()
|
} else {
|
||||||
.height(3.dp)
|
0f
|
||||||
.clip(RoundedCornerShape(2.dp))
|
}
|
||||||
.background(Color(0x44FFFFFF)),
|
// Progress met scrub-knop
|
||||||
) {
|
Box(Modifier.fillMaxWidth().height(18.dp), contentAlignment = Alignment.CenterStart) {
|
||||||
val pct = if (durationMs > 0) (positionMs.toFloat() / durationMs).coerceIn(0f, 1f) else 0f
|
Box(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(if (scrubHint != null) 5.dp else 4.dp)
|
||||||
|
.clip(RoundedCornerShape(3.dp))
|
||||||
|
.background(Color(0x55FFFFFF)),
|
||||||
|
)
|
||||||
Box(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth(pct)
|
.fillMaxWidth(pct)
|
||||||
.height(3.dp)
|
.height(if (scrubHint != null) 5.dp else 4.dp)
|
||||||
|
.clip(RoundedCornerShape(3.dp))
|
||||||
.background(McColors.Brand),
|
.background(McColors.Brand),
|
||||||
)
|
)
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth(pct)
|
||||||
|
.height(18.dp),
|
||||||
|
contentAlignment = Alignment.CenterEnd,
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.size(if (scrubHint != null) 16.dp else 12.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(Color.White),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Row(
|
Row(
|
||||||
Modifier.fillMaxWidth(),
|
Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(formatTime(positionMs), color = Color(0xFFB3B3B3), fontSize = 13.sp)
|
Text(formatTime(positionMs), color = Color(0xFFE8E8E8), fontSize = 14.sp, fontWeight = FontWeight.Medium)
|
||||||
val endsAtBar = remainingMsFromDuration(durationMs, positionMs)
|
val endsAtBar = remainingMsFromDuration(durationMs, positionMs)
|
||||||
?.let { formatEndsAt(it) }
|
?.let { formatEndsAt(it) }
|
||||||
if (endsAtBar != null) {
|
if (endsAtBar != null) {
|
||||||
Text(endsAtBar, color = Color(0xFFFFD54F), fontSize = 13.sp)
|
Text(endsAtBar, color = Color(0xFFB0B0B0), fontSize = 13.sp)
|
||||||
}
|
}
|
||||||
Text(formatTime(durationMs), color = Color(0xFFB3B3B3), fontSize = 13.sp)
|
Text(formatTime(durationMs), color = Color(0xFFE8E8E8), fontSize = 14.sp, fontWeight = FontWeight.Medium)
|
||||||
}
|
}
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.focusRequester(controlsFocus),
|
.focusRequester(controlsFocus),
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
NetflixIconButton(if (playing) "❚❚" else "▶", primary = true) { togglePlay() }
|
NetflixIconButton(if (playing) "❚❚" else "▶", primary = true) { togglePlay() }
|
||||||
|
NetflixIconButton("−10") {
|
||||||
|
scrubBy(-10_000)
|
||||||
|
}
|
||||||
|
NetflixIconButton("+10") {
|
||||||
|
scrubBy(10_000)
|
||||||
|
}
|
||||||
if (nextEp != null && onPlayNext != null) {
|
if (nextEp != null && onPlayNext != null) {
|
||||||
NetflixIconButton("⏭") {
|
NetflixIconButton("⏭") { goNext() }
|
||||||
goNext()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NetflixIconButton("↺ 10") {
|
|
||||||
player.seekBack()
|
|
||||||
showControls()
|
|
||||||
}
|
|
||||||
NetflixIconButton("10 ↻") {
|
|
||||||
player.seekForward()
|
|
||||||
showControls()
|
|
||||||
}
|
}
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
NetflixTextButton("Audio") {
|
NetflixTextButton("Audio") {
|
||||||
menu = "audio"
|
menu = "audio"
|
||||||
showControls()
|
showControls(navMode = true)
|
||||||
}
|
}
|
||||||
NetflixTextButton("Ondertiteling") {
|
NetflixTextButton("Ondertiteling") {
|
||||||
menu = "text"
|
menu = "text"
|
||||||
showControls()
|
showControls(navMode = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!controlsNavMode && scrubHint == null) {
|
||||||
|
Text(
|
||||||
|
"← → spoelen · ↓ knoppen · OK pauze",
|
||||||
|
color = Color(0x88FFFFFF),
|
||||||
|
fontSize = 12.sp,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -804,19 +882,25 @@ private fun NetflixIconButton(
|
||||||
Surface(
|
Surface(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
shape = ClickableSurfaceDefaults.shape(shape = CircleShape),
|
shape = ClickableSurfaceDefaults.shape(shape = CircleShape),
|
||||||
scale = ClickableSurfaceDefaults.scale(focusedScale = 1.15f),
|
scale = ClickableSurfaceDefaults.scale(focusedScale = 1.12f),
|
||||||
|
border = ClickableSurfaceDefaults.border(
|
||||||
|
focusedBorder = androidx.tv.material3.Border(
|
||||||
|
border = androidx.compose.foundation.BorderStroke(2.dp, Color.White),
|
||||||
|
shape = CircleShape,
|
||||||
|
),
|
||||||
|
),
|
||||||
colors = ClickableSurfaceDefaults.colors(
|
colors = ClickableSurfaceDefaults.colors(
|
||||||
containerColor = if (primary) Color.White else Color(0x22FFFFFF),
|
containerColor = if (primary) Color.White else Color(0x33FFFFFF),
|
||||||
focusedContainerColor = Color.White,
|
focusedContainerColor = Color.White,
|
||||||
contentColor = if (primary) Color.Black else Color.White,
|
contentColor = if (primary) Color.Black else Color.White,
|
||||||
focusedContentColor = Color.Black,
|
focusedContentColor = Color.Black,
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
Modifier.size(52.dp),
|
Modifier.size(56.dp),
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
Text(label, color = Color.Unspecified, fontSize = 14.sp, fontWeight = FontWeight.Bold)
|
Text(label, color = Color.Unspecified, fontSize = 15.sp, fontWeight = FontWeight.Bold)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -826,18 +910,24 @@ private fun NetflixIconButton(
|
||||||
private fun NetflixTextButton(label: String, onClick: () -> Unit) {
|
private fun NetflixTextButton(label: String, onClick: () -> Unit) {
|
||||||
Surface(
|
Surface(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
shape = ClickableSurfaceDefaults.shape(shape = RoundedCornerShape(4.dp)),
|
shape = ClickableSurfaceDefaults.shape(shape = RoundedCornerShape(6.dp)),
|
||||||
scale = ClickableSurfaceDefaults.scale(focusedScale = 1.06f),
|
scale = ClickableSurfaceDefaults.scale(focusedScale = 1.05f),
|
||||||
|
border = ClickableSurfaceDefaults.border(
|
||||||
|
focusedBorder = androidx.tv.material3.Border(
|
||||||
|
border = androidx.compose.foundation.BorderStroke(2.dp, Color.White),
|
||||||
|
shape = RoundedCornerShape(6.dp),
|
||||||
|
),
|
||||||
|
),
|
||||||
colors = ClickableSurfaceDefaults.colors(
|
colors = ClickableSurfaceDefaults.colors(
|
||||||
containerColor = Color.Transparent,
|
containerColor = Color(0x22FFFFFF),
|
||||||
focusedContainerColor = Color(0x33FFFFFF),
|
focusedContainerColor = Color.White,
|
||||||
contentColor = Color(0xFFE5E5E5),
|
contentColor = Color(0xFFF0F0F0),
|
||||||
focusedContentColor = Color.White,
|
focusedContentColor = Color.Black,
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
modifier = Modifier.padding(horizontal = 14.dp, vertical = 10.dp),
|
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||||
color = Color.Unspecified,
|
color = Color.Unspecified,
|
||||||
fontSize = 15.sp,
|
fontSize = 15.sp,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue