Refactored repository for DataStore

Also renamed app theme from AppTheme to UnittoTheme so that it's less confusing
This commit is contained in:
Sad Ellie 2022-05-31 21:58:58 +03:00
parent 37826b7177
commit 941e260a15
6 changed files with 192 additions and 149 deletions

View File

@ -5,7 +5,7 @@ import android.content.Context
import androidx.room.Room import androidx.room.Room
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import com.sadellie.unitto.data.preferences.UserPreferences import com.sadellie.unitto.data.preferences.UserPreferencesRepository
import com.sadellie.unitto.data.units.ALL_UNITS import com.sadellie.unitto.data.units.ALL_UNITS
import com.sadellie.unitto.data.units.MyUnitIDS import com.sadellie.unitto.data.units.MyUnitIDS
import com.sadellie.unitto.data.units.database.MyBasedUnitDatabase import com.sadellie.unitto.data.units.database.MyBasedUnitDatabase
@ -26,7 +26,7 @@ class SwapUnitsTest {
fun setUp() { fun setUp() {
val context = ApplicationProvider.getApplicationContext<Context>() val context = ApplicationProvider.getApplicationContext<Context>()
viewModel = MainViewModel( viewModel = MainViewModel(
UserPreferences(context), UserPreferencesRepository(context),
MyBasedUnitsRepository( MyBasedUnitsRepository(
Room.inMemoryDatabaseBuilder( Room.inMemoryDatabaseBuilder(
context, context,

View File

@ -1,6 +1,5 @@
package com.sadellie.unitto package com.sadellie.unitto
import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
@ -8,8 +7,6 @@ import androidx.activity.viewModels
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.NavType import androidx.navigation.NavType
@ -24,7 +21,7 @@ import com.sadellie.unitto.screens.about.AboutScreen
import com.sadellie.unitto.screens.main.MainScreen import com.sadellie.unitto.screens.main.MainScreen
import com.sadellie.unitto.screens.second.SecondScreen import com.sadellie.unitto.screens.second.SecondScreen
import com.sadellie.unitto.screens.setttings.SettingsScreen import com.sadellie.unitto.screens.setttings.SettingsScreen
import com.sadellie.unitto.ui.theme.AppTheme import com.sadellie.unitto.ui.theme.UnittoTheme
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@ -37,11 +34,11 @@ class MainActivity : ComponentActivity() {
setContent { setContent {
val navController = rememberNavController() val navController = rememberNavController()
val currentAppTheme: Int by mainViewModel.currentAppTheme.collectAsState(AppTheme.NOT_SET) val currentAppTheme: Int = mainViewModel.currentTheme
// We don't draw anything until we know what theme we need to use // We don't draw anything until we know what theme we need to use
if (currentAppTheme != AppTheme.NOT_SET) { if (currentAppTheme != AppTheme.NOT_SET) {
AppTheme( UnittoTheme(
currentAppTheme = currentAppTheme currentAppTheme = currentAppTheme
) { ) {
UnittoApp( UnittoApp(
@ -54,7 +51,7 @@ class MainActivity : ComponentActivity() {
} }
override fun onPause() { override fun onPause() {
mainViewModel.saveMe() mainViewModel.saveLatestPairOfUnits()
super.onPause() super.onPause()
} }
} }

View File

@ -1,8 +1,11 @@
package com.sadellie.unitto.data.preferences package com.sadellie.unitto.data.preferences
import android.content.Context import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.* import androidx.datastore.preferences.core.*
import androidx.datastore.preferences.preferencesDataStore import androidx.datastore.preferences.preferencesDataStore
import com.sadellie.unitto.data.units.AbstractUnit
import com.sadellie.unitto.data.units.MyUnitIDS
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
@ -14,81 +17,142 @@ import javax.inject.Inject
private val Context.settingsDataStore by preferencesDataStore("settings") private val Context.settingsDataStore by preferencesDataStore("settings")
/** /**
* Keys for DataStore * Represents user preferences that are user across the app
*
* @property currentAppTheme Current [AppTheme] to be used
* @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 enableAnalytics Whether or not user wants to share application usage data
*/ */
object UserPreferenceKeys { data class UserPreferences(
val CURRENT_APP_THEME = intPreferencesKey("CURRENT_APP_THEME") val currentAppTheme: Int,
val DIGITS_PRECISION = intPreferencesKey("DIGITS_PRECISION_PREF_KEY") val digitsPrecision: Int,
val SEPARATOR = intPreferencesKey("SEPARATOR_PREF_KEY") val separator: Int,
val OUTPUT_FORMAT = intPreferencesKey("OUTPUT_FORMAT_PREF_KEY") val outputFormat: Int,
val LATEST_LEFT_SIDE = stringPreferencesKey("LATEST_LEFT_SIDE_PREF_KEY") val latestLeftSideUnit: String,
val LATEST_RIGHT_SIDE = stringPreferencesKey("LATEST_RIGHT_SIDE_PREF_KEY") val latestRightSideUnit: String,
val ENABLE_ANALYTICS = booleanPreferencesKey("ENABLE_ANALYTICS_PREF_KEY") val enableAnalytics: Boolean
} )
/** /**
* Repository that works with DataStore * Repository that works with DataStore
* *
* @property context * @property context
*/ */
class UserPreferences @Inject constructor(@ApplicationContext private val context: Context) { class UserPreferencesRepository @Inject constructor(@ApplicationContext private val context: Context) {
private val dataStore: DataStore<Preferences> = context.settingsDataStore
/** /**
* Gets string from datastore * Keys for DataStore
*/
private object PrefsKeys {
val CURRENT_APP_THEME = intPreferencesKey("CURRENT_APP_THEME")
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 ENABLE_ANALYTICS = booleanPreferencesKey("ENABLE_ANALYTICS_PREF_KEY")
}
val userPreferencesFlow: Flow<UserPreferences> = dataStore.data
.map { preferences ->
val currentAppTheme: Int =
preferences[PrefsKeys.CURRENT_APP_THEME] ?: AppTheme.AUTO
val digitsPrecision: Int =
preferences[PrefsKeys.DIGITS_PRECISION] ?: 3
val separator: Int =
preferences[PrefsKeys.SEPARATOR] ?: Separator.SPACES
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 enableAnalytics: Boolean =
preferences[PrefsKeys.ENABLE_ANALYTICS] ?: true
UserPreferences(
currentAppTheme = currentAppTheme,
digitsPrecision = digitsPrecision,
separator = separator,
outputFormat = outputFormat,
latestLeftSideUnit = latestLeftSideUnit,
latestRightSideUnit = latestRightSideUnit,
enableAnalytics = enableAnalytics
)
}
/**
* Update current theme preference in DataStore
* *
* @param[default] Value to return if didn't find anything on fresh install * @param appTheme [AppTheme] to change to
*/ */
fun getItem(key: Preferences.Key<String>, default: String): Flow<String> { suspend fun updateCurrentAppTheme(appTheme: Int) {
return context.settingsDataStore.data.map { dataStore.edit { preferences ->
it[key] ?: default preferences[PrefsKeys.CURRENT_APP_THEME] = appTheme
} }
} }
/** /**
* Gets int from datastore * Update precision preference in DataStore
* *
* @param[default] Value to return if didn't find anything. Used on fresh install * @param precision One of the [PRECISIONS] to change to
*/ */
fun getItem(key: Preferences.Key<Int>, default: Int): Flow<Int> { suspend fun updateDigitsPrecision(precision: Int) {
return context.settingsDataStore.data.map { dataStore.edit { preferences ->
it[key] ?: default preferences[PrefsKeys.DIGITS_PRECISION] = precision
} }
} }
/** /**
* Gets boolean from datastore * Update separator preference in DataStore
* *
* @param[default] Value to return if didn't find anything on fresh install * @param separator One of the [Separator] to change to
*/ */
fun getItem(key: Preferences.Key<Boolean>, default: Boolean): Flow<Boolean> { suspend fun updateSeparator(separator: Int) {
return context.settingsDataStore.data.map { dataStore.edit { preferences ->
it[key] ?: default preferences[PrefsKeys.SEPARATOR] = separator
} }
} }
/** /**
* Saves string value by key * Update outputFormat preference in DataStore
*
* @param outputFormat One of the [OutputFormat] to change to
*/ */
suspend fun saveItem(key: Preferences.Key<String>, value: String) { suspend fun updateOutputFormat(outputFormat: Int) {
context.settingsDataStore.edit { dataStore.edit { preferences ->
it[key] = value preferences[PrefsKeys.OUTPUT_FORMAT] = outputFormat
} }
} }
/** /**
* Saves int value by key * Update analytics preference in DataStore
*
* @param enableAnalytics True if user wants to share data, False if not
*/ */
suspend fun saveItem(key: Preferences.Key<Int>, value: Int) { suspend fun updateEnableAnalytics(enableAnalytics: Boolean) {
context.settingsDataStore.edit { dataStore.edit { preferences ->
it[key] = value preferences[PrefsKeys.ENABLE_ANALYTICS] = enableAnalytics
} }
} }
/** /**
* Saves boolean value by key * Update latest used pair of [AbstractUnit] in DataStore. Need it so when user restarts the app,
* this pair will be already set.
*
* @param leftSideUnit [AbstractUnit] on the left
* @param rightSideUnit [AbstractUnit] on the right
*/ */
suspend fun saveItem(key: Preferences.Key<Boolean>, value: Boolean) { suspend fun updateLatestPairOfUnits(leftSideUnit: AbstractUnit, rightSideUnit: AbstractUnit) {
context.settingsDataStore.edit { dataStore.edit { preferences ->
it[key] = value preferences[PrefsKeys.LATEST_LEFT_SIDE] = leftSideUnit.unitId
preferences[PrefsKeys.LATEST_RIGHT_SIDE] = rightSideUnit.unitId
} }
} }
} }

View File

@ -33,81 +33,67 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class MainViewModel @Inject constructor( class MainViewModel @Inject constructor(
private val mySettingsPrefs: UserPreferences, private val userPrefsRepository: UserPreferencesRepository,
private val basedUnitRepository: MyBasedUnitsRepository, private val basedUnitRepository: MyBasedUnitsRepository,
private val application: Application private val application: Application
) : ViewModel() { ) : ViewModel() {
/** var currentTheme: Int by mutableStateOf(AppTheme.NOT_SET)
* APP THEME
*/
val currentAppTheme =
mySettingsPrefs.getItem(UserPreferenceKeys.CURRENT_APP_THEME, AppTheme.AUTO)
fun saveCurrentAppTheme(value: Int) {
viewModelScope.launch {
mySettingsPrefs.saveItem(key = UserPreferenceKeys.CURRENT_APP_THEME, value)
}
}
/**
* CONVERSION PRECISION
*/
var precision: Int by mutableStateOf(0)
private set private set
var precision: Int by mutableStateOf(3)
fun setPrecisionPref(value: Int) {
viewModelScope.launch {
precision = value
mySettingsPrefs.saveItem(UserPreferenceKeys.DIGITS_PRECISION, value)
convertValue()
}
}
/**
* SEPARATOR
*/
var separator: Int by mutableStateOf(0)
private set private set
var separator: Int by mutableStateOf(Separator.SPACES)
fun setSeparatorPref(value: Int) { private set
separator = value var outputFormat: Int by mutableStateOf(OutputFormat.PLAIN)
viewModelScope.launch {
Formatter.setSeparator(value)
mySettingsPrefs.saveItem(UserPreferenceKeys.SEPARATOR, value)
convertValue()
}
}
/**
* OUTPUT FORMAT
*/
var outputFormat: Int by mutableStateOf(0)
private set private set
/**
* Sets given output format and saves it in user preference store
* @param value [OutputFormat] to set
*/
fun setOutputFormatPref(value: Int) {
// Updating value in memory
outputFormat = value
// Updating value on disk
viewModelScope.launch {
mySettingsPrefs.saveItem(UserPreferenceKeys.OUTPUT_FORMAT, value)
convertValue()
}
}
/**
* ANALYTICS
*/
var enableAnalytics: Boolean by mutableStateOf(false) var enableAnalytics: Boolean by mutableStateOf(false)
private set
fun setAnalyticsPref(value: Boolean) { /**
enableAnalytics = value * See [UserPreferencesRepository.updateCurrentAppTheme]
*/
fun updateCurrentAppTheme(appTheme: Int) {
viewModelScope.launch { viewModelScope.launch {
mySettingsPrefs.saveItem(UserPreferenceKeys.ENABLE_ANALYTICS, value) userPrefsRepository.updateCurrentAppTheme(appTheme)
}
}
/**
* See [UserPreferencesRepository.updateDigitsPrecision]
*/
fun updatePrecision(precision: Int) {
viewModelScope.launch {
userPrefsRepository.updateDigitsPrecision(precision)
convertValue()
}
}
/**
* See [UserPreferencesRepository.updateSeparator]
*/
fun updateSeparator(separator: Int) {
viewModelScope.launch {
userPrefsRepository.updateSeparator(separator)
convertValue()
}
}
/**
* See [UserPreferencesRepository.updateOutputFormat]
*/
fun updateOutputFormat(outputFormat: Int) {
viewModelScope.launch {
userPrefsRepository.updateOutputFormat(outputFormat)
convertValue()
}
}
/**
* See [UserPreferencesRepository.updateEnableAnalytics]
*/
fun updateEnableAnalytics(enableAnalytics: Boolean) {
viewModelScope.launch {
userPrefsRepository.updateEnableAnalytics(enableAnalytics)
FirebaseAnalytics.getInstance(application).setAnalyticsCollectionEnabled(enableAnalytics) FirebaseAnalytics.getInstance(application).setAnalyticsCollectionEnabled(enableAnalytics)
} }
} }
@ -412,10 +398,9 @@ class MainViewModel @Inject constructor(
/** /**
* Saves latest pair of units into datastore * Saves latest pair of units into datastore
*/ */
fun saveMe() { fun saveLatestPairOfUnits() {
viewModelScope.launch { viewModelScope.launch {
mySettingsPrefs.saveItem(UserPreferenceKeys.LATEST_LEFT_SIDE, unitFrom.unitId) userPrefsRepository.updateLatestPairOfUnits(unitFrom, unitTo)
mySettingsPrefs.saveItem(UserPreferenceKeys.LATEST_RIGHT_SIDE, unitTo.unitId)
} }
} }
@ -477,21 +462,34 @@ class MainViewModel @Inject constructor(
} }
} }
init { /**
* Observes changes in user preferences and updated values in this ViewModel.
*
* Any change in user preferences will update mutableStateOf so composables, that rely on this
* values recompose when actually needed. For example, when you change output format, composable
* in MainActivity will not be recomposed even though it needs currentTheme which is also in
* user preferences/
*/
private fun observePreferenceChanges() {
viewModelScope.launch { viewModelScope.launch {
val latestLeftSideUnitId = mySettingsPrefs.getItem( userPrefsRepository.userPreferencesFlow.collect { userPref ->
UserPreferenceKeys.LATEST_LEFT_SIDE, currentTheme = userPref.currentAppTheme
MyUnitIDS.kilometer precision = userPref.digitsPrecision
).first() separator = userPref.separator.also { Formatter.setSeparator(it) }
outputFormat = userPref.outputFormat
enableAnalytics = userPref.enableAnalytics
}
}
}
val latestRightSideUnitId = mySettingsPrefs.getItem( init {
UserPreferenceKeys.LATEST_RIGHT_SIDE, observePreferenceChanges()
MyUnitIDS.mile viewModelScope.launch {
).first() val snapshot = userPrefsRepository.userPreferencesFlow.first()
// First we load latest pair of units // First we load latest pair of units
unitFrom = try { unitFrom = try {
ALL_UNITS.first { it.unitId == latestLeftSideUnitId } ALL_UNITS.first { it.unitId == snapshot.latestLeftSideUnit }
} catch (e: java.util.NoSuchElementException) { } catch (e: java.util.NoSuchElementException) {
Log.w("MainViewModel", "No unit with the given unitId") Log.w("MainViewModel", "No unit with the given unitId")
ALL_UNITS.first { it.unitId == MyUnitIDS.kilometer } ALL_UNITS.first { it.unitId == MyUnitIDS.kilometer }
@ -499,25 +497,12 @@ class MainViewModel @Inject constructor(
unitTo = try { unitTo = try {
ALL_UNITS ALL_UNITS
.first { it.unitId == latestRightSideUnitId } .first { it.unitId == snapshot.latestRightSideUnit }
} catch (e: java.util.NoSuchElementException) { } catch (e: java.util.NoSuchElementException) {
Log.w("MainViewModel", "No unit with the given unitId") Log.w("MainViewModel", "No unit with the given unitId")
ALL_UNITS.first { it.unitId == MyUnitIDS.mile } ALL_UNITS.first { it.unitId == MyUnitIDS.mile }
} }
// Now we get the precision so we can convert values
precision = mySettingsPrefs.getItem(UserPreferenceKeys.DIGITS_PRECISION, 3).first()
// Getting separator and changing it in number formatter
separator =
mySettingsPrefs
.getItem(UserPreferenceKeys.SEPARATOR, Separator.SPACES).first()
.also { Formatter.setSeparator(it) }
// Getting output format
outputFormat =
mySettingsPrefs
.getItem(UserPreferenceKeys.OUTPUT_FORMAT, OutputFormat.PLAIN)
.first()
convertValue() convertValue()
val allBasedUnits = basedUnitRepository.getAll() val allBasedUnits = basedUnitRepository.getAll()
@ -546,9 +531,6 @@ class MainViewModel @Inject constructor(
* He can choose another unit group and doesn't need to wait for network to appear. * He can choose another unit group and doesn't need to wait for network to appear.
* */ * */
updateCurrenciesBasicUnits() updateCurrenciesBasicUnits()
enableAnalytics = mySettingsPrefs.getItem(UserPreferenceKeys.ENABLE_ANALYTICS, true).first()
FirebaseAnalytics.getInstance(application).setAnalyticsCollectionEnabled(enableAnalytics)
} }
} }
} }

View File

@ -40,7 +40,7 @@ fun SettingsScreen(
} }
var currentDialogState: Int by rememberSaveable { mutableStateOf(0) } var currentDialogState: Int by rememberSaveable { mutableStateOf(0) }
val currentAppTheme: Int by mainViewModel.currentAppTheme.collectAsState(AppTheme.NOT_SET) val currentAppTheme: Int = mainViewModel.currentTheme
Scaffold( Scaffold(
modifier = Modifier modifier = Modifier
@ -134,7 +134,7 @@ fun SettingsScreen(
label = stringResource(id = R.string.send_usage_statistics), label = stringResource(id = R.string.send_usage_statistics),
supportText = stringResource(id = R.string.send_usage_statistics_support), supportText = stringResource(id = R.string.send_usage_statistics_support),
switchState = mainViewModel.enableAnalytics, switchState = mainViewModel.enableAnalytics,
onSwitchChange = { mainViewModel.setAnalyticsPref(!it) }) onSwitchChange = { mainViewModel.updateEnableAnalytics(!it) })
SettingsListItem( SettingsListItem(
label = stringResource(R.string.third_party_licenses), label = stringResource(R.string.third_party_licenses),
onClick = { navControllerAction(ABOUT_SCREEN) } onClick = { navControllerAction(ABOUT_SCREEN) }
@ -164,7 +164,7 @@ fun SettingsScreen(
title = stringResource(id = R.string.precision_setting), title = stringResource(id = R.string.precision_setting),
listItems = PRECISIONS, listItems = PRECISIONS,
selectedItemIndex = mainViewModel.precision, selectedItemIndex = mainViewModel.precision,
selectAction = { mainViewModel.setPrecisionPref(it) }, selectAction = { mainViewModel.updatePrecision(it) },
dismissAction = { currentDialogState = 0 }, dismissAction = { currentDialogState = 0 },
supportText = stringResource(id = R.string.precision_setting_info) supportText = stringResource(id = R.string.precision_setting_info)
) )
@ -174,7 +174,7 @@ fun SettingsScreen(
title = stringResource(id = R.string.separator_setting), title = stringResource(id = R.string.separator_setting),
listItems = SEPARATORS, listItems = SEPARATORS,
selectedItemIndex = mainViewModel.separator, selectedItemIndex = mainViewModel.separator,
selectAction = { mainViewModel.setSeparatorPref(it) }, selectAction = { mainViewModel.updateSeparator(it) },
dismissAction = { currentDialogState = 0 } dismissAction = { currentDialogState = 0 }
) )
} }
@ -183,7 +183,7 @@ fun SettingsScreen(
title = stringResource(id = R.string.output_format_setting), title = stringResource(id = R.string.output_format_setting),
listItems = OUTPUT_FORMAT, listItems = OUTPUT_FORMAT,
selectedItemIndex = mainViewModel.outputFormat, selectedItemIndex = mainViewModel.outputFormat,
selectAction = { mainViewModel.setOutputFormatPref(it) }, selectAction = { mainViewModel.updateOutputFormat(it) },
dismissAction = { currentDialogState = 0 }, dismissAction = { currentDialogState = 0 },
supportText = stringResource(id = R.string.output_format_setting_info) supportText = stringResource(id = R.string.output_format_setting_info)
) )
@ -193,7 +193,7 @@ fun SettingsScreen(
title = stringResource(id = R.string.theme_setting), title = stringResource(id = R.string.theme_setting),
listItems = APP_THEMES, listItems = APP_THEMES,
selectedItemIndex = currentAppTheme, selectedItemIndex = currentAppTheme,
selectAction = { mainViewModel.saveCurrentAppTheme(it) }, selectAction = { mainViewModel.updateCurrentAppTheme(it) },
dismissAction = { currentDialogState = 0 }, dismissAction = { currentDialogState = 0 },
// Show note for users with devices that support custom Dynamic theming // Show note for users with devices that support custom Dynamic theming
supportText = if (Build.VERSION.SDK_INT in (Build.VERSION_CODES.O_MR1..Build.VERSION_CODES.R)) stringResource( supportText = if (Build.VERSION.SDK_INT in (Build.VERSION_CODES.O_MR1..Build.VERSION_CODES.R)) stringResource(

View File

@ -10,7 +10,7 @@ import com.sadellie.unitto.data.preferences.AppTheme
@Composable @Composable
fun AppTheme( fun UnittoTheme(
currentAppTheme: Int, currentAppTheme: Int,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {