feat(settings): add TvSettingsScreen and integrate settings management

This commit is contained in:
2026-05-03 17:37:48 +02:00
parent 5d12b97922
commit 63a8a108e2
4 changed files with 242 additions and 2 deletions

View File

@@ -13,6 +13,7 @@ import hu.bbara.purefin.navigation.tvLibrarySection
import hu.bbara.purefin.navigation.tvLoginSection
import hu.bbara.purefin.navigation.tvMovieSection
import hu.bbara.purefin.navigation.tvPlayerSection
import hu.bbara.purefin.navigation.tvSettingsSection
import hu.bbara.purefin.navigation.tvSeriesSection
@Module
@@ -60,4 +61,10 @@ object TvNavigationModule {
fun provideTvLibraryEntryBuilder(): EntryProviderScope<Route>.() -> Unit = {
tvLibrarySection()
}
@IntoSet
@Provides
fun provideTvSettingsEntryBuilder(): EntryProviderScope<Route>.() -> Unit = {
tvSettingsSection()
}
}

View File

@@ -9,6 +9,7 @@ import hu.bbara.purefin.ui.screen.login.LoginScreen
import hu.bbara.purefin.ui.screen.TvAppScreen
import hu.bbara.purefin.ui.screen.library.TvLibraryScreen
import hu.bbara.purefin.ui.screen.player.TvPlayerScreen
import hu.bbara.purefin.ui.screen.settings.TvSettingsScreen
fun EntryProviderScope<Route>.tvHomeSection() {
entry<Route.Home> {
@@ -62,3 +63,9 @@ fun EntryProviderScope<Route>.tvLibrarySection() {
)
}
}
fun EntryProviderScope<Route>.tvSettingsSection() {
entry<Route.SettingsRoute> {
TvSettingsScreen()
}
}

View File

@@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.outlined.Movie
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material.icons.outlined.Tv
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
@@ -27,6 +28,7 @@ import hu.bbara.purefin.ui.screen.home.TvHomeScreen
import hu.bbara.purefin.ui.screen.home.components.TvDrawerDestinationItem
import hu.bbara.purefin.ui.screen.home.components.TvNavigationDrawer
import hu.bbara.purefin.ui.screen.library.TvLibraryScreen
import hu.bbara.purefin.ui.screen.settings.TvSettingsScreen
@Composable
fun TvAppScreen(
@@ -62,7 +64,11 @@ fun TvAppScreen(
LibraryKind.SERIES -> Icons.Outlined.Tv
}
)
}
} + TvDrawerDestinationItem(
destination = Route.SettingsRoute,
label = "Settings",
icon = Icons.Outlined.Settings
)
}
LifecycleResumeEffect(Unit) {
@@ -88,6 +94,12 @@ fun TvAppScreen(
modifier = Modifier.fillMaxSize()
)
}
entry<Route.SettingsRoute> {
TvSettingsScreen(
onBack = { backStack.removeLastOrNull() },
modifier = Modifier.fillMaxSize()
)
}
}
TvNavigationDrawer(
@@ -97,7 +109,7 @@ fun TvAppScreen(
if (selectedDestination != destination) {
backStack.clear()
backStack.add(Route.Home)
if (destination is Route.LibraryRoute) {
if (destination != Route.Home) {
backStack.add(destination)
}
}

View File

@@ -0,0 +1,214 @@
package hu.bbara.purefin.ui.screen.settings
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Slider
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import hu.bbara.purefin.feature.settings.SettingsViewModel
import hu.bbara.purefin.settings.SettingsOptions
import java.util.Locale
@Composable
fun TvSettingsScreen(
viewModel: SettingsViewModel = hiltViewModel(),
onBack: () -> Unit = viewModel::onBack,
modifier: Modifier = Modifier
) {
Scaffold(
modifier = modifier.fillMaxSize(),
containerColor = MaterialTheme.colorScheme.background,
contentColor = MaterialTheme.colorScheme.onBackground,
topBar = {
TvSettingsTopBar(onBack = onBack)
}
) { innerPadding ->
LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
) {
item {
Text(
text = "Settings",
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.padding(horizontal = 28.dp, vertical = 16.dp)
)
}
item {
Text(
text = "Playback",
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(horizontal = 28.dp, vertical = 8.dp)
)
}
SettingsOptions.numberSettings.forEach { option ->
item(key = option.key) {
val value by viewModel.value(option).collectAsState(initial = option.defaultValue)
TvNumberSettingItem(
title = option.title,
value = value,
valueRange = option.valueRange,
onValueChange = { viewModel.set(option, it) }
)
HorizontalDivider()
}
}
SettingsOptions.booleanSettings.forEach { option ->
item(key = option.key) {
val value by viewModel.value(option).collectAsState(initial = option.defaultValue)
TvBooleanSettingItem(
title = option.title,
value = value,
onValueChange = { viewModel.set(option, it) }
)
HorizontalDivider()
}
}
SettingsOptions.stringSettings.forEach { option ->
item(key = option.key) {
val value by viewModel.value(option).collectAsState(initial = option.defaultValue)
TvStringSettingItem(
title = option.title,
value = value,
onValueChange = { viewModel.set(option, it) }
)
HorizontalDivider()
}
}
}
}
}
@Composable
private fun TvSettingsTopBar(
onBack: () -> Unit
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
IconButton(onClick = onBack) {
Icon(
imageVector = Icons.AutoMirrored.Outlined.ArrowBack,
contentDescription = "Back"
)
}
}
}
@Composable
private fun TvNumberSettingItem(
title: String,
value: Double,
valueRange: ClosedFloatingPointRange<Double>,
onValueChange: (Double) -> Unit,
modifier: Modifier = Modifier
) {
var sliderValue by remember(value) { mutableStateOf(value.toFloat()) }
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 28.dp, vertical = 16.dp)
) {
Text(
text = title,
style = MaterialTheme.typography.bodyLarge
)
Text(
text = String.format(Locale.US, "%.1f", sliderValue),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Slider(
value = sliderValue,
onValueChange = { sliderValue = it },
onValueChangeFinished = { onValueChange(sliderValue.toDouble()) },
valueRange = valueRange.start.toFloat()..valueRange.endInclusive.toFloat(),
modifier = Modifier.fillMaxWidth()
)
}
}
@Composable
private fun TvBooleanSettingItem(
title: String,
value: Boolean,
onValueChange: (Boolean) -> Unit,
modifier: Modifier = Modifier
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = modifier
.fillMaxWidth()
.clickable { onValueChange(!value) }
.padding(horizontal = 28.dp, vertical = 16.dp)
) {
Text(
text = title,
style = MaterialTheme.typography.bodyLarge
)
Switch(
checked = value,
onCheckedChange = onValueChange
)
}
}
@Composable
private fun TvStringSettingItem(
title: String,
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
OutlinedTextField(
value = value,
onValueChange = onValueChange,
label = { Text(title) },
singleLine = true,
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Words
),
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 28.dp, vertical = 16.dp)
)
}