fix: Fix some major bugs with the URI to string (and vice versa) conversion

This commit is contained in:
Nishant Mishra
2025-09-16 00:39:15 +05:30
parent 37c74dc844
commit 55de7589ee
4 changed files with 42 additions and 27 deletions

View File

@@ -11,6 +11,7 @@ import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy.Companion.REPLACE import androidx.room.OnConflictStrategy.Companion.REPLACE
import androidx.room.Query import androidx.room.Query
import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface PreferenceDao { interface PreferenceDao {
@@ -38,6 +39,12 @@ interface PreferenceDao {
@Query("SELECT value FROM boolean_preference WHERE `key` = :key") @Query("SELECT value FROM boolean_preference WHERE `key` = :key")
suspend fun getBooleanPreference(key: String): Boolean? suspend fun getBooleanPreference(key: String): Boolean?
@Query("SELECT value FROM boolean_preference WHERE `key` = :key")
fun getBooleanPreferenceFlow(key: String): Flow<Boolean>
@Query("SELECT value FROM string_preference WHERE `key` = :key") @Query("SELECT value FROM string_preference WHERE `key` = :key")
suspend fun getStringPreference(key: String): String? suspend fun getStringPreference(key: String): String?
@Query("SELECT value FROM string_preference WHERE `key` = :key")
fun getStringPreferenceFlow(key: String): Flow<String>
} }

View File

@@ -9,6 +9,7 @@ package org.nsh07.pomodoro.data
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
/** /**
@@ -42,11 +43,21 @@ interface PreferenceRepository {
*/ */
suspend fun getBooleanPreference(key: String): Boolean? suspend fun getBooleanPreference(key: String): Boolean?
/**
* Retrieves a boolean preference key-value pair as a flow from the database.
*/
fun getBooleanPreferenceFlow(key: String): Flow<Boolean>
/** /**
* Retrieves a string preference key-value pair from the database. * Retrieves a string preference key-value pair from the database.
*/ */
suspend fun getStringPreference(key: String): String? suspend fun getStringPreference(key: String): String?
/**
* Retrieves a string preference key-value pair as a flow from the database.
*/
fun getStringPreferenceFlow(key: String): Flow<String>
/** /**
* Erases all integer preference key-value pairs in the database. Do note that the default values * Erases all integer preference key-value pairs in the database. Do note that the default values
* will need to be rewritten manually * will need to be rewritten manually
@@ -87,10 +98,16 @@ class AppPreferenceRepository(
preferenceDao.getBooleanPreference(key) preferenceDao.getBooleanPreference(key)
} }
override fun getBooleanPreferenceFlow(key: String): Flow<Boolean> =
preferenceDao.getBooleanPreferenceFlow(key)
override suspend fun getStringPreference(key: String): String? = withContext(ioDispatcher) { override suspend fun getStringPreference(key: String): String? = withContext(ioDispatcher) {
preferenceDao.getStringPreference(key) preferenceDao.getStringPreference(key)
} }
override fun getStringPreferenceFlow(key: String): Flow<String> =
preferenceDao.getStringPreferenceFlow(key)
override suspend fun resetSettings() = withContext(ioDispatcher) { override suspend fun resetSettings() = withContext(ioDispatcher) {
preferenceDao.resetIntPreferences() preferenceDao.resetIntPreferences()
preferenceDao.resetBooleanPreferences() preferenceDao.resetBooleanPreferences()

View File

@@ -71,6 +71,7 @@ import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.core.net.toUri
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import org.nsh07.pomodoro.R import org.nsh07.pomodoro.R
@@ -101,9 +102,9 @@ fun SettingsScreenRoot(
viewModel.longBreakTimeTextFieldState viewModel.longBreakTimeTextFieldState
} }
val alarmEnabled by viewModel.alarmEnabled.collectAsStateWithLifecycle() val alarmEnabled by viewModel.alarmEnabled.collectAsStateWithLifecycle(true)
val vibrateEnabled by viewModel.vibrateEnabled.collectAsStateWithLifecycle() val vibrateEnabled by viewModel.vibrateEnabled.collectAsStateWithLifecycle(true)
val alarmSound by viewModel.alarmSound.collectAsStateWithLifecycle() val alarmSound by viewModel.alarmSound.collectAsStateWithLifecycle("")
val sessionsSliderState = rememberSaveable( val sessionsSliderState = rememberSaveable(
saver = SliderState.Saver( saver = SliderState.Saver(
@@ -144,7 +145,7 @@ private fun SettingsScreen(
sessionsSliderState: SliderState, sessionsSliderState: SliderState,
alarmEnabled: Boolean, alarmEnabled: Boolean,
vibrateEnabled: Boolean, vibrateEnabled: Boolean,
alarmSound: Uri?, alarmSound: String,
onAlarmEnabledChange: (Boolean) -> Unit, onAlarmEnabledChange: (Boolean) -> Unit,
onVibrateEnabledChange: (Boolean) -> Unit, onVibrateEnabledChange: (Boolean) -> Unit,
onAlarmSoundChanged: (Uri?) -> Unit, onAlarmSoundChanged: (Uri?) -> Unit,
@@ -155,7 +156,7 @@ private fun SettingsScreen(
checkedIconColor = colorScheme.primary, checkedIconColor = colorScheme.primary,
) )
var selectedSoundUri by remember { mutableStateOf(alarmSound) } var selectedSoundUri by remember(alarmSound) { mutableStateOf(alarmSound.toUri()) }
var selectedSoundName by remember { mutableStateOf("...") } var selectedSoundName by remember { mutableStateOf("...") }
val context = LocalContext.current val context = LocalContext.current
@@ -173,7 +174,6 @@ private fun SettingsScreen(
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
result.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) result.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
} }
selectedSoundUri = uri
onAlarmSoundChanged(uri) onAlarmSoundChanged(uri)
} }
} }
@@ -436,7 +436,7 @@ fun SettingsScreenPreview() {
sessionsSliderState = rememberSliderState(value = 3f, steps = 3, valueRange = 1f..5f), sessionsSliderState = rememberSliderState(value = 3f, steps = 3, valueRange = 1f..5f),
alarmEnabled = true, alarmEnabled = true,
vibrateEnabled = true, vibrateEnabled = true,
alarmSound = Settings.System.DEFAULT_ALARM_ALERT_URI, alarmSound = Settings.System.DEFAULT_ALARM_ALERT_URI.toString(),
onAlarmEnabledChange = {}, onAlarmEnabledChange = {},
onVibrateEnabledChange = {}, onVibrateEnabledChange = {},
onAlarmSoundChanged = {}, onAlarmSoundChanged = {},

View File

@@ -20,10 +20,8 @@ import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory import androidx.lifecycle.viewmodel.viewModelFactory
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.nsh07.pomodoro.TomatoApplication import org.nsh07.pomodoro.TomatoApplication
import org.nsh07.pomodoro.data.AppPreferenceRepository import org.nsh07.pomodoro.data.AppPreferenceRepository
@@ -48,16 +46,12 @@ class SettingsViewModel(
onValueChangeFinished = ::updateSessionLength onValueChangeFinished = ::updateSessionLength
) )
private val _alarmSound = MutableStateFlow(timerRepository.alarmSoundUri) val alarmSound =
val alarmSound: StateFlow<Uri?> = _alarmSound.asStateFlow() preferenceRepository.getStringPreferenceFlow("alarm_sound").distinctUntilChanged()
val alarmEnabled =
private val _alarmEnabled: MutableStateFlow<Boolean> = preferenceRepository.getBooleanPreferenceFlow("alarm_enabled").distinctUntilChanged()
MutableStateFlow(timerRepository.alarmEnabled) val vibrateEnabled =
val alarmEnabled: StateFlow<Boolean> = _alarmEnabled.asStateFlow() preferenceRepository.getBooleanPreferenceFlow("vibrate_enabled").distinctUntilChanged()
private val _vibrateEnabled: MutableStateFlow<Boolean> =
MutableStateFlow(timerRepository.alarmEnabled)
val vibrateEnabled: StateFlow<Boolean> = _vibrateEnabled.asStateFlow()
init { init {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
@@ -109,17 +103,15 @@ class SettingsViewModel(
fun saveAlarmEnabled(enabled: Boolean) { fun saveAlarmEnabled(enabled: Boolean) {
viewModelScope.launch { viewModelScope.launch {
timerRepository.alarmEnabled = preferenceRepository preferenceRepository.saveBooleanPreference("alarm_enabled", enabled)
.saveBooleanPreference("alarm_enabled", enabled) timerRepository.alarmEnabled = enabled
_alarmEnabled.value = enabled
} }
} }
fun saveVibrateEnabled(enabled: Boolean) { fun saveVibrateEnabled(enabled: Boolean) {
viewModelScope.launch { viewModelScope.launch {
timerRepository.vibrateEnabled = preferenceRepository preferenceRepository.saveBooleanPreference("vibrate_enabled", enabled)
.saveBooleanPreference("vibrate_enabled", enabled) timerRepository.vibrateEnabled = enabled
_vibrateEnabled.value = enabled
} }
} }
@@ -127,7 +119,6 @@ class SettingsViewModel(
viewModelScope.launch { viewModelScope.launch {
preferenceRepository.saveStringPreference("alarm_sound", uri.toString()) preferenceRepository.saveStringPreference("alarm_sound", uri.toString())
timerRepository.alarmSoundUri = uri timerRepository.alarmSoundUri = uri
_alarmSound.value = uri
} }
} }