mirror of
https://github.com/bbara04/Purefin.git
synced 2026-07-23 19:26:50 +00:00
feat(settings): add TvSettingsScreen and integrate settings management
This commit is contained in:
@@ -13,6 +13,7 @@ import hu.bbara.purefin.navigation.tvLibrarySection
|
|||||||
import hu.bbara.purefin.navigation.tvLoginSection
|
import hu.bbara.purefin.navigation.tvLoginSection
|
||||||
import hu.bbara.purefin.navigation.tvMovieSection
|
import hu.bbara.purefin.navigation.tvMovieSection
|
||||||
import hu.bbara.purefin.navigation.tvPlayerSection
|
import hu.bbara.purefin.navigation.tvPlayerSection
|
||||||
|
import hu.bbara.purefin.navigation.tvSettingsSection
|
||||||
import hu.bbara.purefin.navigation.tvSeriesSection
|
import hu.bbara.purefin.navigation.tvSeriesSection
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@@ -60,4 +61,10 @@ object TvNavigationModule {
|
|||||||
fun provideTvLibraryEntryBuilder(): EntryProviderScope<Route>.() -> Unit = {
|
fun provideTvLibraryEntryBuilder(): EntryProviderScope<Route>.() -> Unit = {
|
||||||
tvLibrarySection()
|
tvLibrarySection()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IntoSet
|
||||||
|
@Provides
|
||||||
|
fun provideTvSettingsEntryBuilder(): EntryProviderScope<Route>.() -> Unit = {
|
||||||
|
tvSettingsSection()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.TvAppScreen
|
||||||
import hu.bbara.purefin.ui.screen.library.TvLibraryScreen
|
import hu.bbara.purefin.ui.screen.library.TvLibraryScreen
|
||||||
import hu.bbara.purefin.ui.screen.player.TvPlayerScreen
|
import hu.bbara.purefin.ui.screen.player.TvPlayerScreen
|
||||||
|
import hu.bbara.purefin.ui.screen.settings.TvSettingsScreen
|
||||||
|
|
||||||
fun EntryProviderScope<Route>.tvHomeSection() {
|
fun EntryProviderScope<Route>.tvHomeSection() {
|
||||||
entry<Route.Home> {
|
entry<Route.Home> {
|
||||||
@@ -62,3 +63,9 @@ fun EntryProviderScope<Route>.tvLibrarySection() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun EntryProviderScope<Route>.tvSettingsSection() {
|
||||||
|
entry<Route.SettingsRoute> {
|
||||||
|
TvSettingsScreen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Home
|
import androidx.compose.material.icons.outlined.Home
|
||||||
import androidx.compose.material.icons.outlined.Movie
|
import androidx.compose.material.icons.outlined.Movie
|
||||||
|
import androidx.compose.material.icons.outlined.Settings
|
||||||
import androidx.compose.material.icons.outlined.Tv
|
import androidx.compose.material.icons.outlined.Tv
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
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.TvDrawerDestinationItem
|
||||||
import hu.bbara.purefin.ui.screen.home.components.TvNavigationDrawer
|
import hu.bbara.purefin.ui.screen.home.components.TvNavigationDrawer
|
||||||
import hu.bbara.purefin.ui.screen.library.TvLibraryScreen
|
import hu.bbara.purefin.ui.screen.library.TvLibraryScreen
|
||||||
|
import hu.bbara.purefin.ui.screen.settings.TvSettingsScreen
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TvAppScreen(
|
fun TvAppScreen(
|
||||||
@@ -62,7 +64,11 @@ fun TvAppScreen(
|
|||||||
LibraryKind.SERIES -> Icons.Outlined.Tv
|
LibraryKind.SERIES -> Icons.Outlined.Tv
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
} + TvDrawerDestinationItem(
|
||||||
|
destination = Route.SettingsRoute,
|
||||||
|
label = "Settings",
|
||||||
|
icon = Icons.Outlined.Settings
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
LifecycleResumeEffect(Unit) {
|
LifecycleResumeEffect(Unit) {
|
||||||
@@ -88,6 +94,12 @@ fun TvAppScreen(
|
|||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
entry<Route.SettingsRoute> {
|
||||||
|
TvSettingsScreen(
|
||||||
|
onBack = { backStack.removeLastOrNull() },
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TvNavigationDrawer(
|
TvNavigationDrawer(
|
||||||
@@ -97,7 +109,7 @@ fun TvAppScreen(
|
|||||||
if (selectedDestination != destination) {
|
if (selectedDestination != destination) {
|
||||||
backStack.clear()
|
backStack.clear()
|
||||||
backStack.add(Route.Home)
|
backStack.add(Route.Home)
|
||||||
if (destination is Route.LibraryRoute) {
|
if (destination != Route.Home) {
|
||||||
backStack.add(destination)
|
backStack.add(destination)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user