diff --git a/app/src/main/java/com/sadellie/unitto/MainActivity.kt b/app/src/main/java/com/sadellie/unitto/MainActivity.kt index f7935a0d..be67595e 100644 --- a/app/src/main/java/com/sadellie/unitto/MainActivity.kt +++ b/app/src/main/java/com/sadellie/unitto/MainActivity.kt @@ -44,7 +44,7 @@ internal class MainActivity : AppCompatActivity() { val prefs = userPrefsRepository.appPrefs .collectAsStateWithLifecycle(null).value - if (prefs != null) UnittoApp(prefs) + UnittoApp(prefs) } } diff --git a/app/src/main/java/com/sadellie/unitto/UnittoApp.kt b/app/src/main/java/com/sadellie/unitto/UnittoApp.kt index df70b5b8..b7cda27b 100644 --- a/app/src/main/java/com/sadellie/unitto/UnittoApp.kt +++ b/app/src/main/java/com/sadellie/unitto/UnittoApp.kt @@ -53,23 +53,14 @@ import com.sadellie.unitto.core.ui.theme.TypographySystem import com.sadellie.unitto.core.ui.theme.TypographyUnitto import com.sadellie.unitto.data.userprefs.AppPreferences import io.github.sadellie.themmo.Themmo -import io.github.sadellie.themmo.rememberThemmoController +import io.github.sadellie.themmo.ThemmoController import kotlinx.coroutines.launch @OptIn(ExperimentalFoundationApi::class) @Composable -internal fun UnittoApp(prefs: AppPreferences) { +internal fun UnittoApp(prefs: AppPreferences?) { val mContext = LocalContext.current - val themmoController = rememberThemmoController( - lightColorScheme = LightThemeColors, - darkColorScheme = DarkThemeColors, - themingMode = prefs.themingMode, - dynamicThemeEnabled = prefs.enableDynamicTheme, - amoledThemeEnabled = prefs.enableAmoledTheme, - customColor = prefs.customColor, - monetMode = prefs.monetMode - ) val navController = rememberNavController() val sysUiController = rememberSystemUiController() @@ -97,66 +88,80 @@ internal fun UnittoApp(prefs: AppPreferences) { } } - Themmo( - themmoController = themmoController, - typography = if (prefs.systemFont) TypographySystem else TypographyUnitto, - animationSpec = tween(250) - ) { - val backgroundColor = MaterialTheme.colorScheme.background - val useDarkIcons by remember(backgroundColor) { - mutableStateOf(backgroundColor.luminance() > 0.5f) + if (prefs != null) { + val themmoController = remember(prefs) { + ThemmoController( + lightColorScheme = LightThemeColors, + darkColorScheme = DarkThemeColors, + themingMode = prefs.themingMode, + dynamicThemeEnabled = prefs.enableDynamicTheme, + amoledThemeEnabled = prefs.enableAmoledTheme, + customColor = prefs.customColor, + monetMode = prefs.monetMode + ) } - UnittoModalNavigationDrawer( - drawer = { - UnittoDrawerSheet( - modifier = Modifier, - tabs = tabs, - currentDestination = navBackStackEntry?.destination?.route - ) { destination -> - drawerScope.launch { drawerState.close() } - - navController.navigate(destination.graph) { - popUpTo(navController.graph.findStartDestination().id) { - saveState = true - } - launchSingleTop = true - restoreState = true - } - - shortcutsScope.launch { - destination.shortcut?.let { shortcut: Shortcut -> - mContext.pushDynamicShortcut( - destination.graph, - shortcut.shortcutShortLabel, - shortcut.shortcutLongLabel, - shortcut.shortcutDrawable - ) - } - } - } - }, - modifier = Modifier, - state = drawerState, - gesturesEnabled = gesturesEnabled, - scope = drawerScope, - content = { - UnittoNavigation( - navController = navController, - themmoController = it, - startDestination = prefs.startingScreen, - openDrawer = { drawerScope.launch { drawerState.open() } } - ) + Themmo( + themmoController = themmoController, + typography = if (prefs.systemFont) TypographySystem else TypographyUnitto, + animationSpec = tween(250) + ) { + val backgroundColor = MaterialTheme.colorScheme.background + val useDarkIcons by remember(backgroundColor) { + mutableStateOf(backgroundColor.luminance() > 0.5f) } - ) - BackHandler(drawerState.isOpen) { - drawerScope.launch { drawerState.close() } - } + UnittoModalNavigationDrawer( + drawer = { + UnittoDrawerSheet( + modifier = Modifier, + tabs = tabs, + currentDestination = navBackStackEntry?.destination?.route + ) { destination -> + drawerScope.launch { drawerState.close() } - LaunchedEffect(useDarkIcons) { - sysUiController.setNavigationBarColor(Color.Transparent, useDarkIcons) - sysUiController.setStatusBarColor(Color.Transparent, useDarkIcons) + navController.navigate(destination.graph) { + popUpTo(navController.graph.findStartDestination().id) { + saveState = true + } + launchSingleTop = true + restoreState = true + } + + shortcutsScope.launch { + destination.shortcut?.let { shortcut: Shortcut -> + mContext.pushDynamicShortcut( + destination.graph, + shortcut.shortcutShortLabel, + shortcut.shortcutLongLabel, + shortcut.shortcutDrawable + ) + } + } + } + }, + modifier = Modifier, + state = drawerState, + gesturesEnabled = gesturesEnabled, + scope = drawerScope, + content = { + UnittoNavigation( + navController = navController, + themmoController = it, + startDestination = prefs.startingScreen, + openDrawer = { drawerScope.launch { drawerState.open() } } + ) + } + ) + + LaunchedEffect(useDarkIcons) { + sysUiController.setNavigationBarColor(Color.Transparent, useDarkIcons) + sysUiController.setStatusBarColor(Color.Transparent, useDarkIcons) + } } } + + BackHandler(drawerState.isOpen) { + drawerScope.launch { drawerState.close() } + } } diff --git a/core/ui/src/main/java/com/sadellie/unitto/core/ui/NavGraphBuilderExtensions.kt b/core/ui/src/main/java/com/sadellie/unitto/core/ui/NavGraphBuilderExtensions.kt index de4953d0..8bda851c 100644 --- a/core/ui/src/main/java/com/sadellie/unitto/core/ui/NavGraphBuilderExtensions.kt +++ b/core/ui/src/main/java/com/sadellie/unitto/core/ui/NavGraphBuilderExtensions.kt @@ -22,9 +22,14 @@ import androidx.compose.animation.AnimatedContentScope import androidx.compose.animation.AnimatedContentTransitionScope import androidx.compose.animation.EnterTransition import androidx.compose.animation.ExitTransition +import androidx.compose.animation.core.FiniteAnimationSpec +import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideOutHorizontally import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.IntOffset import androidx.navigation.NamedNavArgument import androidx.navigation.NavBackStackEntry import androidx.navigation.NavDeepLink @@ -40,16 +45,16 @@ fun NavGraphBuilder.unittoComposable( arguments: List = emptyList(), deepLinks: List = emptyList(), enterTransition: (@JvmSuppressWildcards - AnimatedContentTransitionScope.() -> EnterTransition?)? = { fadeIn() }, + AnimatedContentTransitionScope.() -> EnterTransition?)? = { unittoFadeIn() }, exitTransition: (@JvmSuppressWildcards - AnimatedContentTransitionScope.() -> ExitTransition?)? = { fadeOut() }, + AnimatedContentTransitionScope.() -> ExitTransition?)? = { unittoFadeOut() }, popEnterTransition: (@JvmSuppressWildcards AnimatedContentTransitionScope.() -> EnterTransition?)? = enterTransition, popExitTransition: (@JvmSuppressWildcards AnimatedContentTransitionScope.() -> ExitTransition?)? = exitTransition, - content: @Composable AnimatedContentScope.(NavBackStackEntry) -> Unit + content: @Composable AnimatedContentScope.(NavBackStackEntry) -> Unit, ): Unit = composable( route = route, arguments = arguments, @@ -61,6 +66,40 @@ fun NavGraphBuilder.unittoComposable( content = content, ) +fun NavGraphBuilder.unittoStackedComposable( + route: String, + arguments: List = emptyList(), + deepLinks: List = emptyList(), + content: @Composable AnimatedContentScope.(NavBackStackEntry) -> Unit, +) { + composable( + route = route, + arguments = arguments, + deepLinks = deepLinks, + enterTransition = { + slideInHorizontally( + animationSpec = unittoEnterTween(), + initialOffsetX = { (it * 0.2f).toInt() }) + unittoFadeIn() + }, + exitTransition = { + slideOutHorizontally( + animationSpec = unittoExitTween(), + targetOffsetX = { -(it * 0.2f).toInt() }) + unittoFadeOut() + }, + popEnterTransition = { + slideInHorizontally( + animationSpec = unittoEnterTween(), + initialOffsetX = { -(it * 0.2f).toInt() }) + unittoFadeIn() + }, + popExitTransition = { + slideOutHorizontally( + animationSpec = unittoExitTween(), + targetOffsetX = { (it * 0.2f).toInt() }) + unittoFadeOut() + }, + content = content, + ) +} + /** * @see NavGraphBuilder.navigation */ @@ -79,7 +118,7 @@ fun NavGraphBuilder.unittoNavigation( popExitTransition: ( AnimatedContentTransitionScope.() -> ExitTransition? )? = exitTransition, - builder: NavGraphBuilder.() -> Unit + builder: NavGraphBuilder.() -> Unit, ): Unit = navigation( startDestination = startDestination, route = route, @@ -91,3 +130,11 @@ fun NavGraphBuilder.unittoNavigation( popExitTransition = popExitTransition, builder = builder ) + +private const val ENTER_DURATION = 350 +private const val EXIT_DURATION = 200 + +private fun unittoFadeIn(): EnterTransition = fadeIn(tween(ENTER_DURATION)) +private fun unittoFadeOut(): ExitTransition = fadeOut(tween(EXIT_DURATION)) +private fun unittoEnterTween(): FiniteAnimationSpec = tween(ENTER_DURATION) +private fun unittoExitTween(): FiniteAnimationSpec = tween(EXIT_DURATION) diff --git a/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/UnittoEmptyScreen.kt b/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/UnittoEmptyScreen.kt new file mode 100644 index 00000000..2d1cab40 --- /dev/null +++ b/core/ui/src/main/java/com/sadellie/unitto/core/ui/common/UnittoEmptyScreen.kt @@ -0,0 +1,29 @@ +/* + * Unitto is a unit converter for Android + * Copyright (c) 2023 Elshan Agaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sadellie.unitto.core.ui.common + +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +fun UnittoEmptyScreen() { + Spacer(modifier = Modifier.fillMaxSize()) +} diff --git a/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/PreferenceModels.kt b/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/PreferenceModels.kt new file mode 100644 index 00000000..9a0ecb47 --- /dev/null +++ b/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/PreferenceModels.kt @@ -0,0 +1,94 @@ +/* + * Unitto is a unit converter for Android + * Copyright (c) 2023 Elshan Agaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sadellie.unitto.data.userprefs + +import androidx.compose.ui.graphics.Color +import com.sadellie.unitto.data.model.ALL_UNIT_GROUPS +import com.sadellie.unitto.data.model.UnitGroup +import com.sadellie.unitto.data.model.UnitsListSorting +import io.github.sadellie.themmo.MonetMode +import io.github.sadellie.themmo.ThemingMode + +data class AppPreferences( + val themingMode: ThemingMode, + val enableDynamicTheme: Boolean, + val enableAmoledTheme: Boolean, + val customColor: Color, + val monetMode: MonetMode, + val startingScreen: String, + val enableToolsExperiment: Boolean, + val systemFont: Boolean, +) + +data class GeneralPreferences( + val enableVibrations: Boolean, +) + +data class CalculatorPreferences( + val radianMode: Boolean, + val enableVibrations: Boolean, + val separator: Int, + val middleZero: Boolean, + val partialHistoryView: Boolean, + val precision: Int, + val outputFormat: Int, +) + +data class ConverterPreferences( + val enableVibrations: Boolean, + val separator: Int, + val middleZero: Boolean, + val precision: Int, + val outputFormat: Int, + val unitConverterFormatTime: Boolean, + val unitConverterSorting: UnitsListSorting, + val shownUnitGroups: List, + val unitConverterFavoritesOnly: Boolean, + val enableToolsExperiment: Boolean, + val latestLeftSideUnit: String, + val latestRightSideUnit: String, +) + +data class DisplayPreferences( + val systemFont: Boolean, + val middleZero: Boolean, +) + +data class FormattingPreferences( + val digitsPrecision: Int, + val separator: Int, + val outputFormat: Int, +) + +data class UnitGroupsPreferences( + val shownUnitGroups: List = ALL_UNIT_GROUPS, +) + +data class AddSubtractPreferences( + val separator: Int, + val enableVibrations: Boolean, +) + +data class AboutPreferences( + val enableToolsExperiment: Boolean, +) + +data class StartingScreenPreferences( + val startingScreen: String, +) diff --git a/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/PrefsKeys.kt b/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/PrefsKeys.kt new file mode 100644 index 00000000..36a4a18f --- /dev/null +++ b/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/PrefsKeys.kt @@ -0,0 +1,49 @@ +/* + * Unitto is a unit converter for Android + * Copyright (c) 2023 Elshan Agaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sadellie.unitto.data.userprefs + +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.intPreferencesKey +import androidx.datastore.preferences.core.longPreferencesKey +import androidx.datastore.preferences.core.stringPreferencesKey + +internal object PrefsKeys { + val THEMING_MODE = stringPreferencesKey("THEMING_MODE_PREF_KEY") + val ENABLE_DYNAMIC_THEME = booleanPreferencesKey("ENABLE_DYNAMIC_THEME_PREF_KEY") + val ENABLE_AMOLED_THEME = booleanPreferencesKey("ENABLE_AMOLED_THEME_PREF_KEY") + val CUSTOM_COLOR = longPreferencesKey("CUSTOM_COLOR_PREF_KEY") + val MONET_MODE = stringPreferencesKey("MONET_MODE_PREF_KEY") + val DIGITS_PRECISION = intPreferencesKey("DIGITS_PRECISION_PREF_KEY") + val SEPARATOR = intPreferencesKey("SEPARATOR_PREF_KEY") + val OUTPUT_FORMAT = intPreferencesKey("OUTPUT_FORMAT_PREF_KEY") + val LATEST_LEFT_SIDE = stringPreferencesKey("LATEST_LEFT_SIDE_PREF_KEY") + val LATEST_RIGHT_SIDE = stringPreferencesKey("LATEST_RIGHT_SIDE_PREF_KEY") + val SHOWN_UNIT_GROUPS = stringPreferencesKey("SHOWN_UNIT_GROUPS_PREF_KEY") + val ENABLE_VIBRATIONS = booleanPreferencesKey("ENABLE_VIBRATIONS_PREF_KEY") + val ENABLE_TOOLS_EXPERIMENT = booleanPreferencesKey("ENABLE_TOOLS_EXPERIMENT_PREF_KEY") + val STARTING_SCREEN = stringPreferencesKey("STARTING_SCREEN_PREF_KEY") + val RADIAN_MODE = booleanPreferencesKey("RADIAN_MODE_PREF_KEY") + val UNIT_CONVERTER_FAVORITES_ONLY = + booleanPreferencesKey("UNIT_CONVERTER_FAVORITES_ONLY_PREF_KEY") + val UNIT_CONVERTER_FORMAT_TIME = booleanPreferencesKey("UNIT_CONVERTER_FORMAT_TIME_PREF_KEY") + val UNIT_CONVERTER_SORTING = stringPreferencesKey("UNIT_CONVERTER_SORTING_PREF_KEY") + val MIDDLE_ZERO = booleanPreferencesKey("MIDDLE_ZERO_PREF_KEY") + val SYSTEM_FONT = booleanPreferencesKey("SYSTEM_FONT_PREF_KEY") + val PARTIAL_HISTORY_VIEW = booleanPreferencesKey("PARTIAL_HISTORY_VIEW_PREF_KEY") +} diff --git a/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/UserPreferences.kt b/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/UserPreferences.kt index 6a97ddd3..a988391d 100644 --- a/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/UserPreferences.kt +++ b/data/userprefs/src/main/java/com/sadellie/unitto/data/userprefs/UserPreferences.kt @@ -21,12 +21,8 @@ package com.sadellie.unitto.data.userprefs import androidx.compose.ui.graphics.Color import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.emptyPreferences -import androidx.datastore.preferences.core.intPreferencesKey -import androidx.datastore.preferences.core.longPreferencesKey -import androidx.datastore.preferences.core.stringPreferencesKey import com.sadellie.unitto.core.base.OutputFormat import com.sadellie.unitto.core.base.Separator import com.sadellie.unitto.core.base.TopLevelDestinations @@ -43,214 +39,107 @@ import kotlinx.coroutines.flow.map import java.io.IOException import javax.inject.Inject -private object PrefsKeys { - val THEMING_MODE = stringPreferencesKey("THEMING_MODE_PREF_KEY") - val ENABLE_DYNAMIC_THEME = booleanPreferencesKey("ENABLE_DYNAMIC_THEME_PREF_KEY") - val ENABLE_AMOLED_THEME = booleanPreferencesKey("ENABLE_AMOLED_THEME_PREF_KEY") - val CUSTOM_COLOR = longPreferencesKey("CUSTOM_COLOR_PREF_KEY") - val MONET_MODE = stringPreferencesKey("MONET_MODE_PREF_KEY") - val DIGITS_PRECISION = intPreferencesKey("DIGITS_PRECISION_PREF_KEY") - val SEPARATOR = intPreferencesKey("SEPARATOR_PREF_KEY") - val OUTPUT_FORMAT = intPreferencesKey("OUTPUT_FORMAT_PREF_KEY") - val LATEST_LEFT_SIDE = stringPreferencesKey("LATEST_LEFT_SIDE_PREF_KEY") - val LATEST_RIGHT_SIDE = stringPreferencesKey("LATEST_RIGHT_SIDE_PREF_KEY") - val SHOWN_UNIT_GROUPS = stringPreferencesKey("SHOWN_UNIT_GROUPS_PREF_KEY") - val ENABLE_VIBRATIONS = booleanPreferencesKey("ENABLE_VIBRATIONS_PREF_KEY") - val ENABLE_TOOLS_EXPERIMENT = booleanPreferencesKey("ENABLE_TOOLS_EXPERIMENT_PREF_KEY") - val STARTING_SCREEN = stringPreferencesKey("STARTING_SCREEN_PREF_KEY") - val RADIAN_MODE = booleanPreferencesKey("RADIAN_MODE_PREF_KEY") - val UNIT_CONVERTER_FAVORITES_ONLY = - booleanPreferencesKey("UNIT_CONVERTER_FAVORITES_ONLY_PREF_KEY") - val UNIT_CONVERTER_FORMAT_TIME = booleanPreferencesKey("UNIT_CONVERTER_FORMAT_TIME_PREF_KEY") - val UNIT_CONVERTER_SORTING = stringPreferencesKey("UNIT_CONVERTER_SORTING_PREF_KEY") - val MIDDLE_ZERO = booleanPreferencesKey("MIDDLE_ZERO_PREF_KEY") - val SYSTEM_FONT = booleanPreferencesKey("SYSTEM_FONT_PREF_KEY") - val PARTIAL_HISTORY_VIEW = booleanPreferencesKey("PARTIAL_HISTORY_VIEW_PREF_KEY") -} - -data class AppPreferences( - val themingMode: ThemingMode = ThemingMode.AUTO, - val enableDynamicTheme: Boolean = true, - val enableAmoledTheme: Boolean = false, - val customColor: Color = Color.Unspecified, - val monetMode: MonetMode = MonetMode.TonalSpot, - val startingScreen: String = TopLevelDestinations.Calculator.graph, - val enableToolsExperiment: Boolean = false, - val systemFont: Boolean = false, -) - -data class GeneralPreferences( - val enableVibrations: Boolean = true, -) - -data class CalculatorPreferences( - val radianMode: Boolean = true, - val enableVibrations: Boolean = true, - val separator: Int = Separator.SPACE, - val middleZero: Boolean = false, - val partialHistoryView: Boolean = true, - val precision: Int = 3, - val outputFormat: Int = OutputFormat.PLAIN, -) - -data class ConverterPreferences( - val enableVibrations: Boolean = true, - val separator: Int = Separator.SPACE, - val middleZero: Boolean = false, - val precision: Int = 3, - val outputFormat: Int = OutputFormat.PLAIN, - val unitConverterFormatTime: Boolean = false, - val unitConverterSorting: UnitsListSorting = UnitsListSorting.USAGE, - val shownUnitGroups: List = ALL_UNIT_GROUPS, - val unitConverterFavoritesOnly: Boolean = false, - val enableToolsExperiment: Boolean = false, - val latestLeftSideUnit: String = MyUnitIDS.kilometer, - val latestRightSideUnit: String = MyUnitIDS.mile, -) - -data class DisplayPreferences( - val systemFont: Boolean = false, - val middleZero: Boolean = false, -) - -data class FormattingPreferences( - val digitsPrecision: Int = 3, - val separator: Int = Separator.SPACE, - val outputFormat: Int = OutputFormat.PLAIN, -) - -data class UnitGroupsPreferences( - val shownUnitGroups: List = ALL_UNIT_GROUPS, -) - -data class AddSubtractPreferences( - val separator: Int = Separator.SPACE, - val enableVibrations: Boolean = true, -) - -data class AboutPreferences( - val enableToolsExperiment: Boolean = false, -) - -data class StartingScreenPreferences( - val startingScreen: String = TopLevelDestinations.Calculator.graph, -) - -class UserPreferencesRepository @Inject constructor(private val dataStore: DataStore) { +class UserPreferencesRepository @Inject constructor( + private val dataStore: DataStore, +) { private val data = dataStore.data .catch { if (it is IOException) emit(emptyPreferences()) else throw it } val appPrefs: Flow = data .map { preferences -> AppPreferences( - themingMode = preferences[PrefsKeys.THEMING_MODE]?.letTryOrNull { - ThemingMode.valueOf(it) - } - ?: ThemingMode.AUTO, - enableDynamicTheme = preferences[PrefsKeys.ENABLE_DYNAMIC_THEME] ?: true, - enableAmoledTheme = preferences[PrefsKeys.ENABLE_AMOLED_THEME] ?: false, - customColor = preferences[PrefsKeys.CUSTOM_COLOR]?.letTryOrNull { Color(it.toULong()) } - ?: Color.Unspecified, - monetMode = preferences[PrefsKeys.MONET_MODE]?.letTryOrNull { MonetMode.valueOf(it) } - ?: MonetMode.TonalSpot, - startingScreen = preferences[PrefsKeys.STARTING_SCREEN] - ?: TopLevelDestinations.Calculator.graph, - enableToolsExperiment = preferences[PrefsKeys.ENABLE_TOOLS_EXPERIMENT] ?: false, - systemFont = preferences[PrefsKeys.SYSTEM_FONT] ?: false + themingMode = preferences.getThemingMode(), + enableDynamicTheme = preferences.getEnableDynamicTheme(), + enableAmoledTheme = preferences.getEnableAmoledTheme(), + customColor = preferences.getCustomColor(), + monetMode = preferences.getMonetMode(), + startingScreen = preferences.getStartingScreen(), + enableToolsExperiment = preferences.getEnableToolsExperiment(), + systemFont = preferences.getSystemFont() ) } val generalPrefs: Flow = data .map { preferences -> GeneralPreferences( - enableVibrations = preferences[PrefsKeys.ENABLE_VIBRATIONS] ?: true, + enableVibrations = preferences.getEnableVibrations(), ) } val calculatorPrefs: Flow = data .map { preferences -> CalculatorPreferences( - radianMode = preferences[PrefsKeys.RADIAN_MODE] ?: true, - enableVibrations = preferences[PrefsKeys.ENABLE_VIBRATIONS] ?: true, - separator = preferences[PrefsKeys.SEPARATOR] ?: Separator.SPACE, - middleZero = preferences[PrefsKeys.MIDDLE_ZERO] ?: false, - partialHistoryView = preferences[PrefsKeys.PARTIAL_HISTORY_VIEW] ?: true, - precision = preferences[PrefsKeys.DIGITS_PRECISION] ?: 3, - outputFormat = preferences[PrefsKeys.OUTPUT_FORMAT] ?: OutputFormat.PLAIN + radianMode = preferences.getRadianMode(), + enableVibrations = preferences.getEnableVibrations(), + separator = preferences.getSeparator(), + middleZero = preferences.getMiddleZero(), + partialHistoryView = preferences.getPartialHistoryView(), + precision = preferences.getDigitsPrecision(), + outputFormat = preferences.getOutputFormat() ) } val converterPrefs: Flow = data .map { preferences -> ConverterPreferences( - enableVibrations = preferences[PrefsKeys.ENABLE_VIBRATIONS] ?: true, - separator = preferences[PrefsKeys.SEPARATOR] ?: Separator.SPACE, - middleZero = preferences[PrefsKeys.MIDDLE_ZERO] ?: false, - precision = preferences[PrefsKeys.DIGITS_PRECISION] ?: 3, - outputFormat = preferences[PrefsKeys.OUTPUT_FORMAT] ?: OutputFormat.PLAIN, - unitConverterFormatTime = preferences[PrefsKeys.UNIT_CONVERTER_FORMAT_TIME] - ?: false, - unitConverterSorting = preferences[PrefsKeys.UNIT_CONVERTER_SORTING] - ?.let { UnitsListSorting.valueOf(it) } ?: UnitsListSorting.USAGE, - shownUnitGroups = preferences[PrefsKeys.SHOWN_UNIT_GROUPS]?.letTryOrNull { list -> - list.ifEmpty { return@letTryOrNull listOf() }.split(",") - .map { UnitGroup.valueOf(it) } - } ?: ALL_UNIT_GROUPS, - unitConverterFavoritesOnly = preferences[PrefsKeys.UNIT_CONVERTER_FAVORITES_ONLY] - ?: false, - enableToolsExperiment = preferences[PrefsKeys.ENABLE_TOOLS_EXPERIMENT] ?: false, - latestLeftSideUnit = preferences[PrefsKeys.LATEST_LEFT_SIDE] ?: MyUnitIDS.kilometer, - latestRightSideUnit = preferences[PrefsKeys.LATEST_RIGHT_SIDE] ?: MyUnitIDS.mile, + enableVibrations = preferences.getEnableVibrations(), + separator = preferences.getSeparator(), + middleZero = preferences.getMiddleZero(), + precision = preferences.getDigitsPrecision(), + outputFormat = preferences.getOutputFormat(), + unitConverterFormatTime = preferences.getUnitConverterFormatTime(), + unitConverterSorting = preferences.getUnitConverterSorting(), + shownUnitGroups = preferences.getShownUnitGroups(), + unitConverterFavoritesOnly = preferences.getUnitConverterFavoritesOnly(), + enableToolsExperiment = preferences.getEnableToolsExperiment(), + latestLeftSideUnit = preferences.getLatestLeftSide(), + latestRightSideUnit = preferences.getLatestRightSide(), ) } val displayPrefs: Flow = data .map { preferences -> DisplayPreferences( - systemFont = preferences[PrefsKeys.SYSTEM_FONT] ?: false, - middleZero = preferences[PrefsKeys.MIDDLE_ZERO] ?: false, + systemFont = preferences.getSystemFont(), + middleZero = preferences.getMiddleZero(), ) } val formattingPrefs: Flow = data .map { preferences -> FormattingPreferences( - digitsPrecision = preferences[PrefsKeys.DIGITS_PRECISION] ?: 3, - separator = preferences[PrefsKeys.SEPARATOR] ?: Separator.SPACE, - outputFormat = preferences[PrefsKeys.OUTPUT_FORMAT] ?: OutputFormat.PLAIN, + digitsPrecision = preferences.getDigitsPrecision(), + separator = preferences.getSeparator(), + outputFormat = preferences.getOutputFormat(), ) } val unitGroupsPrefs: Flow = data .map { preferences -> UnitGroupsPreferences( - shownUnitGroups = preferences[PrefsKeys.SHOWN_UNIT_GROUPS]?.letTryOrNull { list -> - list.ifEmpty { return@letTryOrNull listOf() }.split(",") - .map { UnitGroup.valueOf(it) } - } ?: ALL_UNIT_GROUPS, + shownUnitGroups = preferences.getShownUnitGroups(), ) } val addSubtractPrefs: Flow = data .map { preferences -> AddSubtractPreferences( - separator = preferences[PrefsKeys.SEPARATOR] ?: Separator.SPACE, - enableVibrations = preferences[PrefsKeys.ENABLE_VIBRATIONS] ?: true, + separator = preferences.getSeparator(), + enableVibrations = preferences.getEnableVibrations(), ) } val aboutPrefs: Flow = data .map { preferences -> AboutPreferences( - enableToolsExperiment = preferences[PrefsKeys.ENABLE_TOOLS_EXPERIMENT] ?: false + enableToolsExperiment = preferences.getEnableToolsExperiment() ) } val startingScreenPrefs: Flow = data .map { preferences -> StartingScreenPreferences( - startingScreen = preferences[PrefsKeys.STARTING_SCREEN] - ?: TopLevelDestinations.Calculator.graph, + startingScreen = preferences.getStartingScreen(), ) } @@ -374,10 +263,104 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS preferences[PrefsKeys.PARTIAL_HISTORY_VIEW] = enabled } } - - private inline fun T.letTryOrNull(block: (T) -> R): R? = try { - this?.let(block) - } catch (e: Exception) { - null - } +} + +private fun Preferences.getEnableDynamicTheme(): Boolean { + return this[PrefsKeys.ENABLE_DYNAMIC_THEME] ?: true +} + +private fun Preferences.getThemingMode(): ThemingMode { + return this[PrefsKeys.THEMING_MODE] + ?.letTryOrNull { ThemingMode.valueOf(it) } + ?: ThemingMode.AUTO +} + +private fun Preferences.getEnableAmoledTheme(): Boolean { + return this[PrefsKeys.ENABLE_AMOLED_THEME] ?: false +} + +private fun Preferences.getCustomColor(): Color { + return this[PrefsKeys.CUSTOM_COLOR]?.letTryOrNull { Color(it.toULong()) } + ?: Color.Unspecified +} + +private fun Preferences.getMonetMode(): MonetMode { + return this[PrefsKeys.MONET_MODE]?.letTryOrNull { MonetMode.valueOf(it) } + ?: MonetMode.TonalSpot +} + +private fun Preferences.getStartingScreen(): String { + return this[PrefsKeys.STARTING_SCREEN] + ?: TopLevelDestinations.Calculator.graph +} + +private fun Preferences.getEnableToolsExperiment(): Boolean { + return this[PrefsKeys.ENABLE_TOOLS_EXPERIMENT] ?: false +} + +private fun Preferences.getSystemFont(): Boolean { + return this[PrefsKeys.SYSTEM_FONT] ?: false +} + +private fun Preferences.getEnableVibrations(): Boolean { + return this[PrefsKeys.ENABLE_VIBRATIONS] ?: true +} + +private fun Preferences.getRadianMode(): Boolean { + return this[PrefsKeys.RADIAN_MODE] ?: true +} + +private fun Preferences.getSeparator(): Int { + return this[PrefsKeys.SEPARATOR] ?: Separator.SPACE +} + +private fun Preferences.getMiddleZero(): Boolean { + return this[PrefsKeys.MIDDLE_ZERO] ?: false +} + +private fun Preferences.getPartialHistoryView(): Boolean { + return this[PrefsKeys.PARTIAL_HISTORY_VIEW] ?: true +} + +private fun Preferences.getDigitsPrecision(): Int { + return this[PrefsKeys.DIGITS_PRECISION] ?: 3 +} + +private fun Preferences.getOutputFormat(): Int { + return this[PrefsKeys.OUTPUT_FORMAT] ?: OutputFormat.PLAIN +} + +private fun Preferences.getUnitConverterFormatTime(): Boolean { + return this[PrefsKeys.UNIT_CONVERTER_FORMAT_TIME] ?: false +} + +private fun Preferences.getUnitConverterSorting(): UnitsListSorting { + return this[PrefsKeys.UNIT_CONVERTER_SORTING] + ?.let { UnitsListSorting.valueOf(it) } ?: UnitsListSorting.USAGE +} + +private fun Preferences.getShownUnitGroups(): List { + return this[PrefsKeys.SHOWN_UNIT_GROUPS]?.letTryOrNull { list -> + list.ifEmpty { return@letTryOrNull listOf() }.split(",") + .map { UnitGroup.valueOf(it) } + } ?: ALL_UNIT_GROUPS +} + +private fun Preferences.getUnitConverterFavoritesOnly(): Boolean { + return this[PrefsKeys.UNIT_CONVERTER_FAVORITES_ONLY] + ?: false +} + +private fun Preferences.getLatestLeftSide(): String { + return this[PrefsKeys.LATEST_LEFT_SIDE] ?: MyUnitIDS.kilometer +} + +private fun Preferences.getLatestRightSide(): String { + return this[PrefsKeys.LATEST_RIGHT_SIDE] ?: MyUnitIDS.mile +} + +private inline fun T.letTryOrNull(block: (T) -> R): R? = try { + this?.let(block) +} catch (e: Exception) { + null } diff --git a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorScreen.kt b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorScreen.kt index 7a3c8b4f..20484ca3 100644 --- a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorScreen.kt +++ b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorScreen.kt @@ -18,24 +18,17 @@ package com.sadellie.unitto.feature.calculator -import android.content.res.Configuration import androidx.compose.animation.Crossfade import androidx.compose.animation.core.tween -import androidx.compose.foundation.background import androidx.compose.foundation.gestures.AnchoredDraggableState import androidx.compose.foundation.gestures.DraggableAnchors import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.anchoredDraggable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.sizeIn -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Delete import androidx.compose.material3.AlertDialog @@ -53,9 +46,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource @@ -70,11 +61,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.ui.common.MenuButton import com.sadellie.unitto.core.ui.common.SettingsButton +import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen import com.sadellie.unitto.core.ui.common.UnittoScreenWithTopBar -import com.sadellie.unitto.core.ui.common.textfield.UnformattedTextField import com.sadellie.unitto.data.model.HistoryItem import com.sadellie.unitto.feature.calculator.components.CalculatorKeyboard -import com.sadellie.unitto.feature.calculator.components.CalculatorKeyboardLoading import com.sadellie.unitto.feature.calculator.components.HistoryItemHeight import com.sadellie.unitto.feature.calculator.components.HistoryList import com.sadellie.unitto.feature.calculator.components.TextBox @@ -117,10 +107,7 @@ internal fun CalculatorScreen( clearHistory: () -> Unit ) { when (uiState) { - is CalculatorUIState.Loading -> Loading( - navigateToMenu = navigateToMenu, - navigateToSettings = navigateToSettings, - ) + is CalculatorUIState.Loading -> UnittoEmptyScreen() is CalculatorUIState.Ready -> Ready( uiState = uiState, navigateToMenu = navigateToMenu, @@ -314,81 +301,6 @@ private fun Ready( } } -@Composable -internal fun Loading( - navigateToMenu: () -> Unit, - navigateToSettings: () -> Unit, -) { - UnittoScreenWithTopBar( - title = { Text(stringResource(R.string.calculator_title)) }, - navigationIcon = { MenuButton { navigateToMenu() } }, - colors = TopAppBarDefaults.topAppBarColors(MaterialTheme.colorScheme.surfaceVariant), - actions = { SettingsButton(navigateToSettings) } - ) { paddingValues -> - BoxWithConstraints( - modifier = Modifier.padding(paddingValues), - ) { - // Input - Column( - Modifier - .height(maxHeight * 0.25f) - .background( - MaterialTheme.colorScheme.surfaceVariant, - RoundedCornerShape( - topStartPercent = 0, topEndPercent = 0, - bottomStartPercent = 20, bottomEndPercent = 20 - ) - ) - .padding(top = 12.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - UnformattedTextField( - modifier = Modifier - .weight(2f) - .fillMaxWidth() - .padding(horizontal = 8.dp), - value = TextFieldValue(), - minRatio = 0.5f, - onCursorChange = {}, - ) - if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT) { - UnformattedTextField( - modifier = Modifier - .weight(1f) - .fillMaxWidth() - .padding(horizontal = 8.dp), - value = TextFieldValue(), - minRatio = 1f, - onCursorChange = {}, - readOnly = true, - ) - } - // Handle - Box( - Modifier - .padding(8.dp) - .background( - MaterialTheme.colorScheme.onSurfaceVariant, - RoundedCornerShape(100) - ) - .sizeIn(24.dp, 4.dp) - ) - } - - // Keyboard - CalculatorKeyboardLoading( - modifier = Modifier - .semantics { testTag = "loading" } - .offset(y = maxHeight * 0.25f) - .height(maxHeight * 0.75f) - .fillMaxWidth() - .padding(horizontal = 8.dp, vertical = 4.dp), - ) - } - } -} - @Preview(widthDp = 432, heightDp = 1008, device = "spec:parent=pixel_5,orientation=portrait") @Preview(widthDp = 432, heightDp = 864, device = "spec:parent=pixel_5,orientation=portrait") @Preview(widthDp = 597, heightDp = 1393, device = "spec:parent=pixel_5,orientation=portrait") diff --git a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorViewModel.kt b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorViewModel.kt index 468f25b4..e4816934 100644 --- a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorViewModel.kt +++ b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorViewModel.kt @@ -22,6 +22,8 @@ import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.sadellie.unitto.core.base.OutputFormat +import com.sadellie.unitto.core.base.Separator import com.sadellie.unitto.core.base.Token import com.sadellie.unitto.core.ui.common.textfield.AllFormatterSymbols import com.sadellie.unitto.core.ui.common.textfield.addTokens @@ -58,7 +60,15 @@ internal class CalculatorViewModel @Inject constructor( userPrefsRepository.calculatorPrefs.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5000L), - CalculatorPreferences() + CalculatorPreferences( + radianMode = false, + enableVibrations = false, + separator = Separator.SPACE, + middleZero = false, + partialHistoryView = true, + precision = 3, + outputFormat = OutputFormat.PLAIN + ) ) private val _input: MutableStateFlow = MutableStateFlow(TextFieldValue()) @@ -79,7 +89,8 @@ internal class CalculatorViewModel @Inject constructor( middleZero = userPrefs.middleZero, partialHistoryView = userPrefs.partialHistoryView, ) - }.stateIn( + } + .stateIn( viewModelScope, SharingStarted.WhileSubscribed(5000L), CalculatorUIState.Loading ) diff --git a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/components/CalculatorKeyboard.kt b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/components/CalculatorKeyboard.kt index b94265da..5651c7ea 100644 --- a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/components/CalculatorKeyboard.kt +++ b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/components/CalculatorKeyboard.kt @@ -24,7 +24,6 @@ import androidx.compose.animation.Crossfade import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -35,7 +34,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material3.Icon @@ -50,11 +48,9 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import com.sadellie.unitto.core.base.Token import com.sadellie.unitto.core.ui.common.ColumnWithConstraints import com.sadellie.unitto.core.ui.common.KeyboardButtonAdditional @@ -145,118 +141,6 @@ internal fun CalculatorKeyboard( } } -@Composable -internal fun CalculatorKeyboardLoading( - modifier: Modifier -) { - if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT) { - PortraitKeyboardLoading(modifier) - } else { - LandscapeKeyboardLoading(modifier) - } -} - -@Composable -private fun PortraitKeyboardLoading( - modifier: Modifier -) { - ColumnWithConstraints( - modifier = modifier - ) { constraints -> - - val additionalButtonHeight by remember { - mutableStateOf(constraints.maxHeight * 0.09f) - } - - val spacerHeight by remember { - mutableStateOf(constraints.maxHeight * 0.025f) - } - - val additionalRowSpacedBy by remember { - mutableStateOf(constraints.maxWidth * 0.03f) - } - - val weightModifier = Modifier.weight(1f) - val additionalButtonModifier = Modifier - .weight(1f) - .height(additionalButtonHeight) - - Spacer(modifier = Modifier.height(spacerHeight)) - - Row( - modifier = Modifier, - horizontalArrangement = Arrangement.spacedBy(additionalRowSpacedBy) - ) { - // Additional buttons - Box(weightModifier) { - AdditionalButtonsPortrait( - modifier = additionalButtonModifier, - allowVibration = false, - addSymbol = {}, - showAdditional = false, - radianMode = false, - toggleAngleMode = {}, - toggleInvMode = {} - ) - } - - Box( - modifier = Modifier.size(additionalButtonHeight), - contentAlignment = Alignment.Center - ) { - // Expand/Collapse - IconButton( - onClick = { }, - colors = IconButtonDefaults.iconButtonColors(containerColor = MaterialTheme.colorScheme.inverseOnSurface) - ) { - Icon(Icons.Default.ExpandMore, null) - } - } - } - - Spacer(modifier = Modifier.height(spacerHeight)) - - Box( - modifier = weightModifier - .clip(RoundedCornerShape(32.dp)) - .background(MaterialTheme.colorScheme.inverseOnSurface) - .fillMaxSize() - ) - - Spacer(modifier = Modifier.height(spacerHeight)) - } -} - -@Composable -private fun LandscapeKeyboardLoading( - modifier: Modifier -) { - RowWithConstraints(modifier) { constraints -> - val buttonModifier = Modifier - .fillMaxWidth() - .weight(1f) - .padding(constraints.maxWidth * 0.005f, constraints.maxHeight * 0.02f) - - AdditionalButtonsLandscape( - modifier = Modifier.weight(1f), - buttonModifier = buttonModifier, - allowVibration = false, - radianMode = false, - addSymbol = {}, - toggleAngleMode = {}, - toggleInvMode = {} - ) - - Box( - modifier = Modifier - .clip(RoundedCornerShape(32.dp)) - .background(MaterialTheme.colorScheme.inverseOnSurface) - .weight(5f) - .fillMaxSize() - ) - } -} - @Composable private fun PortraitKeyboard( modifier: Modifier, diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterScreen.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterScreen.kt index d3263145..0900b721 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterScreen.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterScreen.kt @@ -31,7 +31,7 @@ import androidx.compose.animation.fadeOut import androidx.compose.animation.shrinkVertically import androidx.compose.animation.togetherWith import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -64,7 +64,6 @@ import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.sadellie.unitto.core.base.OutputFormat @@ -74,6 +73,7 @@ import com.sadellie.unitto.core.ui.common.ColumnWithConstraints import com.sadellie.unitto.core.ui.common.MenuButton import com.sadellie.unitto.core.ui.common.PortraitLandscape import com.sadellie.unitto.core.ui.common.SettingsButton +import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen import com.sadellie.unitto.core.ui.common.UnittoScreenWithTopBar import com.sadellie.unitto.core.ui.common.textfield.ExpressionTextField import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols @@ -124,6 +124,56 @@ private fun ConverterScreen( clearInput: () -> Unit, onCursorChange: (TextRange) -> Unit, onErrorClick: (AbstractUnit) -> Unit, +) { + when (uiState) { + UnitConverterUIState.Loading -> UnittoEmptyScreen() + + is UnitConverterUIState.NumberBase -> { + UnitConverterTopBar( + navigateToMenu = navigateToMenu, + navigateToSettings = navigateToSettings + ) { + NumberBase( + modifier = Modifier.padding(it), + uiState = uiState, + onCursorChange = onCursorChange, + processInput = processInput, + deleteDigit = deleteDigit, + navigateToLeftScreen = navigateToLeftScreen, + swapUnits = swapUnits, + navigateToRightScreen = navigateToRightScreen, + clearInput = clearInput + ) + } + } + + is UnitConverterUIState.Default -> { + UnitConverterTopBar( + navigateToMenu = navigateToMenu, + navigateToSettings = navigateToSettings + ) { + Default( + modifier = Modifier.padding(it), + uiState = uiState, + onCursorChange = onCursorChange, + processInput = processInput, + deleteDigit = deleteDigit, + navigateToLeftScreen = navigateToLeftScreen, + swapUnits = swapUnits, + navigateToRightScreen = navigateToRightScreen, + clearInput = clearInput, + refreshCurrencyRates = onErrorClick + ) + } + } + } +} + +@Composable +private fun UnitConverterTopBar( + navigateToMenu: () -> Unit, + navigateToSettings: () -> Unit, + content: @Composable (PaddingValues) -> Unit ) { UnittoScreenWithTopBar( title = { Text(stringResource(R.string.unit_converter_title)) }, @@ -131,83 +181,8 @@ private fun ConverterScreen( actions = { SettingsButton(navigateToSettings) }, - colors = TopAppBarDefaults - .centerAlignedTopAppBarColors(containerColor = Color.Transparent), - content = { padding -> - when (uiState) { - is UnitConverterUIState.Loading -> { - ConverterLoading(modifier = Modifier.padding(padding)) - } - - is UnitConverterUIState.NumberBase -> { - NumberBase( - modifier = Modifier.padding(padding), - uiState = uiState, - onCursorChange = onCursorChange, - processInput = processInput, - deleteDigit = deleteDigit, - navigateToLeftScreen = navigateToLeftScreen, - swapUnits = swapUnits, - navigateToRightScreen = navigateToRightScreen, - clearInput = clearInput - ) - } - - is UnitConverterUIState.Default -> { - Default( - modifier = Modifier.padding(padding), - uiState = uiState, - onCursorChange = onCursorChange, - processInput = processInput, - deleteDigit = deleteDigit, - navigateToLeftScreen = navigateToLeftScreen, - swapUnits = swapUnits, - navigateToRightScreen = navigateToRightScreen, - clearInput = clearInput, - refreshCurrencyRates = onErrorClick - ) - } - } - } - ) -} - -@Composable -private fun ConverterLoading(modifier: Modifier) { - PortraitLandscape( - modifier = modifier.fillMaxSize(), - content1 = { contentModifier -> - ColumnWithConstraints(modifier = contentModifier) { - val textFieldModifier = Modifier.weight(2f) - - UnformattedTextField( - modifier = textFieldModifier, - value = TextFieldValue(stringResource(R.string.loading_label)), - onCursorChange = {}, - minRatio = 0.7f, - readOnly = true - ) - AnimatedUnitShortName() - - ConverterResultTextField( - modifier = textFieldModifier, - result = ConverterResult.Loading - ) - AnimatedUnitShortName() - - Spacer(modifier = Modifier.height(it.maxHeight * 0.03f)) - - UnitSelectionButtons() - } - }, - content2 = { - Box( - modifier = it - .clip(RoundedCornerShape(32.dp)) - .background(MaterialTheme.colorScheme.inverseOnSurface) - .fillMaxSize() - ) - } + colors = TopAppBarDefaults.centerAlignedTopAppBarColors(containerColor = Color.Transparent), + content = { content(it) } ) } diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideScreen.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideScreen.kt index f3292274..76460b92 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideScreen.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideScreen.kt @@ -23,7 +23,6 @@ import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.LinearOutSlowInEasing import androidx.compose.animation.core.tween import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding @@ -48,6 +47,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.sadellie.unitto.core.base.R +import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen import com.sadellie.unitto.core.ui.common.UnittoSearchBar import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.UnitsListSorting @@ -71,9 +71,7 @@ internal fun LeftSideRoute( when ( val uiState = viewModel.leftSideUIState.collectAsStateWithLifecycle().value ) { - is LeftSideUIState.Loading -> { - Box(modifier = Modifier.fillMaxSize()) - } + is LeftSideUIState.Loading -> UnittoEmptyScreen() is LeftSideUIState.Ready -> LeftSideScreen( uiState = uiState, onQueryChange = viewModel::queryChangeLeft, diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/RightSideScreen.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/RightSideScreen.kt index 34cdd10e..842781ab 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/RightSideScreen.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/RightSideScreen.kt @@ -19,7 +19,6 @@ package com.sadellie.unitto.feature.converter import androidx.compose.animation.Crossfade -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -36,6 +35,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.sadellie.unitto.core.base.OutputFormat import com.sadellie.unitto.core.base.R +import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen import com.sadellie.unitto.core.ui.common.UnittoSearchBar import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.core.ui.common.textfield.formatExpression @@ -62,7 +62,7 @@ internal fun RightSideRoute( when ( val uiState = viewModel.rightSideUIState.collectAsStateWithLifecycle().value ) { - is RightSideUIState.Loading -> Box(Modifier.fillMaxSize()) + is RightSideUIState.Loading -> UnittoEmptyScreen() is RightSideUIState.Ready -> RightSideScreen( uiState = uiState, diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/SettingsScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/SettingsScreen.kt index f0372ffa..52d9cc05 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/SettingsScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/SettingsScreen.kt @@ -54,6 +54,7 @@ import com.sadellie.unitto.core.base.BuildConfig import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.ui.common.Header import com.sadellie.unitto.core.ui.common.NavigateUpButton +import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen import com.sadellie.unitto.core.ui.common.UnittoListItem import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar import com.sadellie.unitto.core.ui.openLink @@ -72,17 +73,22 @@ internal fun SettingsRoute( navigateUp: () -> Unit, navControllerAction: (String) -> Unit, ) { - val userPrefs = viewModel.userPrefs.collectAsStateWithLifecycle() + val userPrefs = viewModel.userPrefs.collectAsStateWithLifecycle().value val cachePercentage = viewModel.cachePercentage.collectAsStateWithLifecycle() - SettingsScreen( - userPrefs = userPrefs.value, - navigateUp = navigateUp, - navControllerAction = navControllerAction, - updateVibrations = viewModel::updateVibrations, - cachePercentage = cachePercentage.value, - clearCache = viewModel::clearCache, - ) + when (userPrefs) { + null -> UnittoEmptyScreen() + else -> { + SettingsScreen( + userPrefs = userPrefs, + navigateUp = navigateUp, + navControllerAction = navControllerAction, + updateVibrations = viewModel::updateVibrations, + cachePercentage = cachePercentage.value, + clearCache = viewModel::clearCache, + ) + } + } } @Composable @@ -197,7 +203,9 @@ private fun PreviewSettingsScreen() { var cacheSize by remember { mutableFloatStateOf(0.9f) } SettingsScreen( - userPrefs = GeneralPreferences(), + userPrefs = GeneralPreferences( + enableVibrations = true + ), navigateUp = {}, navControllerAction = {}, updateVibrations = {}, diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/SettingsViewModel.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/SettingsViewModel.kt index a8f7c389..8071438a 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/SettingsViewModel.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/SettingsViewModel.kt @@ -22,7 +22,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.sadellie.unitto.data.common.stateIn import com.sadellie.unitto.data.database.CurrencyRatesDao -import com.sadellie.unitto.data.userprefs.GeneralPreferences import com.sadellie.unitto.data.userprefs.UserPreferencesRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers @@ -36,7 +35,7 @@ internal class SettingsViewModel @Inject constructor( private val currencyRatesDao: CurrencyRatesDao, ) : ViewModel() { val userPrefs = userPrefsRepository.generalPrefs - .stateIn(viewModelScope, GeneralPreferences()) + .stateIn(viewModelScope, null) val cachePercentage = currencyRatesDao.size() .map { diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/about/AboutScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/about/AboutScreen.kt index 75d64825..2d693864 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/about/AboutScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/about/AboutScreen.kt @@ -47,6 +47,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.sadellie.unitto.core.base.BuildConfig import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.ui.common.NavigateUpButton +import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen import com.sadellie.unitto.core.ui.common.UnittoListItem import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar import com.sadellie.unitto.core.ui.openLink @@ -59,14 +60,17 @@ internal fun AboutRoute( navigateUpAction: () -> Unit, navigateToThirdParty: () -> Unit, ) { - val prefs = viewModel.prefs.collectAsStateWithLifecycle() - - AboutScreen( - prefs = prefs.value, - navigateUpAction = navigateUpAction, - navigateToThirdParty = navigateToThirdParty, - enableToolsExperiment = viewModel::enableToolsExperiment - ) + when (val prefs = viewModel.prefs.collectAsStateWithLifecycle().value) { + null -> UnittoEmptyScreen() + else -> { + AboutScreen( + prefs = prefs, + navigateUpAction = navigateUpAction, + navigateToThirdParty = navigateToThirdParty, + enableToolsExperiment = viewModel::enableToolsExperiment + ) + } + } } @Composable @@ -196,7 +200,9 @@ private fun AboutScreen( @Composable fun PreviewAboutScreen() { AboutScreen( - prefs = AboutPreferences(), + prefs = AboutPreferences( + enableToolsExperiment = false + ), navigateUpAction = {}, navigateToThirdParty = {}, enableToolsExperiment = {} diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/about/AboutViewModel.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/about/AboutViewModel.kt index 60bc004d..17a2e0d7 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/about/AboutViewModel.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/about/AboutViewModel.kt @@ -21,7 +21,6 @@ package com.sadellie.unitto.feature.settings.about import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.sadellie.unitto.data.common.stateIn -import com.sadellie.unitto.data.userprefs.AboutPreferences import com.sadellie.unitto.data.userprefs.UserPreferencesRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch @@ -32,7 +31,7 @@ internal class AboutViewModel @Inject constructor( private val userPrefsRepository: UserPreferencesRepository, ) : ViewModel() { val prefs = userPrefsRepository.aboutPrefs - .stateIn(viewModelScope, AboutPreferences()) + .stateIn(viewModelScope, null) fun enableToolsExperiment() = viewModelScope.launch { userPrefsRepository.updateToolsExperiment(true) diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/calculator/CalculatorSettingsScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/calculator/CalculatorSettingsScreen.kt index 355a8bd4..5fcaf898 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/calculator/CalculatorSettingsScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/calculator/CalculatorSettingsScreen.kt @@ -26,8 +26,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.sadellie.unitto.core.base.OutputFormat import com.sadellie.unitto.core.base.R +import com.sadellie.unitto.core.base.Separator import com.sadellie.unitto.core.ui.common.NavigateUpButton +import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen import com.sadellie.unitto.core.ui.common.UnittoListItem import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar import com.sadellie.unitto.data.userprefs.CalculatorPreferences @@ -37,13 +40,16 @@ internal fun CalculatorSettingsRoute( viewModel: CalculatorViewModel = hiltViewModel(), navigateUpAction: () -> Unit, ) { - val prefs = viewModel.prefs.collectAsStateWithLifecycle() - - CalculatorSettingsScreen( - prefs = prefs.value, - navigateUpAction = navigateUpAction, - updatePartialHistoryView = viewModel::updatePartialHistoryView - ) + when (val prefs = viewModel.prefs.collectAsStateWithLifecycle().value) { + null -> UnittoEmptyScreen() + else -> { + CalculatorSettingsScreen( + prefs = prefs, + navigateUpAction = navigateUpAction, + updatePartialHistoryView = viewModel::updatePartialHistoryView + ) + } + } } @Composable @@ -75,7 +81,15 @@ private fun CalculatorSettingsScreen( @Composable private fun PreviewCalculatorSettingsScreen() { CalculatorSettingsScreen( - prefs = CalculatorPreferences(), + prefs = CalculatorPreferences( + radianMode = false, + enableVibrations = false, + separator = Separator.SPACE, + middleZero = false, + partialHistoryView = true, + precision = 3, + outputFormat = OutputFormat.PLAIN + ), navigateUpAction = {}, updatePartialHistoryView = {} ) diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/calculator/CalculatorViewModel.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/calculator/CalculatorViewModel.kt index 550cb750..92dce3dc 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/calculator/CalculatorViewModel.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/calculator/CalculatorViewModel.kt @@ -21,7 +21,6 @@ package com.sadellie.unitto.feature.settings.calculator import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.sadellie.unitto.data.common.stateIn -import com.sadellie.unitto.data.userprefs.CalculatorPreferences import com.sadellie.unitto.data.userprefs.UserPreferencesRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch @@ -32,7 +31,7 @@ class CalculatorViewModel @Inject constructor( private val userPrefsRepository: UserPreferencesRepository, ) : ViewModel() { val prefs = userPrefsRepository.calculatorPrefs - .stateIn(viewModelScope, CalculatorPreferences()) + .stateIn(viewModelScope, null) fun updatePartialHistoryView(enabled: Boolean) = viewModelScope.launch { userPrefsRepository.updatePartialHistoryView(enabled) diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/converter/ConverterSettingsScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/converter/ConverterSettingsScreen.kt index 224ceef4..61b5b4bb 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/converter/ConverterSettingsScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/converter/ConverterSettingsScreen.kt @@ -34,10 +34,14 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.sadellie.unitto.core.base.OutputFormat import com.sadellie.unitto.core.base.R +import com.sadellie.unitto.core.base.Separator import com.sadellie.unitto.core.ui.common.NavigateUpButton +import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen import com.sadellie.unitto.core.ui.common.UnittoListItem import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar +import com.sadellie.unitto.data.model.ALL_UNIT_GROUPS import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.userprefs.ConverterPreferences import com.sadellie.unitto.feature.settings.components.AlertDialogWithList @@ -48,15 +52,18 @@ internal fun ConverterSettingsRoute( navigateUpAction: () -> Unit, navigateToUnitsGroup: () -> Unit, ) { - val prefs = viewModel.prefs.collectAsStateWithLifecycle() - - ConverterSettingsScreen( - prefs = prefs.value, - navigateUpAction = navigateUpAction, - navigateToUnitsGroup = navigateToUnitsGroup, - updateUnitConverterFormatTime = viewModel::updateUnitConverterFormatTime, - updateUnitConverterSorting = viewModel::updateUnitConverterSorting - ) + when (val prefs = viewModel.prefs.collectAsStateWithLifecycle().value) { + null -> UnittoEmptyScreen() + else -> { + ConverterSettingsScreen( + prefs = prefs, + navigateUpAction = navigateUpAction, + navigateToUnitsGroup = navigateToUnitsGroup, + updateUnitConverterFormatTime = viewModel::updateUnitConverterFormatTime, + updateUnitConverterSorting = viewModel::updateUnitConverterSorting + ) + } + } } @Composable @@ -127,7 +134,20 @@ private fun ConverterSettingsScreen( @Composable private fun PreviewConverterSettingsScreen() { ConverterSettingsScreen( - prefs = ConverterPreferences(), + prefs = ConverterPreferences( + enableVibrations = true, + separator = Separator.SPACE, + middleZero = false, + precision = 3, + outputFormat = OutputFormat.PLAIN, + unitConverterFormatTime = false, + unitConverterSorting = UnitsListSorting.USAGE, + shownUnitGroups = ALL_UNIT_GROUPS, + unitConverterFavoritesOnly = false, + enableToolsExperiment = false, + latestLeftSideUnit = "kilometer", + latestRightSideUnit = "mile", + ), navigateUpAction = {}, navigateToUnitsGroup = {}, updateUnitConverterFormatTime = {}, diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/converter/ConverterViewModel.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/converter/ConverterViewModel.kt index 993ac33b..4ae1a0de 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/converter/ConverterViewModel.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/converter/ConverterViewModel.kt @@ -22,7 +22,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.sadellie.unitto.data.common.stateIn import com.sadellie.unitto.data.model.UnitsListSorting -import com.sadellie.unitto.data.userprefs.ConverterPreferences import com.sadellie.unitto.data.userprefs.UserPreferencesRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch @@ -33,7 +32,7 @@ internal class ConverterViewModel @Inject constructor( private val userPrefsRepository: UserPreferencesRepository, ) : ViewModel() { val prefs = userPrefsRepository.converterPrefs - .stateIn(viewModelScope, ConverterPreferences()) + .stateIn(viewModelScope, null) fun updateUnitConverterFormatTime(enabled: Boolean) = viewModelScope.launch { userPrefsRepository.updateUnitConverterFormatTime(enabled) diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/display/DisplayScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/display/DisplayScreen.kt index b3eaf960..b6da7f26 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/display/DisplayScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/display/DisplayScreen.kt @@ -59,6 +59,7 @@ import com.sadellie.unitto.core.ui.common.Header import com.sadellie.unitto.core.ui.common.NavigateUpButton import com.sadellie.unitto.core.ui.common.SegmentedButton import com.sadellie.unitto.core.ui.common.SegmentedButtonsRow +import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen import com.sadellie.unitto.core.ui.common.UnittoListItem import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar import com.sadellie.unitto.feature.settings.components.ColorSelector @@ -75,46 +76,49 @@ internal fun DisplayRoute( themmoController: ThemmoController, navigateToLanguages: () -> Unit, ) { - val prefs = viewModel.prefs.collectAsStateWithLifecycle() - - DisplayScreen( - navigateUp = navigateUp, - currentThemingMode = themmoController.currentThemingMode, - onThemeChange = { - themmoController.setThemingMode(it) - viewModel.updateThemingMode(it) - }, - isDynamicThemeEnabled = themmoController.isDynamicThemeEnabled, - onDynamicThemeChange = { - // Prevent old devices from using other monet modes when dynamic theming is on - if (it) { - themmoController.setMonetMode(MonetMode.TonalSpot) - viewModel.updateMonetMode(MonetMode.TonalSpot) - } - themmoController.enableDynamicTheme(it) - viewModel.updateDynamicTheme(it) - }, - isAmoledThemeEnabled = themmoController.isAmoledThemeEnabled, - onAmoledThemeChange = { - themmoController.enableAmoledTheme(it) - viewModel.updateAmoledTheme(it) - }, - selectedColor = themmoController.currentCustomColor, - onColorChange = { - themmoController.setCustomColor(it) - viewModel.updateCustomColor(it) - }, - monetMode = themmoController.currentMonetMode, - onMonetModeChange = { - themmoController.setMonetMode(it) - viewModel.updateMonetMode(it) - }, - systemFont = prefs.value.systemFont, - updateSystemFont = viewModel::updateSystemFont, - middleZero = prefs.value.middleZero, - updateMiddleZero = viewModel::updateMiddleZero, - navigateToLanguages = navigateToLanguages - ) + when (val prefs = viewModel.prefs.collectAsStateWithLifecycle().value) { + null -> UnittoEmptyScreen() + else -> { + DisplayScreen( + navigateUp = navigateUp, + currentThemingMode = themmoController.currentThemingMode, + onThemeChange = { newValue -> + themmoController.setThemingMode(newValue) + viewModel.updateThemingMode(newValue) + }, + isDynamicThemeEnabled = themmoController.isDynamicThemeEnabled, + onDynamicThemeChange = { newValue -> + // Prevent old devices from using other monet modes when dynamic theming is on + if (newValue) { + themmoController.setMonetMode(MonetMode.TonalSpot) + viewModel.updateMonetMode(MonetMode.TonalSpot) + } + themmoController.enableDynamicTheme(newValue) + viewModel.updateDynamicTheme(newValue) + }, + isAmoledThemeEnabled = themmoController.isAmoledThemeEnabled, + onAmoledThemeChange = { newValue -> + themmoController.enableAmoledTheme(newValue) + viewModel.updateAmoledTheme(newValue) + }, + selectedColor = themmoController.currentCustomColor, + onColorChange = { newValue -> + themmoController.setCustomColor(newValue) + viewModel.updateCustomColor(newValue) + }, + monetMode = themmoController.currentMonetMode, + onMonetModeChange = { newValue -> + themmoController.setMonetMode(newValue) + viewModel.updateMonetMode(newValue) + }, + systemFont = prefs.systemFont, + updateSystemFont = viewModel::updateSystemFont, + middleZero = prefs.middleZero, + updateMiddleZero = viewModel::updateMiddleZero, + navigateToLanguages = navigateToLanguages + ) + } + } } @Composable diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/display/DisplayViewModel.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/display/DisplayViewModel.kt index 1b0d8fb0..21e9a5d7 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/display/DisplayViewModel.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/display/DisplayViewModel.kt @@ -22,7 +22,6 @@ import androidx.compose.ui.graphics.Color import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.sadellie.unitto.data.common.stateIn -import com.sadellie.unitto.data.userprefs.DisplayPreferences import com.sadellie.unitto.data.userprefs.UserPreferencesRepository import dagger.hilt.android.lifecycle.HiltViewModel import io.github.sadellie.themmo.MonetMode @@ -36,7 +35,7 @@ class DisplayViewModel @Inject constructor( ) : ViewModel() { val prefs = userPrefsRepository.displayPrefs - .stateIn(viewModelScope, DisplayPreferences()) + .stateIn(viewModelScope, null) /** * @see UserPreferencesRepository.updateThemingMode diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingScreen.kt index c236c3fc..faf1cf61 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingScreen.kt @@ -57,6 +57,7 @@ import com.sadellie.unitto.core.base.Separator import com.sadellie.unitto.core.ui.common.NavigateUpButton import com.sadellie.unitto.core.ui.common.SegmentedButton import com.sadellie.unitto.core.ui.common.SegmentedButtonsRow +import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen import com.sadellie.unitto.core.ui.common.UnittoListItem import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar import com.sadellie.unitto.core.ui.common.UnittoSlider @@ -70,16 +71,19 @@ fun FormattingRoute( viewModel: FormattingViewModel = hiltViewModel(), navigateUpAction: () -> Unit, ) { - val uiState = viewModel.uiState.collectAsStateWithLifecycle() - - FormattingScreen( - navigateUpAction = navigateUpAction, - uiState = uiState.value, - onPrecisionChange = viewModel::updatePrecision, - onSeparatorChange = viewModel::updateSeparator, - onOutputFormatChange = viewModel::updateOutputFormat, - togglePreview = viewModel::togglePreview - ) + when (val uiState = viewModel.uiState.collectAsStateWithLifecycle().value) { + null -> UnittoEmptyScreen() + else -> { + FormattingScreen( + navigateUpAction = navigateUpAction, + uiState = uiState, + onPrecisionChange = viewModel::updatePrecision, + onSeparatorChange = viewModel::updateSeparator, + onOutputFormatChange = viewModel::updateOutputFormat, + togglePreview = viewModel::togglePreview + ) + } + } } @Composable diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingViewModel.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingViewModel.kt index a65e46d2..42c88b77 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingViewModel.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/formatting/FormattingViewModel.kt @@ -61,7 +61,7 @@ class FormattingViewModel @Inject constructor( formatterSymbols = formatterSymbols ) } - .stateIn(viewModelScope, FormattingUIState()) + .stateIn(viewModelScope, null) fun togglePreview() = _fractional.update { !it } diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/navigation/SettingsNavigation.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/navigation/SettingsNavigation.kt index 26a1cb4c..4d3d702d 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/navigation/SettingsNavigation.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/navigation/SettingsNavigation.kt @@ -23,8 +23,8 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.navDeepLink import com.sadellie.unitto.core.base.TopLevelDestinations -import com.sadellie.unitto.core.ui.unittoComposable import com.sadellie.unitto.core.ui.unittoNavigation +import com.sadellie.unitto.core.ui.unittoStackedComposable import com.sadellie.unitto.feature.settings.SettingsRoute import com.sadellie.unitto.feature.settings.about.AboutRoute import com.sadellie.unitto.feature.settings.calculator.CalculatorSettingsRoute @@ -68,14 +68,14 @@ fun NavGraphBuilder.settingGraph( navDeepLink { uriPattern = "app://com.sadellie.unitto/$graph" } ) ) { - unittoComposable(start) { + unittoStackedComposable(start) { SettingsRoute( navigateUp = navController::navigateUp, navControllerAction = navController::navigate ) } - unittoComposable(displayRoute) { + unittoStackedComposable(displayRoute) { DisplayRoute( navigateUp = navController::navigateUp, themmoController = themmoController, @@ -83,51 +83,51 @@ fun NavGraphBuilder.settingGraph( ) } - unittoComposable(languageRoute) { + unittoStackedComposable(languageRoute) { LanguageRoute( navigateUp = navController::navigateUp, ) } - unittoComposable(startingScreenRoute) { + unittoStackedComposable(startingScreenRoute) { StartingScreenRoute( navigateUp = navController::navigateUp, ) } - unittoComposable(formattingRoute) { + unittoStackedComposable(formattingRoute) { FormattingRoute( navigateUpAction = navController::navigateUp ) } - unittoComposable(calculatorSettingsRoute) { + unittoStackedComposable(calculatorSettingsRoute) { CalculatorSettingsRoute( navigateUpAction = navController::navigateUp, ) } - unittoComposable(converterSettingsRoute) { + unittoStackedComposable(converterSettingsRoute) { ConverterSettingsRoute( navigateUpAction = navController::navigateUp, navigateToUnitsGroup = { navController.navigate(unitsGroupRoute) } ) } - unittoComposable(unitsGroupRoute) { + unittoStackedComposable(unitsGroupRoute) { UnitGroupsScreen( navigateUpAction = navController::navigateUp, ) } - unittoComposable(aboutRoute) { + unittoStackedComposable(aboutRoute) { AboutRoute( navigateUpAction = navController::navigateUp, navigateToThirdParty = { navController.navigate(thirdPartyRoute) } ) } - unittoComposable(thirdPartyRoute) { + unittoStackedComposable(thirdPartyRoute) { ThirdPartyLicensesScreen( navigateUpAction = navController::navigateUp, ) diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/startingscreen/StartingScreenScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/startingscreen/StartingScreenScreen.kt index 6a36c581..0dbb9b28 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/startingscreen/StartingScreenScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/startingscreen/StartingScreenScreen.kt @@ -40,6 +40,7 @@ import com.sadellie.unitto.core.base.TOP_LEVEL_DESTINATIONS import com.sadellie.unitto.core.base.TopLevelDestinations import com.sadellie.unitto.core.ui.addShortcut import com.sadellie.unitto.core.ui.common.NavigateUpButton +import com.sadellie.unitto.core.ui.common.UnittoEmptyScreen import com.sadellie.unitto.core.ui.common.UnittoListItem import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar @@ -48,13 +49,16 @@ internal fun StartingScreenRoute( viewModel: StartingScreenViewModel = hiltViewModel(), navigateUp: () -> Unit ) { - val prefs = viewModel.prefs.collectAsStateWithLifecycle() - - StartingScreenScreen( - startingScreen = prefs.value.startingScreen, - updateStartingScreen = viewModel::updateStartingScreen, - navigateUp = navigateUp - ) + when (val prefs = viewModel.prefs.collectAsStateWithLifecycle().value) { + null -> UnittoEmptyScreen() + else -> { + StartingScreenScreen( + startingScreen = prefs.startingScreen, + updateStartingScreen = viewModel::updateStartingScreen, + navigateUp = navigateUp + ) + } + } } @Composable diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/startingscreen/StartingScreenViewModel.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/startingscreen/StartingScreenViewModel.kt index 45b61526..879b8acf 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/startingscreen/StartingScreenViewModel.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/startingscreen/StartingScreenViewModel.kt @@ -21,7 +21,6 @@ package com.sadellie.unitto.feature.settings.startingscreen import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.sadellie.unitto.data.common.stateIn -import com.sadellie.unitto.data.userprefs.StartingScreenPreferences import com.sadellie.unitto.data.userprefs.UserPreferencesRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch @@ -32,7 +31,7 @@ internal class StartingScreenViewModel @Inject constructor( private val userPrefsRepository: UserPreferencesRepository, ) : ViewModel() { val prefs = userPrefsRepository.startingScreenPrefs - .stateIn(viewModelScope, StartingScreenPreferences()) + .stateIn(viewModelScope, null) fun updateStartingScreen(startingScreen: String) = viewModelScope.launch { userPrefsRepository.updateStartingScreen(startingScreen)