diff --git a/app/src/main/java/com/sadellie/unitto/MainActivity.kt b/app/src/main/java/com/sadellie/unitto/MainActivity.kt index bdb22819..f7935a0d 100644 --- a/app/src/main/java/com/sadellie/unitto/MainActivity.kt +++ b/app/src/main/java/com/sadellie/unitto/MainActivity.kt @@ -41,10 +41,10 @@ internal class MainActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContent { - val uiPrefs = userPrefsRepository.uiPreferencesFlow + val prefs = userPrefsRepository.appPrefs .collectAsStateWithLifecycle(null).value - if (uiPrefs != null) UnittoApp(uiPrefs) + if (prefs != null) 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 998d38a5..1e0556a6 100644 --- a/app/src/main/java/com/sadellie/unitto/UnittoApp.kt +++ b/app/src/main/java/com/sadellie/unitto/UnittoApp.kt @@ -46,28 +46,28 @@ import com.sadellie.unitto.core.ui.common.isOpen import com.sadellie.unitto.core.ui.common.open import com.sadellie.unitto.core.ui.common.rememberUnittoDrawerState import com.sadellie.unitto.core.ui.model.DrawerItems -import com.sadellie.unitto.core.ui.theme.AppTypographyUnitto import com.sadellie.unitto.core.ui.theme.AppTypographySystem +import com.sadellie.unitto.core.ui.theme.AppTypographyUnitto import com.sadellie.unitto.core.ui.theme.DarkThemeColors import com.sadellie.unitto.core.ui.theme.LightThemeColors -import com.sadellie.unitto.data.userprefs.UIPreferences +import com.sadellie.unitto.data.userprefs.AppPreferences import io.github.sadellie.themmo.Themmo import io.github.sadellie.themmo.rememberThemmoController import kotlinx.coroutines.launch @OptIn(ExperimentalFoundationApi::class) @Composable -internal fun UnittoApp(uiPrefs: UIPreferences) { +internal fun UnittoApp(prefs: AppPreferences) { val mContext = LocalContext.current val themmoController = rememberThemmoController( lightColorScheme = LightThemeColors, darkColorScheme = DarkThemeColors, - themingMode = uiPrefs.themingMode, - dynamicThemeEnabled = uiPrefs.enableDynamicTheme, - amoledThemeEnabled = uiPrefs.enableAmoledTheme, - customColor = uiPrefs.customColor, - monetMode = uiPrefs.monetMode + themingMode = prefs.themingMode, + dynamicThemeEnabled = prefs.enableDynamicTheme, + amoledThemeEnabled = prefs.enableAmoledTheme, + customColor = prefs.customColor, + monetMode = prefs.monetMode ) val navController = rememberNavController() val sysUiController = rememberSystemUiController() @@ -78,9 +78,9 @@ internal fun UnittoApp(uiPrefs: UIPreferences) { val shortcutsScope = rememberCoroutineScope() - val tabs by remember(uiPrefs.enableToolsExperiment) { + val tabs by remember(prefs.enableToolsExperiment) { derivedStateOf { - if (uiPrefs.enableToolsExperiment) { + if (prefs.enableToolsExperiment) { listOf( DrawerItems.Calculator, DrawerItems.Converter, @@ -106,7 +106,7 @@ internal fun UnittoApp(uiPrefs: UIPreferences) { Themmo( themmoController = themmoController, - typography = if (uiPrefs.systemFont) AppTypographySystem else AppTypographyUnitto, + typography = if (prefs.systemFont) AppTypographySystem else AppTypographyUnitto, animationSpec = tween(250) ) { val backgroundColor = MaterialTheme.colorScheme.background @@ -151,7 +151,7 @@ internal fun UnittoApp(uiPrefs: UIPreferences) { UnittoNavigation( navController = navController, themmoController = it, - startDestination = uiPrefs.startingScreen, + startDestination = prefs.startingScreen, openDrawer = { drawerScope.launch { drawerState.open() } } ) } diff --git a/data/common/build.gradle.kts b/data/common/build.gradle.kts index 6c1f9cf4..196b31af 100644 --- a/data/common/build.gradle.kts +++ b/data/common/build.gradle.kts @@ -25,6 +25,8 @@ android { } dependencies { + implementation(libs.androidx.core) + implementation(libs.androidx.lifecycle.runtime.compose) implementation(project(mapOf("path" to ":core:base"))) testImplementation(libs.junit) } \ No newline at end of file diff --git a/data/units/src/main/java/com/sadellie/unitto/data/units/FlowUtils.kt b/data/common/src/main/java/com/sadellie/unitto/data/common/FlowUtils.kt similarity index 98% rename from data/units/src/main/java/com/sadellie/unitto/data/units/FlowUtils.kt rename to data/common/src/main/java/com/sadellie/unitto/data/common/FlowUtils.kt index cb8ea0a6..c668de26 100644 --- a/data/units/src/main/java/com/sadellie/unitto/data/units/FlowUtils.kt +++ b/data/common/src/main/java/com/sadellie/unitto/data/common/FlowUtils.kt @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package com.sadellie.unitto.data.units +package com.sadellie.unitto.data.common import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow 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 1f202d61..7d70a9f6 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 @@ -39,255 +39,222 @@ import io.github.sadellie.themmo.MonetMode import io.github.sadellie.themmo.ThemingMode import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import java.io.IOException import javax.inject.Inject -/** - * Represents user preferences that are user across the app - * - * @property themingMode [ThemingMode] from Themmo. - * @property enableDynamicTheme Use dynamic color scheme - * @property enableAmoledTheme Use amoled color scheme - * @property customColor Generate custom color scheme from this color. - * @property digitsPrecision Current [PRECISIONS]. Number of digits in fractional part - * @property separator Current [Separator] that used to separate thousands - * @property outputFormat Current [OutputFormat] that is applied to converted value (not input) - * @property latestLeftSideUnit Latest [AbstractUnit] that was on the left side - * @property latestRightSideUnit Latest [AbstractUnit] that was on the right side - * @property shownUnitGroups [UnitGroup]s that user wants to see. Excludes other [UnitGroup]s, - * @property enableVibrations When true will use haptic feedback in app. - * @property enableToolsExperiment When true will enable experimental Tools screen. - * @property radianMode AngleMode in mxParser. When true - Radian, when False - Degree. - * @property unitConverterFavoritesOnly If true will show only units that are marked as favorite. - * @property unitConverterFormatTime If true will format time to be more human readable. - * @property unitConverterSorting Units list sorting mode. - */ -data class UserPreferences( - val themingMode: ThemingMode = ThemingMode.AUTO, - val enableDynamicTheme: Boolean = true, - val enableAmoledTheme: Boolean = false, - val customColor: Color = Color.Unspecified, - val monetMode: MonetMode = MonetMode.TONAL_SPOT, - val digitsPrecision: Int = 3, - val separator: Int = Separator.SPACE, - val outputFormat: Int = OutputFormat.PLAIN, - val latestLeftSideUnit: String = MyUnitIDS.kilometer, - val latestRightSideUnit: String = MyUnitIDS.mile, - val shownUnitGroups: List = ALL_UNIT_GROUPS, - val enableVibrations: Boolean = true, - val enableToolsExperiment: Boolean = false, - val startingScreen: String = TopLevelDestinations.Calculator.graph, - val radianMode: Boolean = true, - val unitConverterFavoritesOnly: Boolean = false, - val unitConverterFormatTime: Boolean = false, - val unitConverterSorting: UnitsListSorting = UnitsListSorting.USAGE, - val middleZero: Boolean = false, - val systemFont: Boolean = false, - val partialHistoryView: Boolean = true, -) +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 UIPreferences( +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.TONAL_SPOT, + val monetMode: MonetMode = MonetMode.values().first(), val startingScreen: String = TopLevelDestinations.Calculator.graph, val enableToolsExperiment: Boolean = false, val systemFont: Boolean = false, ) -data class MainPreferences( +data class GeneralPreferences( + val startingScreen: String = TopLevelDestinations.Calculator.graph, + val enableVibrations: Boolean = true, + val middleZero: Boolean = false, +) + +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 ThemePreferences( + val systemFont: Boolean = false, +) + +data class FormattingPreferences( val digitsPrecision: Int = 3, val separator: Int = Separator.SPACE, val outputFormat: Int = OutputFormat.PLAIN, - val latestLeftSideUnit: String = MyUnitIDS.kilometer, - val latestRightSideUnit: String = MyUnitIDS.mile, - val shownUnitGroups: List = ALL_UNIT_GROUPS, - val enableVibrations: Boolean = true, - val radianMode: Boolean = true, - val unitConverterFavoritesOnly: Boolean = false, - val unitConverterFormatTime: Boolean = false, - val unitConverterSorting: UnitsListSorting = UnitsListSorting.USAGE, - val middleZero: Boolean = false, - val enableToolsExperiment: Boolean = false, - val partialHistoryView: Boolean = true, ) -/** - * Repository that works with DataStore - */ +data class UnitGroupsPreferences( + val shownUnitGroups: List = ALL_UNIT_GROUPS, +) + +data class AddSubtractPreferences( + val separator: Int = Separator.SPACE, +) + +data class AboutPreferences( + val enableToolsExperiment: Boolean = false, +) + class UserPreferencesRepository @Inject constructor(private val dataStore: DataStore) { - /** - * Keys for DataStore - */ - 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") - } + private val data = dataStore.data + .catch { if (it is IOException) emit(emptyPreferences()) else throw it } - val uiPreferencesFlow: Flow = dataStore.data - .catch { exception -> - if (exception is IOException) { - emit(emptyPreferences()) - } else { - throw exception - } - } + val appPrefs: Flow = data .map { preferences -> - val themingMode: ThemingMode = preferences[PrefsKeys.THEMING_MODE]?.let { ThemingMode.valueOf(it) } - ?: ThemingMode.AUTO - val enableDynamicTheme: Boolean = preferences[PrefsKeys.ENABLE_DYNAMIC_THEME] ?: true - val enableAmoledTheme: Boolean = preferences[PrefsKeys.ENABLE_AMOLED_THEME] ?: false - val customColor: Color = preferences[PrefsKeys.CUSTOM_COLOR]?.let { Color(it.toULong()) } ?: Color.Unspecified - val monetMode: MonetMode = preferences[PrefsKeys.MONET_MODE]?.let { MonetMode.valueOf(it) } - ?: MonetMode.TONAL_SPOT - val startingScreen: String = preferences[PrefsKeys.STARTING_SCREEN] ?: TopLevelDestinations.Calculator.graph - val enableToolsExperiment: Boolean = preferences[PrefsKeys.ENABLE_TOOLS_EXPERIMENT] ?: false - val systemFont: Boolean = preferences[PrefsKeys.SYSTEM_FONT] ?: false - - UIPreferences( - themingMode = themingMode, - enableDynamicTheme = enableDynamicTheme, - enableAmoledTheme = enableAmoledTheme, - customColor = customColor, - monetMode = monetMode, - startingScreen = startingScreen, - enableToolsExperiment = enableToolsExperiment, - systemFont = systemFont + 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.values().first(), + startingScreen = preferences[PrefsKeys.STARTING_SCREEN] + ?: TopLevelDestinations.Calculator.graph, + enableToolsExperiment = preferences[PrefsKeys.ENABLE_TOOLS_EXPERIMENT] ?: false, + systemFont = preferences[PrefsKeys.SYSTEM_FONT] ?: false ) } - val mainPrefsFlow: Flow = dataStore.data - .catch { exception -> - if (exception is IOException) { - emit(emptyPreferences()) - } else { - throw exception - } - } + val generalPrefs: Flow = data .map { preferences -> - val digitsPrecision: Int = preferences[PrefsKeys.DIGITS_PRECISION] ?: 3 - val separator: Int = preferences[PrefsKeys.SEPARATOR] ?: Separator.SPACE - val outputFormat: Int = preferences[PrefsKeys.OUTPUT_FORMAT] ?: OutputFormat.PLAIN - val latestLeftSideUnit: String = preferences[PrefsKeys.LATEST_LEFT_SIDE] ?: MyUnitIDS.kilometer - val latestRightSideUnit: String = preferences[PrefsKeys.LATEST_RIGHT_SIDE] ?: MyUnitIDS.mile - val shownUnitGroups: List = - preferences[PrefsKeys.SHOWN_UNIT_GROUPS]?.let { list -> - // Everything is in hidden (nothing in shown) - list.ifEmpty { return@let listOf() } - - try { - list.split(",").map { UnitGroup.valueOf(it) } - } catch (e: Exception) { - // Bad thing happened, return null so all units will be shown - null - } - - } ?: ALL_UNIT_GROUPS - val enableVibrations: Boolean = preferences[PrefsKeys.ENABLE_VIBRATIONS] ?: true - val radianMode: Boolean = preferences[PrefsKeys.RADIAN_MODE] ?: true - val unitConverterFavoritesOnly: Boolean = preferences[PrefsKeys.UNIT_CONVERTER_FAVORITES_ONLY] ?: false - val unitConverterFormatTime: Boolean = preferences[PrefsKeys.UNIT_CONVERTER_FORMAT_TIME] ?: false - val unitConverterSorting: UnitsListSorting = preferences[PrefsKeys.UNIT_CONVERTER_SORTING]?.let { UnitsListSorting.valueOf(it) } ?: UnitsListSorting.USAGE - val middleZero: Boolean = preferences[PrefsKeys.MIDDLE_ZERO] ?: false - val enableToolsExperiment: Boolean = preferences[PrefsKeys.ENABLE_TOOLS_EXPERIMENT] ?: false - val partialHistoryView: Boolean = preferences[PrefsKeys.PARTIAL_HISTORY_VIEW] ?: true - - MainPreferences( - digitsPrecision = digitsPrecision, - separator = separator, - outputFormat = outputFormat, - latestLeftSideUnit = latestLeftSideUnit, - latestRightSideUnit = latestRightSideUnit, - shownUnitGroups = shownUnitGroups, - enableVibrations = enableVibrations, - radianMode = radianMode, - unitConverterFavoritesOnly = unitConverterFavoritesOnly, - unitConverterFormatTime = unitConverterFormatTime, - unitConverterSorting = unitConverterSorting, - middleZero = middleZero, - enableToolsExperiment = enableToolsExperiment, - partialHistoryView = partialHistoryView + GeneralPreferences( + startingScreen = preferences[PrefsKeys.STARTING_SCREEN] + ?: TopLevelDestinations.Calculator.graph, + enableVibrations = preferences[PrefsKeys.ENABLE_VIBRATIONS] ?: true, + middleZero = preferences[PrefsKeys.MIDDLE_ZERO] ?: false, ) } - val allPreferencesFlow = combine( - mainPrefsFlow, uiPreferencesFlow - ) { main, ui -> - return@combine UserPreferences( - themingMode = ui.themingMode, - enableDynamicTheme = ui.enableDynamicTheme, - enableAmoledTheme = ui.enableAmoledTheme, - customColor = ui.customColor, - monetMode = ui.monetMode, - digitsPrecision = main.digitsPrecision, - separator = main.separator, - outputFormat = main.outputFormat, - latestLeftSideUnit = main.latestLeftSideUnit, - latestRightSideUnit = main.latestRightSideUnit, - shownUnitGroups = main.shownUnitGroups, - enableVibrations = main.enableVibrations, - enableToolsExperiment = ui.enableToolsExperiment, - startingScreen = ui.startingScreen, - radianMode = main.radianMode, - unitConverterFavoritesOnly = main.unitConverterFavoritesOnly, - unitConverterFormatTime = main.unitConverterFormatTime, - unitConverterSorting = main.unitConverterSorting, - middleZero = main.middleZero, - systemFont = ui.systemFont, - partialHistoryView = main.partialHistoryView - ) - } + 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 + ) + } + + 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, + ) + } + + val themePrefs: Flow = data + .map { preferences -> + ThemePreferences( + systemFont = preferences[PrefsKeys.SYSTEM_FONT] ?: false + ) + } + + 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, + ) + } + + 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, + ) + } + + val addSubtractPrefs: Flow = data + .map { preferences -> + AddSubtractPreferences( + separator = preferences[PrefsKeys.SEPARATOR] ?: Separator.SPACE + ) + } + + val aboutPrefs: Flow = data + .map { preferences -> + AboutPreferences( + enableToolsExperiment = preferences[PrefsKeys.ENABLE_TOOLS_EXPERIMENT] ?: false + ) + } - /** - * Update precision preference in DataStore - * - * @param precision One of the [PRECISIONS] to change to - */ suspend fun updateDigitsPrecision(precision: Int) { dataStore.edit { preferences -> preferences[PrefsKeys.DIGITS_PRECISION] = precision } } - /** - * Update separator preference in DataStore - * - * @param separator One of the [Separator] to change to - */ suspend fun updateSeparator(separator: Int) { dataStore.edit { preferences -> preferences[PrefsKeys.SEPARATOR] = separator } } - /** - * Update outputFormat preference in DataStore - * - * @param outputFormat One of the [OutputFormat] to change to - */ suspend fun updateOutputFormat(outputFormat: Int) { dataStore.edit { preferences -> preferences[PrefsKeys.OUTPUT_FORMAT] = outputFormat @@ -301,66 +268,36 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS } } - /** - * Update [ThemingMode]. Saves value as a string. - * - * @param themingMode [ThemingMode] to save. - */ suspend fun updateThemingMode(themingMode: ThemingMode) { dataStore.edit { preferences -> preferences[PrefsKeys.THEMING_MODE] = themingMode.name } } - /** - * Update preference on whether or not generate color scheme from device wallpaper. - * - * @param enabled True if user wants to enable this feature. - */ suspend fun updateDynamicTheme(enabled: Boolean) { dataStore.edit { preferences -> preferences[PrefsKeys.ENABLE_DYNAMIC_THEME] = enabled } } - /** - * Update preference on whether or not use true black colors. - * - * @param enabled True if user wants to enable this feature. - */ suspend fun updateAmoledTheme(enabled: Boolean) { dataStore.edit { preferences -> preferences[PrefsKeys.ENABLE_AMOLED_THEME] = enabled } } - /** - * Update preference on custom color scheme. - * - * @param color New custom color value. - */ suspend fun updateCustomColor(color: Color) { dataStore.edit { preferences -> preferences[PrefsKeys.CUSTOM_COLOR] = color.value.toLong() } } - /** - * Update [MonetMode]. Saves value as a string. - * - * @param monetMode [MonetMode] to save. - */ suspend fun updateMonetMode(monetMode: MonetMode) { dataStore.edit { preferences -> preferences[PrefsKeys.MONET_MODE] = monetMode.name } } - /** - * Update preference on starting screen route. - * - * @param startingScreen Route from [TopLevelDestinations]. - */ suspend fun updateStartingScreen(startingScreen: String) { dataStore.edit { preferences -> preferences[PrefsKeys.STARTING_SCREEN] = startingScreen @@ -373,102 +310,63 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS } } - /** - * Update preference on whether or not use haptic feedback. - * - * @param enabled True if user wants to enable this feature. - */ suspend fun updateVibrations(enabled: Boolean) { dataStore.edit { preferences -> preferences[PrefsKeys.ENABLE_VIBRATIONS] = enabled } } - /** - * Update preference on where zero should be. - * - * @param enabled True if user wants zero button to be in the middle. - */ suspend fun updateMiddleZero(enabled: Boolean) { dataStore.edit { preferences -> preferences[PrefsKeys.MIDDLE_ZERO] = enabled } } - /** - * Update preference on whether or not show tools screen. - * - * @param enabled True if user wants to enable this feature. - */ suspend fun updateToolsExperiment(enabled: Boolean) { dataStore.edit { preferences -> preferences[PrefsKeys.ENABLE_TOOLS_EXPERIMENT] = enabled } } - /** - * Update angle mode for calculator. - * - * @param radianMode When true - Radian, when False - Degree. - */ suspend fun updateRadianMode(radianMode: Boolean) { dataStore.edit { preferences -> preferences[PrefsKeys.RADIAN_MODE] = radianMode } } - /** - * Update units list favorite filter state. - * - * @param enabled When true will show only favorite units. - */ suspend fun updateUnitConverterFavoritesOnly(enabled: Boolean) { dataStore.edit { preferences -> preferences[PrefsKeys.UNIT_CONVERTER_FAVORITES_ONLY] = enabled } } - /** - * Update [UserPreferences.unitConverterFormatTime]. - * - * @see UserPreferences.unitConverterFormatTime - */ suspend fun updateUnitConverterFormatTime(enabled: Boolean) { dataStore.edit { preferences -> preferences[PrefsKeys.UNIT_CONVERTER_FORMAT_TIME] = enabled } } - /** - * Update [UserPreferences.unitConverterSorting]. - * - * @see UserPreferences.unitConverterSorting - */ suspend fun updateUnitConverterSorting(sorting: UnitsListSorting) { dataStore.edit { preferences -> preferences[PrefsKeys.UNIT_CONVERTER_SORTING] = sorting.name } } - /** - * Update system font preference. - * - * @param enabled When true will use system font. - */ suspend fun updateSystemFont(enabled: Boolean) { dataStore.edit { preferences -> preferences[PrefsKeys.SYSTEM_FONT] = enabled } } - /** - * Update partial history view preference. - * - * @param enabled When true will enable partial history view. - */ suspend fun updatePartialHistoryView(enabled: Boolean) { dataStore.edit { preferences -> preferences[PrefsKeys.PARTIAL_HISTORY_VIEW] = enabled } } + + 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/CalculatorViewModel.kt b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorViewModel.kt index 7be04e32..468f25b4 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 @@ -31,7 +31,7 @@ import com.sadellie.unitto.data.common.isExpression import com.sadellie.unitto.data.common.setMinimumRequiredScale import com.sadellie.unitto.data.common.toStringWith import com.sadellie.unitto.data.common.trimZeros -import com.sadellie.unitto.data.userprefs.MainPreferences +import com.sadellie.unitto.data.userprefs.CalculatorPreferences import com.sadellie.unitto.data.userprefs.UserPreferencesRepository import dagger.hilt.android.lifecycle.HiltViewModel import io.github.sadellie.evaluatto.Expression @@ -54,11 +54,11 @@ internal class CalculatorViewModel @Inject constructor( private val userPrefsRepository: UserPreferencesRepository, private val calculatorHistoryRepository: CalculatorHistoryRepository, ) : ViewModel() { - private val _userPrefs: StateFlow = - userPrefsRepository.mainPrefsFlow.stateIn( + private val _prefs: StateFlow = + userPrefsRepository.calculatorPrefs.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5000L), - MainPreferences() + CalculatorPreferences() ) private val _input: MutableStateFlow = MutableStateFlow(TextFieldValue()) @@ -67,7 +67,7 @@ internal class CalculatorViewModel @Inject constructor( private val _history = calculatorHistoryRepository.historyFlow val uiState = combine( - _input, _output, _history, _userPrefs + _input, _output, _history, _prefs ) { input, output, history, userPrefs -> return@combine CalculatorUIState.Ready( input = input, @@ -112,7 +112,7 @@ internal class CalculatorViewModel @Inject constructor( } fun toggleCalculatorMode() = viewModelScope.launch { - userPrefsRepository.updateRadianMode(!_userPrefs.value.radianMode) + userPrefsRepository.updateRadianMode(!_prefs.value.radianMode) } fun clearHistory() = viewModelScope.launch(Dispatchers.IO) { @@ -126,14 +126,14 @@ internal class CalculatorViewModel @Inject constructor( return try { CalculationResult.Default( - Expression(currentInput, radianMode = _userPrefs.value.radianMode) + Expression(currentInput, radianMode = _prefs.value.radianMode) .calculate() .also { if (it > BigDecimal.valueOf(Double.MAX_VALUE)) throw ExpressionException.TooBig() } - .setMinimumRequiredScale(_userPrefs.value.digitsPrecision) + .setMinimumRequiredScale(_prefs.value.precision) .trimZeros() - .toStringWith(_userPrefs.value.outputFormat) + .toStringWith(_prefs.value.outputFormat) ) } catch (e: ExpressionException.DivideByZero) { CalculationResult.DivideByZeroError @@ -145,7 +145,7 @@ internal class CalculatorViewModel @Inject constructor( init { // Observe and invoke calculation without UI lag. viewModelScope.launch(Dispatchers.Default) { - merge(_userPrefs, _input).collectLatest { + merge(_prefs, _input).collectLatest { val calculated = calculateInput() _output.update { // Don't show error when simply entering stuff diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt index de206ce8..037c79ab 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt @@ -33,8 +33,8 @@ import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.unit.DefaultUnit import com.sadellie.unitto.data.model.unit.NumberBaseUnit import com.sadellie.unitto.data.units.UnitsRepository -import com.sadellie.unitto.data.units.combine -import com.sadellie.unitto.data.units.stateIn +import com.sadellie.unitto.data.common.combine +import com.sadellie.unitto.data.common.stateIn import com.sadellie.unitto.data.userprefs.UserPreferencesRepository import dagger.hilt.android.lifecycle.HiltViewModel import io.github.sadellie.evaluatto.Expression @@ -79,7 +79,7 @@ internal class ConverterViewModel @Inject constructor( _result, _unitFrom, _unitTo, - userPrefsRepository.mainPrefsFlow, + userPrefsRepository.converterPrefs, _loadingCurrencies ) { input, calculation, result, unitFrom, unitTo, prefs, _ -> return@combine when { @@ -93,7 +93,7 @@ internal class ConverterViewModel @Inject constructor( enableHaptic = prefs.enableVibrations, middleZero = prefs.middleZero, formatterSymbols = AllFormatterSymbols.getById(prefs.separator), - scale = prefs.digitsPrecision, + scale = prefs.precision, outputFormat = prefs.outputFormat, formatTime = prefs.unitConverterFormatTime, ) @@ -147,7 +147,7 @@ internal class ConverterViewModel @Inject constructor( val leftSideUIState = combine( _unitFrom, _leftSideUIState, - userPrefsRepository.mainPrefsFlow, + userPrefsRepository.converterPrefs, unitsRepo.allUnits ) { unitFrom, ui, prefs, _ -> return@combine ui.copy( @@ -175,7 +175,7 @@ internal class ConverterViewModel @Inject constructor( _input, _calculation, _rightSideUIState, - userPrefsRepository.mainPrefsFlow, + userPrefsRepository.converterPrefs, unitsRepo.allUnits ) { unitFrom, unitTo, input, calculation, ui, prefs, _ -> return@combine ui.copy( @@ -184,7 +184,7 @@ internal class ConverterViewModel @Inject constructor( sorting = prefs.unitConverterSorting, favorites = prefs.unitConverterFavoritesOnly, input = calculation?.toPlainString() ?: input.text, - scale = prefs.digitsPrecision, + scale = prefs.precision, outputFormat = prefs.outputFormat, formatterSymbols = AllFormatterSymbols.getById(prefs.separator) ) @@ -387,10 +387,9 @@ internal class ConverterViewModel @Inject constructor( _result.update { ConverterResult.NumberBase(conversion) } } - init { viewModelScope.launch(Dispatchers.Default) { - val userPrefs = userPrefsRepository.mainPrefsFlow.first() + val userPrefs = userPrefsRepository.converterPrefs.first() val unitFrom = unitsRepo.getById(userPrefs.latestLeftSideUnit) val unitTo = unitsRepo.getById(userPrefs.latestRightSideUnit) diff --git a/feature/datecalculator/src/main/java/com/sadellie/unitto/feature/datecalculator/addsubtract/AddSubtractViewModel.kt b/feature/datecalculator/src/main/java/com/sadellie/unitto/feature/datecalculator/addsubtract/AddSubtractViewModel.kt index 09395f49..710be166 100644 --- a/feature/datecalculator/src/main/java/com/sadellie/unitto/feature/datecalculator/addsubtract/AddSubtractViewModel.kt +++ b/feature/datecalculator/src/main/java/com/sadellie/unitto/feature/datecalculator/addsubtract/AddSubtractViewModel.kt @@ -42,7 +42,7 @@ internal class AddSubtractViewModel @Inject constructor( private val _uiState = MutableStateFlow(AddSubtractState()) val uiState: StateFlow = _uiState - .combine(userPreferencesRepository.allPreferencesFlow) { uiState, userPrefs -> + .combine(userPreferencesRepository.addSubtractPrefs) { uiState, userPrefs -> return@combine uiState.copy( formatterSymbols = AllFormatterSymbols.getById(userPrefs.separator) ) 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 71a17048..962a05b6 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 @@ -20,86 +20,38 @@ package com.sadellie.unitto.feature.settings import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.sadellie.unitto.data.model.UnitsListSorting -import com.sadellie.unitto.data.userprefs.UserPreferences +import com.sadellie.unitto.data.common.stateIn +import com.sadellie.unitto.data.userprefs.GeneralPreferences import com.sadellie.unitto.data.userprefs.UserPreferencesRepository import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel -class SettingsViewModel @Inject constructor( +internal class SettingsViewModel @Inject constructor( private val userPrefsRepository: UserPreferencesRepository, ) : ViewModel() { - val userPrefs = userPrefsRepository.allPreferencesFlow - .stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(5000), - UserPreferences() - ) + val userPrefs = userPrefsRepository.generalPrefs + .stateIn(viewModelScope, GeneralPreferences()) /** * @see UserPreferencesRepository.updateVibrations */ - fun updateVibrations(enabled: Boolean) { - viewModelScope.launch { - userPrefsRepository.updateVibrations(enabled) - } + fun updateVibrations(enabled: Boolean) = viewModelScope.launch { + userPrefsRepository.updateVibrations(enabled) } /** * @see UserPreferencesRepository.updateMiddleZero */ - fun updateMiddleZero(enabled: Boolean) { - viewModelScope.launch { - userPrefsRepository.updateMiddleZero(enabled) - } + fun updateMiddleZero(enabled: Boolean) = viewModelScope.launch { + userPrefsRepository.updateMiddleZero(enabled) } /** * @see UserPreferencesRepository.updateStartingScreen */ - fun updateStartingScreen(startingScreen: String) { - viewModelScope.launch { - userPrefsRepository.updateStartingScreen(startingScreen) - } - } - - /** - * @see UserPreferencesRepository.updateToolsExperiment - */ - fun enableToolsExperiment() { - viewModelScope.launch { - userPrefsRepository.updateToolsExperiment(true) - } - } - - /** - * @see UserPreferencesRepository.updateUnitConverterFormatTime - */ - fun updateUnitConverterFormatTime(enabled: Boolean) { - viewModelScope.launch { - userPrefsRepository.updateUnitConverterFormatTime(enabled) - } - } - - /** - * @see UserPreferencesRepository.updateUnitConverterSorting - */ - fun updateUnitConverterSorting(sorting: UnitsListSorting) { - viewModelScope.launch { - userPrefsRepository.updateUnitConverterSorting(sorting) - } - } - - /** - * @see UserPreferencesRepository.updatePartialHistoryView - */ - fun updatePartialHistoryView(enabled: Boolean) { - viewModelScope.launch { - userPrefsRepository.updatePartialHistoryView(enabled) - } + fun updateStartingScreen(startingScreen: String) = viewModelScope.launch { + userPrefsRepository.updateStartingScreen(startingScreen) } } diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/AboutScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/about/AboutScreen.kt similarity index 97% rename from feature/settings/src/main/java/com/sadellie/unitto/feature/settings/AboutScreen.kt rename to feature/settings/src/main/java/com/sadellie/unitto/feature/settings/about/AboutScreen.kt index f8af1e5b..89861b1f 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/AboutScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/about/AboutScreen.kt @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package com.sadellie.unitto.feature.settings +package com.sadellie.unitto.feature.settings.about import android.widget.Toast import androidx.compose.foundation.clickable @@ -54,12 +54,12 @@ import com.sadellie.unitto.core.ui.openLink @Composable internal fun AboutScreen( - viewModel: SettingsViewModel = hiltViewModel(), + viewModel: AboutViewModel = hiltViewModel(), navigateUpAction: () -> Unit, navigateToThirdParty: () -> Unit, ) { val mContext = LocalContext.current - val userPrefs = viewModel.userPrefs.collectAsStateWithLifecycle() + val prefs = viewModel.prefs.collectAsStateWithLifecycle() var aboutItemClick: Int by rememberSaveable { mutableIntStateOf(0) } var showDialog: Boolean by rememberSaveable { mutableStateOf(false) } @@ -185,7 +185,7 @@ internal fun AboutScreen( headlineContent = { Text(stringResource(R.string.app_version_name_setting)) }, supportingContent = { Text("${BuildConfig.APP_NAME} (${BuildConfig.APP_CODE})") }, modifier = Modifier.combinedClickable { - if (userPrefs.value.enableToolsExperiment) { + if (prefs.value.enableToolsExperiment) { Toast.makeText(mContext, "Experiments features are already enabled!", Toast.LENGTH_LONG).show() return@combinedClickable } 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 new file mode 100644 index 00000000..60bc004d --- /dev/null +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/about/AboutViewModel.kt @@ -0,0 +1,40 @@ +/* + * 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.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 +import javax.inject.Inject + +@HiltViewModel +internal class AboutViewModel @Inject constructor( + private val userPrefsRepository: UserPreferencesRepository, +) : ViewModel() { + val prefs = userPrefsRepository.aboutPrefs + .stateIn(viewModelScope, AboutPreferences()) + + fun enableToolsExperiment() = viewModelScope.launch { + userPrefsRepository.updateToolsExperiment(true) + } +} diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/CalculatorSettingsScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/calculator/CalculatorSettingsScreen.kt similarity index 88% rename from feature/settings/src/main/java/com/sadellie/unitto/feature/settings/CalculatorSettingsScreen.kt rename to feature/settings/src/main/java/com/sadellie/unitto/feature/settings/calculator/CalculatorSettingsScreen.kt index 73dc57fa..b635c692 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/CalculatorSettingsScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/calculator/CalculatorSettingsScreen.kt @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package com.sadellie.unitto.feature.settings +package com.sadellie.unitto.feature.settings.calculator import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons @@ -24,6 +24,7 @@ import androidx.compose.material.icons.filled.Timer import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource +import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.ui.common.NavigateUpButton @@ -32,10 +33,10 @@ import com.sadellie.unitto.core.ui.common.UnittoScreenWithLargeTopBar @Composable internal fun CalculatorSettingsScreen( - viewModel: SettingsViewModel, + viewModel: CalculatorViewModel = hiltViewModel(), navigateUpAction: () -> Unit, ) { - val userPrefs = viewModel.userPrefs.collectAsStateWithLifecycle() + val prefs = viewModel.prefs.collectAsStateWithLifecycle() UnittoScreenWithLargeTopBar( title = stringResource(R.string.calculator), @@ -52,7 +53,7 @@ internal fun CalculatorSettingsScreen( ) }, supportContent = stringResource(R.string.partial_history_view_setting_support), - switchState = userPrefs.value.partialHistoryView, + switchState = prefs.value.partialHistoryView, onSwitchChange = viewModel::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 new file mode 100644 index 00000000..550cb750 --- /dev/null +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/calculator/CalculatorViewModel.kt @@ -0,0 +1,40 @@ +/* + * 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.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 +import javax.inject.Inject + +@HiltViewModel +class CalculatorViewModel @Inject constructor( + private val userPrefsRepository: UserPreferencesRepository, +) : ViewModel() { + val prefs = userPrefsRepository.calculatorPrefs + .stateIn(viewModelScope, CalculatorPreferences()) + + fun updatePartialHistoryView(enabled: Boolean) = viewModelScope.launch { + userPrefsRepository.updatePartialHistoryView(enabled) + } +} diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/ConverterSettingsScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/converter/ConverterSettingsScreen.kt similarity index 92% rename from feature/settings/src/main/java/com/sadellie/unitto/feature/settings/ConverterSettingsScreen.kt rename to feature/settings/src/main/java/com/sadellie/unitto/feature/settings/converter/ConverterSettingsScreen.kt index 4bb5c77a..1a656f91 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/ConverterSettingsScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/converter/ConverterSettingsScreen.kt @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package com.sadellie.unitto.feature.settings +package com.sadellie.unitto.feature.settings.converter import androidx.compose.foundation.clickable import androidx.compose.foundation.lazy.LazyColumn @@ -34,6 +34,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.ui.common.NavigateUpButton @@ -44,11 +45,11 @@ import com.sadellie.unitto.feature.settings.components.AlertDialogWithList @Composable internal fun ConverterSettingsScreen( - viewModel: SettingsViewModel, + viewModel: ConverterViewModel = hiltViewModel(), navigateUpAction: () -> Unit, - navigateToUnitsGroup: () -> Unit + navigateToUnitsGroup: () -> Unit, ) { - val userPrefs = viewModel.userPrefs.collectAsStateWithLifecycle() + val prefs = viewModel.prefs.collectAsStateWithLifecycle() var showDialog: Boolean by rememberSaveable { mutableStateOf(false) } UnittoScreenWithLargeTopBar( @@ -97,7 +98,7 @@ internal fun ConverterSettingsScreen( ) }, supportContent = stringResource(R.string.format_time_support), - switchState = userPrefs.value.unitConverterFormatTime, + switchState = prefs.value.unitConverterFormatTime, onSwitchChange = viewModel::updateUnitConverterFormatTime ) } @@ -113,7 +114,7 @@ internal fun ConverterSettingsScreen( UnitsListSorting.SCALE_DESC to R.string.sort_by_scale_desc, UnitsListSorting.SCALE_ASC to R.string.sort_by_scale_asc, ), - selectedItemIndex = userPrefs.value.unitConverterSorting, + selectedItemIndex = prefs.value.unitConverterSorting, selectAction = viewModel::updateUnitConverterSorting, dismissAction = { showDialog = false } ) 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 new file mode 100644 index 00000000..993ac33b --- /dev/null +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/converter/ConverterViewModel.kt @@ -0,0 +1,45 @@ +/* + * 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.feature.settings.converter + +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 +import javax.inject.Inject + +@HiltViewModel +internal class ConverterViewModel @Inject constructor( + private val userPrefsRepository: UserPreferencesRepository, +) : ViewModel() { + val prefs = userPrefsRepository.converterPrefs + .stateIn(viewModelScope, ConverterPreferences()) + + fun updateUnitConverterFormatTime(enabled: Boolean) = viewModelScope.launch { + userPrefsRepository.updateUnitConverterFormatTime(enabled) + } + + fun updateUnitConverterSorting(sorting: UnitsListSorting) = viewModelScope.launch { + userPrefsRepository.updateUnitConverterSorting(sorting) + } +} 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 ef779937..a9929b51 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 @@ -49,6 +49,7 @@ import androidx.compose.ui.res.stringResource 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.MAX_PRECISION import com.sadellie.unitto.core.base.OutputFormat @@ -66,7 +67,7 @@ import kotlin.math.roundToInt @Composable fun FormattingRoute( - viewModel: FormattingViewModel, + viewModel: FormattingViewModel = hiltViewModel(), navigateUpAction: () -> Unit, ) { val uiState = viewModel.uiState.collectAsStateWithLifecycle() 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 e091d4db..a65e46d2 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 @@ -25,14 +25,13 @@ import com.sadellie.unitto.core.ui.common.textfield.AllFormatterSymbols import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.core.ui.common.textfield.formatExpression import com.sadellie.unitto.data.common.setMinimumRequiredScale +import com.sadellie.unitto.data.common.stateIn import com.sadellie.unitto.data.common.toStringWith import com.sadellie.unitto.data.common.trimZeros import com.sadellie.unitto.data.userprefs.UserPreferencesRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import java.math.BigDecimal @@ -43,10 +42,10 @@ import kotlin.math.ceil class FormattingViewModel @Inject constructor( private val userPreferencesRepository: UserPreferencesRepository ) : ViewModel() { - private val _mainPreferences = userPreferencesRepository.mainPrefsFlow + private val _prefs = userPreferencesRepository.formattingPrefs private val _fractional = MutableStateFlow(false) - val uiState = combine(_mainPreferences, _fractional) { mainPrefs, fractional -> + val uiState = combine(_prefs, _fractional) { mainPrefs, fractional -> val formatterSymbols = AllFormatterSymbols.getById(mainPrefs.separator) return@combine FormattingUIState( @@ -62,7 +61,7 @@ class FormattingViewModel @Inject constructor( formatterSymbols = formatterSymbols ) } - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000L), FormattingUIState()) + .stateIn(viewModelScope, FormattingUIState()) fun togglePreview() = _fractional.update { !it } @@ -88,29 +87,23 @@ class FormattingViewModel @Inject constructor( /** * @see UserPreferencesRepository.updateDigitsPrecision */ - fun updatePrecision(precision: Int) { - viewModelScope.launch { - // In UI the slider for precision goes from 0 to 16, where 16 is treated as 1000 (MAX) - val newPrecision = if (precision > 15) MAX_PRECISION else precision - userPreferencesRepository.updateDigitsPrecision(newPrecision) - } + fun updatePrecision(precision: Int) = viewModelScope.launch { + // In UI the slider for precision goes from 0 to 16, where 16 is treated as 1000 (MAX) + val newPrecision = if (precision > 15) MAX_PRECISION else precision + userPreferencesRepository.updateDigitsPrecision(newPrecision) } /** * @see UserPreferencesRepository.updateSeparator */ - fun updateSeparator(separator: Int) { - viewModelScope.launch { - userPreferencesRepository.updateSeparator(separator) - } + fun updateSeparator(separator: Int) = viewModelScope.launch { + userPreferencesRepository.updateSeparator(separator) } /** * @see UserPreferencesRepository.updateOutputFormat */ - fun updateOutputFormat(outputFormat: Int) { - viewModelScope.launch { - userPreferencesRepository.updateOutputFormat(outputFormat) - } + fun updateOutputFormat(outputFormat: Int) = viewModelScope.launch { + userPreferencesRepository.updateOutputFormat(outputFormat) } } 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 9e92e729..bd710433 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 @@ -18,8 +18,6 @@ package com.sadellie.unitto.feature.settings.navigation -import androidx.compose.runtime.remember -import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController @@ -27,13 +25,13 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.navigation import androidx.navigation.navDeepLink import com.sadellie.unitto.core.base.TopLevelDestinations -import com.sadellie.unitto.feature.settings.AboutScreen -import com.sadellie.unitto.feature.settings.CalculatorSettingsScreen -import com.sadellie.unitto.feature.settings.ConverterSettingsScreen import com.sadellie.unitto.feature.settings.SettingsScreen -import com.sadellie.unitto.feature.settings.ThirdPartyLicensesScreen +import com.sadellie.unitto.feature.settings.about.AboutScreen +import com.sadellie.unitto.feature.settings.calculator.CalculatorSettingsScreen +import com.sadellie.unitto.feature.settings.converter.ConverterSettingsScreen import com.sadellie.unitto.feature.settings.formatting.FormattingRoute import com.sadellie.unitto.feature.settings.themes.ThemesRoute +import com.sadellie.unitto.feature.settings.thirdparty.ThirdPartyLicensesScreen import com.sadellie.unitto.feature.settings.unitgroups.UnitGroupsScreen import io.github.sadellie.themmo.ThemmoController @@ -57,7 +55,7 @@ fun NavController.navigateToUnitGroups() { fun NavGraphBuilder.settingGraph( themmoController: ThemmoController, - navController: NavHostController + navController: NavHostController, ) { navigation( startDestination = start, @@ -67,10 +65,7 @@ fun NavGraphBuilder.settingGraph( ) ) { composable(start) { - val parent = remember(it) { navController.getBackStackEntry(graph) } - SettingsScreen( - viewModel = hiltViewModel(parent), menuButtonClick = navController::navigateUp, navControllerAction = navController::navigate ) @@ -78,59 +73,47 @@ fun NavGraphBuilder.settingGraph( composable(themesRoute) { ThemesRoute( - viewModel = hiltViewModel(), navigateUpAction = navController::navigateUp, themmoController = themmoController, ) } + composable(formattingRoute) { + FormattingRoute( + navigateUpAction = navController::navigateUp + ) + } + + composable(calculatorSettingsRoute) { + CalculatorSettingsScreen( + navigateUpAction = navController::navigateUp, + ) + } + + composable(converterSettingsRoute) { + ConverterSettingsScreen( + navigateUpAction = navController::navigateUp, + navigateToUnitsGroup = { navController.navigate(unitsGroupRoute) } + ) + } + + composable(unitsGroupRoute) { + UnitGroupsScreen( + navigateUpAction = navController::navigateUp, + ) + } + + composable(aboutRoute) { + AboutScreen( + navigateUpAction = navController::navigateUp, + navigateToThirdParty = { navController.navigate(thirdPartyRoute) } + ) + } + composable(thirdPartyRoute) { ThirdPartyLicensesScreen( navigateUpAction = navController::navigateUp, ) } - - composable(aboutRoute) { - val parent = remember(it) { navController.getBackStackEntry(graph) } - - AboutScreen( - viewModel = hiltViewModel(parent), - navigateUpAction = navController::navigateUp, - navigateToThirdParty = { navController.navigate(thirdPartyRoute) } - ) - } - - composable(unitsGroupRoute) { - UnitGroupsScreen( - viewModel = hiltViewModel(), - navigateUpAction = navController::navigateUp, - ) - } - - composable(formattingRoute) { - FormattingRoute( - viewModel = hiltViewModel(), - navigateUpAction = navController::navigateUp - ) - } - - composable(calculatorSettingsRoute) { - val parent = remember(it) { navController.getBackStackEntry(graph) } - - CalculatorSettingsScreen( - viewModel = hiltViewModel(parent), - navigateUpAction = navController::navigateUp, - ) - } - - composable(converterSettingsRoute) { - val parent = remember(it) { navController.getBackStackEntry(graph) } - - ConverterSettingsScreen( - viewModel = hiltViewModel(parent), - navigateUpAction = navController::navigateUp, - navigateToUnitsGroup = { navController.navigate(unitsGroupRoute) } - ) - } } } diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/themes/ThemesScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/themes/ThemesScreen.kt index efcbe2f1..84f7a4cd 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/themes/ThemesScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/themes/ThemesScreen.kt @@ -48,6 +48,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource 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.R import com.sadellie.unitto.core.ui.common.Header @@ -80,7 +81,7 @@ private val colorSchemes: List by lazy { @Composable internal fun ThemesRoute( - viewModel: ThemesViewModel, + viewModel: ThemesViewModel = hiltViewModel(), navigateUpAction: () -> Unit = {}, themmoController: ThemmoController, ) { diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/themes/ThemesViewModel.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/themes/ThemesViewModel.kt index c6f80946..3fabf58d 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/themes/ThemesViewModel.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/themes/ThemesViewModel.kt @@ -21,13 +21,12 @@ package com.sadellie.unitto.feature.settings.themes 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.UserPreferencesRepository import dagger.hilt.android.lifecycle.HiltViewModel import io.github.sadellie.themmo.MonetMode import io.github.sadellie.themmo.ThemingMode -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import javax.inject.Inject @@ -36,13 +35,9 @@ class ThemesViewModel @Inject constructor( private val userPrefsRepository: UserPreferencesRepository ) : ViewModel() { - val systemFont = userPrefsRepository.uiPreferencesFlow + val systemFont = userPrefsRepository.themePrefs .map { it.systemFont } - .stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(5000L), - false - ) + .stateIn(viewModelScope, false) /** * @see UserPreferencesRepository.updateThemingMode diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/ThirdPartyLicensesScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/thirdparty/ThirdPartyLicensesScreen.kt similarity index 98% rename from feature/settings/src/main/java/com/sadellie/unitto/feature/settings/ThirdPartyLicensesScreen.kt rename to feature/settings/src/main/java/com/sadellie/unitto/feature/settings/thirdparty/ThirdPartyLicensesScreen.kt index 2bba1fe2..fc1a3311 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/ThirdPartyLicensesScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/thirdparty/ThirdPartyLicensesScreen.kt @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package com.sadellie.unitto.feature.settings +package com.sadellie.unitto.feature.settings.thirdparty import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/unitgroups/UnitGroupsScreen.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/unitgroups/UnitGroupsScreen.kt index ac1c10f8..af314a15 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/unitgroups/UnitGroupsScreen.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/unitgroups/UnitGroupsScreen.kt @@ -47,6 +47,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.ui.common.Header import com.sadellie.unitto.core.ui.common.NavigateUpButton @@ -59,8 +60,8 @@ import org.burnoutcrew.reorderable.reorderable @Composable internal fun UnitGroupsScreen( - viewModel: UnitGroupsViewModel, - navigateUpAction: () -> Unit + viewModel: UnitGroupsViewModel = hiltViewModel(), + navigateUpAction: () -> Unit, ) { UnittoScreenWithLargeTopBar( title = stringResource(R.string.unit_groups_setting), diff --git a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/unitgroups/UnitGroupsViewModel.kt b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/unitgroups/UnitGroupsViewModel.kt index 26fdf74d..4116b704 100644 --- a/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/unitgroups/UnitGroupsViewModel.kt +++ b/feature/settings/src/main/java/com/sadellie/unitto/feature/settings/unitgroups/UnitGroupsViewModel.kt @@ -87,7 +87,7 @@ class UnitGroupsViewModel @Inject constructor( init { viewModelScope.launch { unitGroupsRepository.updateShownGroups( - userPrefsRepository.mainPrefsFlow.first().shownUnitGroups + userPrefsRepository.unitGroupsPrefs.first().shownUnitGroups ) } }