From 27cb2f670b80f56904e0e0da64861ca5ceb341d3 Mon Sep 17 00:00:00 2001 From: Fabian Zobrist Date: Fri, 8 Sep 2023 18:20:56 +0200 Subject: [PATCH] Settings as state flow. --- .../me/zobrist/tichucounter/MainActivity.kt | 36 ++++-- .../tichucounter/domain/SettingsAdapter.kt | 113 +++++------------- .../ui/counter/CounterViewModel.kt | 19 +-- .../ui/settings/SettingsViewModel.kt | 25 ++-- 4 files changed, 73 insertions(+), 120 deletions(-) diff --git a/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt b/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt index ca5b423..751a6ec 100644 --- a/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt +++ b/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt @@ -34,6 +34,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.lifecycle.lifecycleScope import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable @@ -44,7 +45,6 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import me.zobrist.tichucounter.domain.DrawerItem -import me.zobrist.tichucounter.domain.ISystemSettingsChangeListener import me.zobrist.tichucounter.domain.KeepScreenOn import me.zobrist.tichucounter.domain.Language import me.zobrist.tichucounter.domain.ReviewService @@ -69,7 +69,7 @@ import me.zobrist.tichucounter.ui.settings.SettingsViewModel import javax.inject.Inject @AndroidEntryPoint -class MainActivity : AppCompatActivity(), ISystemSettingsChangeListener { +class MainActivity : AppCompatActivity() { @Inject lateinit var settingsAdapter: SettingsAdapter @@ -86,7 +86,26 @@ class MainActivity : AppCompatActivity(), ISystemSettingsChangeListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - settingsAdapter.registerOnChangeListener(this) + changeTheme(settingsAdapter.theme.value) + setKeepScreenOn(settingsAdapter.keepScreenOn.value) + changeLanguage(settingsAdapter.language.value) + + + lifecycleScope.launch { + settingsAdapter.theme.collect { + changeTheme(it) + } + } + lifecycleScope.launch { + settingsAdapter.keepScreenOn.collect { + setKeepScreenOn(it) + } + } + lifecycleScope.launch { + settingsAdapter.language.collect { + changeLanguage(it) + } + } mainViewModel.onNewGame = { reviewService.request() @@ -101,16 +120,11 @@ class MainActivity : AppCompatActivity(), ISystemSettingsChangeListener { } } - override fun onDestroy() { - super.onDestroy() - settingsAdapter.unregisterOnChangeListener(this) - } - - override fun onLanguageChanged(language: Language) { + private fun changeLanguage(language: Language) { AppCompatDelegate.setApplicationLocales(language.value) } - override fun onThemeChanged(theme: Theme) { + private fun changeTheme(theme: Theme) { val themeValue = when (theme) { Theme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO Theme.DARK -> AppCompatDelegate.MODE_NIGHT_YES @@ -119,7 +133,7 @@ class MainActivity : AppCompatActivity(), ISystemSettingsChangeListener { AppCompatDelegate.setDefaultNightMode(themeValue) } - override fun onScreenOnChanged(keepOn: KeepScreenOn) { + private fun setKeepScreenOn(keepOn: KeepScreenOn) { if (keepOn.value) { window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) } else { diff --git a/app/src/main/java/me/zobrist/tichucounter/domain/SettingsAdapter.kt b/app/src/main/java/me/zobrist/tichucounter/domain/SettingsAdapter.kt index e011f75..70acbbd 100644 --- a/app/src/main/java/me/zobrist/tichucounter/domain/SettingsAdapter.kt +++ b/app/src/main/java/me/zobrist/tichucounter/domain/SettingsAdapter.kt @@ -4,6 +4,10 @@ import android.content.Context import androidx.core.os.LocaleListCompat import androidx.preference.PreferenceManager import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch import javax.inject.Inject import javax.inject.Singleton @@ -18,99 +22,64 @@ enum class KeepScreenOn(val value: Boolean) { ON(true), OFF(false) } typealias VictoryPoints = Int -interface ISettingsChangeListener -interface ISystemSettingsChangeListener : ISettingsChangeListener { - fun onLanguageChanged(language: Language) - fun onThemeChanged(theme: Theme) - fun onScreenOnChanged(keepOn: KeepScreenOn) -} - -interface IGameSettingsChangeListener : ISettingsChangeListener { - fun onVictoryPointsChanged(victoryPoints: Int) -} - @Singleton class SettingsAdapter @Inject constructor(@ApplicationContext private val context: Context) { private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) - private var listenerList = mutableListOf() - var language: Language - private set + val language = MutableStateFlow(Language.DEFAULT) - var theme: Theme - private set + val theme = MutableStateFlow(Theme.DEFAULT) - var keepScreenOn: KeepScreenOn - private set + val keepScreenOn = MutableStateFlow(KeepScreenOn.OFF) - var victoryPoints: Int - private set + val victoryPoints = MutableStateFlow(0) init { - language = try { + language.value = try { enumValueOf(sharedPreferences.getString(Language::class.simpleName, null)!!) } catch (_: NullPointerException) { Language.DEFAULT } - theme = try { + theme.value = try { enumValueOf(sharedPreferences.getString(Theme::class.simpleName, null)!!) } catch (_: java.lang.Exception) { Theme.DEFAULT } - keepScreenOn = try { + keepScreenOn.value = try { enumValueOf(sharedPreferences.getString(KeepScreenOn::class.simpleName, null)!!) } catch (_: java.lang.Exception) { KeepScreenOn.OFF } - victoryPoints = sharedPreferences.getInt(VictoryPoints::class.simpleName, 1000) - } + victoryPoints.value = sharedPreferences.getInt(VictoryPoints::class.simpleName, 1000) - fun registerOnChangeListener(listener: ISettingsChangeListener) { - listenerList.add(listener) - if (listener is ISystemSettingsChangeListener) { - listener.onThemeChanged(theme) - listener.onLanguageChanged(language) - listener.onScreenOnChanged(keepScreenOn) + CoroutineScope(Dispatchers.IO).launch { + language.collect { + updatePreference(Language::class.simpleName, it.name) + } } - if (listener is IGameSettingsChangeListener) { - listener.onVictoryPointsChanged(victoryPoints) + CoroutineScope(Dispatchers.IO).launch { + theme.collect { + updatePreference(Theme::class.simpleName, it.name) + } } - } - fun unregisterOnChangeListener(listener: ISettingsChangeListener?) { - if (listener != null) { - listenerList.remove(listener) + CoroutineScope(Dispatchers.IO).launch { + keepScreenOn.collect { + updatePreference(KeepScreenOn::class.simpleName, it.name) + } } - } - fun setLanguage(language: Language) { - this.language = language - updatePreference(Language::class.simpleName, language.name) - notifyListeners(language) - } - - fun setTheme(theme: Theme) { - this.theme = theme - updatePreference(Theme::class.simpleName, theme.name) - notifyListeners(theme) - } - - fun setKeepScreenOn(setting: KeepScreenOn) { - this.keepScreenOn = setting - updatePreference(KeepScreenOn::class.simpleName, setting.name) - notifyListeners(setting) - } - - fun setVictoryPoints(setting: Int) { - this.victoryPoints = setting - updatePreference(VictoryPoints::class.simpleName, setting) - notifyListeners(setting) + CoroutineScope(Dispatchers.IO).launch { + victoryPoints.collect { + updatePreference(VictoryPoints::class.simpleName, it) + } + } } private fun updatePreference(name: String?, value: String) { @@ -130,28 +99,4 @@ class SettingsAdapter @Inject constructor(@ApplicationContext private val contex editor.putInt(name, value) editor.apply() } - - private fun notifyListeners(language: Language) { - listenerList.filterIsInstance().forEach { - it.onLanguageChanged(language) - } - } - - private fun notifyListeners(theme: Theme) { - listenerList.filterIsInstance().forEach { - it.onThemeChanged(theme) - } - } - - private fun notifyListeners(victoryPoints: VictoryPoints) { - listenerList.filterIsInstance().forEach { - it.onVictoryPointsChanged(victoryPoints) - } - } - - private fun notifyListeners(keepScreenOn: KeepScreenOn) { - listenerList.filterIsInstance().forEach { - it.onScreenOnChanged(keepScreenOn) - } - } } \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt index e56330f..a18f8a5 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt @@ -13,7 +13,6 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import me.zobrist.tichucounter.data.entity.Game import me.zobrist.tichucounter.data.entity.Round -import me.zobrist.tichucounter.domain.IGameSettingsChangeListener import me.zobrist.tichucounter.domain.SettingsAdapter import me.zobrist.tichucounter.domain.Tichu import me.zobrist.tichucounter.domain.digitCount @@ -73,7 +72,7 @@ class CounterViewModel @Inject constructor( private val gameRepository: GameRepository, private val settings: SettingsAdapter ) : - ViewModel(), ICounterViewModel, IGameSettingsChangeListener { + ViewModel(), ICounterViewModel { override var roundScoreList by mutableStateOf(emptyList()) private set @@ -188,7 +187,7 @@ class CounterViewModel @Inject constructor( } if (!victoryDialogShown) { - if (totalScoreA >= settings.victoryPoints || totalScoreB >= settings.victoryPoints) { + if (totalScoreA >= settings.victoryPoints.value || totalScoreB >= settings.victoryPoints.value) { showVictoryDialog = true } } @@ -202,13 +201,11 @@ class CounterViewModel @Inject constructor( buildTeamNameSuggestions() } + + settings.victoryPoints.collect { + victoryDialogShown = false + } } - - settings.registerOnChangeListener(this) - } - - override fun onCleared() { - settings.unregisterOnChangeListener(this) } override fun focusLastInput() { @@ -401,8 +398,4 @@ class CounterViewModel @Inject constructor( return filtered.sorted().sortedBy { it.length }.take(10) } - - override fun onVictoryPointsChanged(victoryPoints: Int) { - victoryDialogShown = false - } } \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/settings/SettingsViewModel.kt b/app/src/main/java/me/zobrist/tichucounter/ui/settings/SettingsViewModel.kt index b3657b3..cd41a25 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/settings/SettingsViewModel.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/settings/SettingsViewModel.kt @@ -1,6 +1,7 @@ package me.zobrist.tichucounter.ui.settings import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel @@ -15,35 +16,35 @@ import javax.inject.Inject class SettingsViewModel @Inject constructor(private val settings: SettingsAdapter) : ViewModel() { - var language by mutableStateOf(settings.language) + var language by mutableStateOf(settings.language.value) private set - var theme by mutableStateOf(settings.theme) + var theme by mutableStateOf(settings.theme.value) private set - var screenOn by mutableStateOf(settings.keepScreenOn) + var screenOn by mutableStateOf(settings.keepScreenOn.value) private set - var victoryPoints by mutableStateOf(settings.victoryPoints) + var victoryPoints by mutableIntStateOf(settings.victoryPoints.value) private set fun updateLanguage(language: Language) { - settings.setLanguage(language) - this.language = settings.language + settings.language.value = language + this.language = language } fun updateTheme(theme: Theme) { - settings.setTheme(theme) - this.theme = settings.theme + settings.theme.value = theme + this.theme = theme } fun updateScreenOn(value: KeepScreenOn) { - settings.setKeepScreenOn(value) - screenOn = settings.keepScreenOn + settings.keepScreenOn.value = value + screenOn = value } fun updateVictoryPoints(value: Int) { - settings.setVictoryPoints(value) - victoryPoints = settings.victoryPoints + settings.victoryPoints.value = value + victoryPoints = value } } \ No newline at end of file