@@ -61,18 +61,6 @@ class MainActivity : ComponentActivity() {
|
||||
val seed = settingsState.colorScheme.toColor()
|
||||
|
||||
val isPlus by settingsViewModel.isPlus.collectAsStateWithLifecycle()
|
||||
val isPurchaseStateLoaded by settingsViewModel.isPurchaseStateLoaded.collectAsStateWithLifecycle()
|
||||
val isSettingsLoaded by settingsViewModel.isSettingsLoaded.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(isPurchaseStateLoaded, isPlus, isSettingsLoaded) {
|
||||
if (isPurchaseStateLoaded && isSettingsLoaded) {
|
||||
if (!isPlus) {
|
||||
settingsViewModel.resetPaywalledSettings()
|
||||
} else {
|
||||
settingsViewModel.reloadSettings()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TomatoTheme(
|
||||
darkTheme = darkTheme,
|
||||
|
||||
@@ -21,5 +21,4 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface BillingManager {
|
||||
val isPlus: StateFlow<Boolean>
|
||||
val isLoaded: StateFlow<Boolean>
|
||||
}
|
||||
@@ -101,10 +101,6 @@ fun SettingsScreenRoot(
|
||||
val longBreakTimeInputFieldState = viewModel.longBreakTimeTextFieldState
|
||||
|
||||
val isPlus by viewModel.isPlus.collectAsStateWithLifecycle()
|
||||
val alarmEnabled by viewModel.alarmEnabled.collectAsStateWithLifecycle(true)
|
||||
val vibrateEnabled by viewModel.vibrateEnabled.collectAsStateWithLifecycle(true)
|
||||
val dndEnabled by viewModel.dndEnabled.collectAsStateWithLifecycle(false)
|
||||
val alarmSound by viewModel.alarmSound.collectAsStateWithLifecycle(viewModel.currentAlarmSound)
|
||||
|
||||
val settingsState by viewModel.settingsState.collectAsStateWithLifecycle()
|
||||
|
||||
@@ -125,10 +121,6 @@ fun SettingsScreenRoot(
|
||||
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
|
||||
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
|
||||
sessionsSliderState = sessionsSliderState,
|
||||
alarmEnabled = alarmEnabled,
|
||||
vibrateEnabled = vibrateEnabled,
|
||||
dndEnabled = dndEnabled,
|
||||
alarmSound = alarmSound,
|
||||
onAction = viewModel::onAction,
|
||||
setShowPaywall = setShowPaywall,
|
||||
modifier = modifier
|
||||
@@ -146,10 +138,6 @@ private fun SettingsScreen(
|
||||
shortBreakTimeInputFieldState: TextFieldState,
|
||||
longBreakTimeInputFieldState: TextFieldState,
|
||||
sessionsSliderState: SliderState,
|
||||
alarmEnabled: Boolean,
|
||||
vibrateEnabled: Boolean,
|
||||
dndEnabled: Boolean,
|
||||
alarmSound: String,
|
||||
onAction: (SettingsAction) -> Unit,
|
||||
setShowPaywall: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
@@ -288,9 +276,6 @@ private fun SettingsScreen(
|
||||
entry<Screen.Settings.Alarm> {
|
||||
AlarmSettings(
|
||||
settingsState = settingsState,
|
||||
alarmEnabled = alarmEnabled,
|
||||
vibrateEnabled = vibrateEnabled,
|
||||
alarmSound = alarmSound,
|
||||
onAction = onAction,
|
||||
onBack = backStack::removeLastOrNull
|
||||
)
|
||||
@@ -307,8 +292,7 @@ private fun SettingsScreen(
|
||||
entry<Screen.Settings.Timer> {
|
||||
TimerSettings(
|
||||
isPlus = isPlus,
|
||||
aodEnabled = settingsState.aodEnabled,
|
||||
dndEnabled = dndEnabled,
|
||||
settingsState = settingsState,
|
||||
focusTimeInputFieldState = focusTimeInputFieldState,
|
||||
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
|
||||
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
|
||||
|
||||
@@ -79,9 +79,6 @@ import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.topListItemShape
|
||||
@Composable
|
||||
fun AlarmSettings(
|
||||
settingsState: SettingsState,
|
||||
alarmEnabled: Boolean,
|
||||
vibrateEnabled: Boolean,
|
||||
alarmSound: String,
|
||||
onAction: (SettingsAction) -> Unit,
|
||||
onBack: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
@@ -91,10 +88,11 @@ fun AlarmSettings(
|
||||
|
||||
var alarmName by remember { mutableStateOf("...") }
|
||||
|
||||
LaunchedEffect(alarmSound) {
|
||||
LaunchedEffect(settingsState.alarmSound) {
|
||||
withContext(Dispatchers.IO) {
|
||||
alarmName =
|
||||
RingtoneManager.getRingtone(context, alarmSound.toUri())?.getTitle(context) ?: ""
|
||||
RingtoneManager.getRingtone(context, settingsState.alarmSound.toUri())
|
||||
?.getTitle(context) ?: ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,30 +115,30 @@ fun AlarmSettings(
|
||||
}
|
||||
|
||||
@SuppressLint("LocalContextGetResourceValueCall")
|
||||
val ringtonePickerIntent = remember(alarmSound) {
|
||||
val ringtonePickerIntent = remember(settingsState.alarmSound) {
|
||||
Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply {
|
||||
putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_ALARM)
|
||||
putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, context.getString(R.string.alarm_sound))
|
||||
putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, alarmSound.toUri())
|
||||
putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, settingsState.alarmSound.toUri())
|
||||
}
|
||||
}
|
||||
|
||||
val switchItems = remember(
|
||||
settingsState.blackTheme,
|
||||
settingsState.aodEnabled,
|
||||
alarmEnabled,
|
||||
vibrateEnabled
|
||||
settingsState.alarmEnabled,
|
||||
settingsState.vibrateEnabled
|
||||
) {
|
||||
listOf(
|
||||
SettingsSwitchItem(
|
||||
checked = alarmEnabled,
|
||||
checked = settingsState.alarmEnabled,
|
||||
icon = R.drawable.alarm_on,
|
||||
label = R.string.sound,
|
||||
description = R.string.alarm_desc,
|
||||
onClick = { onAction(SettingsAction.SaveAlarmEnabled(it)) }
|
||||
),
|
||||
SettingsSwitchItem(
|
||||
checked = vibrateEnabled,
|
||||
checked = settingsState.vibrateEnabled,
|
||||
icon = R.drawable.mobile_vibrate,
|
||||
label = R.string.vibrate,
|
||||
description = R.string.vibrate_desc,
|
||||
@@ -245,9 +243,7 @@ fun AlarmSettingsPreview() {
|
||||
val settingsState = SettingsState()
|
||||
AlarmSettings(
|
||||
settingsState = settingsState,
|
||||
alarmEnabled = true,
|
||||
vibrateEnabled = false,
|
||||
alarmSound = "",
|
||||
onAction = {},
|
||||
onBack = {})
|
||||
onBack = {}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.input.TextFieldState
|
||||
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.FilledTonalIconToggleButton
|
||||
@@ -57,6 +58,7 @@ import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.SwitchDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberSliderState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -78,6 +80,7 @@ import org.nsh07.pomodoro.ui.settingsScreen.SettingsSwitchItem
|
||||
import org.nsh07.pomodoro.ui.settingsScreen.components.MinuteInputField
|
||||
import org.nsh07.pomodoro.ui.settingsScreen.components.PlusDivider
|
||||
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsAction
|
||||
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsState
|
||||
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
|
||||
import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
|
||||
import org.nsh07.pomodoro.ui.theme.CustomColors.switchColors
|
||||
@@ -92,14 +95,13 @@ import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.topListItemShape
|
||||
@Composable
|
||||
fun TimerSettings(
|
||||
isPlus: Boolean,
|
||||
aodEnabled: Boolean,
|
||||
dndEnabled: Boolean,
|
||||
settingsState: SettingsState,
|
||||
focusTimeInputFieldState: TextFieldState,
|
||||
shortBreakTimeInputFieldState: TextFieldState,
|
||||
longBreakTimeInputFieldState: TextFieldState,
|
||||
sessionsSliderState: SliderState,
|
||||
setShowPaywall: (Boolean) -> Unit,
|
||||
onAction: (SettingsAction) -> Unit,
|
||||
setShowPaywall: (Boolean) -> Unit,
|
||||
onBack: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
@@ -116,7 +118,7 @@ fun TimerSettings(
|
||||
|
||||
val switchItems = listOf(
|
||||
SettingsSwitchItem(
|
||||
checked = dndEnabled,
|
||||
checked = settingsState.dndEnabled,
|
||||
icon = R.drawable.dnd,
|
||||
label = R.string.dnd,
|
||||
description = R.string.dnd_desc,
|
||||
@@ -133,7 +135,7 @@ fun TimerSettings(
|
||||
}
|
||||
),
|
||||
SettingsSwitchItem(
|
||||
checked = aodEnabled,
|
||||
checked = settingsState.aodEnabled,
|
||||
icon = R.drawable.aod,
|
||||
label = R.string.always_on_display,
|
||||
description = R.string.always_on_display_desc,
|
||||
@@ -393,18 +395,17 @@ fun TimerSettings(
|
||||
@Preview
|
||||
@Composable
|
||||
private fun TimerSettingsPreview() {
|
||||
val focusTimeInputFieldState = TextFieldState("25")
|
||||
val shortBreakTimeInputFieldState = TextFieldState("5")
|
||||
val longBreakTimeInputFieldState = TextFieldState("15")
|
||||
val sessionsSliderState = SliderState(
|
||||
val focusTimeInputFieldState = rememberTextFieldState("25")
|
||||
val shortBreakTimeInputFieldState = rememberTextFieldState("5")
|
||||
val longBreakTimeInputFieldState = rememberTextFieldState("15")
|
||||
val sessionsSliderState = rememberSliderState(
|
||||
value = 4f,
|
||||
valueRange = 1f..8f,
|
||||
steps = 6
|
||||
)
|
||||
TimerSettings(
|
||||
isPlus = false,
|
||||
aodEnabled = true,
|
||||
dndEnabled = false,
|
||||
settingsState = remember { SettingsState() },
|
||||
focusTimeInputFieldState = focusTimeInputFieldState,
|
||||
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
|
||||
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
|
||||
|
||||
@@ -23,7 +23,11 @@ import androidx.compose.ui.graphics.Color
|
||||
@Immutable
|
||||
data class SettingsState(
|
||||
val theme: String = "auto",
|
||||
val alarmSound: String = "",
|
||||
val colorScheme: String = Color.White.toString(),
|
||||
val blackTheme: Boolean = false,
|
||||
val aodEnabled: Boolean = false
|
||||
val aodEnabled: Boolean = false,
|
||||
val alarmEnabled: Boolean = true,
|
||||
val vibrateEnabled: Boolean = true,
|
||||
val dndEnabled: Boolean = false
|
||||
)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package org.nsh07.pomodoro.ui.settingsScreen.viewModel
|
||||
|
||||
import android.net.Uri
|
||||
import android.provider.Settings
|
||||
import androidx.compose.foundation.text.input.TextFieldState
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.SliderState
|
||||
@@ -36,7 +37,6 @@ import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import org.nsh07.pomodoro.TomatoApplication
|
||||
@@ -54,10 +54,6 @@ class SettingsViewModel(
|
||||
val backStack = mutableStateListOf<Screen.Settings>(Screen.Settings.Main)
|
||||
|
||||
val isPlus = billingManager.isPlus
|
||||
val isPurchaseStateLoaded = billingManager.isLoaded
|
||||
|
||||
private val _isSettingsLoaded = MutableStateFlow(false)
|
||||
val isSettingsLoaded = _isSettingsLoaded.asStateFlow()
|
||||
|
||||
private val _settingsState = MutableStateFlow(SettingsState())
|
||||
val settingsState = _settingsState.asStateFlow()
|
||||
@@ -81,25 +77,13 @@ class SettingsViewModel(
|
||||
)
|
||||
}
|
||||
|
||||
val currentAlarmSound = timerRepository.alarmSoundUri.toString()
|
||||
|
||||
private var focusFlowCollectionJob: Job? = null
|
||||
private var shortBreakFlowCollectionJob: Job? = null
|
||||
private var longBreakFlowCollectionJob: Job? = null
|
||||
|
||||
val alarmSound =
|
||||
preferenceRepository.getStringPreferenceFlow("alarm_sound").distinctUntilChanged()
|
||||
val alarmEnabled =
|
||||
preferenceRepository.getBooleanPreferenceFlow("alarm_enabled").distinctUntilChanged()
|
||||
val vibrateEnabled =
|
||||
preferenceRepository.getBooleanPreferenceFlow("vibrate_enabled").distinctUntilChanged()
|
||||
val dndEnabled =
|
||||
preferenceRepository.getBooleanPreferenceFlow("dnd_enabled").distinctUntilChanged()
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
reloadSettings()
|
||||
_isSettingsLoaded.value = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +160,9 @@ class SettingsViewModel(
|
||||
private fun saveAlarmEnabled(enabled: Boolean) {
|
||||
viewModelScope.launch {
|
||||
timerRepository.alarmEnabled = enabled
|
||||
_settingsState.update { currentState ->
|
||||
currentState.copy(alarmEnabled = enabled)
|
||||
}
|
||||
preferenceRepository.saveBooleanPreference("alarm_enabled", enabled)
|
||||
}
|
||||
}
|
||||
@@ -183,6 +170,9 @@ class SettingsViewModel(
|
||||
private fun saveVibrateEnabled(enabled: Boolean) {
|
||||
viewModelScope.launch {
|
||||
timerRepository.vibrateEnabled = enabled
|
||||
_settingsState.update { currentState ->
|
||||
currentState.copy(vibrateEnabled = enabled)
|
||||
}
|
||||
preferenceRepository.saveBooleanPreference("vibrate_enabled", enabled)
|
||||
}
|
||||
}
|
||||
@@ -190,6 +180,9 @@ class SettingsViewModel(
|
||||
private fun saveDndEnabled(enabled: Boolean) {
|
||||
viewModelScope.launch {
|
||||
timerRepository.dndEnabled = enabled
|
||||
_settingsState.update { currentState ->
|
||||
currentState.copy(dndEnabled = enabled)
|
||||
}
|
||||
preferenceRepository.saveBooleanPreference("dnd_enabled", enabled)
|
||||
}
|
||||
}
|
||||
@@ -197,6 +190,9 @@ class SettingsViewModel(
|
||||
private fun saveAlarmSound(uri: Uri?) {
|
||||
viewModelScope.launch {
|
||||
timerRepository.alarmSoundUri = uri
|
||||
_settingsState.update { currentState ->
|
||||
currentState.copy(alarmSound = uri.toString())
|
||||
}
|
||||
preferenceRepository.saveStringPreference("alarm_sound", uri.toString())
|
||||
}
|
||||
}
|
||||
@@ -237,16 +233,6 @@ class SettingsViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun resetPaywalledSettings() {
|
||||
_settingsState.update { currentState ->
|
||||
currentState.copy(
|
||||
aodEnabled = false,
|
||||
blackTheme = false,
|
||||
colorScheme = Color.White.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun reloadSettings() {
|
||||
val theme = preferenceRepository.getStringPreference("theme")
|
||||
?: preferenceRepository.saveStringPreference("theme", "auto")
|
||||
@@ -256,13 +242,29 @@ class SettingsViewModel(
|
||||
?: preferenceRepository.saveBooleanPreference("black_theme", false)
|
||||
val aodEnabled = preferenceRepository.getBooleanPreference("aod_enabled")
|
||||
?: preferenceRepository.saveBooleanPreference("aod_enabled", false)
|
||||
val alarmSound = preferenceRepository.getStringPreference("alarm_sound")
|
||||
?: preferenceRepository.saveStringPreference(
|
||||
"alarm_sound",
|
||||
(Settings.System.DEFAULT_ALARM_ALERT_URI
|
||||
?: Settings.System.DEFAULT_RINGTONE_URI).toString()
|
||||
)
|
||||
val alarmEnabled = preferenceRepository.getBooleanPreference("alarm_enabled")
|
||||
?: preferenceRepository.saveBooleanPreference("alarm_enabled", true)
|
||||
val vibrateEnabled = preferenceRepository.getBooleanPreference("vibrate_enabled")
|
||||
?: preferenceRepository.saveBooleanPreference("vibrate_enabled", true)
|
||||
val dndEnabled = preferenceRepository.getBooleanPreference("dnd_enabled")
|
||||
?: preferenceRepository.saveBooleanPreference("dnd_enabled", false)
|
||||
|
||||
_settingsState.update { currentState ->
|
||||
currentState.copy(
|
||||
theme = theme,
|
||||
colorScheme = colorScheme,
|
||||
alarmSound = alarmSound,
|
||||
blackTheme = blackTheme,
|
||||
aodEnabled = aodEnabled
|
||||
aodEnabled = aodEnabled,
|
||||
alarmEnabled = alarmEnabled,
|
||||
vibrateEnabled = vibrateEnabled,
|
||||
dndEnabled = dndEnabled
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user