diff --git a/app/src/main/java/hu/bme/kszk/szobatarsch/composable/RadioGroup.kt b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/RadioGroup.kt new file mode 100644 index 0000000000000000000000000000000000000000..9ef540a5a832b737d96f2867216737f3cfeedbb6 --- /dev/null +++ b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/RadioGroup.kt @@ -0,0 +1,70 @@ +package hu.bme.kszk.szobatarsch.composable + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + + +data class RadioButtonItem( + val id: Int, + val title: String, +) + +@Composable +fun RadioGroup( + items: Iterable<RadioButtonItem>, + selected: Int, + onItemSelect: ((Int) -> Unit)?, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier.selectableGroup() + ) { + items.forEach { item -> + RadioGroupItem( + item = item, + selected = selected == item.id, + onClick = { onItemSelect?.invoke(item.id) }, + modifier = Modifier.fillMaxWidth() + ) + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun RadioGroupItem( + item: RadioButtonItem, + selected: Boolean, + onClick: ((Int) -> Unit)?, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .selectable( + selected = selected, + onClick = { onClick?.invoke(item.id) }, + role = androidx.compose.ui.semantics.Role.RadioButton + ) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + RadioButton( + selected = selected, + onClick = null, + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = item.title, + style = MaterialTheme.typography.bodyMedium, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/hu/bme/kszk/szobatarsch/composable/Settings.kt b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/Settings.kt new file mode 100644 index 0000000000000000000000000000000000000000..1b45e44cbb790c4e3776848978e1b761e34982fb --- /dev/null +++ b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/Settings.kt @@ -0,0 +1,146 @@ +package hu.bme.kszk.szobatarsch.composable + +import android.app.Activity +import android.content.Intent +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.google.firebase.auth.FirebaseAuth +import hu.bme.kszk.szobatarsch.MainActivity +import hu.bme.kszk.szobatarsch.R +import hu.bme.kszk.szobatarsch.data.UserThemeSetting +import hu.bme.kszk.szobatarsch.data.Theme +import kotlinx.coroutines.runBlocking + +@Composable +fun Settings() { + val context = LocalContext.current + + val dataStore = UserThemeSetting(context) + + val theme = dataStore.getTheme.collectAsState(initial = 0) + + val openThemeDialog = remember { mutableStateOf(false) } + + Column( + Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(20.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(top = 80.dp, start = 12.dp), + text = "Beállítások", + style = MaterialTheme.typography.headlineLarge + ) + + if (openThemeDialog.value) { + ThemeSetting(theme.value!!, openDialog = openThemeDialog, dataStore = dataStore) + } + + Setting(title = "Téma", subtitle = themes[theme.value!!]) { + openThemeDialog.value = true + } + + Button(onClick = { + FirebaseAuth.getInstance().signOut() + val intent = Intent(context, MainActivity::class.java) + context.startActivity(intent) + (context as Activity).finish() + }) { + Text(text = stringResource(id = R.string.log_out)) + } + } +} + +@Composable +fun ThemeSetting(selectedPrevious: Int, openDialog: MutableState<Boolean>, dataStore: UserThemeSetting) { + var selected by remember { + mutableStateOf(selectedPrevious) + } + + AlertDialog( + onDismissRequest = { + openDialog.value = false + }, + title = { + Text(text = "Téma") + }, + text = { + Column { + RadioGroup( + items = themeItems, + selected = selected, + onItemSelect = { selected = it }) + } + }, + confirmButton = { + TextButton( + onClick = { + openDialog.value = false + runBlocking { dataStore.saveTheme(selected) } + } + ) { + Text("Elfogad") + } + }, + dismissButton = { + TextButton( + onClick = { + openDialog.value = false + } + ) { + Text("Mégse") + } + } + ) +} + +val themes = listOf("Rendszer", "Világos", "Sötét") + +val themeItems = listOf( + RadioButtonItem( + id = Theme.System.ordinal, + title = "Rendszer", + ), + RadioButtonItem( + id = Theme.Light.ordinal, + title = "Világos", + ), + RadioButtonItem( + id = Theme.Dark.ordinal, + title = "Sötét", + ), +) + +@Composable +fun Setting(title: String, subtitle: String, onClick: () -> Unit) { + Column( + Modifier + .fillMaxWidth() + .clickable { + onClick() + }) { + Text( + modifier = Modifier.padding(start = 12.dp, top = 12.dp), + text = title, + style = MaterialTheme.typography.titleLarge + ) + Text( + modifier = Modifier.padding(start = 14.dp, bottom = 12.dp), + text = subtitle, + style = MaterialTheme.typography.bodyMedium + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/hu/bme/kszk/szobatarsch/data/Theme.kt b/app/src/main/java/hu/bme/kszk/szobatarsch/data/Theme.kt new file mode 100644 index 0000000000000000000000000000000000000000..cb81c990cdd643585b7219eac2d380073716b765 --- /dev/null +++ b/app/src/main/java/hu/bme/kszk/szobatarsch/data/Theme.kt @@ -0,0 +1,7 @@ +package hu.bme.kszk.szobatarsch.data + +enum class Theme { + System, + Light, + Dark +} \ No newline at end of file diff --git a/app/src/main/java/hu/bme/kszk/szobatarsch/data/UserThemeSetting.kt b/app/src/main/java/hu/bme/kszk/szobatarsch/data/UserThemeSetting.kt new file mode 100644 index 0000000000000000000000000000000000000000..9e9c463f8efa291959ae986972ddf25edcaf3db8 --- /dev/null +++ b/app/src/main/java/hu/bme/kszk/szobatarsch/data/UserThemeSetting.kt @@ -0,0 +1,27 @@ +package hu.bme.kszk.szobatarsch.data + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.* +import androidx.datastore.preferences.preferencesDataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +class UserThemeSetting(private val context: Context) { + companion object { + private val Context.dataStore: DataStore<Preferences> by preferencesDataStore("themeSettings") + val USER_THEME_KEY = intPreferencesKey("theme") + } + + val getTheme: Flow<Int?> = context.dataStore.data + .map { preferences -> + preferences[USER_THEME_KEY] ?: 0 + } + + suspend fun saveTheme(theme: Int) { + context.dataStore.edit { preferences -> + preferences[USER_THEME_KEY] = theme + } + } +} \ No newline at end of file diff --git a/app/src/main/java/hu/bme/kszk/szobatarsch/ui/theme/Theme.kt b/app/src/main/java/hu/bme/kszk/szobatarsch/ui/theme/Theme.kt index cd07f795a8818daed65a9257baf21e305ca6af77..2c7532919ca221b62a411eb493a0bb52e27772d9 100644 --- a/app/src/main/java/hu/bme/kszk/szobatarsch/ui/theme/Theme.kt +++ b/app/src/main/java/hu/bme/kszk/szobatarsch/ui/theme/Theme.kt @@ -1,44 +1,126 @@ package hu.bme.kszk.szobatarsch.ui.theme +import android.annotation.SuppressLint +import android.app.Activity +import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material.MaterialTheme -import androidx.compose.material.darkColors -import androidx.compose.material.lightColors +import androidx.compose.material3.* import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.core.view.ViewCompat +import com.google.accompanist.systemuicontroller.rememberSystemUiController -private val DarkColorPalette = darkColors( - primary = Purple200, - primaryVariant = Purple700, - secondary = Teal200 +private val LightThemeColors = lightColorScheme( + + primary = md_theme_light_primary, + onPrimary = md_theme_light_onPrimary, + primaryContainer = md_theme_light_primaryContainer, + onPrimaryContainer = md_theme_light_onPrimaryContainer, + secondary = md_theme_light_secondary, + onSecondary = md_theme_light_onSecondary, + secondaryContainer = md_theme_light_secondaryContainer, + onSecondaryContainer = md_theme_light_onSecondaryContainer, + tertiary = md_theme_light_tertiary, + onTertiary = md_theme_light_onTertiary, + tertiaryContainer = md_theme_light_tertiaryContainer, + onTertiaryContainer = md_theme_light_onTertiaryContainer, + error = md_theme_light_error, + errorContainer = md_theme_light_errorContainer, + onError = md_theme_light_onError, + onErrorContainer = md_theme_light_onErrorContainer, + background = md_theme_light_background, + onBackground = md_theme_light_onBackground, + surface = md_theme_light_surface, + onSurface = md_theme_light_onSurface, + surfaceVariant = md_theme_light_surfaceVariant, + onSurfaceVariant = md_theme_light_onSurfaceVariant, + outline = md_theme_light_outline, + inverseOnSurface = md_theme_light_inverseOnSurface, + inverseSurface = md_theme_light_inverseSurface, + inversePrimary = md_theme_light_inversePrimary, ) +private val DarkThemeColors = darkColorScheme( -private val LightColorPalette = lightColors( - primary = Purple500, - primaryVariant = Purple700, - secondary = Teal200 - - /* Other default colors to override - background = Color.White, - surface = Color.White, - onPrimary = Color.White, - onSecondary = Color.Black, - onBackground = Color.Black, - onSurface = Color.Black, - */ + primary = md_theme_dark_primary, + onPrimary = md_theme_dark_onPrimary, + primaryContainer = md_theme_dark_primaryContainer, + onPrimaryContainer = md_theme_dark_onPrimaryContainer, + secondary = md_theme_dark_secondary, + onSecondary = md_theme_dark_onSecondary, + secondaryContainer = md_theme_dark_secondaryContainer, + onSecondaryContainer = md_theme_dark_onSecondaryContainer, + tertiary = md_theme_dark_tertiary, + onTertiary = md_theme_dark_onTertiary, + tertiaryContainer = md_theme_dark_tertiaryContainer, + onTertiaryContainer = md_theme_dark_onTertiaryContainer, + error = md_theme_dark_error, + errorContainer = md_theme_dark_errorContainer, + onError = md_theme_dark_onError, + onErrorContainer = md_theme_dark_onErrorContainer, + background = md_theme_dark_background, + onBackground = md_theme_dark_onBackground, + surface = md_theme_dark_surface, + onSurface = md_theme_dark_onSurface, + surfaceVariant = md_theme_dark_surfaceVariant, + onSurfaceVariant = md_theme_dark_onSurfaceVariant, + outline = md_theme_dark_outline, + inverseOnSurface = md_theme_dark_inverseOnSurface, + inverseSurface = md_theme_dark_inverseSurface, + inversePrimary = md_theme_dark_inversePrimary, ) +@SuppressLint("NewApi") @Composable -fun SzobatarSCHTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { - val colors = if (darkTheme) { - DarkColorPalette - } else { - LightColorPalette +fun SzobatarSCHTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + darkTheme -> DarkThemeColors + else -> LightThemeColors } +/* val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + (view.context as Activity).window.statusBarColor = colorScheme.primary.toArgb() + ViewCompat.getWindowInsetsController(view)?.isAppearanceLightStatusBars = darkTheme + } + } + + MaterialTheme( + colorScheme = colorScheme, + typography = AppTypography, + content = content + )*/ + + val systemUiController = rememberSystemUiController() + MaterialTheme( - colors = colors, - typography = Typography, - shapes = Shapes, - content = content + colorScheme = colorScheme, + typography = AppTypography, + content = { + SideEffect { + systemUiController.setStatusBarColor( + color = colorScheme.background, + darkIcons = !darkTheme, + ) + systemUiController.setNavigationBarColor( + color = colorScheme.background, + darkIcons = !darkTheme, + ) + } + + content() + } ) -} \ No newline at end of file +} +