mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 15:29:15 +01:00
polish stream quality settings
- label the rows 'Stream quality: Wi-Fi' / 'Stream quality: Mobile' - default mobile to 192kbps/mp3 while Wi-Fi stays unlimited/server default - apply the selection only when Done is pressed, cancelling discards the draft - fade the bottom of the dialog list so the remaining options are visible as scrollable
This commit is contained in:
@@ -56,7 +56,9 @@ class UserPreferences(
|
|||||||
|
|
||||||
fun streamQuality(mode: NetworkMode): Flow<StreamQuality> =
|
fun streamQuality(mode: NetworkMode): Flow<StreamQuality> =
|
||||||
store.data.map { prefs ->
|
store.data.map { prefs ->
|
||||||
prefs[streamQualityKey(mode)]?.let(::decodeStreamQuality) ?: legacyStreamQuality(prefs)
|
prefs[streamQualityKey(mode)]?.let(::decodeStreamQuality)
|
||||||
|
?: legacyStreamQuality(prefs)
|
||||||
|
?: defaultStreamQuality(mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun setStreamQuality(
|
suspend fun setStreamQuality(
|
||||||
@@ -82,11 +84,21 @@ class UserPreferences(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun legacyStreamQuality(prefs: Preferences): StreamQuality =
|
private fun legacyStreamQuality(prefs: Preferences): StreamQuality? {
|
||||||
StreamQuality(
|
val bitrate = prefs[LEGACY_BITRATE]
|
||||||
maxBitrate = prefs[LEGACY_BITRATE] ?: 0,
|
val format = prefs[LEGACY_FORMAT]
|
||||||
format = prefs[LEGACY_FORMAT]?.takeIf { it.isNotEmpty() },
|
if (bitrate == null && format == null) return null
|
||||||
|
return StreamQuality(
|
||||||
|
maxBitrate = bitrate ?: 0,
|
||||||
|
format = format?.takeIf { it.isNotEmpty() },
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun defaultStreamQuality(mode: NetworkMode): StreamQuality =
|
||||||
|
when (mode) {
|
||||||
|
NetworkMode.Wifi -> StreamQuality()
|
||||||
|
NetworkMode.Mobile -> StreamQuality(maxBitrate = 192, format = "mp3")
|
||||||
|
}
|
||||||
|
|
||||||
private fun decode(
|
private fun decode(
|
||||||
stored: String?,
|
stored: String?,
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.subtracks.ui.settings
|
package com.subtracks.ui.settings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
@@ -39,6 +41,7 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
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
|
||||||
@@ -150,7 +153,7 @@ fun SettingsScreen(
|
|||||||
item { SectionHeader("Network") }
|
item { SectionHeader("Network") }
|
||||||
item {
|
item {
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = { Text("Wi-Fi") },
|
headlineContent = { Text("Stream quality: Wi-Fi") },
|
||||||
supportingContent = { Text(qualityLabel(wifiQuality)) },
|
supportingContent = { Text(qualityLabel(wifiQuality)) },
|
||||||
modifier = Modifier.clickable { dialog = SettingsDialog.WifiQuality },
|
modifier = Modifier.clickable { dialog = SettingsDialog.WifiQuality },
|
||||||
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
|
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
|
||||||
@@ -158,7 +161,7 @@ fun SettingsScreen(
|
|||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = { Text("Mobile data") },
|
headlineContent = { Text("Stream quality: Mobile") },
|
||||||
supportingContent = { Text(qualityLabel(mobileQuality)) },
|
supportingContent = { Text(qualityLabel(mobileQuality)) },
|
||||||
modifier = Modifier.clickable { dialog = SettingsDialog.MobileQuality },
|
modifier = Modifier.clickable { dialog = SettingsDialog.MobileQuality },
|
||||||
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
|
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
|
||||||
@@ -171,7 +174,7 @@ fun SettingsScreen(
|
|||||||
when (dialog) {
|
when (dialog) {
|
||||||
SettingsDialog.WifiQuality -> {
|
SettingsDialog.WifiQuality -> {
|
||||||
QualityDialog(
|
QualityDialog(
|
||||||
title = "Wi-Fi",
|
title = "Stream quality: Wi-Fi",
|
||||||
quality = wifiQuality,
|
quality = wifiQuality,
|
||||||
onSelect = onWifiQualityChange,
|
onSelect = onWifiQualityChange,
|
||||||
onDismiss = { dialog = null },
|
onDismiss = { dialog = null },
|
||||||
@@ -180,7 +183,7 @@ fun SettingsScreen(
|
|||||||
|
|
||||||
SettingsDialog.MobileQuality -> {
|
SettingsDialog.MobileQuality -> {
|
||||||
QualityDialog(
|
QualityDialog(
|
||||||
title = "Mobile data",
|
title = "Stream quality: Mobile",
|
||||||
quality = mobileQuality,
|
quality = mobileQuality,
|
||||||
onSelect = onMobileQualityChange,
|
onSelect = onMobileQualityChange,
|
||||||
onDismiss = { dialog = null },
|
onDismiss = { dialog = null },
|
||||||
@@ -205,34 +208,53 @@ private fun QualityDialog(
|
|||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
) {
|
) {
|
||||||
var draft by remember(quality) { mutableStateOf(quality) }
|
var draft by remember(quality) { mutableStateOf(quality) }
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = onDismiss,
|
onDismissRequest = onDismiss,
|
||||||
title = { Text(title) },
|
title = { Text(title) },
|
||||||
text = {
|
text = {
|
||||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
Box {
|
||||||
ChoiceGroup(
|
Column(modifier = Modifier.verticalScroll(scrollState)) {
|
||||||
header = "Maximum bitrate",
|
ChoiceGroup(
|
||||||
options = bitrateOptions.map { it to bitrateLabel(it) },
|
header = "Maximum bitrate",
|
||||||
selected = draft.maxBitrate,
|
options = bitrateOptions.map { it to bitrateLabel(it) },
|
||||||
onSelect = {
|
selected = draft.maxBitrate,
|
||||||
draft = draft.copy(maxBitrate = it)
|
onSelect = { draft = draft.copy(maxBitrate = it) },
|
||||||
onSelect(draft)
|
)
|
||||||
},
|
Spacer(Modifier.height(16.dp))
|
||||||
)
|
ChoiceGroup(
|
||||||
Spacer(Modifier.height(16.dp))
|
header = "Preferred format",
|
||||||
ChoiceGroup(
|
options = streamFormats.map { it to (it ?: "Use server default") },
|
||||||
header = "Preferred format",
|
selected = draft.format,
|
||||||
options = streamFormats.map { it to (it ?: "Use server default") },
|
onSelect = { draft = draft.copy(format = it) },
|
||||||
selected = draft.format,
|
)
|
||||||
onSelect = {
|
}
|
||||||
draft = draft.copy(format = it)
|
if (scrollState.canScrollForward) {
|
||||||
onSelect(draft)
|
Box(
|
||||||
},
|
modifier =
|
||||||
)
|
Modifier
|
||||||
|
.align(Alignment.BottomCenter)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(32.dp)
|
||||||
|
.background(
|
||||||
|
Brush.verticalGradient(
|
||||||
|
listOf(Color.Transparent, MaterialTheme.colorScheme.surfaceContainerHigh),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(onClick = onDismiss) { Text("Done") }
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
onSelect(draft)
|
||||||
|
onDismiss()
|
||||||
|
},
|
||||||
|
) { Text("Done") }
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = onDismiss) { Text("Cancel") }
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class UserPreferencesTest {
|
|||||||
val prefs = UserPreferences(store)
|
val prefs = UserPreferences(store)
|
||||||
|
|
||||||
assertEquals(StreamQuality(), prefs.streamQuality(NetworkMode.Wifi).first())
|
assertEquals(StreamQuality(), prefs.streamQuality(NetworkMode.Wifi).first())
|
||||||
assertEquals(StreamQuality(), prefs.streamQuality(NetworkMode.Mobile).first())
|
assertEquals(StreamQuality(192, "mp3"), prefs.streamQuality(NetworkMode.Mobile).first())
|
||||||
|
|
||||||
prefs.setStreamQuality(NetworkMode.Wifi, StreamQuality(320, null))
|
prefs.setStreamQuality(NetworkMode.Wifi, StreamQuality(320, null))
|
||||||
prefs.setStreamQuality(NetworkMode.Mobile, StreamQuality(96, "opus"))
|
prefs.setStreamQuality(NetworkMode.Mobile, StreamQuality(96, "opus"))
|
||||||
|
|||||||
Reference in New Issue
Block a user