diff --git a/app/src/main/kotlin/com/meloda/app/fast/MainViewModel.kt b/app/src/main/kotlin/com/meloda/app/fast/MainViewModel.kt index f52132e9..13145565 100644 --- a/app/src/main/kotlin/com/meloda/app/fast/MainViewModel.kt +++ b/app/src/main/kotlin/com/meloda/app/fast/MainViewModel.kt @@ -9,14 +9,15 @@ import androidx.lifecycle.viewModelScope import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.PermissionStatus import com.meloda.app.fast.auth.AuthGraph +import com.meloda.app.fast.common.LongPollController import com.meloda.app.fast.common.UserConfig import com.meloda.app.fast.common.extensions.ifEmpty import com.meloda.app.fast.common.extensions.listenValue import com.meloda.app.fast.common.extensions.setValue +import com.meloda.app.fast.common.model.LongPollState import com.meloda.app.fast.data.db.GetCurrentAccountUseCase -import com.meloda.app.fast.datastore.SettingsController +import com.meloda.app.fast.datastore.AppSettings import com.meloda.app.fast.datastore.UserSettings -import com.meloda.app.fast.datastore.model.LongPollState import com.meloda.app.fast.model.BaseError import com.meloda.app.fast.navigation.Main import kotlinx.coroutines.Dispatchers @@ -54,7 +55,8 @@ interface MainViewModel { class MainViewModelImpl( private val getCurrentAccountUseCase: GetCurrentAccountUseCase, - private val userSettings: UserSettings + private val userSettings: UserSettings, + private val longPollController: LongPollController ) : MainViewModel, ViewModel() { init { @@ -83,22 +85,21 @@ class MainViewModelImpl( override fun onAppResumed() { if (isNeedToShowNotificationsRationaleDialog.value) { + isNeedToShowNotificationsRationaleDialog.update { false } isNeedToCheckNotificationsPermission.update { true } } - userSettings.onLanguageChanged( - AppCompatDelegate.getApplicationLocales() + val newLanguage = AppCompatDelegate.getApplicationLocales() + .toLanguageTags() + .ifEmpty { null } + ?: LocaleListCompat.getDefault() .toLanguageTags() - .ifEmpty { null } - ?: LocaleListCompat.getDefault() - .toLanguageTags() - .split(",") - .firstOrNull() - .orEmpty() - .take(5) - ) + .split(",") + .firstOrNull() + .orEmpty() + .take(5) - userSettings.updateUsingDarkTheme() + userSettings.onAppLanguageChanged(newLanguage) } @ExperimentalPermissionsApi @@ -151,7 +152,7 @@ class MainViewModelImpl( } private fun listenLongPollState() { - userSettings.longPollStateToApply.listenValue { newState -> + longPollController.stateToApply.listenValue { newState -> if (newState == LongPollState.Background) { isNeedToCheckNotificationsPermission.update { true } } @@ -174,8 +175,8 @@ class MainViewModelImpl( this.trustedHash = currentAccount.trustedHash } - userSettings.setLongPollStateToApply( - if (SettingsController.isLongPollInBackgroundEnabled) { + longPollController.setStateToApply( + if (AppSettings.Debug.longPollInBackground) { LongPollState.Background } else { LongPollState.InApp @@ -191,7 +192,7 @@ class MainViewModelImpl( } private fun disableBackgroundLongPoll() { - SettingsController.isLongPollInBackgroundEnabled = false - userSettings.setLongPollStateToApply(LongPollState.InApp) + AppSettings.Debug.longPollInBackground = false + longPollController.setStateToApply(LongPollState.InApp) } } diff --git a/app/src/main/kotlin/com/meloda/app/fast/common/AppGlobal.kt b/app/src/main/kotlin/com/meloda/app/fast/common/AppGlobal.kt index f76e8f06..400ba776 100644 --- a/app/src/main/kotlin/com/meloda/app/fast/common/AppGlobal.kt +++ b/app/src/main/kotlin/com/meloda/app/fast/common/AppGlobal.kt @@ -5,7 +5,7 @@ import androidx.preference.PreferenceManager import coil.ImageLoader import coil.ImageLoaderFactory import com.meloda.app.fast.common.di.applicationModule -import com.meloda.app.fast.datastore.SettingsController +import com.meloda.app.fast.datastore.AppSettings import org.koin.android.ext.android.get import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger @@ -17,7 +17,7 @@ class AppGlobal : Application(), ImageLoaderFactory { super.onCreate() val preferences = PreferenceManager.getDefaultSharedPreferences(this) - SettingsController.init(preferences) + AppSettings.init(preferences) UserConfig.init(preferences) initKoin() diff --git a/app/src/main/kotlin/com/meloda/app/fast/presentation/MainActivity.kt b/app/src/main/kotlin/com/meloda/app/fast/presentation/MainActivity.kt index 729d7ccc..603ea45a 100644 --- a/app/src/main/kotlin/com/meloda/app/fast/presentation/MainActivity.kt +++ b/app/src/main/kotlin/com/meloda/app/fast/presentation/MainActivity.kt @@ -33,15 +33,17 @@ import com.google.accompanist.permissions.rememberPermissionState import com.meloda.app.fast.MainViewModel import com.meloda.app.fast.MainViewModelImpl import com.meloda.app.fast.common.AppConstants +import com.meloda.app.fast.common.LongPollController import com.meloda.app.fast.common.extensions.isSdkAtLeast -import com.meloda.app.fast.datastore.SettingsController +import com.meloda.app.fast.common.model.LongPollState +import com.meloda.app.fast.datastore.AppSettings import com.meloda.app.fast.datastore.UserSettings -import com.meloda.app.fast.datastore.model.LongPollState import com.meloda.app.fast.service.OnlineService import com.meloda.app.fast.service.longpolling.LongPollingService import com.meloda.app.fast.ui.model.ThemeConfig import com.meloda.app.fast.ui.theme.AppTheme -import com.meloda.app.fast.ui.theme.LocalTheme +import com.meloda.app.fast.ui.theme.LocalThemeConfig +import com.meloda.app.fast.ui.util.isNeedToEnableDarkMode import org.koin.androidx.compose.koinViewModel import org.koin.compose.KoinContext import org.koin.compose.koinInject @@ -53,7 +55,7 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - SettingsController.deviceId = Settings.Secure.getString( + AppSettings.deviceId = Settings.Secure.getString( contentResolver, Settings.Secure.ANDROID_ID ) @@ -78,10 +80,12 @@ class MainActivity : AppCompatActivity() { setContent { KoinContext { val context = LocalContext.current - val userSettings: UserSettings = koinInject() - val longPollCurrentState by userSettings.longPollCurrentState.collectAsStateWithLifecycle() - val longPollStateToApply by userSettings.longPollStateToApply.collectAsStateWithLifecycle() + val userSettings: UserSettings = koinInject() + val longPollController: LongPollController = koinInject() + + val longPollCurrentState by longPollController.currentState.collectAsStateWithLifecycle() + val longPollStateToApply by longPollController.stateToApply.collectAsStateWithLifecycle() val viewModel: MainViewModel = koinViewModel() @@ -136,9 +140,9 @@ class MainActivity : AppCompatActivity() { } } - val isOnline by userSettings.online.collectAsStateWithLifecycle() - LifecycleResumeEffect(isOnline) { - toggleOnlineService(isOnline) + val sendOnline by userSettings.sendOnlineStatus.collectAsStateWithLifecycle() + LifecycleResumeEffect(sendOnline) { + toggleOnlineService(sendOnline) onPauseOrDispose { toggleOnlineService(false) @@ -151,25 +155,22 @@ class MainActivity : AppCompatActivity() { } } - val theme by userSettings.theme.collectAsStateWithLifecycle() - CompositionLocalProvider( - LocalTheme provides ThemeConfig( - usingDarkStyle = theme.usingDarkStyle, - usingDynamicColors = theme.usingDynamicColors, - selectedColorScheme = theme.selectedColorScheme, - usingAmoledBackground = theme.usingAmoledBackground, - usingBlur = theme.usingBlur, - isMultiline = theme.isMultiline, - isDeviceCompact = isDeviceCompact - ) - ) { - val currentTheme = LocalTheme.current + val themeConfig = ThemeConfig( + darkMode = isNeedToEnableDarkMode(userSettings.darkMode.value), + dynamicColors = userSettings.enableDynamicColors.value, + selectedColorScheme = 0, + amoledDark = userSettings.enableAmoledDark.value, + enableBlur = userSettings.useBlur.value, + enableMultiline = userSettings.enableMultiline.value, + isDeviceCompact = isDeviceCompact + ) + CompositionLocalProvider(LocalThemeConfig provides themeConfig) { AppTheme( - useDarkTheme = currentTheme.usingDarkStyle, - useDynamicColors = currentTheme.usingDynamicColors, - selectedColorScheme = currentTheme.selectedColorScheme, - useAmoledBackground = currentTheme.usingAmoledBackground, + useDarkTheme = themeConfig.darkMode, + useDynamicColors = themeConfig.dynamicColors, + selectedColorScheme = themeConfig.selectedColorScheme, + useAmoledBackground = themeConfig.amoledDark, ) { RootScreen(viewModel = viewModel) } @@ -220,7 +221,7 @@ class MainActivity : AppCompatActivity() { private fun toggleLongPollService( enable: Boolean, - inBackground: Boolean = SettingsController.isLongPollInBackgroundEnabled + inBackground: Boolean = AppSettings.Debug.longPollInBackground ) { if (enable) { val longPollIntent = Intent(this, LongPollingService::class.java) @@ -246,7 +247,7 @@ class MainActivity : AppCompatActivity() { private fun stopServices() { toggleOnlineService(enable = false) - val asForeground = SettingsController.isLongPollInBackgroundEnabled + val asForeground = AppSettings.Debug.longPollInBackground if (!asForeground) { toggleLongPollService(enable = false) diff --git a/app/src/main/kotlin/com/meloda/app/fast/presentation/MainScreen.kt b/app/src/main/kotlin/com/meloda/app/fast/presentation/MainScreen.kt index 32d3611a..774a0227 100644 --- a/app/src/main/kotlin/com/meloda/app/fast/presentation/MainScreen.kt +++ b/app/src/main/kotlin/com/meloda/app/fast/presentation/MainScreen.kt @@ -28,7 +28,7 @@ import androidx.navigation.compose.rememberNavController import com.meloda.app.fast.conversations.navigation.conversationsScreen import com.meloda.app.fast.ui.theme.LocalBottomPadding import com.meloda.app.fast.ui.theme.LocalHazeState -import com.meloda.app.fast.ui.theme.LocalTheme +import com.meloda.app.fast.ui.theme.LocalThemeConfig import com.meloda.app.fast.friends.navigation.friendsScreen import com.meloda.app.fast.model.BaseError import com.meloda.app.fast.model.BottomNavigationItem @@ -47,7 +47,7 @@ fun MainScreen( onSettingsButtonClicked: () -> Unit = {}, onConversationItemClicked: (conversationId: Int) -> Unit = {} ) { - val currentTheme = LocalTheme.current + val currentTheme = LocalThemeConfig.current val hazeState = remember { HazeState() } val navController = rememberNavController() @@ -60,7 +60,7 @@ fun MainScreen( NavigationBar( modifier = Modifier .then( - if (currentTheme.usingBlur) { + if (currentTheme.enableBlur) { Modifier.hazeChild( state = hazeState, style = HazeMaterials.thick() @@ -69,7 +69,7 @@ fun MainScreen( ) .fillMaxWidth(), containerColor = NavigationBarDefaults.containerColor.copy( - alpha = if (currentTheme.usingBlur) 0f else 1f + alpha = if (currentTheme.enableBlur) 0f else 1f ) ) { navigationItems.forEachIndexed { index, item -> @@ -105,11 +105,11 @@ fun MainScreen( Box( modifier = Modifier .fillMaxSize() - .padding(bottom = if (currentTheme.usingBlur) 0.dp else padding.calculateBottomPadding()) + .padding(bottom = if (currentTheme.enableBlur) 0.dp else padding.calculateBottomPadding()) ) { CompositionLocalProvider( LocalHazeState provides hazeState, - LocalBottomPadding provides if (currentTheme.usingBlur) padding.calculateBottomPadding() else 0.dp + LocalBottomPadding provides if (currentTheme.enableBlur) padding.calculateBottomPadding() else 0.dp ) { NavHost( navController = navController, diff --git a/app/src/main/kotlin/com/meloda/app/fast/provider/ApiLanguageProvider.kt b/app/src/main/kotlin/com/meloda/app/fast/provider/ApiLanguageProvider.kt index f1d3fd7a..52194bc2 100644 --- a/app/src/main/kotlin/com/meloda/app/fast/provider/ApiLanguageProvider.kt +++ b/app/src/main/kotlin/com/meloda/app/fast/provider/ApiLanguageProvider.kt @@ -1,13 +1,13 @@ package com.meloda.app.fast.provider -import com.meloda.app.fast.common.ApiLanguage +import com.meloda.app.fast.common.model.ApiLanguage import com.meloda.app.fast.common.provider.Provider import com.meloda.app.fast.datastore.UserSettings class ApiLanguageProvider(private val userSettings: UserSettings) : Provider { override fun provide(): ApiLanguage? { - val language = userSettings.language.value + val language = userSettings.appLanguage.value return when { language == "ru-RU" -> "ru" diff --git a/app/src/main/kotlin/com/meloda/app/fast/service/longpolling/LongPollingService.kt b/app/src/main/kotlin/com/meloda/app/fast/service/longpolling/LongPollingService.kt index 936f86e7..d12fa870 100644 --- a/app/src/main/kotlin/com/meloda/app/fast/service/longpolling/LongPollingService.kt +++ b/app/src/main/kotlin/com/meloda/app/fast/service/longpolling/LongPollingService.kt @@ -3,7 +3,6 @@ package com.meloda.app.fast.service.longpolling import android.app.PendingIntent import android.app.Service import android.content.Intent -import android.content.SharedPreferences import android.net.Uri import android.os.Build import android.os.IBinder @@ -13,19 +12,18 @@ import androidx.core.app.NotificationCompat import androidx.core.app.ServiceCompat import com.conena.nanokt.android.app.stopForegroundCompat import com.meloda.app.fast.common.AppConstants +import com.meloda.app.fast.common.LongPollController import com.meloda.app.fast.common.UserConfig import com.meloda.app.fast.common.VkConstants import com.meloda.app.fast.common.extensions.listenValue +import com.meloda.app.fast.common.model.LongPollState import com.meloda.app.fast.data.LongPollUpdatesParser import com.meloda.app.fast.data.LongPollUseCase import com.meloda.app.fast.data.processState -import com.meloda.app.fast.datastore.SettingsController -import com.meloda.app.fast.datastore.SettingsKeys -import com.meloda.app.fast.datastore.UserSettings -import com.meloda.app.fast.datastore.model.LongPollState -import com.meloda.app.fast.ui.R +import com.meloda.app.fast.datastore.AppSettings import com.meloda.app.fast.model.api.data.LongPollUpdates import com.meloda.app.fast.model.api.data.VkLongPollData +import com.meloda.app.fast.ui.R import com.meloda.app.fast.util.NotificationsUtils import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope @@ -40,7 +38,7 @@ import kotlin.coroutines.suspendCoroutine class LongPollingService : Service() { - private val userSettings: UserSettings by inject() + private val longPollController: LongPollController by inject() private val job = SupervisorJob() @@ -51,8 +49,8 @@ class LongPollingService : Service() { throwable.printStackTrace() } - userSettings.updateLongPollCurrentState(LongPollState.Exception) - userSettings.setLongPollStateToApply(LongPollState.Exception) + longPollController.updateCurrentState(LongPollState.Exception) + longPollController.setStateToApply(LongPollState.Exception) } private val coroutineContext: CoroutineContext @@ -62,7 +60,6 @@ class LongPollingService : Service() { private val longPollUseCase: LongPollUseCase by inject() private val updatesParser: LongPollUpdatesParser by inject() - private val preferences: SharedPreferences by inject() private var currentJob: Job? = null @@ -79,10 +76,7 @@ class LongPollingService : Service() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { if (startId > 1) return START_STICKY - val inBackground = preferences.getBoolean( - SettingsKeys.KEY_FEATURES_LONG_POLL_IN_BACKGROUND, - SettingsKeys.DEFAULT_VALUE_FEATURES_LONG_POLL_IN_BACKGROUND - ) + val inBackground = AppSettings.Debug.longPollInBackground Log.d( STATE_TAG, @@ -114,7 +108,7 @@ class LongPollingService : Service() { PendingIntent.FLAG_IMMUTABLE ) - userSettings.updateLongPollCurrentState( + longPollController.updateCurrentState( if (inBackground) LongPollState.Background else LongPollState.InApp ) @@ -254,11 +248,9 @@ class LongPollingService : Service() { override fun onDestroy() { Log.d(STATE_TAG, "onDestroy") - userSettings.updateLongPollCurrentState(LongPollState.Stopped) + longPollController.updateCurrentState(LongPollState.Stopped) try { - SettingsController.edit { - putBoolean(KEY_LONG_POLL_WAS_DESTROYED, true) - } + AppSettings.edit { putBoolean(KEY_LONG_POLL_WAS_DESTROYED, true) } job.cancel() } catch (e: Exception) { e.printStackTrace() @@ -268,6 +260,7 @@ class LongPollingService : Service() { override fun onLowMemory() { Log.d(STATE_TAG, "onLowMemory") + longPollController.updateCurrentState(LongPollState.Stopped) super.onLowMemory() } diff --git a/core/common/src/main/kotlin/com/meloda/app/fast/common/LongPollController.kt b/core/common/src/main/kotlin/com/meloda/app/fast/common/LongPollController.kt new file mode 100644 index 00000000..23555a90 --- /dev/null +++ b/core/common/src/main/kotlin/com/meloda/app/fast/common/LongPollController.kt @@ -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 + val stateToApply: StateFlow + + fun updateCurrentState(newState: LongPollState) + fun setStateToApply(newState: LongPollState) +} + +class LongPollControllerImpl : LongPollController { + + override val currentState = MutableStateFlow(LongPollState.Stopped) + override val stateToApply = MutableStateFlow(LongPollState.Stopped) + + override fun updateCurrentState(newState: LongPollState) { + currentState.value = newState + } + + override fun setStateToApply(newState: LongPollState) { + currentState.value = newState + } +} diff --git a/core/common/src/main/kotlin/com/meloda/app/fast/common/di/CommonModule.kt b/core/common/src/main/kotlin/com/meloda/app/fast/common/di/CommonModule.kt index 633eaa09..33a363c8 100644 --- a/core/common/src/main/kotlin/com/meloda/app/fast/common/di/CommonModule.kt +++ b/core/common/src/main/kotlin/com/meloda/app/fast/common/di/CommonModule.kt @@ -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 } diff --git a/core/common/src/main/kotlin/com/meloda/app/fast/common/CustomNavType.kt b/core/common/src/main/kotlin/com/meloda/app/fast/common/extensions/CustomNavType.kt similarity index 94% rename from core/common/src/main/kotlin/com/meloda/app/fast/common/CustomNavType.kt rename to core/common/src/main/kotlin/com/meloda/app/fast/common/extensions/CustomNavType.kt index 23d7af9a..664cea87 100644 --- a/core/common/src/main/kotlin/com/meloda/app/fast/common/CustomNavType.kt +++ b/core/common/src/main/kotlin/com/meloda/app/fast/common/extensions/CustomNavType.kt @@ -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 diff --git a/core/common/src/main/kotlin/com/meloda/app/fast/common/ApiLanguage.kt b/core/common/src/main/kotlin/com/meloda/app/fast/common/model/ApiLanguage.kt similarity index 51% rename from core/common/src/main/kotlin/com/meloda/app/fast/common/ApiLanguage.kt rename to core/common/src/main/kotlin/com/meloda/app/fast/common/model/ApiLanguage.kt index d6e4bdba..f39e772c 100644 --- a/core/common/src/main/kotlin/com/meloda/app/fast/common/ApiLanguage.kt +++ b/core/common/src/main/kotlin/com/meloda/app/fast/common/model/ApiLanguage.kt @@ -1,3 +1,3 @@ -package com.meloda.app.fast.common +package com.meloda.app.fast.common.model data class ApiLanguage(val value: String) diff --git a/core/common/src/main/kotlin/com/meloda/app/fast/common/model/DarkMode.kt b/core/common/src/main/kotlin/com/meloda/app/fast/common/model/DarkMode.kt new file mode 100644 index 00000000..46cb9757 --- /dev/null +++ b/core/common/src/main/kotlin/com/meloda/app/fast/common/model/DarkMode.kt @@ -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") + } +} diff --git a/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/model/LongPollState.kt b/core/common/src/main/kotlin/com/meloda/app/fast/common/model/LongPollState.kt similarity index 89% rename from core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/model/LongPollState.kt rename to core/common/src/main/kotlin/com/meloda/app/fast/common/model/LongPollState.kt index e4f77e6d..6098faa7 100644 --- a/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/model/LongPollState.kt +++ b/core/common/src/main/kotlin/com/meloda/app/fast/common/model/LongPollState.kt @@ -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() diff --git a/core/common/src/main/kotlin/com/meloda/app/fast/common/UiImage.kt b/core/common/src/main/kotlin/com/meloda/app/fast/common/model/UiImage.kt similarity index 94% rename from core/common/src/main/kotlin/com/meloda/app/fast/common/UiImage.kt rename to core/common/src/main/kotlin/com/meloda/app/fast/common/model/UiImage.kt index 709f50cd..39763821 100644 --- a/core/common/src/main/kotlin/com/meloda/app/fast/common/UiImage.kt +++ b/core/common/src/main/kotlin/com/meloda/app/fast/common/model/UiImage.kt @@ -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 diff --git a/core/common/src/main/kotlin/com/meloda/app/fast/common/UiText.kt b/core/common/src/main/kotlin/com/meloda/app/fast/common/model/UiText.kt similarity index 96% rename from core/common/src/main/kotlin/com/meloda/app/fast/common/UiText.kt rename to core/common/src/main/kotlin/com/meloda/app/fast/common/model/UiText.kt index 0f06da12..21f115ae 100644 --- a/core/common/src/main/kotlin/com/meloda/app/fast/common/UiText.kt +++ b/core/common/src/main/kotlin/com/meloda/app/fast/common/model/UiText.kt @@ -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? { diff --git a/core/common/src/main/kotlin/com/meloda/app/fast/common/provider/ResourceProvider.kt b/core/common/src/main/kotlin/com/meloda/app/fast/common/provider/ResourceProvider.kt new file mode 100644 index 00000000..0586a6fc --- /dev/null +++ b/core/common/src/main/kotlin/com/meloda/app/fast/common/provider/ResourceProvider.kt @@ -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) + } +} diff --git a/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/AppSettings.kt b/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/AppSettings.kt new file mode 100644 index 00000000..9da167d4 --- /dev/null +++ b/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/AppSettings.kt @@ -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 get(clazz: KClass, 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 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 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) + } +} diff --git a/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/Extensions.kt b/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/Extensions.kt deleted file mode 100644 index b070ebc5..00000000 --- a/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/Extensions.kt +++ /dev/null @@ -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 -) diff --git a/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/SettingsController.kt b/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/SettingsController.kt deleted file mode 100644 index 653b14e6..00000000 --- a/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/SettingsController.kt +++ /dev/null @@ -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 get(clazz: KClass, 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 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 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) -} diff --git a/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/SettingsKeys.kt b/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/SettingsKeys.kt index 68cc61a7..db2ec922 100644 --- a/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/SettingsKeys.kt +++ b/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/SettingsKeys.kt @@ -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 = "¯\\_(ツ)_/¯" diff --git a/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/UserSettings.kt b/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/UserSettings.kt index 91a375e5..9fd41add 100644 --- a/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/UserSettings.kt +++ b/core/datastore/src/main/kotlin/com/meloda/app/fast/datastore/UserSettings.kt @@ -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 - val longPollStateToApply: StateFlow - val longPollCurrentState: StateFlow - val online: StateFlow - val debugSettingsEnabled: StateFlow + val useContactNames: StateFlow - val language: StateFlow val enablePullToRefresh: StateFlow - 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 + val darkMode: StateFlow + val enableAmoledDark: StateFlow + val enableDynamicColors: StateFlow + val appLanguage: StateFlow + + val fastText: StateFlow + + val sendOnlineStatus: StateFlow + + val showAlertAfterCrash: StateFlow + val longPollInBackground: StateFlow + val useBlur: StateFlow + val showEmojiButton: StateFlow + val showDebugCategory: StateFlow + 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.Stopped) - override val longPollCurrentState = MutableStateFlow(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 } } diff --git a/core/network/src/main/kotlin/com/meloda/app/fast/network/interceptor/LanguageInterceptor.kt b/core/network/src/main/kotlin/com/meloda/app/fast/network/interceptor/LanguageInterceptor.kt index 307e8b56..f6e0db55 100644 --- a/core/network/src/main/kotlin/com/meloda/app/fast/network/interceptor/LanguageInterceptor.kt +++ b/core/network/src/main/kotlin/com/meloda/app/fast/network/interceptor/LanguageInterceptor.kt @@ -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 diff --git a/core/ui/src/main/kotlin/com/meloda/app/fast/ui/basic/ContentAlpha.kt b/core/ui/src/main/kotlin/com/meloda/app/fast/ui/basic/ContentAlpha.kt index 951638f7..b5a4d985 100644 --- a/core/ui/src/main/kotlin/com/meloda/app/fast/ui/basic/ContentAlpha.kt +++ b/core/ui/src/main/kotlin/com/meloda/app/fast/ui/basic/ContentAlpha.kt @@ -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 diff --git a/core/ui/src/main/kotlin/com/meloda/app/fast/ui/components/MaterialDialog.kt b/core/ui/src/main/kotlin/com/meloda/app/fast/ui/components/MaterialDialog.kt index 267ee00c..ac1564c8 100644 --- a/core/ui/src/main/kotlin/com/meloda/app/fast/ui/components/MaterialDialog.kt +++ b/core/ui/src/main/kotlin/com/meloda/app/fast/ui/components/MaterialDialog.kt @@ -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 diff --git a/core/ui/src/main/kotlin/com/meloda/app/fast/ui/model/ThemeConfig.kt b/core/ui/src/main/kotlin/com/meloda/app/fast/ui/model/ThemeConfig.kt index 3e794ba7..11ac2b70 100644 --- a/core/ui/src/main/kotlin/com/meloda/app/fast/ui/model/ThemeConfig.kt +++ b/core/ui/src/main/kotlin/com/meloda/app/fast/ui/model/ThemeConfig.kt @@ -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 ) diff --git a/core/ui/src/main/kotlin/com/meloda/app/fast/ui/theme/AppTheme.kt b/core/ui/src/main/kotlin/com/meloda/app/fast/ui/theme/AppTheme.kt index 283c047e..7410488a 100644 --- a/core/ui/src/main/kotlin/com/meloda/app/fast/ui/theme/AppTheme.kt +++ b/core/ui/src/main/kotlin/com/meloda/app/fast/ui/theme/AppTheme.kt @@ -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 + ) } diff --git a/core/ui/src/main/kotlin/com/meloda/app/fast/ui/util/Extensions.kt b/core/ui/src/main/kotlin/com/meloda/app/fast/ui/util/Extensions.kt index a6af1ba3..36482cd3 100644 --- a/core/ui/src/main/kotlin/com/meloda/app/fast/ui/util/Extensions.kt +++ b/core/ui/src/main/kotlin/com/meloda/app/fast/ui/util/Extensions.kt @@ -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()?.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) +} diff --git a/feature/auth/login/src/main/kotlin/com/meloda/fast/auth/login/LoginViewModel.kt b/feature/auth/login/src/main/kotlin/com/meloda/fast/auth/login/LoginViewModel.kt index 59fd7e95..e0fc060c 100644 --- a/feature/auth/login/src/main/kotlin/com/meloda/fast/auth/login/LoginViewModel.kt +++ b/feature/auth/login/src/main/kotlin/com/meloda/fast/auth/login/LoginViewModel.kt @@ -4,18 +4,19 @@ import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.meloda.app.fast.auth.login.BuildConfig +import com.meloda.app.fast.common.LongPollController import com.meloda.app.fast.common.UserConfig import com.meloda.app.fast.common.VkConstants import com.meloda.app.fast.common.extensions.listenValue import com.meloda.app.fast.common.extensions.setValue import com.meloda.app.fast.common.extensions.updateValue +import com.meloda.app.fast.common.model.LongPollState import com.meloda.app.fast.data.State import com.meloda.app.fast.data.api.users.UsersUseCase import com.meloda.app.fast.data.db.AccountsRepository import com.meloda.app.fast.data.processState -import com.meloda.app.fast.datastore.SettingsController +import com.meloda.app.fast.datastore.AppSettings import com.meloda.app.fast.datastore.UserSettings -import com.meloda.app.fast.datastore.model.LongPollState import com.meloda.app.fast.model.database.AccountEntity import com.meloda.app.fast.network.OAuthErrorDomain import com.meloda.fast.auth.login.model.CaptchaArguments @@ -71,7 +72,8 @@ class LoginViewModelImpl( private val usersUseCase: UsersUseCase, private val accountsRepository: AccountsRepository, private val loginValidator: LoginValidator, - private val userSettings: UserSettings + private val userSettings: UserSettings, + private val longPollController: LongPollController ) : ViewModel(), LoginViewModel { override val screenState = MutableStateFlow(LoginScreenState.EMPTY) @@ -347,8 +349,8 @@ class LoginViewModelImpl( } private fun startLongPoll() { - userSettings.setLongPollStateToApply( - if (SettingsController.isLongPollInBackgroundEnabled) { + longPollController.setStateToApply( + if (AppSettings.Debug.longPollInBackground) { LongPollState.Background } else { LongPollState.InApp diff --git a/feature/auth/login/src/main/kotlin/com/meloda/fast/auth/login/presentation/LoginScreen.kt b/feature/auth/login/src/main/kotlin/com/meloda/fast/auth/login/presentation/LoginScreen.kt index 2b77f047..6b1cf873 100644 --- a/feature/auth/login/src/main/kotlin/com/meloda/fast/auth/login/presentation/LoginScreen.kt +++ b/feature/auth/login/src/main/kotlin/com/meloda/fast/auth/login/presentation/LoginScreen.kt @@ -50,13 +50,13 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.meloda.app.fast.common.UiText +import com.meloda.app.fast.common.model.UiText import com.meloda.app.fast.ui.basic.autoFillRequestHandler import com.meloda.app.fast.ui.basic.connectNode import com.meloda.app.fast.ui.basic.defaultFocusChangeAutoFill import com.meloda.app.fast.ui.components.MaterialDialog import com.meloda.app.fast.ui.components.TextFieldErrorText -import com.meloda.app.fast.ui.theme.LocalTheme +import com.meloda.app.fast.ui.theme.LocalThemeConfig import com.meloda.app.fast.ui.util.handleEnterKey import com.meloda.app.fast.ui.util.handleTabKey import com.meloda.fast.auth.login.LoginViewModel @@ -157,7 +157,7 @@ fun LoginScreen( onPasswordFieldGoAction: () -> Unit = {}, onSignInButtonClicked: () -> Unit = {} ) { - val currentTheme = LocalTheme.current + val currentTheme = LocalThemeConfig.current val focusManager = LocalFocusManager.current val (loginFocusable, passwordFocusable) = FocusRequester.createRefs() diff --git a/feature/auth/login/src/main/kotlin/com/meloda/fast/auth/login/presentation/LogoScreen.kt b/feature/auth/login/src/main/kotlin/com/meloda/fast/auth/login/presentation/LogoScreen.kt index 6279a4ac..672f41b8 100644 --- a/feature/auth/login/src/main/kotlin/com/meloda/fast/auth/login/presentation/LogoScreen.kt +++ b/feature/auth/login/src/main/kotlin/com/meloda/fast/auth/login/presentation/LogoScreen.kt @@ -31,7 +31,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.meloda.app.fast.ui.theme.LocalTheme +import com.meloda.app.fast.ui.theme.LocalThemeConfig import com.meloda.fast.auth.login.LoginViewModel import com.meloda.fast.auth.login.LoginViewModelImpl import org.koin.androidx.compose.koinViewModel @@ -64,7 +64,7 @@ fun LogoScreen( onLogoLongClicked: () -> Unit = {}, onGoNextButtonClicked: () -> Unit = {} ) { - val currentTheme = LocalTheme.current + val currentTheme = LocalThemeConfig.current Scaffold { padding -> val topPadding by animateDpAsState( diff --git a/feature/auth/validation/src/main/kotlin/com/meloda/app/fast/auth/validation/navigation/ValidationNavigation.kt b/feature/auth/validation/src/main/kotlin/com/meloda/app/fast/auth/validation/navigation/ValidationNavigation.kt index 8b28d99f..04f3975d 100644 --- a/feature/auth/validation/src/main/kotlin/com/meloda/app/fast/auth/validation/navigation/ValidationNavigation.kt +++ b/feature/auth/validation/src/main/kotlin/com/meloda/app/fast/auth/validation/navigation/ValidationNavigation.kt @@ -7,7 +7,7 @@ import androidx.navigation.compose.composable import androidx.navigation.toRoute import com.meloda.app.fast.auth.validation.model.ValidationArguments import com.meloda.app.fast.auth.validation.presentation.ValidationRoute -import com.meloda.app.fast.common.customNavType +import com.meloda.app.fast.common.extensions.customNavType import kotlinx.serialization.Serializable import kotlin.reflect.typeOf diff --git a/feature/chatmaterials/src/main/kotlin/com/meloda/app/fast/chatmaterials/presentation/ChatMaterialsScreen.kt b/feature/chatmaterials/src/main/kotlin/com/meloda/app/fast/chatmaterials/presentation/ChatMaterialsScreen.kt index 2fe2b9f5..29c80c6f 100644 --- a/feature/chatmaterials/src/main/kotlin/com/meloda/app/fast/chatmaterials/presentation/ChatMaterialsScreen.kt +++ b/feature/chatmaterials/src/main/kotlin/com/meloda/app/fast/chatmaterials/presentation/ChatMaterialsScreen.kt @@ -66,7 +66,7 @@ import com.meloda.app.fast.chatmaterials.ChatMaterialsViewModelImpl import com.meloda.app.fast.chatmaterials.model.ChatMaterialsScreenState import com.meloda.app.fast.datastore.UserSettings import com.meloda.app.fast.ui.R -import com.meloda.app.fast.ui.theme.LocalTheme +import com.meloda.app.fast.ui.theme.LocalThemeConfig import dev.chrisbanes.haze.HazeState import dev.chrisbanes.haze.haze import dev.chrisbanes.haze.hazeChild @@ -110,7 +110,7 @@ fun ChatMaterialsScreen( onRefreshDropdownItemClicked: () -> Unit = {}, onRefresh: () -> Unit = {} ) { - val currentTheme = LocalTheme.current + val currentTheme = LocalThemeConfig.current val attachments = screenState.materials @@ -154,7 +154,7 @@ fun ChatMaterialsScreen( Log.d("ChatMaterialsScreen", "ChatMaterialsScreen: canScrollBackward: $canScrollBackward") val topBarContainerColorAlpha by animateFloatAsState( - targetValue = if (!currentTheme.usingBlur || !canScrollBackward) 1f else 0f, + targetValue = if (!currentTheme.enableBlur || !canScrollBackward) 1f else 0f, label = "toolbarColorAlpha", animationSpec = tween( durationMillis = 200, @@ -164,7 +164,7 @@ fun ChatMaterialsScreen( val topBarContainerColor by animateColorAsState( targetValue = - if (currentTheme.usingBlur || !canScrollBackward) + if (currentTheme.enableBlur || !canScrollBackward) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp), @@ -182,7 +182,7 @@ fun ChatMaterialsScreen( Column( modifier = Modifier .then( - if (currentTheme.usingBlur) { + if (currentTheme.enableBlur) { Modifier.hazeChild( state = hazeState, style = hazeStyle @@ -244,7 +244,7 @@ fun ChatMaterialsScreen( } ) - if (currentTheme.usingBlur) { + if (currentTheme.enableBlur) { DropdownMenuItem( text = { Text(text = if (moreClearBlur) "Default blur" else "Clearer blur") @@ -300,7 +300,7 @@ fun ChatMaterialsScreen( horizontalArrangement = Arrangement.spacedBy(2.dp), modifier = Modifier .then( - if (currentTheme.usingBlur) { + if (currentTheme.enableBlur) { Modifier.haze( state = hazeState, style = hazeStyle @@ -331,7 +331,7 @@ fun ChatMaterialsScreen( state = listState, modifier = Modifier .then( - if (currentTheme.usingBlur) { + if (currentTheme.enableBlur) { Modifier.haze( state = hazeState, style = hazeStyle diff --git a/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/model/ConversationOption.kt b/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/model/ConversationOption.kt index 016e4526..f35ebc78 100644 --- a/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/model/ConversationOption.kt +++ b/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/model/ConversationOption.kt @@ -1,7 +1,7 @@ package com.meloda.app.fast.conversations.model -import com.meloda.app.fast.common.UiImage -import com.meloda.app.fast.common.UiText +import com.meloda.app.fast.common.model.UiImage +import com.meloda.app.fast.common.model.UiText import com.meloda.app.fast.ui.R as UiR sealed class ConversationOption( diff --git a/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/model/UiConversation.kt b/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/model/UiConversation.kt index bb81b498..ded71320 100644 --- a/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/model/UiConversation.kt +++ b/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/model/UiConversation.kt @@ -2,7 +2,7 @@ package com.meloda.app.fast.conversations.model import androidx.compose.runtime.Immutable import androidx.compose.ui.text.AnnotatedString -import com.meloda.app.fast.common.UiImage +import com.meloda.app.fast.common.model.UiImage import com.meloda.app.fast.model.api.PeerType import com.meloda.app.fast.model.api.domain.VkMessage import com.meloda.app.fast.ui.util.ImmutableList diff --git a/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/presentation/ConversationItem.kt b/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/presentation/ConversationItem.kt index d3058d88..1bf96c15 100644 --- a/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/presentation/ConversationItem.kt +++ b/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/presentation/ConversationItem.kt @@ -52,7 +52,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import coil.compose.AsyncImage -import com.meloda.app.fast.common.UiImage +import com.meloda.app.fast.common.model.UiImage import com.meloda.app.fast.conversations.model.ConversationOption import com.meloda.app.fast.conversations.model.UiConversation import com.meloda.app.fast.ui.basic.ContentAlpha diff --git a/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/presentation/ConversationsScreen.kt b/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/presentation/ConversationsScreen.kt index 47a554da..7f5610a5 100644 --- a/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/presentation/ConversationsScreen.kt +++ b/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/presentation/ConversationsScreen.kt @@ -78,7 +78,7 @@ import com.meloda.app.fast.ui.components.FullScreenLoader import com.meloda.app.fast.ui.components.MaterialDialog import com.meloda.app.fast.ui.theme.LocalBottomPadding import com.meloda.app.fast.ui.theme.LocalHazeState -import com.meloda.app.fast.ui.theme.LocalTheme +import com.meloda.app.fast.ui.theme.LocalThemeConfig import com.meloda.app.fast.ui.util.isScrollingUp import dev.chrisbanes.haze.haze import dev.chrisbanes.haze.hazeChild @@ -159,10 +159,10 @@ fun ConversationsScreen( onRefresh: () -> Unit = {} ) { val view = LocalView.current - val currentTheme = LocalTheme.current + val currentTheme = LocalThemeConfig.current val maxLines by remember(currentTheme) { - mutableIntStateOf(if (currentTheme.isMultiline) 2 else 1) + mutableIntStateOf(if (currentTheme.enableMultiline) 2 else 1) } val listState = rememberLazyListState() @@ -195,7 +195,7 @@ fun ConversationsScreen( val toolbarContainerColor by animateColorAsState( targetValue = - if (currentTheme.usingBlur || !listState.canScrollBackward) + if (currentTheme.enableBlur || !listState.canScrollBackward) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp), @@ -258,12 +258,12 @@ fun ConversationsScreen( }, colors = TopAppBarDefaults.topAppBarColors( containerColor = toolbarContainerColor.copy( - alpha = if (currentTheme.usingBlur) toolbarColorAlpha else 1f + alpha = if (currentTheme.enableBlur) toolbarColorAlpha else 1f ) ), modifier = Modifier .then( - if (currentTheme.usingBlur) { + if (currentTheme.enableBlur) { Modifier.hazeChild( state = hazeState, style = HazeMaterials.thick() @@ -355,7 +355,7 @@ fun ConversationsScreen( screenState = screenState, state = listState, maxLines = maxLines, - modifier = if (currentTheme.usingBlur) { + modifier = if (currentTheme.enableBlur) { Modifier.haze( state = hazeState, style = HazeMaterials.thick() diff --git a/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/util/ConversationDomainMapper.kt b/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/util/ConversationDomainMapper.kt index 11971e46..079ba7fa 100644 --- a/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/util/ConversationDomainMapper.kt +++ b/feature/conversations/src/main/kotlin/com/meloda/app/fast/conversations/util/ConversationDomainMapper.kt @@ -9,11 +9,11 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.withStyle import com.conena.nanokt.jvm.util.dayOfMonth import com.conena.nanokt.jvm.util.month -import com.meloda.app.fast.common.UiImage -import com.meloda.app.fast.common.UiText +import com.meloda.app.fast.common.model.UiImage +import com.meloda.app.fast.common.model.UiText import com.meloda.app.fast.common.UserConfig import com.meloda.app.fast.common.extensions.orDots -import com.meloda.app.fast.common.parseString +import com.meloda.app.fast.common.model.parseString import com.meloda.app.fast.common.util.TimeUtils import com.meloda.app.fast.conversations.model.ActionState import com.meloda.app.fast.conversations.model.UiConversation diff --git a/feature/friends/src/main/kotlin/com/meloda/app/fast/friends/model/UiFriend.kt b/feature/friends/src/main/kotlin/com/meloda/app/fast/friends/model/UiFriend.kt index d2b33960..68489e70 100644 --- a/feature/friends/src/main/kotlin/com/meloda/app/fast/friends/model/UiFriend.kt +++ b/feature/friends/src/main/kotlin/com/meloda/app/fast/friends/model/UiFriend.kt @@ -1,6 +1,6 @@ package com.meloda.app.fast.friends.model -import com.meloda.app.fast.common.UiImage +import com.meloda.app.fast.common.model.UiImage import com.meloda.app.fast.model.api.domain.OnlineStatus data class UiFriend( diff --git a/feature/friends/src/main/kotlin/com/meloda/app/fast/friends/presentation/FriendsScreen.kt b/feature/friends/src/main/kotlin/com/meloda/app/fast/friends/presentation/FriendsScreen.kt index d4795642..15bf126a 100644 --- a/feature/friends/src/main/kotlin/com/meloda/app/fast/friends/presentation/FriendsScreen.kt +++ b/feature/friends/src/main/kotlin/com/meloda/app/fast/friends/presentation/FriendsScreen.kt @@ -59,7 +59,7 @@ import com.meloda.app.fast.ui.components.FullScreenLoader import com.meloda.app.fast.ui.components.NoItemsView import com.meloda.app.fast.ui.model.TabItem import com.meloda.app.fast.ui.theme.LocalHazeState -import com.meloda.app.fast.ui.theme.LocalTheme +import com.meloda.app.fast.ui.theme.LocalThemeConfig import com.meloda.app.fast.ui.util.ImmutableList import dev.chrisbanes.haze.haze import dev.chrisbanes.haze.hazeChild @@ -122,11 +122,11 @@ fun FriendsScreen( onPaginationConditionsMet: () -> Unit = {}, onRefresh: () -> Unit = {} ) { - val currentTheme = LocalTheme.current + val currentTheme = LocalThemeConfig.current val maxLines by remember { derivedStateOf { - if (currentTheme.isMultiline) 2 else 1 + if (currentTheme.enableMultiline) 2 else 1 } } @@ -149,7 +149,7 @@ fun FriendsScreen( val hazeState = LocalHazeState.current val topBarContainerColorAlpha by animateFloatAsState( - targetValue = if (!currentTheme.usingBlur || !listState.canScrollBackward) 1f else 0f, + targetValue = if (!currentTheme.enableBlur || !listState.canScrollBackward) 1f else 0f, label = "toolbarColorAlpha", animationSpec = tween( durationMillis = 200, @@ -159,7 +159,7 @@ fun FriendsScreen( val topBarContainerColor by animateColorAsState( targetValue = - if (currentTheme.usingBlur || !listState.canScrollBackward) + if (currentTheme.enableBlur || !listState.canScrollBackward) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp), @@ -196,7 +196,7 @@ fun FriendsScreen( Column( modifier = Modifier .then( - if (currentTheme.usingBlur) { + if (currentTheme.enableBlur) { Modifier.hazeChild( state = hazeState, style = HazeMaterials.thick() @@ -295,7 +295,7 @@ fun FriendsScreen( val friendsToDisplay = screenState.friends FriendsList( - modifier = if (currentTheme.usingBlur) { + modifier = if (currentTheme.enableBlur) { Modifier.haze( state = hazeState, style = HazeMaterials.thick() diff --git a/feature/friends/src/main/kotlin/com/meloda/app/fast/friends/util/FriendMapper.kt b/feature/friends/src/main/kotlin/com/meloda/app/fast/friends/util/FriendMapper.kt index d87618d9..55585cbc 100644 --- a/feature/friends/src/main/kotlin/com/meloda/app/fast/friends/util/FriendMapper.kt +++ b/feature/friends/src/main/kotlin/com/meloda/app/fast/friends/util/FriendMapper.kt @@ -1,6 +1,6 @@ package com.meloda.app.fast.friends.util -import com.meloda.app.fast.common.UiImage +import com.meloda.app.fast.common.model.UiImage import com.meloda.app.fast.data.VkMemoryCache import com.meloda.app.fast.friends.model.UiFriend import com.meloda.app.fast.model.api.domain.VkUser diff --git a/feature/languagepicker/src/main/kotlin/com/meloda/app/fast/languagepicker/LanguagePickerViewModel.kt b/feature/languagepicker/src/main/kotlin/com/meloda/app/fast/languagepicker/LanguagePickerViewModel.kt index 87266a6d..573bc53b 100644 --- a/feature/languagepicker/src/main/kotlin/com/meloda/app/fast/languagepicker/LanguagePickerViewModel.kt +++ b/feature/languagepicker/src/main/kotlin/com/meloda/app/fast/languagepicker/LanguagePickerViewModel.kt @@ -4,9 +4,9 @@ import android.content.res.Resources import androidx.appcompat.app.AppCompatDelegate import androidx.core.os.LocaleListCompat import androidx.lifecycle.ViewModel -import com.meloda.app.fast.common.UiText +import com.meloda.app.fast.common.model.UiText import com.meloda.app.fast.common.extensions.setValue -import com.meloda.app.fast.common.parseString +import com.meloda.app.fast.common.model.parseString import com.meloda.app.fast.languagepicker.model.LanguagePickerScreenState import com.meloda.app.fast.languagepicker.model.SelectableLanguage import kotlinx.coroutines.flow.MutableStateFlow diff --git a/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/model/MessagesHistoryScreenState.kt b/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/model/MessagesHistoryScreenState.kt index 50fb76ee..e8534503 100644 --- a/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/model/MessagesHistoryScreenState.kt +++ b/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/model/MessagesHistoryScreenState.kt @@ -1,7 +1,7 @@ package com.meloda.app.fast.messageshistory.model import androidx.compose.runtime.Immutable -import com.meloda.app.fast.common.UiImage +import com.meloda.app.fast.common.model.UiImage import com.meloda.app.fast.model.api.domain.VkAttachment @Immutable diff --git a/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/model/UiMessage.kt b/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/model/UiMessage.kt index ffddcfb6..6a3f52ab 100644 --- a/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/model/UiMessage.kt +++ b/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/model/UiMessage.kt @@ -1,6 +1,6 @@ package com.meloda.app.fast.messageshistory.model -import com.meloda.app.fast.common.UiImage +import com.meloda.app.fast.common.model.UiImage data class UiMessage( val id: Int, diff --git a/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/navigation/MessagesHistoryNavigation.kt b/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/navigation/MessagesHistoryNavigation.kt index 121926ab..4e735c6e 100644 --- a/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/navigation/MessagesHistoryNavigation.kt +++ b/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/navigation/MessagesHistoryNavigation.kt @@ -5,7 +5,7 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import androidx.navigation.toRoute -import com.meloda.app.fast.common.customNavType +import com.meloda.app.fast.common.extensions.customNavType import com.meloda.app.fast.messageshistory.model.MessagesHistoryArguments import com.meloda.app.fast.messageshistory.presentation.MessagesHistoryRoute import com.meloda.app.fast.model.BaseError diff --git a/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/presentation/MessagesHistoryScreen.kt b/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/presentation/MessagesHistoryScreen.kt index ade30f59..1fbc7f30 100644 --- a/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/presentation/MessagesHistoryScreen.kt +++ b/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/presentation/MessagesHistoryScreen.kt @@ -72,7 +72,7 @@ import com.meloda.app.fast.messageshistory.MessagesHistoryViewModelImpl import com.meloda.app.fast.messageshistory.model.ActionMode import com.meloda.app.fast.messageshistory.model.MessagesHistoryScreenState import com.meloda.app.fast.model.BaseError -import com.meloda.app.fast.ui.theme.LocalTheme +import com.meloda.app.fast.ui.theme.LocalThemeConfig import com.meloda.app.fast.ui.util.ImmutableList import dev.chrisbanes.haze.HazeState import dev.chrisbanes.haze.hazeChild @@ -131,7 +131,7 @@ fun MessagesHistoryScreen( val view = LocalView.current val preferences: SharedPreferences = koinInject() - val currentTheme = LocalTheme.current + val currentTheme = LocalThemeConfig.current val listState = rememberLazyListState() @@ -178,7 +178,7 @@ fun MessagesHistoryScreen( TopAppBar( modifier = Modifier .then( - if (currentTheme.usingBlur) { + if (currentTheme.enableBlur) { Modifier.hazeChild( state = hazeSate, style = HazeMaterials.thick() @@ -203,7 +203,7 @@ fun MessagesHistoryScreen( }, colors = TopAppBarDefaults.topAppBarColors( containerColor = MaterialTheme.colorScheme.surface.copy( - alpha = if (currentTheme.usingBlur) toolbarColorAlpha else 1f + alpha = if (currentTheme.enableBlur) toolbarColorAlpha else 1f ) ), actions = { diff --git a/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/presentation/MessagesList.kt b/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/presentation/MessagesList.kt index 4b4fcd1b..87a11ddd 100644 --- a/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/presentation/MessagesList.kt +++ b/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/presentation/MessagesList.kt @@ -17,7 +17,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.meloda.app.fast.messageshistory.model.UiMessage -import com.meloda.app.fast.ui.theme.LocalTheme +import com.meloda.app.fast.ui.theme.LocalThemeConfig import com.meloda.app.fast.ui.util.ImmutableList import dev.chrisbanes.haze.HazeState import dev.chrisbanes.haze.haze @@ -35,13 +35,13 @@ fun MessagesList( enableAnimations: Boolean ) { val messages = immutableMessages.toList() - val currentTheme = LocalTheme.current + val currentTheme = LocalThemeConfig.current LazyColumn( modifier = modifier .fillMaxWidth() .then( - if (currentTheme.usingBlur) { + if (currentTheme.enableBlur) { Modifier.haze( state = hazeState, style = HazeMaterials.regular() diff --git a/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/util/MessageMapper.kt b/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/util/MessageMapper.kt index db1b94ef..083f3a34 100644 --- a/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/util/MessageMapper.kt +++ b/feature/messageshistory/src/main/kotlin/com/meloda/app/fast/messageshistory/util/MessageMapper.kt @@ -1,11 +1,11 @@ package com.meloda.app.fast.messageshistory.util import android.content.res.Resources -import com.meloda.app.fast.common.UiImage -import com.meloda.app.fast.common.UiText +import com.meloda.app.fast.common.model.UiImage +import com.meloda.app.fast.common.model.UiText import com.meloda.app.fast.common.UserConfig import com.meloda.app.fast.common.extensions.orDots -import com.meloda.app.fast.common.parseString +import com.meloda.app.fast.common.model.parseString import com.meloda.app.fast.data.VkMemoryCache import com.meloda.app.fast.ui.R import com.meloda.app.fast.messageshistory.model.UiMessage diff --git a/feature/photoviewer/src/main/kotlin/com/meloda/app/fast/photoviewer/model/PhotoViewArguments.kt b/feature/photoviewer/src/main/kotlin/com/meloda/app/fast/photoviewer/model/PhotoViewArguments.kt index a4a38977..b04a3cfe 100644 --- a/feature/photoviewer/src/main/kotlin/com/meloda/app/fast/photoviewer/model/PhotoViewArguments.kt +++ b/feature/photoviewer/src/main/kotlin/com/meloda/app/fast/photoviewer/model/PhotoViewArguments.kt @@ -1,7 +1,7 @@ package com.meloda.app.fast.photoviewer.model import androidx.compose.runtime.Immutable -import com.meloda.app.fast.common.UiImage +import com.meloda.app.fast.common.model.UiImage @Immutable data class PhotoViewArguments( diff --git a/feature/photoviewer/src/main/kotlin/com/meloda/app/fast/photoviewer/model/PhotoViewState.kt b/feature/photoviewer/src/main/kotlin/com/meloda/app/fast/photoviewer/model/PhotoViewState.kt index 1af1a2bb..62570f1c 100644 --- a/feature/photoviewer/src/main/kotlin/com/meloda/app/fast/photoviewer/model/PhotoViewState.kt +++ b/feature/photoviewer/src/main/kotlin/com/meloda/app/fast/photoviewer/model/PhotoViewState.kt @@ -1,7 +1,7 @@ package com.meloda.app.fast.photoviewer.model import androidx.compose.runtime.Immutable -import com.meloda.app.fast.common.UiImage +import com.meloda.app.fast.common.model.UiImage @Immutable data class PhotoViewState( diff --git a/feature/photoviewer/src/main/kotlin/com/meloda/app/fast/photoviewer/presentation/PhotoViewScreenContent.kt b/feature/photoviewer/src/main/kotlin/com/meloda/app/fast/photoviewer/presentation/PhotoViewScreenContent.kt index 5a6744bc..e4530352 100644 --- a/feature/photoviewer/src/main/kotlin/com/meloda/app/fast/photoviewer/presentation/PhotoViewScreenContent.kt +++ b/feature/photoviewer/src/main/kotlin/com/meloda/app/fast/photoviewer/presentation/PhotoViewScreenContent.kt @@ -44,7 +44,7 @@ import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import coil.compose.AsyncImage -import com.meloda.app.fast.common.UiImage +import com.meloda.app.fast.common.model.UiImage import com.meloda.app.fast.photoviewer.PhotoViewViewModel import com.meloda.app.fast.photoviewer.model.PhotoViewState diff --git a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/SettingsViewModel.kt b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/SettingsViewModel.kt index ab64e536..8157e097 100644 --- a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/SettingsViewModel.kt +++ b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/SettingsViewModel.kt @@ -6,17 +6,18 @@ import androidx.appcompat.app.AppCompatDelegate import androidx.core.view.HapticFeedbackConstantsCompat import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.meloda.app.fast.common.UiText +import com.meloda.app.fast.common.LongPollController import com.meloda.app.fast.common.UserConfig import com.meloda.app.fast.common.extensions.findWithIndex import com.meloda.app.fast.common.extensions.isSdkAtLeast import com.meloda.app.fast.common.extensions.setValue +import com.meloda.app.fast.common.model.DarkMode +import com.meloda.app.fast.common.model.LongPollState +import com.meloda.app.fast.common.model.UiText import com.meloda.app.fast.data.db.AccountsRepository -import com.meloda.app.fast.datastore.SettingsController +import com.meloda.app.fast.datastore.AppSettings import com.meloda.app.fast.datastore.SettingsKeys import com.meloda.app.fast.datastore.UserSettings -import com.meloda.app.fast.datastore.isDebugSettingsShown -import com.meloda.app.fast.datastore.model.LongPollState import com.meloda.app.fast.model.database.AccountEntity import com.meloda.app.fast.settings.model.SettingsItem import com.meloda.app.fast.settings.model.SettingsScreenState @@ -32,8 +33,7 @@ import com.meloda.app.fast.ui.R as UiR interface SettingsViewModel { val screenState: StateFlow - - val isLongPollBackgroundEnabled: StateFlow + val hapticType: StateFlow fun onLogOutAlertDismissed() fun onLogOutAlertPositiveClick() @@ -46,22 +46,20 @@ interface SettingsViewModel { fun onSettingsItemChanged(key: String, newValue: Any?) fun onHapticPerformed() - - fun onNotificationsPermissionRequested() } class SettingsViewModelImpl( private val accountsRepository: AccountsRepository, private val userSettings: UserSettings, - private val resources: Resources + private val resources: Resources, + private val longPollController: LongPollController ) : SettingsViewModel, ViewModel() { override val screenState = MutableStateFlow(SettingsScreenState.EMPTY) + override val hapticType = MutableStateFlow(null) private val settings = MutableStateFlow>>(emptyList()) - override val isLongPollBackgroundEnabled = MutableStateFlow(null) - init { createSettings() } @@ -106,22 +104,15 @@ class SettingsViewModelImpl( } SettingsKeys.KEY_DEBUG_HIDE_DEBUG_LIST -> { - val showDebugCategory = isDebugSettingsShown() + val showDebugCategory = AppSettings.Debug.showDebugCategory if (!showDebugCategory) return - SettingsController.put( - SettingsKeys.KEY_SHOW_DEBUG_CATEGORY, - false - ) + onSettingsItemChanged(key, false) createSettings() - screenState.setValue { old -> - old.copy( - useHaptics = HapticType.REJECT, - showDebugOptions = false - ) - } + hapticType.update { HapticType.REJECT } + screenState.setValue { old -> old.copy(showDebugOptions = false) } } } } @@ -129,18 +120,14 @@ class SettingsViewModelImpl( override fun onSettingsItemLongClicked(key: String) { when (key) { SettingsKeys.KEY_ACTIVITY_SEND_ONLINE_STATUS -> { - if (isDebugSettingsShown()) return + if (AppSettings.Debug.showDebugCategory) return - SettingsController.put(SettingsKeys.KEY_SHOW_DEBUG_CATEGORY, true) + onSettingsItemChanged(key, true) createSettings() - screenState.setValue { old -> - old.copy( - useHaptics = HapticType.LONG_PRESS, - showDebugOptions = true - ) - } + hapticType.update { HapticType.LONG_PRESS } + screenState.setValue { old -> old.copy(showDebugOptions = true) } } } } @@ -160,70 +147,98 @@ class SettingsViewModelImpl( } when (key) { - SettingsKeys.KEY_FEATURES_LONG_POLL_IN_BACKGROUND -> { - val isEnabled = (newValue as? Boolean) == true - userSettings.setLongPollStateToApply( - userSettings.longPollStateToApply.value.let { state -> - if (state.isLaunched()) { - if (isEnabled) LongPollState.Background - else LongPollState.InApp - } else state - } - ) - - if (isEnabled) { - // TODO: 26/11/2023, Danil Nikolaev: implement - val isNotificationsPermissionGranted = false - - if (!isNotificationsPermissionGranted) { - // TODO: 26/11/2023, Danil Nikolaev: implement restart - } - } - } - - SettingsKeys.KEY_APPEARANCE_MULTILINE -> { - val isUsing = newValue as? Boolean ?: false - userSettings.useMultiline(isUsing) - } - - SettingsKeys.KEY_APPEARANCE_AMOLED_THEME -> { - val isUsing = newValue as? Boolean ?: false - userSettings.useAmoledThemeChanged(isUsing) - } - - SettingsKeys.KEY_USE_DYNAMIC_COLORS -> { - val isUsing = newValue as? Boolean ?: false - userSettings.useDynamicColorsChanged(isUsing) - } - - SettingsKeys.KEY_APPEARANCE_BLUR -> { - val isUsing = newValue as? Boolean ?: false - userSettings.useBlurChanged(isUsing) - } - - SettingsKeys.KEY_ACTIVITY_SEND_ONLINE_STATUS -> { - val isUsing = newValue as? Boolean ?: false - userSettings.setOnline(isUsing) - } - SettingsKeys.KEY_USE_CONTACT_NAMES -> { - val isUsing = newValue as? Boolean ?: false + val isUsing = newValue as? Boolean ?: SettingsKeys.DEFAULT_VALUE_USE_CONTACT_NAMES userSettings.onUseContactNamesChanged(isUsing) } SettingsKeys.KEY_ENABLE_PULL_TO_REFRESH -> { - val enable = newValue as? Boolean ?: false + val enable = + newValue as? Boolean ?: SettingsKeys.DEFAULT_VALUE_ENABLE_PULL_TO_REFRESH userSettings.onEnablePullToRefreshChanged(enable) } + + + SettingsKeys.KEY_APPEARANCE_MULTILINE -> { + val isUsing = newValue as? Boolean ?: SettingsKeys.DEFAULT_VALUE_MULTILINE + userSettings.onEnableMultilineChanged(isUsing) + } + + SettingsKeys.KEY_APPEARANCE_DARK_MODE -> { + val newMode = newValue as? Int ?: SettingsKeys.DEFAULT_VALUE_APPEARANCE_DARK_MODE + AppCompatDelegate.setDefaultNightMode(newMode) + userSettings.onDarkModeChanged(DarkMode.parse(newMode)) + } + + SettingsKeys.KEY_APPEARANCE_AMOLED_THEME -> { + val isUsing = + newValue as? Boolean ?: SettingsKeys.DEFAULT_VALUE_APPEARANCE_AMOLED_THEME + userSettings.onEnableAmoledDarkChanged(isUsing) + } + + SettingsKeys.KEY_USE_DYNAMIC_COLORS -> { + val isUsing = newValue as? Boolean ?: SettingsKeys.DEFAULT_VALUE_USE_DYNAMIC_COLORS + userSettings.onEnableDynamicColorsChanged(isUsing) + } + + SettingsKeys.KEY_APPEARANCE_LANGUAGE -> { + val newLanguage = newValue as? String ?: SettingsKeys.DEFAULT_APPEARANCE_LANGUAGE + userSettings.onAppLanguageChanged(newLanguage) + } + + + SettingsKeys.DEFAULT_VALUE_FEATURES_FAST_TEXT -> { + val newText = newValue as? String ?: SettingsKeys.DEFAULT_VALUE_FEATURES_FAST_TEXT + userSettings.onFastTextChanged(newText) + } + + + SettingsKeys.KEY_ACTIVITY_SEND_ONLINE_STATUS -> { + val isUsing = newValue as? Boolean + ?: SettingsKeys.DEFAULT_VALUE_KEY_ACTIVITY_SEND_ONLINE_STATUS + userSettings.onSendOnlineStatusChanged(isUsing) + } + + SettingsKeys.KEY_DEBUG_SHOW_CRASH_ALERT -> { + val show = newValue as? Boolean ?: SettingsKeys.DEFAULT_VALUE_KEY_SHOW_EMOJI_BUTTON + userSettings.onShowAlertAfterCrashChanged(show) + } + + SettingsKeys.KEY_FEATURES_LONG_POLL_IN_BACKGROUND -> { + val inBackground = newValue as? Boolean + ?: SettingsKeys.DEFAULT_VALUE_FEATURES_LONG_POLL_IN_BACKGROUND + userSettings.onLongPollInBackgroundChanged(inBackground) + + longPollController.setStateToApply( + longPollController.stateToApply.value.let { state -> + if (state.isLaunched()) { + if (inBackground) LongPollState.Background + else LongPollState.InApp + } else state + } + ) + } + + SettingsKeys.KEY_APPEARANCE_USE_BLUR -> { + val isUsing = + newValue as? Boolean ?: SettingsKeys.DEFAULT_VALUE_KEY_APPEARANCE_USE_BLUR + userSettings.onUseBlurChanged(isUsing) + } + + SettingsKeys.KEY_SHOW_EMOJI_BUTTON -> { + val show = newValue as? Boolean ?: SettingsKeys.DEFAULT_VALUE_KEY_SHOW_EMOJI_BUTTON + userSettings.onShowEmojiButtonChanged(show) + } + + SettingsKeys.KEY_SHOW_DEBUG_CATEGORY -> { + val show = newValue as? Boolean ?: false + userSettings.onShowDebugCategoryChanged(show) + } } } override fun onHapticPerformed() { - screenState.setValue { old -> old.copy(useHaptics = null) } - } - - override fun onNotificationsPermissionRequested() { - screenState.setValue { old -> old.copy(isNeedToRequestNotificationPermission = false) } + hapticType.update { null } } private fun emitShowOptions(function: (SettingsShowOptions) -> SettingsShowOptions) { @@ -273,22 +288,22 @@ class SettingsViewModelImpl( ) val darkThemeValues = listOf( - AppCompatDelegate.MODE_NIGHT_YES to UiText.Resource(UiR.string.settings_dark_theme_value_enabled), - AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM to UiText.Resource(UiR.string.settings_dark_theme_value_follow_system), - AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY to UiText.Resource(UiR.string.settings_dark_theme_value_battery_saver), - AppCompatDelegate.MODE_NIGHT_NO to UiText.Resource(UiR.string.settings_dark_theme_value_disabled) + DarkMode.ENABLED to UiText.Resource(UiR.string.settings_dark_theme_value_enabled), + DarkMode.FOLLOW_SYSTEM to UiText.Resource(UiR.string.settings_dark_theme_value_follow_system), + DarkMode.AUTO_BATTERY to UiText.Resource(UiR.string.settings_dark_theme_value_battery_saver), + DarkMode.DISABLED to UiText.Resource(UiR.string.settings_dark_theme_value_disabled) ).toMap() val appearanceDarkTheme = SettingsItem.ListItem( - key = SettingsKeys.KEY_APPEARANCE_DARK_THEME, + key = SettingsKeys.KEY_APPEARANCE_DARK_MODE, title = UiText.Resource(UiR.string.settings_dark_theme), valueClass = Int::class, - defaultValue = SettingsKeys.DEFAULT_VALUE_APPEARANCE_DARK_THEME, + defaultValue = SettingsKeys.DEFAULT_VALUE_APPEARANCE_DARK_MODE, titles = darkThemeValues.values.toList(), - values = darkThemeValues.keys.toList() + values = darkThemeValues.keys.toList().map(DarkMode::value) ).apply { textProvider = TextProvider { item -> - val darkThemeValue = darkThemeValues[item.value] + val darkThemeValue = darkThemeValues[DarkMode.parse(item.value)] UiText.ResourceParams( value = UiR.string.settings_dark_theme_current_value, @@ -368,8 +383,8 @@ class SettingsViewModelImpl( text = UiText.Simple("Shows alert dialog with stacktrace after app crashed\n(it will be not shown if you perform crash manually)") ) val debugUseBlur = SettingsItem.Switch( - key = SettingsKeys.KEY_APPEARANCE_BLUR, - defaultValue = SettingsKeys.DEFAULT_VALUE_KEY_APPEARANCE_BLUR, + key = SettingsKeys.KEY_APPEARANCE_USE_BLUR, + defaultValue = SettingsKeys.DEFAULT_VALUE_KEY_APPEARANCE_USE_BLUR, title = UiText.Simple("[WIP] Use blur"), text = UiText.Simple("Adds blur wherever possible\nOn android 11 and older will have transparency instead of blurring"), ) @@ -432,7 +447,7 @@ class SettingsViewModelImpl( debugList, ).forEach(settingsList::addAll) - if (!isDebugSettingsShown()) { + if (!AppSettings.Debug.showDebugCategory) { settingsList.removeAll(debugList) } diff --git a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/model/SettingsItem.kt b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/model/SettingsItem.kt index c0787b35..147b0ddc 100644 --- a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/model/SettingsItem.kt +++ b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/model/SettingsItem.kt @@ -2,9 +2,9 @@ package com.meloda.app.fast.settings.model import android.content.res.Resources import androidx.compose.runtime.Immutable -import com.meloda.app.fast.common.UiText -import com.meloda.app.fast.common.parseString -import com.meloda.app.fast.datastore.SettingsController +import com.meloda.app.fast.common.model.UiText +import com.meloda.app.fast.common.model.parseString +import com.meloda.app.fast.datastore.AppSettings import kotlin.reflect.KClass @Immutable @@ -37,7 +37,7 @@ sealed class SettingsItem( protected set(newValue) { field = newValue - SettingsController.put(key, newValue) + AppSettings.put(key, newValue) } var title: UiText? = null @@ -165,7 +165,7 @@ sealed class SettingsItem( val values: List ) : SettingsItem( key = key, - value = selectedValue ?: SettingsController.get(valueClass, key, defaultValue), + value = selectedValue ?: AppSettings.get(valueClass, key, defaultValue), defaultValue = defaultValue ) { @@ -245,6 +245,6 @@ private inline fun getCurrentValue(key: String, defaultValue: T): T if (T::class == Nothing::class) { throw IllegalStateException("Items with Nothing does not have a value") } else { - return SettingsController.get(key, defaultValue) + return AppSettings.get(key, defaultValue) } } diff --git a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/model/SettingsScreenState.kt b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/model/SettingsScreenState.kt index 20fa4955..38dd7a3e 100644 --- a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/model/SettingsScreenState.kt +++ b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/model/SettingsScreenState.kt @@ -1,15 +1,12 @@ package com.meloda.app.fast.settings.model import androidx.compose.runtime.Immutable -import com.meloda.app.fast.datastore.isDebugSettingsShown -import com.meloda.app.fast.settings.HapticType +import com.meloda.app.fast.datastore.AppSettings @Immutable data class SettingsScreenState( val showOptions: SettingsShowOptions, val settings: List, - val useHaptics: HapticType?, - val isNeedToRequestNotificationPermission: Boolean, val showDebugOptions: Boolean ) { @@ -17,9 +14,7 @@ data class SettingsScreenState( val EMPTY: SettingsScreenState = SettingsScreenState( showOptions = SettingsShowOptions.EMPTY, settings = emptyList(), - useHaptics = null, - isNeedToRequestNotificationPermission = false, - showDebugOptions = isDebugSettingsShown() + showDebugOptions = AppSettings.Debug.showDebugCategory ) } } diff --git a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/model/TextProvider.kt b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/model/TextProvider.kt index a01a5166..ff5dfb70 100644 --- a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/model/TextProvider.kt +++ b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/model/TextProvider.kt @@ -1,6 +1,6 @@ package com.meloda.app.fast.settings.model -import com.meloda.app.fast.common.UiText +import com.meloda.app.fast.common.model.UiText fun interface TextProvider> { fun provideText(item: S): UiText? diff --git a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/model/TitleProvider.kt b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/model/TitleProvider.kt index f90dd816..bfcd9a1b 100644 --- a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/model/TitleProvider.kt +++ b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/model/TitleProvider.kt @@ -1,6 +1,6 @@ package com.meloda.app.fast.settings.model -import com.meloda.app.fast.common.UiText +import com.meloda.app.fast.common.model.UiText fun interface TitleProvider> { fun provideTitle(item: S): UiText? diff --git a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/SettingsScreen.kt b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/SettingsScreen.kt index bea533be..6bbbebb7 100644 --- a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/SettingsScreen.kt +++ b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/SettingsScreen.kt @@ -1,7 +1,5 @@ package com.meloda.app.fast.settings.presentation -import android.os.PowerManager -import androidx.appcompat.app.AppCompatDelegate import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.calculateEndPadding @@ -27,17 +25,14 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.LayoutDirection -import androidx.core.content.getSystemService import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.meloda.app.fast.common.UserConfig import com.meloda.app.fast.datastore.SettingsKeys -import com.meloda.app.fast.datastore.UserSettings -import com.meloda.app.fast.datastore.isUsingDarkMode +import com.meloda.app.fast.settings.HapticType import com.meloda.app.fast.settings.SettingsViewModel import com.meloda.app.fast.settings.SettingsViewModelImpl import com.meloda.app.fast.settings.model.SettingsScreenState @@ -49,14 +44,13 @@ import com.meloda.app.fast.settings.presentation.item.TitleItem import com.meloda.app.fast.settings.presentation.item.TitleTextItem import com.meloda.app.fast.ui.components.ActionInvokeDismiss import com.meloda.app.fast.ui.components.MaterialDialog -import com.meloda.app.fast.ui.theme.LocalTheme +import com.meloda.app.fast.ui.theme.LocalThemeConfig import dev.chrisbanes.haze.HazeState import dev.chrisbanes.haze.haze import dev.chrisbanes.haze.hazeChild import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi import dev.chrisbanes.haze.materials.HazeMaterials import org.koin.androidx.compose.koinViewModel -import org.koin.compose.koinInject import com.meloda.app.fast.ui.R as UiR @Composable @@ -66,16 +60,12 @@ fun SettingsRoute( onLanguageItemClicked: () -> Unit, viewModel: SettingsViewModel = koinViewModel() ) { - val context = LocalContext.current val screenState by viewModel.screenState.collectAsStateWithLifecycle() + val hapticType by viewModel.hapticType.collectAsStateWithLifecycle() - val userSettings: UserSettings = koinInject() - - LaunchedEffect(true) { - userSettings.enableDebugSettings(screenState.showDebugOptions) - } - - SettingsScreen(screenState = screenState, + SettingsScreen( + screenState = screenState, + hapticType = hapticType, onBack = onBack, onHapticPerformed = viewModel::onHapticPerformed, onSettingsItemClicked = { key -> @@ -88,25 +78,7 @@ fun SettingsRoute( } }, onSettingsItemLongClicked = viewModel::onSettingsItemLongClicked, - onSettingsItemValueChanged = { key, newValue -> - viewModel.onSettingsItemChanged(key, newValue) - - when (key) { - SettingsKeys.KEY_APPEARANCE_DARK_THEME -> { - val newMode = newValue as? Int ?: 0 - AppCompatDelegate.setDefaultNightMode(newMode) - - val isUsing = context.getSystemService()?.let { manager -> - isUsingDarkMode( - context.resources, - manager - ) - } ?: false - - userSettings.useDarkThemeChanged(isUsing) - } - } - } + onSettingsItemValueChanged = viewModel::onSettingsItemChanged ) HandlePopups( @@ -128,6 +100,7 @@ fun SettingsRoute( @Composable fun SettingsScreen( screenState: SettingsScreenState = SettingsScreenState.EMPTY, + hapticType: HapticType? = null, onBack: () -> Unit = {}, onHapticPerformed: () -> Unit = {}, onSettingsItemClicked: (key: String) -> Unit = {}, @@ -135,7 +108,6 @@ fun SettingsScreen( onSettingsItemValueChanged: (key: String, newValue: Any?) -> Unit = { _, _ -> } ) { val view = LocalView.current - val hapticType = screenState.useHaptics LaunchedEffect(hapticType) { if (hapticType != null) { @@ -144,7 +116,7 @@ fun SettingsScreen( } } - val currentTheme = LocalTheme.current + val themeConfig = LocalThemeConfig.current val hazeState = remember { HazeState() } @@ -164,12 +136,12 @@ fun SettingsScreen( }, colors = TopAppBarDefaults.topAppBarColors( containerColor = MaterialTheme.colorScheme.surface.copy( - alpha = if (currentTheme.usingBlur) 0f else 1f + alpha = if (themeConfig.enableBlur) 0f else 1f ) ), modifier = Modifier .then( - if (currentTheme.usingBlur) { + if (themeConfig.enableBlur) { Modifier.hazeChild( state = hazeState, style = HazeMaterials.thick() @@ -185,7 +157,7 @@ fun SettingsScreen( LazyColumn( modifier = Modifier .then( - if (currentTheme.usingBlur) { + if (themeConfig.enableBlur) { Modifier.haze( state = hazeState, style = HazeMaterials.thick() diff --git a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/ListItem.kt b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/ListItem.kt index 793af564..1273b73f 100644 --- a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/ListItem.kt +++ b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/ListItem.kt @@ -32,7 +32,7 @@ import com.meloda.app.fast.ui.basic.LocalContentAlpha import com.meloda.app.fast.ui.components.ActionInvokeDismiss import com.meloda.app.fast.ui.components.MaterialDialog import com.meloda.app.fast.ui.components.SelectionType -import com.meloda.app.fast.ui.theme.LocalTheme +import com.meloda.app.fast.ui.theme.LocalThemeConfig import com.meloda.app.fast.ui.util.ImmutableList import com.meloda.app.fast.ui.util.ImmutableList.Companion.toImmutableList @@ -51,7 +51,7 @@ fun ListItem( mutableStateOf(false) } - val currentTheme = LocalTheme.current + val currentTheme = LocalThemeConfig.current Row( modifier = modifier @@ -81,7 +81,7 @@ fun ListItem( Text( text = title, style = MaterialTheme.typography.headlineSmall, - maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1, + maxLines = if (currentTheme.enableMultiline) Int.MAX_VALUE else 1, overflow = TextOverflow.Ellipsis, ) } @@ -94,7 +94,7 @@ fun ListItem( Text( text = text, style = MaterialTheme.typography.bodyMedium, - maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1, + maxLines = if (currentTheme.enableMultiline) Int.MAX_VALUE else 1, overflow = TextOverflow.Ellipsis, ) } diff --git a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/SwitchItem.kt b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/SwitchItem.kt index c5ea6ccc..d62d9b92 100644 --- a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/SwitchItem.kt +++ b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/SwitchItem.kt @@ -22,7 +22,7 @@ import androidx.compose.ui.unit.dp import com.meloda.app.fast.settings.model.UiItem import com.meloda.app.fast.ui.basic.ContentAlpha import com.meloda.app.fast.ui.basic.LocalContentAlpha -import com.meloda.app.fast.ui.theme.LocalTheme +import com.meloda.app.fast.ui.theme.LocalThemeConfig @OptIn(ExperimentalFoundationApi::class) @Composable @@ -35,7 +35,7 @@ fun SwitchItem( ) { if (!item.isVisible) return - val currentTheme = LocalTheme.current + val currentTheme = LocalThemeConfig.current Row( modifier = modifier @@ -65,7 +65,7 @@ fun SwitchItem( Text( text = title, style = MaterialTheme.typography.headlineSmall, - maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1, + maxLines = if (currentTheme.enableMultiline) Int.MAX_VALUE else 1, overflow = TextOverflow.Ellipsis, ) } @@ -78,7 +78,7 @@ fun SwitchItem( Text( text = text, style = MaterialTheme.typography.bodyMedium, - maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1, + maxLines = if (currentTheme.enableMultiline) Int.MAX_VALUE else 1, overflow = TextOverflow.Ellipsis, ) } diff --git a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/TextFieldItem.kt b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/TextFieldItem.kt index 346e1354..77fbd5b0 100644 --- a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/TextFieldItem.kt +++ b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/TextFieldItem.kt @@ -38,7 +38,7 @@ import com.meloda.app.fast.ui.basic.ContentAlpha import com.meloda.app.fast.ui.basic.LocalContentAlpha import com.meloda.app.fast.ui.components.ActionInvokeDismiss import com.meloda.app.fast.ui.components.MaterialDialog -import com.meloda.app.fast.ui.theme.LocalTheme +import com.meloda.app.fast.ui.theme.LocalThemeConfig @OptIn(ExperimentalFoundationApi::class) @Composable @@ -51,7 +51,7 @@ fun TextFieldItem( ) { if (!item.isVisible) return - val currentTheme = LocalTheme.current + val currentTheme = LocalThemeConfig.current var showDialog by rememberSaveable { mutableStateOf(false) @@ -93,7 +93,7 @@ fun TextFieldItem( Text( text = title, style = MaterialTheme.typography.headlineSmall, - maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1, + maxLines = if (currentTheme.enableMultiline) Int.MAX_VALUE else 1, overflow = TextOverflow.Ellipsis, ) } @@ -106,7 +106,7 @@ fun TextFieldItem( Text( text = text, style = MaterialTheme.typography.bodyMedium, - maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1, + maxLines = if (currentTheme.enableMultiline) Int.MAX_VALUE else 1, overflow = TextOverflow.Ellipsis, ) } diff --git a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/TitleItem.kt b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/TitleItem.kt index f206461e..25430af6 100644 --- a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/TitleItem.kt +++ b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/TitleItem.kt @@ -9,7 +9,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.meloda.app.fast.settings.model.UiItem -import com.meloda.app.fast.ui.theme.LocalTheme +import com.meloda.app.fast.ui.theme.LocalThemeConfig @Composable fun TitleItem( @@ -18,7 +18,7 @@ fun TitleItem( ) { if (!item.isVisible) return - val currentTheme = LocalTheme.current + val currentTheme = LocalThemeConfig.current Text( text = item.title, @@ -32,7 +32,7 @@ fun TitleItem( bottom = 4.dp ) .animateContentSize(), - maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1, + maxLines = if (currentTheme.enableMultiline) Int.MAX_VALUE else 1, overflow = TextOverflow.Ellipsis, ) } diff --git a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/TitleTextItem.kt b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/TitleTextItem.kt index 061c6407..75a9123f 100644 --- a/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/TitleTextItem.kt +++ b/feature/settings/src/main/kotlin/com/meloda/app/fast/settings/presentation/item/TitleTextItem.kt @@ -21,7 +21,7 @@ import androidx.compose.ui.unit.dp import com.meloda.app.fast.settings.model.UiItem import com.meloda.app.fast.ui.basic.ContentAlpha import com.meloda.app.fast.ui.basic.LocalContentAlpha -import com.meloda.app.fast.ui.theme.LocalTheme +import com.meloda.app.fast.ui.theme.LocalThemeConfig @OptIn(ExperimentalFoundationApi::class) @Composable @@ -33,7 +33,7 @@ fun TitleTextItem( ) { if (!item.isVisible) return - val currentTheme = LocalTheme.current + val currentTheme = LocalThemeConfig.current Row( modifier = modifier @@ -62,7 +62,7 @@ fun TitleTextItem( Text( text = title, style = MaterialTheme.typography.headlineSmall, - maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1, + maxLines = if (currentTheme.enableMultiline) Int.MAX_VALUE else 1, overflow = TextOverflow.Ellipsis, ) } @@ -75,7 +75,7 @@ fun TitleTextItem( Text( text = text, style = MaterialTheme.typography.bodyMedium, - maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1, + maxLines = if (currentTheme.enableMultiline) Int.MAX_VALUE else 1, overflow = TextOverflow.Ellipsis, ) }