clean up settings and some other things

This commit is contained in:
2024-07-16 02:12:19 +03:00
parent 46d3fe63fa
commit b252c03be7
60 changed files with 698 additions and 613 deletions
@@ -0,0 +1,190 @@
package com.meloda.app.fast.datastore
import android.content.SharedPreferences
import androidx.core.content.edit
import com.meloda.app.fast.common.model.DarkMode
import kotlin.properties.Delegates
import kotlin.reflect.KClass
object AppSettings {
private var preferences: SharedPreferences by Delegates.notNull()
fun init(preferences: SharedPreferences) {
this.preferences = preferences
}
fun edit(
commit: Boolean = false,
action: SharedPreferences.Editor.() -> Unit
) {
preferences.edit(commit, action)
}
fun getString(key: String, defaultValue: String?): String? {
return preferences.getString(key, defaultValue)
}
fun getBoolean(key: String, defaultValue: Boolean): Boolean {
return preferences.getBoolean(key, defaultValue)
}
fun getInt(key: String, defaultValue: Int): Int {
return preferences.getInt(key, defaultValue)
}
fun getLong(key: String, defaultValue: Long): Long {
return preferences.getLong(key, defaultValue)
}
fun getFloat(key: String, defaultValue: Float): Float {
return preferences.getFloat(key, defaultValue)
}
@Suppress("UNCHECKED_CAST")
fun <T : Any> get(clazz: KClass<T>, key: String, defaultValue: T): T {
return when (clazz) {
String::class -> getString(key, defaultValue as String)
Boolean::class -> getBoolean(key, defaultValue as Boolean)
Int::class -> getInt(key, defaultValue as Int)
Long::class -> getLong(key, defaultValue as Long)
Float::class -> getFloat(key, defaultValue as Float)
else -> throw IllegalStateException("Unsupported class: $clazz")
} as T
}
inline fun <reified T> get(key: String, defaultValue: T): T {
return when (T::class) {
String::class -> getString(key, defaultValue as String)
Boolean::class -> getBoolean(key, defaultValue as Boolean)
Int::class -> getInt(key, defaultValue as Int)
Long::class -> getLong(key, defaultValue as Long)
Float::class -> getFloat(key, defaultValue as Float)
else -> throw IllegalStateException("Unsupported class: ${T::class}")
} as T
}
fun <T> put(key: String, newValue: T?) {
preferences.edit {
when (newValue) {
is String -> putString(key, newValue)
is Boolean -> putBoolean(key, newValue)
is Int -> putInt(key, newValue)
is Long -> putLong(key, newValue)
is Float -> putFloat(key, newValue)
}
}
}
var deviceId: String
get() = get("device_id", "")
set(value) = put("device_id", value)
object General {
var useContactNames: Boolean
get() = get(
SettingsKeys.KEY_USE_CONTACT_NAMES,
SettingsKeys.DEFAULT_VALUE_USE_CONTACT_NAMES
)
set(value) = put(SettingsKeys.KEY_USE_CONTACT_NAMES, value)
var enablePullToRefresh: Boolean
get() = get(
SettingsKeys.KEY_ENABLE_PULL_TO_REFRESH,
SettingsKeys.DEFAULT_VALUE_ENABLE_PULL_TO_REFRESH
)
set(value) = put(SettingsKeys.KEY_ENABLE_PULL_TO_REFRESH, value)
}
object Appearance {
var enableMultiline: Boolean
get() = get(
SettingsKeys.KEY_APPEARANCE_MULTILINE,
SettingsKeys.DEFAULT_VALUE_MULTILINE
)
set(value) = put(SettingsKeys.KEY_APPEARANCE_MULTILINE, value)
var darkMode: DarkMode
get() = get(
SettingsKeys.KEY_APPEARANCE_DARK_MODE,
SettingsKeys.DEFAULT_VALUE_APPEARANCE_DARK_MODE
).let(DarkMode.Companion::parse)
set(mode) = put(SettingsKeys.KEY_APPEARANCE_DARK_MODE, mode.value)
var enableAmoledDark: Boolean
get() = get(
SettingsKeys.KEY_APPEARANCE_AMOLED_THEME,
SettingsKeys.DEFAULT_VALUE_APPEARANCE_AMOLED_THEME
)
set(value) = put(SettingsKeys.KEY_APPEARANCE_AMOLED_THEME, value)
var enableDynamicColors: Boolean
get() = get(
SettingsKeys.KEY_USE_DYNAMIC_COLORS,
SettingsKeys.DEFAULT_VALUE_USE_DYNAMIC_COLORS
)
set(value) = put(SettingsKeys.KEY_USE_DYNAMIC_COLORS, value)
var appLanguage: String
get() = get(
SettingsKeys.KEY_APPEARANCE_LANGUAGE,
SettingsKeys.DEFAULT_APPEARANCE_LANGUAGE
)
set(value) = put(SettingsKeys.KEY_APPEARANCE_LANGUAGE, value)
}
object Features {
var fastText: String
get() = get(
SettingsKeys.KEY_FEATURES_FAST_TEXT,
SettingsKeys.DEFAULT_VALUE_FEATURES_FAST_TEXT
)
set(value) = put(SettingsKeys.KEY_FEATURES_FAST_TEXT, value)
}
object Activity {
var sendOnlineStatus: Boolean
get() = get(
SettingsKeys.KEY_ACTIVITY_SEND_ONLINE_STATUS,
SettingsKeys.DEFAULT_VALUE_KEY_ACTIVITY_SEND_ONLINE_STATUS
)
set(value) = put(SettingsKeys.KEY_ACTIVITY_SEND_ONLINE_STATUS, value)
}
object Debug {
var showAlertAfterCrash: Boolean
get() = get(
SettingsKeys.KEY_DEBUG_SHOW_CRASH_ALERT,
true
)
set(value) = put(SettingsKeys.KEY_DEBUG_SHOW_CRASH_ALERT, value)
var longPollInBackground: Boolean
get() = get(
SettingsKeys.KEY_FEATURES_LONG_POLL_IN_BACKGROUND,
SettingsKeys.DEFAULT_VALUE_FEATURES_LONG_POLL_IN_BACKGROUND
)
set(value) = put(SettingsKeys.KEY_FEATURES_LONG_POLL_IN_BACKGROUND, value)
var useBlur: Boolean
get() = get(
SettingsKeys.KEY_APPEARANCE_USE_BLUR,
SettingsKeys.DEFAULT_VALUE_KEY_APPEARANCE_USE_BLUR
)
set(value) = put(SettingsKeys.KEY_APPEARANCE_USE_BLUR, value)
var showEmojiButton: Boolean
get() = get(
SettingsKeys.KEY_SHOW_EMOJI_BUTTON,
SettingsKeys.DEFAULT_VALUE_KEY_SHOW_EMOJI_BUTTON
)
set(value) = put(SettingsKeys.KEY_SHOW_EMOJI_BUTTON, value)
var showDebugCategory: Boolean
get() = get(
SettingsKeys.KEY_SHOW_DEBUG_CATEGORY,
false
)
set(value) = put(SettingsKeys.KEY_SHOW_DEBUG_CATEGORY, value)
}
}
@@ -1,57 +0,0 @@
package com.meloda.app.fast.datastore
import android.content.res.Configuration
import android.content.res.Resources
import android.os.PowerManager
import androidx.appcompat.app.AppCompatDelegate
fun isUsingDarkMode(
resources: Resources,
powerManager: PowerManager,
): Boolean {
val nightThemeMode: Int = SettingsController.getInt(
SettingsKeys.KEY_APPEARANCE_DARK_THEME,
SettingsKeys.DEFAULT_VALUE_APPEARANCE_DARK_THEME
)
val appForceDarkMode = nightThemeMode == AppCompatDelegate.MODE_NIGHT_YES
val appBatterySaver = nightThemeMode == AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY
val systemUiNightMode = resources.configuration.uiMode
val isSystemBatterySaver = powerManager.isPowerSaveMode
val isSystemUsingDarkTheme =
systemUiNightMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
return appForceDarkMode || (appBatterySaver && isSystemBatterySaver) || (!appBatterySaver && isSystemUsingDarkTheme && nightThemeMode == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
}
fun isUsingDynamicColors(): Boolean = SettingsController.getBoolean(
SettingsKeys.KEY_USE_DYNAMIC_COLORS,
SettingsKeys.DEFAULT_VALUE_USE_DYNAMIC_COLORS
)
fun isUsingAmoledBackground(): Boolean = SettingsController.getBoolean(
SettingsKeys.KEY_APPEARANCE_AMOLED_THEME,
SettingsKeys.DEFAULT_VALUE_APPEARANCE_AMOLED_THEME
)
fun selectedColorScheme(): Int = SettingsController.getInt(
SettingsKeys.KEY_APPEARANCE_COLOR_SCHEME,
SettingsKeys.DEFAULT_VALUE_APPEARANCE_COLOR_SCHEME
)
fun isUsingBlur(): Boolean = SettingsController.getBoolean(
SettingsKeys.KEY_APPEARANCE_BLUR,
SettingsKeys.DEFAULT_VALUE_KEY_APPEARANCE_BLUR
)
fun isDebugSettingsShown(): Boolean = SettingsController.getBoolean(
SettingsKeys.KEY_SHOW_DEBUG_CATEGORY,
false
)
fun isMultiline(): Boolean = SettingsController.getBoolean(
SettingsKeys.KEY_APPEARANCE_MULTILINE,
SettingsKeys.DEFAULT_VALUE_MULTILINE
)
@@ -1,95 +0,0 @@
package com.meloda.app.fast.datastore
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.Delegates
import kotlin.reflect.KClass
object SettingsController {
private var preferences: SharedPreferences by Delegates.notNull()
fun init(preferences: SharedPreferences) {
this.preferences = preferences
}
fun edit(
commit: Boolean = false,
action: SharedPreferences.Editor.() -> Unit
) {
preferences.edit(commit, action)
}
fun getString(key: String, defaultValue: String?): String? {
return preferences.getString(key, defaultValue)
}
fun getBoolean(key: String, defaultValue: Boolean): Boolean {
return preferences.getBoolean(key, defaultValue)
}
fun getInt(key: String, defaultValue: Int): Int {
return preferences.getInt(key, defaultValue)
}
fun getLong(key: String, defaultValue: Long): Long {
return preferences.getLong(key, defaultValue)
}
fun getFloat(key: String, defaultValue: Float): Float {
return preferences.getFloat(key, defaultValue)
}
@Suppress("UNCHECKED_CAST")
fun <T : Any> get(clazz: KClass<T>, key: String, defaultValue: T): T {
return when (clazz) {
String::class -> getString(key, defaultValue as String)
Boolean::class -> getBoolean(key, defaultValue as Boolean)
Int::class -> getInt(key, defaultValue as Int)
Long::class -> getLong(key, defaultValue as Long)
Float::class -> getFloat(key, defaultValue as Float)
else -> throw IllegalStateException("Unsupported class: $clazz")
} as T
}
inline fun <reified T> get(key: String, defaultValue: T): T {
return when (T::class) {
String::class -> getString(key, defaultValue as String)
Boolean::class -> getBoolean(key, defaultValue as Boolean)
Int::class -> getInt(key, defaultValue as Int)
Long::class -> getLong(key, defaultValue as Long)
Float::class -> getFloat(key, defaultValue as Float)
else -> throw IllegalStateException("Unsupported class: ${T::class}")
} as T
}
fun <T> put(key: String, newValue: T?) {
preferences.edit {
when (newValue) {
is String -> putString(key, newValue)
is Boolean -> putBoolean(key, newValue)
is Int -> putInt(key, newValue)
is Long -> putLong(key, newValue)
is Float -> putFloat(key, newValue)
}
}
}
var isLongPollInBackgroundEnabled: Boolean
get() = get(
SettingsKeys.KEY_FEATURES_LONG_POLL_IN_BACKGROUND,
SettingsKeys.DEFAULT_VALUE_FEATURES_LONG_POLL_IN_BACKGROUND
)
set(value) = put(SettingsKeys.KEY_FEATURES_LONG_POLL_IN_BACKGROUND, value)
var deviceId: String
get() = get("device_id", "")
set(value) = put("device_id", value)
var enablePullToRefresh: Boolean
get() = get(
SettingsKeys.KEY_ENABLE_PULL_TO_REFRESH,
SettingsKeys.DEFAULT_VALUE_ENABLE_PULL_TO_REFRESH
)
set(value) = put(SettingsKeys.KEY_ENABLE_PULL_TO_REFRESH, value)
}
@@ -17,8 +17,8 @@ object SettingsKeys {
const val KEY_APPEARANCE = "appearance"
const val KEY_APPEARANCE_MULTILINE = "appearance_multiline"
const val DEFAULT_VALUE_MULTILINE = true
const val KEY_APPEARANCE_DARK_THEME = "appearance_appearance_dark_theme"
const val DEFAULT_VALUE_APPEARANCE_DARK_THEME = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
const val KEY_APPEARANCE_DARK_MODE = "appearance_appearance_dark_mode"
const val DEFAULT_VALUE_APPEARANCE_DARK_MODE = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
const val KEY_APPEARANCE_AMOLED_THEME = "appearance_amoled_theme"
const val DEFAULT_VALUE_APPEARANCE_AMOLED_THEME = false
const val KEY_USE_DYNAMIC_COLORS = "appearance_use_dynamic_colors"
@@ -26,8 +26,9 @@ object SettingsKeys {
const val KEY_APPEARANCE_COLOR_SCHEME = "appearance_color_scheme"
const val DEFAULT_VALUE_APPEARANCE_COLOR_SCHEME = 0
const val KEY_APPEARANCE_LANGUAGE = "appearance_language"
const val KEY_APPEARANCE_BLUR = "appearance_blur"
const val DEFAULT_VALUE_KEY_APPEARANCE_BLUR = false
const val DEFAULT_APPEARANCE_LANGUAGE = ""
const val KEY_APPEARANCE_USE_BLUR = "appearance_use_blur"
const val DEFAULT_VALUE_KEY_APPEARANCE_USE_BLUR = false
const val KEY_FEATURES_FAST_TEXT = "features_fast_text"
const val DEFAULT_VALUE_FEATURES_FAST_TEXT = "¯\\_(ツ)_/¯"
@@ -1,144 +1,124 @@
package com.meloda.app.fast.datastore
import android.content.res.Resources
import android.os.PowerManager
import android.util.Log
import com.meloda.app.fast.datastore.model.LongPollState
import com.meloda.app.fast.ui.model.ThemeConfig
import com.meloda.app.fast.common.model.DarkMode
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
interface UserSettings {
val theme: StateFlow<ThemeConfig>
val longPollStateToApply: StateFlow<LongPollState>
val longPollCurrentState: StateFlow<LongPollState>
val online: StateFlow<Boolean>
val debugSettingsEnabled: StateFlow<Boolean>
val useContactNames: StateFlow<Boolean>
val language: StateFlow<String>
val enablePullToRefresh: StateFlow<Boolean>
fun updateUsingDarkTheme()
fun useDarkThemeChanged(use: Boolean)
fun useAmoledThemeChanged(use: Boolean)
fun useDynamicColorsChanged(use: Boolean)
fun useBlurChanged(use: Boolean)
fun useMultiline(use: Boolean)
fun setLongPollStateToApply(newState: LongPollState)
fun updateLongPollCurrentState(currentState: LongPollState)
fun setOnline(use: Boolean)
fun enableDebugSettings(enable: Boolean)
val enableMultiline: StateFlow<Boolean>
val darkMode: StateFlow<DarkMode>
val enableAmoledDark: StateFlow<Boolean>
val enableDynamicColors: StateFlow<Boolean>
val appLanguage: StateFlow<String>
val fastText: StateFlow<String>
val sendOnlineStatus: StateFlow<Boolean>
val showAlertAfterCrash: StateFlow<Boolean>
val longPollInBackground: StateFlow<Boolean>
val useBlur: StateFlow<Boolean>
val showEmojiButton: StateFlow<Boolean>
val showDebugCategory: StateFlow<Boolean>
fun onUseContactNamesChanged(use: Boolean)
fun onLanguageChanged(newLanguage: String)
fun onEnablePullToRefreshChanged(enable: Boolean)
fun onEnableMultilineChanged(enable: Boolean)
fun onDarkModeChanged(mode: DarkMode)
fun onEnableAmoledDarkChanged(enable: Boolean)
fun onEnableDynamicColorsChanged(enable: Boolean)
fun onAppLanguageChanged(language: String)
fun onFastTextChanged(text: String)
fun onSendOnlineStatusChanged(send: Boolean)
fun onShowAlertAfterCrashChanged(show: Boolean)
fun onLongPollInBackgroundChanged(inBackground: Boolean)
fun onUseBlurChanged(use: Boolean)
fun onShowEmojiButtonChanged(show: Boolean)
fun onShowDebugCategoryChanged(show: Boolean)
}
class UserSettingsImpl(
private val resources: Resources,
private val powerManager: PowerManager
) : UserSettings {
class UserSettingsImpl : UserSettings {
override val theme = MutableStateFlow(
ThemeConfig(
usingDarkStyle = isUsingDarkMode(resources, powerManager),
usingDynamicColors = isUsingDynamicColors(),
selectedColorScheme = selectedColorScheme(),
usingAmoledBackground = isUsingAmoledBackground(),
usingBlur = isUsingBlur(),
isMultiline = isMultiline(),
isDeviceCompact = false
)
)
override val useContactNames = MutableStateFlow(AppSettings.General.useContactNames)
override val enablePullToRefresh = MutableStateFlow(AppSettings.General.enablePullToRefresh)
override val longPollStateToApply = MutableStateFlow<LongPollState>(LongPollState.Stopped)
override val longPollCurrentState = MutableStateFlow<LongPollState>(LongPollState.Stopped)
override val enableMultiline = MutableStateFlow(AppSettings.Appearance.enableMultiline)
override val darkMode = MutableStateFlow(AppSettings.Appearance.darkMode)
override val enableAmoledDark = MutableStateFlow(AppSettings.Appearance.enableAmoledDark)
override val enableDynamicColors = MutableStateFlow(AppSettings.Appearance.enableDynamicColors)
override val appLanguage = MutableStateFlow(AppSettings.Appearance.appLanguage)
override val online = MutableStateFlow(
SettingsController.getBoolean(
SettingsKeys.KEY_ACTIVITY_SEND_ONLINE_STATUS,
SettingsKeys.DEFAULT_VALUE_KEY_ACTIVITY_SEND_ONLINE_STATUS
)
)
override val fastText = MutableStateFlow(AppSettings.Features.fastText)
override val debugSettingsEnabled = MutableStateFlow(
SettingsController.getBoolean(
SettingsKeys.KEY_SHOW_DEBUG_CATEGORY,
false
)
)
override val sendOnlineStatus = MutableStateFlow(AppSettings.Activity.sendOnlineStatus)
override val useContactNames = MutableStateFlow(
SettingsController.getBoolean(
SettingsKeys.KEY_USE_CONTACT_NAMES,
SettingsKeys.DEFAULT_VALUE_USE_CONTACT_NAMES
)
)
override val language = MutableStateFlow("")
override val enablePullToRefresh = MutableStateFlow(SettingsController.enablePullToRefresh)
override fun updateUsingDarkTheme() {
useDarkThemeChanged(
isUsingDarkMode(
resources = resources,
powerManager = powerManager,
)
)
}
override fun useDarkThemeChanged(use: Boolean) {
theme.value = theme.value.copy(
usingDarkStyle = use
)
}
override fun useAmoledThemeChanged(use: Boolean) {
theme.value = theme.value.copy(
usingAmoledBackground = use
)
}
override fun useDynamicColorsChanged(use: Boolean) {
theme.value = theme.value.copy(usingDynamicColors = use)
}
override fun useBlurChanged(use: Boolean) {
theme.value = theme.value.copy(usingBlur = use)
}
override fun useMultiline(use: Boolean) {
theme.value = theme.value.copy(isMultiline = use)
}
override fun setLongPollStateToApply(newState: LongPollState) {
longPollStateToApply.update { newState }
Log.d("UserSettings", "setLongPollState: $newState")
}
override fun updateLongPollCurrentState(currentState: LongPollState) {
longPollCurrentState.update { currentState }
Log.d("UserSettings", "updateLongPollCurrentState: $currentState")
}
override fun setOnline(use: Boolean) {
online.value = use
}
override fun enableDebugSettings(enable: Boolean) {
debugSettingsEnabled.update { enable }
}
override val showAlertAfterCrash = MutableStateFlow(AppSettings.Debug.showAlertAfterCrash)
override val longPollInBackground = MutableStateFlow(AppSettings.Debug.longPollInBackground)
override val useBlur = MutableStateFlow(AppSettings.Debug.useBlur)
override val showEmojiButton = MutableStateFlow(AppSettings.Debug.showEmojiButton)
override val showDebugCategory = MutableStateFlow(AppSettings.Debug.showDebugCategory)
override fun onUseContactNamesChanged(use: Boolean) {
useContactNames.update { use }
}
override fun onLanguageChanged(newLanguage: String) {
language.update { newLanguage }
useContactNames.value = use
}
override fun onEnablePullToRefreshChanged(enable: Boolean) {
enablePullToRefresh.update { enable }
enablePullToRefresh.value = enable
}
override fun onEnableMultilineChanged(enable: Boolean) {
enableMultiline.value = enable
}
override fun onDarkModeChanged(mode: DarkMode) {
darkMode.value = mode
}
override fun onEnableAmoledDarkChanged(enable: Boolean) {
enableAmoledDark.value = enable
}
override fun onEnableDynamicColorsChanged(enable: Boolean) {
enableDynamicColors.value = enable
}
override fun onAppLanguageChanged(language: String) {
appLanguage.value = language
}
override fun onFastTextChanged(text: String) {
fastText.value = text
}
override fun onSendOnlineStatusChanged(send: Boolean) {
sendOnlineStatus.value = send
}
override fun onShowAlertAfterCrashChanged(show: Boolean) {
showAlertAfterCrash.value = show
}
override fun onLongPollInBackgroundChanged(inBackground: Boolean) {
longPollInBackground.value = inBackground
}
override fun onUseBlurChanged(use: Boolean) {
useBlur.value = use
}
override fun onShowEmojiButtonChanged(show: Boolean) {
showEmojiButton.value = show
}
override fun onShowDebugCategoryChanged(show: Boolean) {
showDebugCategory.value = show
}
}
@@ -1,14 +0,0 @@
package com.meloda.app.fast.datastore.model
sealed class LongPollState {
data object Stopped : LongPollState()
// TODO: 15/07/2024, Danil Nikolaev: support for android 15
// data object Terminated : LongPollState()
data object InApp : LongPollState()
data object Background : LongPollState()
data object Exception : LongPollState()
fun isLaunched(): Boolean = this in listOf(InApp, Background)
}