diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7a149894..f828a3a0 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -24,8 +24,8 @@ android { applicationId = "org.grakovne.lissen" minSdk = 28 targetSdk = 35 - versionCode = 8 - versionName = "1.0.7" + versionCode = 9 + versionName = "1.0.8" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/app/src/main/java/org/grakovne/lissen/common/ColorScheme.kt b/app/src/main/java/org/grakovne/lissen/common/ColorScheme.kt new file mode 100644 index 00000000..fe6c4f14 --- /dev/null +++ b/app/src/main/java/org/grakovne/lissen/common/ColorScheme.kt @@ -0,0 +1,7 @@ +package org.grakovne.lissen.common + +enum class ColorScheme { + FOLLOW_SYSTEM, + LIGHT, + DARK +} diff --git a/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt b/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt index ce5ec8d5..2c7eb972 100644 --- a/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt +++ b/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt @@ -6,7 +6,12 @@ import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyProperties import android.util.Base64 import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.distinctUntilChanged import org.grakovne.lissen.channel.common.ChannelCode +import org.grakovne.lissen.common.ColorScheme import org.grakovne.lissen.domain.Library import java.security.KeyStore import java.util.UUID @@ -81,12 +86,31 @@ class LissenSharedPreferences @Inject constructor(@ApplicationContext context: C saveActiveLibraryName(library.title) } + fun saveColorScheme(colorScheme: ColorScheme) = + sharedPreferences.edit().putString(KEY_PREFERRED_COLOR_SCHEME, colorScheme.name).apply() + + fun getColorScheme(): ColorScheme = + sharedPreferences.getString(KEY_PREFERRED_COLOR_SCHEME, ColorScheme.FOLLOW_SYSTEM.name) + ?.let { ColorScheme.valueOf(it) } + ?: ColorScheme.FOLLOW_SYSTEM + fun savePlaybackSpeed(factor: Float) = sharedPreferences.edit().putFloat(KEY_PREFERRED_PLAYBACK_SPEED, factor).apply() fun getPlaybackSpeed(): Float = sharedPreferences.getFloat(KEY_PREFERRED_PLAYBACK_SPEED, 1f) + val colorSchemeFlow: Flow = callbackFlow { + val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key -> + if (key == KEY_PREFERRED_COLOR_SCHEME) { + trySend(getColorScheme()) + } + } + sharedPreferences.registerOnSharedPreferenceChangeListener(listener) + trySend(getColorScheme()) + awaitClose { sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener) } + }.distinctUntilChanged() + private fun saveActiveLibraryId(host: String) = sharedPreferences.edit().putString(KEY_PREFERRED_LIBRARY_ID, host).apply() @@ -139,6 +163,8 @@ class LissenSharedPreferences @Inject constructor(@ApplicationContext context: C private const val KEY_PREFERRED_PLAYBACK_SPEED = "preferred_playback_speed" + private const val KEY_PREFERRED_COLOR_SCHEME = "preferred_color_scheme" + private const val ANDROID_KEYSTORE = "AndroidKeyStore" private const val TRANSFORMATION = "AES/GCM/NoPadding" diff --git a/app/src/main/java/org/grakovne/lissen/ui/activity/AppActivity.kt b/app/src/main/java/org/grakovne/lissen/ui/activity/AppActivity.kt index 57a8be30..4e7027a2 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/activity/AppActivity.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/activity/AppActivity.kt @@ -4,6 +4,8 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.navigation.compose.rememberNavController import coil.ImageLoader import dagger.hilt.android.AndroidEntryPoint @@ -26,12 +28,21 @@ class AppActivity : ComponentActivity() { @Inject lateinit var networkQualityService: NetworkQualityService + @Inject + lateinit var sharedPreferences: LissenSharedPreferences + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() + setContent { - LissenTheme { + val colorScheme by sharedPreferences + .colorSchemeFlow + .collectAsState(initial = sharedPreferences.getColorScheme()) + + LissenTheme(colorScheme) { val navController = rememberNavController() + AppNavHost( navController = navController, navigationService = AppNavigationService(navController), diff --git a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt index 7e9dd2f2..a1948b2e 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt @@ -1,9 +1,12 @@ package org.grakovne.lissen.ui.navigation +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier import androidx.navigation.NavHostController import androidx.navigation.NavType import androidx.navigation.compose.NavHost @@ -35,45 +38,49 @@ fun AppNavHost( else -> "login_screen" } - NavHost( - navController = navController, - startDestination = startDestination - ) { - composable("library_screen") { - LibraryScreen( - navController = navigationService, - imageLoader = imageLoader, - networkQualityService = networkQualityService - ) - } + Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> + println(innerPadding) - composable( - "player_screen/{bookId}?bookTitle={bookTitle}", - arguments = listOf( - navArgument("bookId") { type = NavType.StringType }, - navArgument("bookTitle") { type = NavType.StringType; nullable = true } - ) - ) { navigationStack -> - val bookId = navigationStack.arguments?.getString("bookId") - val bookTitle = navigationStack.arguments?.getString("bookTitle") + NavHost( + navController = navController, + startDestination = startDestination + ) { + composable("library_screen") { + LibraryScreen( + navController = navigationService, + imageLoader = imageLoader, + networkQualityService = networkQualityService + ) + } - PlayerScreen( - navController = navigationService, - imageLoader = imageLoader, - bookId = bookId, - bookTitle = bookTitle - ) - } + composable( + "player_screen/{bookId}?bookTitle={bookTitle}", + arguments = listOf( + navArgument("bookId") { type = NavType.StringType }, + navArgument("bookTitle") { type = NavType.StringType; nullable = true } + ) + ) { navigationStack -> + val bookId = navigationStack.arguments?.getString("bookId") + val bookTitle = navigationStack.arguments?.getString("bookTitle") - composable("login_screen") { - LoginScreen(navigationService) - } + PlayerScreen( + navController = navigationService, + imageLoader = imageLoader, + bookId = bookId, + bookTitle = bookTitle + ) + } + + composable("login_screen") { + LoginScreen(navigationService) + } - composable("settings_screen") { - SettingsScreen( - onBack = { navController.popBackStack() }, - navController = navigationService - ) + composable("settings_screen") { + SettingsScreen( + onBack = { navController.popBackStack() }, + navController = navigationService + ) + } } } } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/library/composables/BookComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/library/composables/BookComposable.kt index 2fa73050..1141c34c 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/library/composables/BookComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/library/composables/BookComposable.kt @@ -49,7 +49,6 @@ import org.grakovne.lissen.domain.BookCachedState import org.grakovne.lissen.ui.components.AsyncShimmeringImage import org.grakovne.lissen.ui.extensions.formatShortly import org.grakovne.lissen.ui.navigation.AppNavigationService -import org.grakovne.lissen.ui.theme.backgroundColor import org.grakovne.lissen.viewmodel.BookCacheAction import org.grakovne.lissen.viewmodel.CachingModelView @@ -172,7 +171,7 @@ fun BookComposable( Text(stringResource(R.string.dialog_dismiss_cancel)) } }, - containerColor = backgroundColor + containerColor = MaterialTheme.colorScheme.background ) } } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/library/composables/fallback/LibraryFallbackComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/library/composables/fallback/LibraryFallbackComposable.kt index b73ae548..15860047 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/library/composables/fallback/LibraryFallbackComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/library/composables/fallback/LibraryFallbackComposable.kt @@ -26,7 +26,6 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import org.grakovne.lissen.R import org.grakovne.lissen.common.NetworkQualityService -import org.grakovne.lissen.ui.theme.ItemAccented import org.grakovne.lissen.viewmodel.CachingModelView @Composable @@ -66,7 +65,7 @@ fun LibraryFallbackComposable( modifier = Modifier .size(120.dp) .clip(CircleShape) - .background(ItemAccented), + .background(MaterialTheme.colorScheme.surfaceContainer), contentAlignment = Alignment.Center ) { Icon( diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/NavigationBarComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/NavigationBarComposable.kt index 7b1c34d9..3e28232d 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/NavigationBarComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/NavigationBarComposable.kt @@ -9,6 +9,7 @@ import androidx.compose.material.icons.outlined.Headset import androidx.compose.material.icons.outlined.Settings import androidx.compose.material.icons.outlined.Speed import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.NavigationBar @@ -30,7 +31,6 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import org.grakovne.lissen.R import org.grakovne.lissen.ui.navigation.AppNavigationService -import org.grakovne.lissen.ui.theme.ItemAccented import org.grakovne.lissen.viewmodel.PlayerViewModel @Composable @@ -77,7 +77,7 @@ fun NavigationBarComposable( onClick = { navController.showLibrary() }, colors = NavigationBarItemDefaults.colors( selectedIconColor = colorScheme.primary, - indicatorColor = ItemAccented + indicatorColor = MaterialTheme.colorScheme.surfaceContainer ) ) @@ -101,7 +101,7 @@ fun NavigationBarComposable( onClick = { onChaptersClick() }, colors = NavigationBarItemDefaults.colors( selectedIconColor = colorScheme.primary, - indicatorColor = ItemAccented + indicatorColor = MaterialTheme.colorScheme.surfaceContainer ) ) @@ -126,7 +126,7 @@ fun NavigationBarComposable( enabled = true, colors = NavigationBarItemDefaults.colors( selectedIconColor = colorScheme.primary, - indicatorColor = ItemAccented + indicatorColor = MaterialTheme.colorScheme.surfaceContainer ) ) @@ -150,7 +150,7 @@ fun NavigationBarComposable( onClick = { navController.showSettings() }, colors = NavigationBarItemDefaults.colors( selectedIconColor = colorScheme.primary, - indicatorColor = ItemAccented + indicatorColor = MaterialTheme.colorScheme.surfaceContainer ) ) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlaybackSpeedComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlaybackSpeedComposable.kt index bd408d52..a629676b 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlaybackSpeedComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/player/composable/PlaybackSpeedComposable.kt @@ -28,13 +28,11 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight.Companion.SemiBold import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import org.grakovne.lissen.R -import org.grakovne.lissen.ui.theme.ItemAccented @SuppressLint("DefaultLocale") @OptIn(ExperimentalMaterial3Api::class) @@ -47,7 +45,7 @@ fun PlaybackSpeedComposable( var selectedPlaybackSpeed by remember { mutableFloatStateOf(currentSpeed) } ModalBottomSheet( - containerColor = Color(0xFFFAFAFA), + containerColor = colorScheme.background, onDismissRequest = onDismissRequest, content = { Column( @@ -110,14 +108,13 @@ fun PlaybackSpeedComposable( colors = ButtonDefaults.buttonColors( containerColor = when (selectedPlaybackSpeed == value) { true -> colorScheme.primary - else -> ItemAccented + else -> colorScheme.surfaceContainer } ), modifier = Modifier.fillMaxSize() ) {} Text( text = String.format("%.2f", value), - color = Color.Black, style = when (selectedPlaybackSpeed == value) { true -> typography.labelMedium.copy(fontWeight = SemiBold) false -> typography.labelMedium diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt index 654db866..0621987a 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt @@ -7,6 +7,8 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.ArrowBack import androidx.compose.material3.ExperimentalMaterial3Api @@ -27,7 +29,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import org.grakovne.lissen.R import org.grakovne.lissen.ui.navigation.AppNavigationService import org.grakovne.lissen.ui.screens.settings.composable.AdditionalComposable -import org.grakovne.lissen.ui.screens.settings.composable.PreferredLibraryComposable +import org.grakovne.lissen.ui.screens.settings.composable.GeneralSettingsComposable import org.grakovne.lissen.ui.screens.settings.composable.ServerComposable import org.grakovne.lissen.viewmodel.SettingsViewModel @@ -78,12 +80,12 @@ fun SettingsScreen( ) { Column( modifier = Modifier - .weight(1f) - .fillMaxWidth(), + .fillMaxWidth() + .verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally ) { ServerComposable(navController, viewModel) - PreferredLibraryComposable(viewModel) + GeneralSettingsComposable(viewModel) } AdditionalComposable() } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/GeneralSettingsComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/GeneralSettingsComposable.kt new file mode 100644 index 00000000..fd4b4d03 --- /dev/null +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/GeneralSettingsComposable.kt @@ -0,0 +1,121 @@ +package org.grakovne.lissen.ui.screens.settings.composable + +import android.content.Context +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme.colorScheme +import androidx.compose.material3.MaterialTheme.typography +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import org.grakovne.lissen.R +import org.grakovne.lissen.common.ColorScheme +import org.grakovne.lissen.viewmodel.SettingsViewModel + +@Composable +fun GeneralSettingsComposable(viewModel: SettingsViewModel) { + val libraries by viewModel.libraries.observeAsState(emptyList()) + val preferredLibrary by viewModel.preferredLibrary.observeAsState() + val preferredColorScheme by viewModel.preferredColorScheme.observeAsState() + + var preferredLibraryExpanded by remember { mutableStateOf(false) } + var colorSchemeExpanded by remember { mutableStateOf(false) } + + val context = LocalContext.current + + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { preferredLibraryExpanded = true } + .padding(horizontal = 16.dp, vertical = 12.dp) + ) { + Column(modifier = Modifier.weight(1f)) { + Text( + text = stringResource(R.string.settings_screen_preferred_library_title), + style = typography.bodyLarge.copy(fontWeight = FontWeight.SemiBold), + modifier = Modifier.padding(bottom = 4.dp) + ) + Text( + text = preferredLibrary?.title ?: "", + style = typography.bodyMedium, + color = colorScheme.onSurfaceVariant + ) + } + } + + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { colorSchemeExpanded = true } + .padding(horizontal = 16.dp, vertical = 12.dp) + ) { + Column( + modifier = Modifier.weight(1f) + ) { + Text( + text = stringResource(R.string.settings_screen_color_scheme_title), + style = typography.bodyLarge.copy(fontWeight = FontWeight.SemiBold), + modifier = Modifier.padding(bottom = 4.dp) + ) + Text( + text = preferredColorScheme?.toItem(context)?.name ?: "", + style = typography.bodyMedium, + color = colorScheme.onSurfaceVariant + ) + } + } + + if (preferredLibraryExpanded) { + GeneralSettingsItemComposable( + items = libraries.map { GeneralSettingsItem(it.id, it.title) }, + selectedItem = preferredLibrary?.let { GeneralSettingsItem(it.id, it.title) }, + onDismissRequest = { preferredLibraryExpanded = false }, + onItemSelected = { item -> + libraries + .find { it.id == item.id } + ?.let { viewModel.preferLibrary(it) } + } + ) + } + + if (colorSchemeExpanded) { + GeneralSettingsItemComposable( + items = listOf( + ColorScheme.LIGHT.toItem(context), + ColorScheme.FOLLOW_SYSTEM.toItem(context), + ColorScheme.DARK.toItem(context) + ), + selectedItem = preferredColorScheme?.toItem(context), + onDismissRequest = { colorSchemeExpanded = false }, + onItemSelected = { item -> + ColorScheme + .entries + .find { it.name == item.id } + ?.let { viewModel.preferColorScheme(it) } + } + ) + } +} + +private fun ColorScheme.toItem(context: Context): GeneralSettingsItem { + val id = this.name + val name = when (this) { + ColorScheme.FOLLOW_SYSTEM -> context.getString(R.string.color_scheme_follow_system) + ColorScheme.LIGHT -> context.getString(R.string.color_scheme_light) + ColorScheme.DARK -> context.getString(R.string.color_scheme_dark) + } + + return GeneralSettingsItem(id, name) +} diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/GeneralSettingsItem.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/GeneralSettingsItem.kt new file mode 100644 index 00000000..d5a5a2ee --- /dev/null +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/GeneralSettingsItem.kt @@ -0,0 +1,6 @@ +package org.grakovne.lissen.ui.screens.settings.composable + +data class GeneralSettingsItem( + val id: String, + val name: String +) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/GeneralSettingsItemComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/GeneralSettingsItemComposable.kt new file mode 100644 index 00000000..90a2ce32 --- /dev/null +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/GeneralSettingsItemComposable.kt @@ -0,0 +1,74 @@ +package org.grakovne.lissen.ui.screens.settings.composable + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Check +import androidx.compose.material3.Divider +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.ListItem +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun GeneralSettingsItemComposable( + items: List, + selectedItem: GeneralSettingsItem?, + onDismissRequest: () -> Unit, + onItemSelected: (GeneralSettingsItem) -> Unit +) { + var activeItem by remember { mutableStateOf(selectedItem) } + + ModalBottomSheet( + containerColor = MaterialTheme.colorScheme.background, + onDismissRequest = onDismissRequest, + content = { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Spacer(modifier = Modifier.height(8.dp)) + LazyColumn(modifier = Modifier.fillMaxWidth()) { + items(items) { item -> + ListItem( + headlineContent = { Text(item.name) }, + trailingContent = { + if (item.id == activeItem?.id) { + Icon( + imageVector = Icons.Outlined.Check, + contentDescription = null + ) + } + }, + modifier = Modifier + .fillMaxWidth() + .clickable { + activeItem = item + onItemSelected(item) + onDismissRequest() + } + ) + Divider() + } + } + } + } + ) +} diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/PreferredLibraryComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/PreferredLibraryComposable.kt deleted file mode 100644 index 1813abed..00000000 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/PreferredLibraryComposable.kt +++ /dev/null @@ -1,79 +0,0 @@ -package org.grakovne.lissen.ui.screens.settings.composable - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.ArrowDropDown -import androidx.compose.material.icons.outlined.ArrowDropUp -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme.colorScheme -import androidx.compose.material3.MaterialTheme.typography -import androidx.compose.material3.OutlinedButton -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.livedata.observeAsState -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import org.grakovne.lissen.R -import org.grakovne.lissen.viewmodel.SettingsViewModel - -@Composable -fun PreferredLibraryComposable(viewModel: SettingsViewModel) { - val isServerConnected by viewModel.isConnected.observeAsState(false) - val libraries by viewModel.libraries.observeAsState(emptyList()) - val preferredLibrary by viewModel.preferredLibrary.observeAsState() - - var expanded by remember { mutableStateOf(false) } - - Column(modifier = Modifier.padding(16.dp)) { - Text( - text = stringResource(R.string.settings_screen_preferred_library_title), - style = typography.titleMedium.copy(fontWeight = FontWeight.SemiBold), - modifier = Modifier.padding(bottom = 12.dp) - ) - - Box { - OutlinedButton( - onClick = { if (isServerConnected) expanded = !expanded }, - modifier = Modifier.fillMaxWidth(), - enabled = isServerConnected - ) { - Text(preferredLibrary?.title ?: "") - Spacer(modifier = Modifier.weight(1f)) - Icon( - imageVector = if (expanded) Icons.Outlined.ArrowDropUp else Icons.Outlined.ArrowDropDown, - contentDescription = null - ) - } - - DropdownMenu( - expanded = expanded, - modifier = Modifier.background(color = colorScheme.background), - onDismissRequest = { expanded = false } - ) { - libraries.forEach { library -> - DropdownMenuItem( - text = { Text(library.title) }, - onClick = { - viewModel.preferLibrary(library) - expanded = false - }, - enabled = true - ) - } - } - } - } -} diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/ServerComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/ServerComposable.kt index 593d047a..0b3c8aa9 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/ServerComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/ServerComposable.kt @@ -39,7 +39,7 @@ fun ServerComposable( Column { Text( text = stringResource(R.string.settings_screen_server_connection), - style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.SemiBold), + style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.SemiBold), modifier = Modifier.padding(start = 16.dp, bottom = 12.dp) ) diff --git a/app/src/main/java/org/grakovne/lissen/ui/theme/Color.kt b/app/src/main/java/org/grakovne/lissen/ui/theme/Color.kt index ac733277..68cca573 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/theme/Color.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/theme/Color.kt @@ -3,5 +3,4 @@ package org.grakovne.lissen.ui.theme import androidx.compose.ui.graphics.Color val FoxOrange = Color(0xFFFF6F3F) -val ItemAccented = Color(0xFFEEEEEE) val Dark = Color(0xFF1C1B1F) diff --git a/app/src/main/java/org/grakovne/lissen/ui/theme/Theme.kt b/app/src/main/java/org/grakovne/lissen/ui/theme/Theme.kt index 2c882ec8..b2320b71 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/theme/Theme.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/theme/Theme.kt @@ -1,39 +1,56 @@ package org.grakovne.lissen.ui.theme +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect import androidx.compose.ui.graphics.Color import com.google.accompanist.systemuicontroller.rememberSystemUiController - -val backgroundColor = Color(0xFFFAFAFA) +import org.grakovne.lissen.common.ColorScheme private val LightColorScheme = lightColorScheme( primary = FoxOrange, secondary = Dark, tertiary = FoxOrange, - background = backgroundColor, - surface = backgroundColor + background = Color(0xFFFAFAFA), + surface = Color(0xFFFAFAFA), + surfaceContainer = Color(0xFFEEEEEE) +) + +private val DarkColorScheme = darkColorScheme( + primary = FoxOrange ) @Composable fun LissenTheme( + colorSchemePreference: ColorScheme, content: @Composable () -> Unit ) { + val isDarkTheme = when (colorSchemePreference) { + ColorScheme.FOLLOW_SYSTEM -> isSystemInDarkTheme() + ColorScheme.LIGHT -> false + ColorScheme.DARK -> true + } + + val colors = if (isDarkTheme) DarkColorScheme else LightColorScheme + val itemAccented = if (isDarkTheme) Color(0xFF444444) else Color(0xFFEEEEEE) val systemUiController = rememberSystemUiController() + SideEffect { + systemUiController.setNavigationBarColor( + color = colors.background, + darkIcons = !isDarkTheme + ) + systemUiController.setStatusBarColor( + color = colors.background, + darkIcons = !isDarkTheme + ) + } + MaterialTheme( - colorScheme = LightColorScheme, + colorScheme = colors, content = content ) - - systemUiController.setNavigationBarColor( - color = backgroundColor, - darkIcons = true - ) - - systemUiController.setStatusBarColor( - color = backgroundColor, - darkIcons = true - ) } diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt index 6db3e298..b4730aac 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt @@ -1,12 +1,12 @@ package org.grakovne.lissen.viewmodel -import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import org.grakovne.lissen.channel.common.ApiResult +import org.grakovne.lissen.common.ColorScheme import org.grakovne.lissen.content.LissenMediaProvider import org.grakovne.lissen.domain.Library import org.grakovne.lissen.persistence.preferences.LissenSharedPreferences @@ -24,15 +24,15 @@ class SettingsViewModel @Inject constructor( private val _username = MutableLiveData(preferences.getUsername()) val username = _username - private val _isConnected = MutableLiveData(true) - val isConnected: LiveData = _isConnected - private val _libraries = MutableLiveData>() val libraries = _libraries private val _preferredLibrary = MutableLiveData(preferences.getPreferredLibrary()) val preferredLibrary = _preferredLibrary + private val _preferredColorScheme = MutableLiveData(preferences.getColorScheme()) + val preferredColorScheme = _preferredColorScheme + init { fetchLibraries() } @@ -71,4 +71,9 @@ class SettingsViewModel @Inject constructor( _preferredLibrary.value = library preferences.savePreferredLibrary(library) } + + fun preferColorScheme(colorScheme: ColorScheme) { + _preferredColorScheme.value = colorScheme + preferences.saveColorScheme(colorScheme) + } } diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 07cd9064..63926c8f 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -41,4 +41,8 @@ Удалить Отмена Скорость воспроизведения + Системная + Светлая + Темная + Цветовая схема \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1c223e64..6f13042c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -41,4 +41,8 @@ Remove Cancel Playback speed + System + Light + Dark + Color Scheme \ No newline at end of file