Settings as state flow.

This commit is contained in:
2023-09-08 18:20:56 +02:00
parent 6a96749501
commit 27cb2f670b
4 changed files with 73 additions and 120 deletions

View File

@@ -34,6 +34,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
@@ -44,7 +45,6 @@ import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.zobrist.tichucounter.domain.DrawerItem import me.zobrist.tichucounter.domain.DrawerItem
import me.zobrist.tichucounter.domain.ISystemSettingsChangeListener
import me.zobrist.tichucounter.domain.KeepScreenOn import me.zobrist.tichucounter.domain.KeepScreenOn
import me.zobrist.tichucounter.domain.Language import me.zobrist.tichucounter.domain.Language
import me.zobrist.tichucounter.domain.ReviewService import me.zobrist.tichucounter.domain.ReviewService
@@ -69,7 +69,7 @@ import me.zobrist.tichucounter.ui.settings.SettingsViewModel
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class MainActivity : AppCompatActivity(), ISystemSettingsChangeListener { class MainActivity : AppCompatActivity() {
@Inject @Inject
lateinit var settingsAdapter: SettingsAdapter lateinit var settingsAdapter: SettingsAdapter
@@ -86,7 +86,26 @@ class MainActivity : AppCompatActivity(), ISystemSettingsChangeListener {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) 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 = { mainViewModel.onNewGame = {
reviewService.request() reviewService.request()
@@ -101,16 +120,11 @@ class MainActivity : AppCompatActivity(), ISystemSettingsChangeListener {
} }
} }
override fun onDestroy() { private fun changeLanguage(language: Language) {
super.onDestroy()
settingsAdapter.unregisterOnChangeListener(this)
}
override fun onLanguageChanged(language: Language) {
AppCompatDelegate.setApplicationLocales(language.value) AppCompatDelegate.setApplicationLocales(language.value)
} }
override fun onThemeChanged(theme: Theme) { private fun changeTheme(theme: Theme) {
val themeValue = when (theme) { val themeValue = when (theme) {
Theme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO Theme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
Theme.DARK -> AppCompatDelegate.MODE_NIGHT_YES Theme.DARK -> AppCompatDelegate.MODE_NIGHT_YES
@@ -119,7 +133,7 @@ class MainActivity : AppCompatActivity(), ISystemSettingsChangeListener {
AppCompatDelegate.setDefaultNightMode(themeValue) AppCompatDelegate.setDefaultNightMode(themeValue)
} }
override fun onScreenOnChanged(keepOn: KeepScreenOn) { private fun setKeepScreenOn(keepOn: KeepScreenOn) {
if (keepOn.value) { if (keepOn.value) {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} else { } else {

View File

@@ -4,6 +4,10 @@ import android.content.Context
import androidx.core.os.LocaleListCompat import androidx.core.os.LocaleListCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import dagger.hilt.android.qualifiers.ApplicationContext 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.Inject
import javax.inject.Singleton import javax.inject.Singleton
@@ -18,99 +22,64 @@ enum class KeepScreenOn(val value: Boolean) { ON(true), OFF(false) }
typealias VictoryPoints = Int 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 @Singleton
class SettingsAdapter @Inject constructor(@ApplicationContext private val context: Context) { class SettingsAdapter @Inject constructor(@ApplicationContext private val context: Context) {
private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
private var listenerList = mutableListOf<ISettingsChangeListener>()
var language: Language val language = MutableStateFlow(Language.DEFAULT)
private set
var theme: Theme val theme = MutableStateFlow(Theme.DEFAULT)
private set
var keepScreenOn: KeepScreenOn val keepScreenOn = MutableStateFlow(KeepScreenOn.OFF)
private set
var victoryPoints: Int val victoryPoints = MutableStateFlow(0)
private set
init { init {
language = try { language.value = try {
enumValueOf(sharedPreferences.getString(Language::class.simpleName, null)!!) enumValueOf(sharedPreferences.getString(Language::class.simpleName, null)!!)
} catch (_: NullPointerException) { } catch (_: NullPointerException) {
Language.DEFAULT Language.DEFAULT
} }
theme = try { theme.value = try {
enumValueOf(sharedPreferences.getString(Theme::class.simpleName, null)!!) enumValueOf(sharedPreferences.getString(Theme::class.simpleName, null)!!)
} catch (_: java.lang.Exception) { } catch (_: java.lang.Exception) {
Theme.DEFAULT Theme.DEFAULT
} }
keepScreenOn = try { keepScreenOn.value = try {
enumValueOf(sharedPreferences.getString(KeepScreenOn::class.simpleName, null)!!) enumValueOf(sharedPreferences.getString(KeepScreenOn::class.simpleName, null)!!)
} catch (_: java.lang.Exception) { } catch (_: java.lang.Exception) {
KeepScreenOn.OFF 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) { CoroutineScope(Dispatchers.IO).launch {
listener.onThemeChanged(theme) language.collect {
listener.onLanguageChanged(language) updatePreference(Language::class.simpleName, it.name)
listener.onScreenOnChanged(keepScreenOn) }
} }
if (listener is IGameSettingsChangeListener) { CoroutineScope(Dispatchers.IO).launch {
listener.onVictoryPointsChanged(victoryPoints) theme.collect {
updatePreference(Theme::class.simpleName, it.name)
}
} }
}
fun unregisterOnChangeListener(listener: ISettingsChangeListener?) { CoroutineScope(Dispatchers.IO).launch {
if (listener != null) { keepScreenOn.collect {
listenerList.remove(listener) updatePreference(KeepScreenOn::class.simpleName, it.name)
}
} }
}
fun setLanguage(language: Language) { CoroutineScope(Dispatchers.IO).launch {
this.language = language victoryPoints.collect {
updatePreference(Language::class.simpleName, language.name) updatePreference(VictoryPoints::class.simpleName, it)
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)
} }
private fun updatePreference(name: String?, value: String) { private fun updatePreference(name: String?, value: String) {
@@ -130,28 +99,4 @@ class SettingsAdapter @Inject constructor(@ApplicationContext private val contex
editor.putInt(name, value) editor.putInt(name, value)
editor.apply() editor.apply()
} }
private fun notifyListeners(language: Language) {
listenerList.filterIsInstance<ISystemSettingsChangeListener>().forEach {
it.onLanguageChanged(language)
}
}
private fun notifyListeners(theme: Theme) {
listenerList.filterIsInstance<ISystemSettingsChangeListener>().forEach {
it.onThemeChanged(theme)
}
}
private fun notifyListeners(victoryPoints: VictoryPoints) {
listenerList.filterIsInstance<IGameSettingsChangeListener>().forEach {
it.onVictoryPointsChanged(victoryPoints)
}
}
private fun notifyListeners(keepScreenOn: KeepScreenOn) {
listenerList.filterIsInstance<ISystemSettingsChangeListener>().forEach {
it.onScreenOnChanged(keepScreenOn)
}
}
} }

View File

@@ -13,7 +13,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.zobrist.tichucounter.data.entity.Game import me.zobrist.tichucounter.data.entity.Game
import me.zobrist.tichucounter.data.entity.Round import me.zobrist.tichucounter.data.entity.Round
import me.zobrist.tichucounter.domain.IGameSettingsChangeListener
import me.zobrist.tichucounter.domain.SettingsAdapter import me.zobrist.tichucounter.domain.SettingsAdapter
import me.zobrist.tichucounter.domain.Tichu import me.zobrist.tichucounter.domain.Tichu
import me.zobrist.tichucounter.domain.digitCount import me.zobrist.tichucounter.domain.digitCount
@@ -73,7 +72,7 @@ class CounterViewModel @Inject constructor(
private val gameRepository: GameRepository, private val gameRepository: GameRepository,
private val settings: SettingsAdapter private val settings: SettingsAdapter
) : ) :
ViewModel(), ICounterViewModel, IGameSettingsChangeListener { ViewModel(), ICounterViewModel {
override var roundScoreList by mutableStateOf(emptyList<Round>()) override var roundScoreList by mutableStateOf(emptyList<Round>())
private set private set
@@ -188,7 +187,7 @@ class CounterViewModel @Inject constructor(
} }
if (!victoryDialogShown) { if (!victoryDialogShown) {
if (totalScoreA >= settings.victoryPoints || totalScoreB >= settings.victoryPoints) { if (totalScoreA >= settings.victoryPoints.value || totalScoreB >= settings.victoryPoints.value) {
showVictoryDialog = true showVictoryDialog = true
} }
} }
@@ -202,13 +201,11 @@ class CounterViewModel @Inject constructor(
buildTeamNameSuggestions() buildTeamNameSuggestions()
} }
settings.victoryPoints.collect {
victoryDialogShown = false
}
} }
settings.registerOnChangeListener(this)
}
override fun onCleared() {
settings.unregisterOnChangeListener(this)
} }
override fun focusLastInput() { override fun focusLastInput() {
@@ -401,8 +398,4 @@ class CounterViewModel @Inject constructor(
return filtered.sorted().sortedBy { it.length }.take(10) return filtered.sorted().sortedBy { it.length }.take(10)
} }
override fun onVictoryPointsChanged(victoryPoints: Int) {
victoryDialogShown = false
}
} }

View File

@@ -1,6 +1,7 @@
package me.zobrist.tichucounter.ui.settings package me.zobrist.tichucounter.ui.settings
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
@@ -15,35 +16,35 @@ import javax.inject.Inject
class SettingsViewModel @Inject constructor(private val settings: SettingsAdapter) : ViewModel() { class SettingsViewModel @Inject constructor(private val settings: SettingsAdapter) : ViewModel() {
var language by mutableStateOf(settings.language) var language by mutableStateOf(settings.language.value)
private set private set
var theme by mutableStateOf(settings.theme) var theme by mutableStateOf(settings.theme.value)
private set private set
var screenOn by mutableStateOf(settings.keepScreenOn) var screenOn by mutableStateOf(settings.keepScreenOn.value)
private set private set
var victoryPoints by mutableStateOf(settings.victoryPoints) var victoryPoints by mutableIntStateOf(settings.victoryPoints.value)
private set private set
fun updateLanguage(language: Language) { fun updateLanguage(language: Language) {
settings.setLanguage(language) settings.language.value = language
this.language = settings.language this.language = language
} }
fun updateTheme(theme: Theme) { fun updateTheme(theme: Theme) {
settings.setTheme(theme) settings.theme.value = theme
this.theme = settings.theme this.theme = theme
} }
fun updateScreenOn(value: KeepScreenOn) { fun updateScreenOn(value: KeepScreenOn) {
settings.setKeepScreenOn(value) settings.keepScreenOn.value = value
screenOn = settings.keepScreenOn screenOn = value
} }
fun updateVictoryPoints(value: Int) { fun updateVictoryPoints(value: Int) {
settings.setVictoryPoints(value) settings.victoryPoints.value = value
victoryPoints = settings.victoryPoints victoryPoints = value
} }
} }