Skip to content

Commit

Permalink
Add TVSlider component
Browse files Browse the repository at this point in the history
  • Loading branch information
MGaetan89 committed May 21, 2024
1 parent 17824a2 commit d852e57
Show file tree
Hide file tree
Showing 4 changed files with 391 additions and 82 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/*
* Copyright (c) SRG SSR. All rights reserved.
* License information is available from the LICENSE file.
*/
package ch.srgssr.pillarbox.demo.tv.ui.components

import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import androidx.tv.material3.MaterialTheme
import ch.srgssr.pillarbox.demo.tv.extension.onDpadEvent
import ch.srgssr.pillarbox.demo.tv.ui.theme.PillarboxTheme

/**
* Slider component suited for use on TV.
*
* @param value The current value of this slider.
* @param range The range of values supported by this slider.
* @param compactMode If `true`, the slider will be thinner.
* @param modifier The [Modifier] to apply to the layout.
* @param enabled Whether or not this slider is enabled.
* @param onSeekBack The action to perform when seeking back.
* @param onSeekForward The action to perform when seeking forward.
*/
@Composable
fun TVSlider(
value: Long,
range: LongRange,
compactMode: Boolean,
modifier: Modifier = Modifier,
enabled: Boolean = true,
onSeekBack: () -> Unit,
onSeekForward: () -> Unit,
) {
val seekBarHeight by animateDpAsState(targetValue = if (compactMode) 8.dp else 16.dp, label = "seek_bar_height")
val thumbColor by animateColorAsState(
targetValue = if (enabled) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f),
label = "thumb_color",
)

val activeTrackWeight by animateFloatAsState(targetValue = value / range.last.toFloat(), label = "active_track_weight")
val activeTrackColor by animateColorAsState(
targetValue = if (enabled) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f),
label = "active_track_color",
)

val inactiveTrackWeight by animateFloatAsState(targetValue = 1f - activeTrackWeight, label = "inactive_track_weight")
val inactiveTrackColor by animateColorAsState(
targetValue = if (enabled) MaterialTheme.colorScheme.surfaceVariant else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f),
label = "inactive_track_color",
)

Row(
modifier = modifier.height(seekBarHeight),
horizontalArrangement = Arrangement.spacedBy(6.dp),
) {
Track(
weight = activeTrackWeight,
color = activeTrackColor,
)

Thumb(
color = thumbColor,
enabled = enabled,
onSeekBack = onSeekBack,
onSeekForward = onSeekForward,
)

Track(
weight = inactiveTrackWeight,
color = inactiveTrackColor,
)
}
}

@Composable
private fun RowScope.Track(
weight: Float,
color: Color,
modifier: Modifier = Modifier,
) {
if (weight > 0f) {
Box(
modifier = modifier
.fillMaxHeight()
.weight(weight)
.background(
color = color,
shape = CircleShape,
),
)
}
}

@Composable
private fun Thumb(
color: Color,
enabled: Boolean,
modifier: Modifier = Modifier,
onSeekBack: () -> Unit,
onSeekForward: () -> Unit,
) {
Box(
modifier = modifier
.fillMaxHeight()
.width(8.dp)
.background(
color = color,
shape = CircleShape,
)
.then(
if (enabled) {
Modifier
.focusable()
.onDpadEvent(
onLeft = {
onSeekBack()
true
},
onRight = {
onSeekForward()
true
},
)
} else {
Modifier
}
),
)
}

@Composable
@PreviewLightDark
private fun TVSliderPreview(
@PreviewParameter(TVSliderPreviewParameters::class) previewParameters: PreviewParameters,
) {
var progress by remember {
mutableLongStateOf(previewParameters.initialValue)
}

PillarboxTheme {
TVSlider(
value = progress,
range = 0L..100L,
compactMode = previewParameters.compactMode,
enabled = previewParameters.enabled,
onSeekBack = { progress-- },
onSeekForward = { progress++ },
)
}
}

private class PreviewParameters(
val compactMode: Boolean,
val enabled: Boolean,
val initialValue: Long,
)

private class TVSliderPreviewParameters : PreviewParameterProvider<PreviewParameters> {
override val values = sequence {
listOf(false, true).forEach { compactMode ->
listOf(false, true).forEach { enabled ->
listOf(0L, 50L, 100L).forEach { initialValue ->
yield(
PreviewParameters(
compactMode = compactMode,
enabled = enabled,
initialValue = initialValue,
)
)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
Expand Down Expand Up @@ -47,10 +46,9 @@ fun MediaMetadataView(
modifier = modifier
.background(
brush = Brush.verticalGradient(
colors = listOf(Color.Transparent, Color.Black),
colors = listOf(Color.Black, Color.Transparent),
),
),
verticalAlignment = Alignment.Bottom,
) {
AsyncImage(
modifier = Modifier
Expand All @@ -67,7 +65,7 @@ fun MediaMetadataView(
modifier = Modifier.padding(
start = MaterialTheme.paddings.mini,
top = MaterialTheme.paddings.small,
end = 72.dp, // baseline + 56dp to not overlap with the settings button
end = MaterialTheme.paddings.baseline,
bottom = MaterialTheme.paddings.small,
)
) {
Expand Down
Loading

0 comments on commit d852e57

Please sign in to comment.