diff --git a/composeApp/src/commonMain/composeResources/values-de/strings.xml b/composeApp/src/commonMain/composeResources/values-de/strings.xml
index 01141177..1722ae84 100644
--- a/composeApp/src/commonMain/composeResources/values-de/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values-de/strings.xml
@@ -649,7 +649,7 @@
Dolby Vision Profil 7 zu HEVC-Fallback für nicht unterstützte Geräte.
Minuten vor Ende
Karte für nächste Episode so viele Minuten vor dem Ende anzeigen.
- %1$d Min.
+ %1$s Min.
Keine Einträge verfügbar
Nicht festgelegt
Standard
@@ -724,9 +724,9 @@
Prozent
Schwellenwert in Prozent
Karte für nächste Episode anzeigen, wenn die Wiedergabe diesen Prozentsatz erreicht.
- %1$d%
+ %1$s%
Sofort
- %1$ds
+ %1$ss
Unbegrenzt
Tunneled-Wiedergabe
Tunneled-Wiedergabe für niedrigere Latenz bei Audio-/Video-Synchronisation aktivieren.
diff --git a/composeApp/src/commonMain/composeResources/values-el/strings.xml b/composeApp/src/commonMain/composeResources/values-el/strings.xml
index 841a751c..2282628b 100644
--- a/composeApp/src/commonMain/composeResources/values-el/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values-el/strings.xml
@@ -494,7 +494,7 @@
Εναλλαγή Dolby Vision Profile 7 σε HEVC για μη υποστηριζόμενες συσκευές.
Λεπτά πριν το τέλος
Εμφάνιση κάρτας επόμενου επεισοδίου τόσα λεπτά πριν το τέλος.
- %1$d λεπτά
+ %1$s λεπτά
Δεν υπάρχουν διαθέσιμα στοιχεία
Δεν έχει οριστεί
Προεπιλογή
@@ -569,9 +569,9 @@
Ποσοστό
Ποσοστό κατωφλίου
Εμφάνιση κάρτας επόμενου επεισοδίου όταν η αναπαραγωγή φτάσει σε αυτό το ποσοστό.
- %1$d%
+ %1$s%
Άμεσα
- %1$ds
+ %1$ss
Απεριόριστο
Tunneled αναπαραγωγή
Ενεργοποίηση tunneled αναπαραγωγής για χαμηλότερη καθυστέρηση συγχρονισμού ήχου/βίντεο.
diff --git a/composeApp/src/commonMain/composeResources/values-es/strings.xml b/composeApp/src/commonMain/composeResources/values-es/strings.xml
index 6a07039f..b52d5056 100644
--- a/composeApp/src/commonMain/composeResources/values-es/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values-es/strings.xml
@@ -612,7 +612,7 @@
Usar Dolby Vision Perfil 7 a HEVC como alternativa para dispositivos no compatibles.
Minutos antes del final
Mostrar la tarjeta del siguiente episodio esta cantidad de minutos antes del final.
- %1$d min
+ %1$s min
No hay elementos disponibles
No establecido
Predeterminado
@@ -687,9 +687,9 @@
Porcentaje
Porcentaje de umbral
Mostrar la tarjeta del siguiente episodio cuando la reproducción alcance este porcentaje.
- %1$d%
+ %1$s%
Instantáneo
- %1$ds
+ %1$ss
Ilimitado
Reproducción tunneled
Activa la reproducción tunneled para una menor latencia en la sincronización de audio/video.
diff --git a/composeApp/src/commonMain/composeResources/values-fr/strings.xml b/composeApp/src/commonMain/composeResources/values-fr/strings.xml
index 71116267..15301839 100644
--- a/composeApp/src/commonMain/composeResources/values-fr/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values-fr/strings.xml
@@ -645,7 +645,7 @@
Utiliser Dolby Vision Profil 7 vers HEVC comme alternative pour les appareils non compatibles.
Minutes avant la fin
Afficher la carte de l'épisode suivant ce nombre de minutes avant la fin.
- %1$d min
+ %1$s min
Aucun élément disponible
Non défini
Par défaut
@@ -720,9 +720,9 @@
Pourcentage
Pourcentage de seuil
Afficher la carte de l'épisode suivant lorsque la lecture atteint ce pourcentage.
- %1$d%
+ %1$s%
Instantané
- %1$ds
+ %1$ss
Illimité
Lecture tunnelisée
Active la lecture tunnelisée pour une latence réduite dans la synchronisation audio/vidéo.
diff --git a/composeApp/src/commonMain/composeResources/values-it/strings.xml b/composeApp/src/commonMain/composeResources/values-it/strings.xml
index fca49b5c..0c4c6ee8 100644
--- a/composeApp/src/commonMain/composeResources/values-it/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values-it/strings.xml
@@ -494,7 +494,7 @@
Fallback da Dolby Vision Profile 7 a HEVC per i dispositivi non supportati.
Minuti prima della fine
Mostra la scheda dell'episodio successivo questo numero di minuti prima della fine.
- %1$d min
+ %1$s min
Nessun elemento disponibile
Non impostato
Predefinito
@@ -569,9 +569,9 @@
Percentuale
Percentuale di soglia
Mostra la scheda dell'episodio successivo quando la riproduzione raggiunge questa percentuale.
- %1$d%
+ %1$s%
Istantaneo
- %1$ds
+ %1$ss
Illimitato
Riproduzione Tunneled
Abilita la riproduzione tunneled per una minore latenza nella sincronizzazione audio/video.
diff --git a/composeApp/src/commonMain/composeResources/values-pl/strings.xml b/composeApp/src/commonMain/composeResources/values-pl/strings.xml
index 7200b49f..00af8afd 100644
--- a/composeApp/src/commonMain/composeResources/values-pl/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values-pl/strings.xml
@@ -612,7 +612,7 @@
Fallback Dolby Vision Profile 7 na HEVC dla nieobsługiwanych urządzeń.
Minuty przed końcem
Pokaż kartę następnego odcinka tyle minut przed końcem.
- %1$d min
+ %1$s min
Brak dostępnych elementów
Nie ustawiono
Domyślny
@@ -687,9 +687,9 @@
Procent
Próg procentowy
Pokaż kartę następnego odcinka, gdy odtwarzanie osiągnie ten procent.
- %1$d%
+ %1$s%
Natychmiast
- %1$ds
+ %1$ss
Bez limitu
Odtwarzanie tunelowane
Włącz odtwarzanie tunelowane dla niższego opóźnienia synchronizacji audio/wideo.
diff --git a/composeApp/src/commonMain/composeResources/values-pt/strings.xml b/composeApp/src/commonMain/composeResources/values-pt/strings.xml
index e5049d24..02896473 100644
--- a/composeApp/src/commonMain/composeResources/values-pt/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values-pt/strings.xml
@@ -624,7 +624,7 @@
Fallback de Dolby Vision Profile 7 para HEVC para dispositivos não suportados.
Minutos Antes do Fim
Mostra o cartão do próximo episódio estes minutos antes do fim.
- %1$d min
+ %1$s min
Nenhum item disponível
Não definido
Padrão
@@ -699,9 +699,9 @@
Percentagem
Percentagem Limite
Mostra o cartão do próximo episódio quando a reprodução atingir esta percentagem.
- %1$d%%
+ %1$s%
Instantâneo
- %1$ds
+ %1$ss
Ilimitado
Reprodução em Túnel
Ativa a reprodução em túnel para menor latência na sincronização de áudio/vídeo.
diff --git a/composeApp/src/commonMain/composeResources/values-tr/strings.xml b/composeApp/src/commonMain/composeResources/values-tr/strings.xml
index e3d66b12..5d2b049f 100644
--- a/composeApp/src/commonMain/composeResources/values-tr/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values-tr/strings.xml
@@ -494,7 +494,7 @@
Desteklenmeyen cihazlar için Dolby Vision Profile 7\'den HEVC\'ye geri dönüş.
Bitmeden kaç dakika önce
Sonraki bölüm kartını bitişten kaç dakika önce göstereceğini seç.
- %1$d dk
+ %1$s dk
Kullanılabilir öğe yok
Ayarlanmadı
Varsayılan
@@ -569,9 +569,9 @@
Yüzde
Eşik yüzdesi
Oynatma bu yüzdeye ulaşınca sonraki bölüm kartını göster.
- %1$d%
+ %1$s%
Hemen
- %1$dsn
+ %1$ssn
Sınırsız
Tunneled oynatma
Daha düşük gecikmeli ses/video senkronu için tunneled oynatmayı aç.
diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml
index e8b01632..f235570b 100644
--- a/composeApp/src/commonMain/composeResources/values/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings.xml
@@ -663,7 +663,7 @@
Map Dolby Vision Profile 7 to standard HEVC for devices without DV hardware support
Threshold Minutes
Fallback when no outro timestamp exists.
- %1$d min
+ %1$s min
No items available
Not set
Default (media file)
@@ -738,9 +738,9 @@
Percentage
Threshold Percentage
Fallback when no outro timestamp exists.
- %1$d%
+ %1$s%
Instant
- %1$ds
+ %1$ss
Unlimited
Tunneled Playback
Hardware-level audio/video sync. May improve playback on some Android TV devices
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/skip/PlayerNextEpisodeRules.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/skip/PlayerNextEpisodeRules.kt
index a703251d..9dd949ae 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/skip/PlayerNextEpisodeRules.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/skip/PlayerNextEpisodeRules.kt
@@ -39,11 +39,11 @@ object PlayerNextEpisodeRules {
if (durationMs <= 0L) return false
when (thresholdMode) {
NextEpisodeThresholdMode.PERCENTAGE -> {
- val clampedPercent = thresholdPercent.coerceIn(97f, 99.5f)
+ val clampedPercent = thresholdPercent.coerceIn(97f, 100f)
(positionMs.toDouble() / durationMs.toDouble()) >= (clampedPercent / 100.0)
}
NextEpisodeThresholdMode.MINUTES_BEFORE_END -> {
- val clampedMinutes = thresholdMinutesBeforeEnd.coerceIn(1f, 3.5f)
+ val clampedMinutes = thresholdMinutesBeforeEnd.coerceIn(0f, 3.5f)
val remainingMs = durationMs - positionMs
remainingMs <= (clampedMinutes * 60_000f).toLong()
}
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt
index 40c2dfb6..18a9c422 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/PlaybackSettingsPage.kt
@@ -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
@@ -64,6 +66,7 @@ import kotlinx.coroutines.launch
import nuvio.composeapp.generated.resources.*
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.stringResource
+import kotlin.math.roundToInt
internal fun LazyListScope.playbackSettingsContent(
isTablet: Boolean,
@@ -103,6 +106,47 @@ internal fun LazyListScope.playbackSettingsContent(
}
}
+private fun formatStep(value: Float): String {
+ return if (value % 1f == 0f) {
+ value.toInt().toString()
+ } else {
+ value.toString()
+ }
+}
+
+fun snapToStep(value: Float, step: Float): Float {
+ return (value / step).roundToInt() * step
+}
+
+fun calculateSteps(
+ min: Float,
+ max: Float,
+ stepSize: Float
+): Int {
+ val totalSteps = ((max - min) / stepSize).roundToInt()
+ return (totalSteps - 1).coerceAtLeast(0)
+}
+
+@Composable
+fun ValueBox(
+ text: String,
+ modifier: Modifier = Modifier
+) {
+ Box(
+ modifier = modifier,
+ contentAlignment = Alignment.CenterEnd
+ ) {
+ Text(
+ text = text,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.primary,
+ fontWeight = FontWeight.SemiBold,
+ )
+ }
+}
+
@Composable
private fun PlaybackSettingsSection(
isTablet: Boolean,
@@ -285,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,
@@ -296,26 +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,
)
}
- Text(
- text = timeoutLabel,
- style = MaterialTheme.typography.bodyMedium,
- color = MaterialTheme.colorScheme.primary,
- fontWeight = FontWeight.SemiBold,
- )
+ 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)
}
},
@@ -323,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,
@@ -556,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,
@@ -567,37 +605,32 @@ 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,
)
}
- Text(
- text = stringResource(
- Res.string.settings_playback_threshold_percentage_value,
- thresholdPercent.toInt(),
- ),
- style = MaterialTheme.typography.bodyMedium,
- color = MaterialTheme.colorScheme.primary,
- fontWeight = FontWeight.SemiBold,
- )
+ ValueBox(text = stringResource(
+ 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,
+ value = sliderValue,
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)
}
},
onValueChangeFinished = {
- PlayerSettingsRepository.setNextEpisodeThresholdPercent(sliderVal)
+ PlayerSettingsRepository.setNextEpisodeThresholdPercent(sliderValue)
},
- valueRange = 50f..100f,
- steps = 49,
+ valueRange = 97f..100f,
+ steps = calculateSteps(97f, 100f, 0.5f),
colors = SliderDefaults.colors(
thumbColor = MaterialTheme.colorScheme.primary,
activeTrackColor = MaterialTheme.colorScheme.primary,
@@ -615,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,
@@ -626,37 +658,32 @@ 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,
)
}
- Text(
- text = stringResource(
+ 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,
+ value = sliderValue,
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)
}
},
onValueChangeFinished = {
- PlayerSettingsRepository.setNextEpisodeThresholdMinutesBeforeEnd(sliderVal)
+ PlayerSettingsRepository.setNextEpisodeThresholdMinutesBeforeEnd(sliderValue)
},
- valueRange = 1f..15f,
- steps = 13,
+ valueRange = 0f..3.5f,
+ steps = calculateSteps(0f, 3.5f, 0.5f),
colors = SliderDefaults.colors(
thumbColor = MaterialTheme.colorScheme.primary,
activeTrackColor = MaterialTheme.colorScheme.primary,