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,27 @@
package com.meloda.app.fast.common
import com.meloda.app.fast.common.model.LongPollState
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
interface LongPollController {
val currentState: StateFlow<LongPollState>
val stateToApply: StateFlow<LongPollState>
fun updateCurrentState(newState: LongPollState)
fun setStateToApply(newState: LongPollState)
}
class LongPollControllerImpl : LongPollController {
override val currentState = MutableStateFlow<LongPollState>(LongPollState.Stopped)
override val stateToApply = MutableStateFlow<LongPollState>(LongPollState.Stopped)
override fun updateCurrentState(newState: LongPollState) {
currentState.value = newState
}
override fun setStateToApply(newState: LongPollState) {
currentState.value = newState
}
}
@@ -1,6 +1,12 @@
package com.meloda.app.fast.common.di
import coil.ImageLoader
import com.meloda.app.fast.common.LongPollController
import com.meloda.app.fast.common.LongPollControllerImpl
import com.meloda.app.fast.common.provider.ResourceProvider
import com.meloda.app.fast.common.provider.ResourceProviderImpl
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.bind
import org.koin.dsl.module
val commonModule = module {
@@ -9,4 +15,7 @@ val commonModule = module {
.crossfade(true)
.build()
}
singleOf(::LongPollControllerImpl) bind LongPollController::class
singleOf(::ResourceProviderImpl) bind ResourceProvider::class
}
@@ -1,4 +1,4 @@
package com.meloda.app.fast.common
package com.meloda.app.fast.common.extensions
import android.os.Bundle
import android.os.Parcelable
@@ -1,3 +1,3 @@
package com.meloda.app.fast.common
package com.meloda.app.fast.common.model
data class ApiLanguage(val value: String)
@@ -0,0 +1,19 @@
package com.meloda.app.fast.common.model
private const val MODE_NIGHT_NO = 1
private const val MODE_NIGHT_YES = 2
private const val MODE_NIGHT_FOLLOW_SYSTEM = -1
private const val MODE_NIGHT_AUTO_BATTERY = 3
enum class DarkMode(val value: Int) {
DISABLED(MODE_NIGHT_NO),
FOLLOW_SYSTEM(MODE_NIGHT_FOLLOW_SYSTEM),
AUTO_BATTERY(MODE_NIGHT_AUTO_BATTERY),
ENABLED(MODE_NIGHT_YES);
companion object {
fun parse(value: Int): DarkMode = entries.firstOrNull { it.value == value }
?: throw IllegalArgumentException("Unknown dark mode with value: $value")
}
}
@@ -1,5 +1,4 @@
package com.meloda.app.fast.datastore.model
package com.meloda.app.fast.common.model
sealed class LongPollState {
data object Stopped : LongPollState()
@@ -1,4 +1,4 @@
package com.meloda.app.fast.common
package com.meloda.app.fast.common.model
import android.graphics.drawable.Drawable
import androidx.annotation.ColorInt
@@ -1,4 +1,4 @@
package com.meloda.app.fast.common
package com.meloda.app.fast.common.model
import android.content.res.Resources
import androidx.annotation.PluralsRes
@@ -18,6 +18,8 @@ sealed class UiText {
data class Simple(val text: String) : UiText()
data class QuantityResource(@PluralsRes val resId: Int, val quantity: Int) : UiText()
}
fun UiText?.parseString(resources: Resources): String? {
@@ -0,0 +1,15 @@
package com.meloda.app.fast.common.provider
import android.content.res.Resources
interface ResourceProvider {
fun getString(resId: Int): String
}
class ResourceProviderImpl(private val resources: Resources) : ResourceProvider {
override fun getString(resId: Int): String {
return resources.getString(resId)
}
}
@@ -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,7 +1,7 @@
package com.meloda.app.fast.network.interceptor
import androidx.core.net.toUri
import com.meloda.app.fast.common.ApiLanguage
import com.meloda.app.fast.common.model.ApiLanguage
import com.meloda.app.fast.common.provider.Provider
import okhttp3.Interceptor
import okhttp3.Response
@@ -22,7 +22,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.graphics.luminance
import com.meloda.app.fast.ui.theme.LocalIsDarkTheme
import com.meloda.app.fast.ui.theme.LocalThemeConfig
/**
* Default alpha levels used by Material components.
@@ -79,7 +79,7 @@ object ContentAlpha {
lowContrastAlpha: Float
): Float {
val contentColor = LocalContentColor.current
return if (!LocalIsDarkTheme.current) {
return if (!LocalThemeConfig.current.darkMode) {
if (contentColor.luminance() > 0.5) highContrastAlpha else lowContrastAlpha
} else {
if (contentColor.luminance() < 0.5) highContrastAlpha else lowContrastAlpha
@@ -33,8 +33,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import com.meloda.app.fast.common.UiText
import com.meloda.app.fast.common.parseString
import com.meloda.app.fast.common.model.UiText
import com.meloda.app.fast.common.model.parseString
import com.meloda.app.fast.ui.util.ImmutableList
import com.meloda.app.fast.ui.util.ImmutableList.Companion.toImmutableList
import com.meloda.app.fast.ui.util.getString
@@ -1,11 +1,11 @@
package com.meloda.app.fast.ui.model
data class ThemeConfig(
val usingDarkStyle: Boolean,
val usingDynamicColors: Boolean,
val darkMode: Boolean,
val dynamicColors: Boolean,
val selectedColorScheme: Int,
val usingAmoledBackground: Boolean,
val usingBlur: Boolean,
val isMultiline: Boolean,
val amoledDark: Boolean,
val enableBlur: Boolean,
val enableMultiline: Boolean,
val isDeviceCompact: Boolean
)
@@ -9,7 +9,6 @@ import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.graphics.Color
@@ -104,20 +103,18 @@ private val robotoFonts = FontFamily(
)
)
val LocalTheme = compositionLocalOf {
val LocalThemeConfig = compositionLocalOf {
ThemeConfig(
usingDarkStyle = false,
usingDynamicColors = false,
darkMode = false,
dynamicColors = false,
selectedColorScheme = 0,
usingAmoledBackground = false,
usingBlur = false,
isMultiline = false,
amoledDark = false,
enableBlur = false,
enableMultiline = false,
isDeviceCompact = false
)
}
val LocalIsDarkTheme = compositionLocalOf { false }
val LocalHazeState = compositionLocalOf {
HazeState()
}
@@ -181,11 +178,9 @@ fun AppTheme(
}
}
CompositionLocalProvider(LocalIsDarkTheme provides useDarkTheme) {
MaterialTheme(
colorScheme = predefinedColorScheme ?: colorScheme,
typography = typography,
content = content
)
}
MaterialTheme(
colorScheme = predefinedColorScheme ?: colorScheme,
typography = typography,
content = content
)
}
@@ -1,5 +1,7 @@
package com.meloda.app.fast.ui.util
import android.content.res.Configuration
import android.os.PowerManager
import android.view.KeyEvent
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Composable
@@ -10,9 +12,12 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import com.meloda.app.fast.common.UiText
import androidx.core.content.getSystemService
import com.meloda.app.fast.common.model.DarkMode
import com.meloda.app.fast.common.model.UiText
@Composable
fun UiText?.getString(): String? {
@@ -75,3 +80,19 @@ fun LazyListState.isScrollingUp(): Boolean {
}
}.value
}
@Composable
fun isNeedToEnableDarkMode(darkMode: DarkMode): Boolean {
val context = LocalContext.current
val appForceDarkMode = darkMode == DarkMode.ENABLED
val appBatterySaver = darkMode == DarkMode.AUTO_BATTERY
val systemUiNightMode = context.resources.configuration.uiMode
val isSystemBatterySaver = context.getSystemService<PowerManager>()?.isPowerSaveMode == true
val isSystemUsingDarkTheme =
systemUiNightMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
return appForceDarkMode || (appBatterySaver && isSystemBatterySaver) || (!appBatterySaver && isSystemUsingDarkTheme && darkMode == DarkMode.FOLLOW_SYSTEM)
}