feat: improved slider behavior, precision, and UI consistency

This commit is contained in:
Alex Schwemler 2026-05-06 20:49:23 +02:00
parent 8e201a6986
commit 064a7ccb70

View file

@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyRow
@ -46,6 +47,7 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.nuvio.app.features.addons.AddonRepository
@ -327,10 +329,9 @@ private fun PlaybackSettingsSection(
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Column(modifier = Modifier.weight(1f)) {
Column(modifier = Modifier.weight(1f).padding(end = 12.dp)) {
Text(
text = stringResource(Res.string.settings_playback_stream_timeout),
style = MaterialTheme.typography.bodyLarge,
@ -338,21 +339,22 @@ private fun PlaybackSettingsSection(
)
Text(
text = stringResource(Res.string.settings_playback_stream_timeout_description),
style = MaterialTheme.typography.bodySmall,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
ValueBox(text = timeoutLabel, modifier = Modifier.wrapContentWidth())
}
var sliderValue by remember(timeoutSec) { mutableFloatStateOf(timeoutSec.toFloat()) }
var lastHapticStep by remember(timeoutSec) { mutableStateOf(timeoutSec) }
var lastHapticStep by remember(timeoutSec) { mutableStateOf(timeoutSec.toFloat()) }
Slider(
value = sliderValue,
onValueChange = {
sliderValue = it
val steppedValue = it.toInt()
if (steppedValue != lastHapticStep) {
lastHapticStep = steppedValue
val snapped = snapToStep(it, 1f)
sliderValue = snapped
if (snapped != lastHapticStep) {
lastHapticStep = snapped
hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove)
}
},
@ -360,7 +362,7 @@ private fun PlaybackSettingsSection(
PlayerSettingsRepository.setStreamAutoPlayTimeoutSeconds(sliderValue.toInt())
},
valueRange = 0f..11f,
steps = 10,
steps = calculateSteps(0f, 11f, 1f),
colors = SliderDefaults.colors(
thumbColor = MaterialTheme.colorScheme.primary,
activeTrackColor = MaterialTheme.colorScheme.primary,
@ -593,10 +595,9 @@ private fun PlaybackSettingsSection(
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Column(modifier = Modifier.weight(1f)) {
Column(modifier = Modifier.weight(1f).padding(end = 12.dp)) {
Text(
text = stringResource(Res.string.settings_playback_threshold_percentage),
style = MaterialTheme.typography.bodyLarge,
@ -604,7 +605,7 @@ private fun PlaybackSettingsSection(
)
Text(
text = stringResource(Res.string.settings_playback_threshold_percentage_description),
style = MaterialTheme.typography.bodySmall,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
@ -612,15 +613,16 @@ private fun PlaybackSettingsSection(
Res.string.settings_playback_threshold_percentage_value,
formatStep(thresholdPercent)), modifier = Modifier.wrapContentWidth())
}
var sliderVal by remember(thresholdPercent) { mutableFloatStateOf(thresholdPercent) }
var lastHapticPercent by remember(thresholdPercent) { mutableStateOf(thresholdPercent.toInt()) }
var sliderValue by remember(thresholdPercent) { mutableFloatStateOf(thresholdPercent) }
var lastHapticPercent by remember(thresholdPercent) { mutableStateOf(thresholdPercent) }
Slider(
value = sliderVal,
onValueChange = {
sliderVal = it
val stepped = it.toInt()
if (stepped != lastHapticPercent) {
lastHapticPercent = stepped
val snapped = snapToStep(it, 0.5f)
sliderValue = snapped
if (snapped != lastHapticPercent) {
lastHapticPercent = snapped
hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove)
}
},
@ -646,10 +648,9 @@ private fun PlaybackSettingsSection(
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Column(modifier = Modifier.weight(1f)) {
Column(modifier = Modifier.weight(1f).padding(end = 12.dp)) {
Text(
text = stringResource(Res.string.settings_playback_minutes_before_end),
style = MaterialTheme.typography.bodyLarge,
@ -657,28 +658,24 @@ private fun PlaybackSettingsSection(
)
Text(
text = stringResource(Res.string.settings_playback_minutes_before_end_description),
style = MaterialTheme.typography.bodySmall,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
ValueBox(text = stringResource(
Res.string.settings_playback_minutes_value,
thresholdMinutes.toInt(),
),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.primary,
fontWeight = FontWeight.SemiBold,
)
formatStep(thresholdMinutes)), modifier = Modifier.wrapContentWidth())
}
var sliderVal by remember(thresholdMinutes) { mutableFloatStateOf(thresholdMinutes) }
var lastHapticMin by remember(thresholdMinutes) { mutableStateOf(thresholdMinutes.toInt()) }
var sliderValue by remember(thresholdMinutes) { mutableFloatStateOf(thresholdMinutes) }
var lastHapticMin by remember(thresholdMinutes) { mutableStateOf(thresholdMinutes) }
Slider(
value = sliderVal,
onValueChange = {
sliderVal = it
val stepped = it.toInt()
if (stepped != lastHapticMin) {
lastHapticMin = stepped
val snapped = snapToStep(it, 0.5f)
sliderValue = snapped
if (snapped != lastHapticMin) {
lastHapticMin = snapped
hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove)
}
},