From 970a424a7c426f05cc5d317c16a9a1777ad6f842 Mon Sep 17 00:00:00 2001 From: Nishant Mishra Date: Mon, 15 Sep 2025 19:42:20 +0530 Subject: [PATCH 1/6] feat: Update navigation bar icons --- app/src/main/java/org/nsh07/pomodoro/MainActivity.kt | 4 ++-- .../nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt | 4 ++-- app/src/main/res/drawable/hourglass.xml | 10 ---------- app/src/main/res/drawable/hourglass_filled.xml | 10 ---------- app/src/main/res/drawable/timer_filled.xml | 9 +++++++++ app/src/main/res/drawable/timer_outlined.xml | 9 +++++++++ 6 files changed, 22 insertions(+), 24 deletions(-) delete mode 100644 app/src/main/res/drawable/hourglass.xml delete mode 100644 app/src/main/res/drawable/hourglass_filled.xml create mode 100644 app/src/main/res/drawable/timer_filled.xml create mode 100644 app/src/main/res/drawable/timer_outlined.xml diff --git a/app/src/main/java/org/nsh07/pomodoro/MainActivity.kt b/app/src/main/java/org/nsh07/pomodoro/MainActivity.kt index c6e55b6..94d8900 100644 --- a/app/src/main/java/org/nsh07/pomodoro/MainActivity.kt +++ b/app/src/main/java/org/nsh07/pomodoro/MainActivity.kt @@ -55,8 +55,8 @@ class MainActivity : ComponentActivity() { val screens = listOf( NavItem( Screen.Timer, - R.drawable.hourglass, - R.drawable.hourglass_filled, + R.drawable.timer_outlined, + R.drawable.timer_filled, "Timer" ), NavItem( diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt index b3683fe..37334df 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt @@ -138,14 +138,14 @@ private fun SettingsScreen( checked = alarmEnabled, icon = R.drawable.alarm_on, label = "Alarm", - description = "Ring your system alarm sound when a timer completes", + description = "Ring alarm when a timer completes", onClick = onAlarmEnabledChange ), SettingsSwitchItem( checked = vibrateEnabled, icon = R.drawable.mobile_vibrate, label = "Vibrate", - description = "Vibrate in a repeating pattern when a timer completes", + description = "Vibrate when a timer completes", onClick = onVibrateEnabledChange ) ) diff --git a/app/src/main/res/drawable/hourglass.xml b/app/src/main/res/drawable/hourglass.xml deleted file mode 100644 index a9fdbda..0000000 --- a/app/src/main/res/drawable/hourglass.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/hourglass_filled.xml b/app/src/main/res/drawable/hourglass_filled.xml deleted file mode 100644 index 7e80e61..0000000 --- a/app/src/main/res/drawable/hourglass_filled.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/timer_filled.xml b/app/src/main/res/drawable/timer_filled.xml new file mode 100644 index 0000000..9cfe048 --- /dev/null +++ b/app/src/main/res/drawable/timer_filled.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/timer_outlined.xml b/app/src/main/res/drawable/timer_outlined.xml new file mode 100644 index 0000000..17b99fb --- /dev/null +++ b/app/src/main/res/drawable/timer_outlined.xml @@ -0,0 +1,9 @@ + + + From fdc1c3f6c57a5170d11a58f15537395ee3d26ce4 Mon Sep 17 00:00:00 2001 From: Nishant Mishra Date: Mon, 15 Sep 2025 20:02:24 +0530 Subject: [PATCH 2/6] feat: Implement an alarm sound picker in the Settings screen Closes: #36 --- .../ui/settingsScreen/SettingsScreen.kt | 68 ++++++++++++++++++- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt index 37334df..98db86c 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt @@ -7,9 +7,18 @@ package org.nsh07.pomodoro.ui.settingsScreen +import android.app.Activity +import android.content.Intent +import android.media.RingtoneManager +import android.net.Uri +import android.os.Build +import android.provider.Settings +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.DrawableRes import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -45,6 +54,7 @@ import androidx.compose.material3.TopAppBar 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 import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -54,6 +64,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.tooling.preview.Devices @@ -68,7 +79,6 @@ import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors import org.nsh07.pomodoro.ui.theme.CustomColors.topBarColors import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.bottomListItemShape -import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.cardShape import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.middleListItemShape import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.topListItemShape import org.nsh07.pomodoro.ui.theme.TomatoTheme @@ -132,6 +142,41 @@ private fun SettingsScreen( checkedIconColor = colorScheme.primary, ) + var selectedSoundUri by remember { mutableStateOf(Settings.System.DEFAULT_ALARM_ALERT_URI) } + var selectedSoundName by remember { mutableStateOf("...") } + val context = LocalContext.current + + // 2. The Activity Result Launcher + val ringtonePickerLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.StartActivityForResult() + ) { result -> + // This block is executed when the user picks a sound and returns to the app + if (result.resultCode == Activity.RESULT_OK) { + val uri = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + result.data?.getParcelableExtra( + RingtoneManager.EXTRA_RINGTONE_PICKED_URI, + Uri::class.java + ) + } else { + @Suppress("DEPRECATION") + result.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) + } + selectedSoundUri = uri + } + } + + // 3. The Intent to launch the picker + val intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply { + // We want to show only alarm sounds + putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_ALARM) + // A title for the picker + putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, "Alarm sound") + + // If a sound is already selected, show it as checked + putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, selectedSoundUri) + } + val switchItems = remember(alarmEnabled, vibrateEnabled) { listOf( SettingsSwitchItem( @@ -151,6 +196,12 @@ private fun SettingsScreen( ) } + LaunchedEffect(selectedSoundUri) { + selectedSoundName = + RingtoneManager.getRingtone(context, selectedSoundUri) + .getTitle(context) + } + Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) { TopAppBar( title = { @@ -266,7 +317,20 @@ private fun SettingsScreen( } }, colors = listItemColors, - modifier = Modifier.clip(cardShape) + modifier = Modifier.clip(topListItemShape) + ) + } + item { + ListItem( + leadingContent = { + Icon(painterResource(R.drawable.alarm), null) + }, + headlineContent = { Text("Alarm sound") }, + supportingContent = { Text(selectedSoundName) }, + colors = listItemColors, + modifier = Modifier + .clip(bottomListItemShape) + .clickable(onClick = { ringtonePickerLauncher.launch(intent) }) ) } item { Spacer(Modifier.height(12.dp)) } From 9c33406151c476d60397d3d647169f0ccb1ab3cd Mon Sep 17 00:00:00 2001 From: Nishant Mishra Date: Mon, 15 Sep 2025 23:23:05 +0530 Subject: [PATCH 3/6] feat: Implement the picked alarm sound to be played #36 --- .../nsh07/pomodoro/data/TimerRepository.kt | 9 ++++++ .../nsh07/pomodoro/service/TimerService.kt | 28 ++++++++++++------- .../ui/settingsScreen/SettingsScreen.kt | 28 ++++++++++++++----- .../viewModel/SettingsViewModel.kt | 9 ++++++ .../timerScreen/viewModel/TimerViewModel.kt | 25 +++++++++-------- 5 files changed, 71 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/org/nsh07/pomodoro/data/TimerRepository.kt b/app/src/main/java/org/nsh07/pomodoro/data/TimerRepository.kt index 850919c..e1e3068 100644 --- a/app/src/main/java/org/nsh07/pomodoro/data/TimerRepository.kt +++ b/app/src/main/java/org/nsh07/pomodoro/data/TimerRepository.kt @@ -7,6 +7,8 @@ package org.nsh07.pomodoro.data +import android.net.Uri +import android.provider.Settings import androidx.compose.material3.ColorScheme import androidx.compose.material3.lightColorScheme @@ -18,12 +20,17 @@ interface TimerRepository { var focusTime: Long var shortBreakTime: Long var longBreakTime: Long + var sessionLength: Int + var timerFrequency: Float + var alarmEnabled: Boolean var vibrateEnabled: Boolean var colorScheme: ColorScheme + + var alarmSoundUri: Uri? } /** @@ -38,4 +45,6 @@ class AppTimerRepository : TimerRepository { override var alarmEnabled = true override var vibrateEnabled = true override var colorScheme = lightColorScheme() + override var alarmSoundUri: Uri? = + Settings.System.DEFAULT_ALARM_ALERT_URI ?: Settings.System.DEFAULT_RINGTONE_URI } \ No newline at end of file diff --git a/app/src/main/java/org/nsh07/pomodoro/service/TimerService.kt b/app/src/main/java/org/nsh07/pomodoro/service/TimerService.kt index f11debe..a3c348c 100644 --- a/app/src/main/java/org/nsh07/pomodoro/service/TimerService.kt +++ b/app/src/main/java/org/nsh07/pomodoro/service/TimerService.kt @@ -10,7 +10,6 @@ import android.os.SystemClock import android.os.VibrationEffect import android.os.Vibrator import android.os.VibratorManager -import android.provider.Settings import androidx.compose.ui.graphics.toArgb import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat @@ -57,11 +56,7 @@ class TimerService : Service() { private val scope = CoroutineScope(Dispatchers.IO + job) private val skipScope = CoroutineScope(Dispatchers.IO + job) - private val alarm by lazy { - MediaPlayer.create( - this, Settings.System.DEFAULT_ALARM_ALERT_URI ?: Settings.System.DEFAULT_RINGTONE_URI - ) - } + private var alarm: MediaPlayer? = null private val vibrator by lazy { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { @@ -78,6 +73,11 @@ class TimerService : Service() { return null } + override fun onCreate() { + super.onCreate() + alarm = MediaPlayer.create(this, timerRepository.alarmSoundUri) + } + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { when (intent?.action) { Actions.TOGGLE.toString() -> { @@ -94,6 +94,8 @@ class TimerService : Service() { Actions.SKIP.toString() -> skipTimer(true) Actions.STOP_ALARM.toString() -> stopAlarm() + + Actions.UPDATE_ALARM_TONE.toString() -> updateAlarmTone() } return super.onStartCommand(intent, flags, startId) } @@ -315,7 +317,7 @@ class TimerService : Service() { } fun startAlarm() { - if (timerRepository.alarmEnabled) alarm.start() + if (timerRepository.alarmEnabled) alarm?.start() if (timerRepository.vibrateEnabled) { if (!vibrator.hasVibrator()) { @@ -330,8 +332,8 @@ class TimerService : Service() { fun stopAlarm() { if (timerRepository.alarmEnabled) { - alarm.pause() - alarm.seekTo(0) + alarm?.pause() + alarm?.seekTo(0) } if (timerRepository.vibrateEnabled) { @@ -351,6 +353,11 @@ class TimerService : Service() { ) } + fun updateAlarmTone() { + alarm?.release() + alarm = MediaPlayer.create(this, timerRepository.alarmSoundUri) + } + suspend fun saveTimeToDb() { when (timerState.value.timerMode) { TimerMode.FOCUS -> statRepository.addFocusTime( @@ -379,10 +386,11 @@ class TimerService : Service() { job.cancel() saveTimeToDb() notificationManager.cancel(1) + alarm?.release() } } enum class Actions { - TOGGLE, SKIP, RESET, STOP_ALARM + TOGGLE, SKIP, RESET, STOP_ALARM, UPDATE_ALARM_TONE } } \ No newline at end of file diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt index 98db86c..c8b6ace 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt @@ -74,6 +74,7 @@ import androidx.compose.ui.unit.sp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import org.nsh07.pomodoro.R +import org.nsh07.pomodoro.service.TimerService import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsViewModel import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors @@ -89,6 +90,7 @@ fun SettingsScreenRoot( modifier: Modifier = Modifier, viewModel: SettingsViewModel = viewModel(factory = SettingsViewModel.Factory) ) { + val context = LocalContext.current val focusTimeInputFieldState = rememberSaveable(saver = TextFieldState.Saver) { viewModel.focusTimeTextFieldState } @@ -99,8 +101,9 @@ fun SettingsScreenRoot( viewModel.longBreakTimeTextFieldState } - val alarmEnabled = viewModel.alarmEnabled.collectAsStateWithLifecycle() - val vibrateEnabled = viewModel.vibrateEnabled.collectAsStateWithLifecycle() + val alarmEnabled by viewModel.alarmEnabled.collectAsStateWithLifecycle() + val vibrateEnabled by viewModel.vibrateEnabled.collectAsStateWithLifecycle() + val alarmSound by viewModel.alarmSound.collectAsStateWithLifecycle() val sessionsSliderState = rememberSaveable( saver = SliderState.Saver( @@ -116,10 +119,18 @@ fun SettingsScreenRoot( shortBreakTimeInputFieldState = shortBreakTimeInputFieldState, longBreakTimeInputFieldState = longBreakTimeInputFieldState, sessionsSliderState = sessionsSliderState, - alarmEnabled = alarmEnabled.value, - vibrateEnabled = vibrateEnabled.value, + alarmEnabled = alarmEnabled, + vibrateEnabled = vibrateEnabled, + alarmSound = alarmSound, onAlarmEnabledChange = viewModel::saveAlarmEnabled, onVibrateEnabledChange = viewModel::saveVibrateEnabled, + onAlarmSoundChanged = { + viewModel.saveAlarmSound(it) + Intent(context, TimerService::class.java).apply { + action = TimerService.Actions.RESET.toString() + context.startService(this) + } + }, modifier = modifier ) } @@ -133,8 +144,10 @@ private fun SettingsScreen( sessionsSliderState: SliderState, alarmEnabled: Boolean, vibrateEnabled: Boolean, + alarmSound: Uri?, onAlarmEnabledChange: (Boolean) -> Unit, onVibrateEnabledChange: (Boolean) -> Unit, + onAlarmSoundChanged: (Uri?) -> Unit, modifier: Modifier = Modifier ) { val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() @@ -142,15 +155,13 @@ private fun SettingsScreen( checkedIconColor = colorScheme.primary, ) - var selectedSoundUri by remember { mutableStateOf(Settings.System.DEFAULT_ALARM_ALERT_URI) } + var selectedSoundUri by remember { mutableStateOf(alarmSound) } var selectedSoundName by remember { mutableStateOf("...") } val context = LocalContext.current - // 2. The Activity Result Launcher val ringtonePickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.StartActivityForResult() ) { result -> - // This block is executed when the user picks a sound and returns to the app if (result.resultCode == Activity.RESULT_OK) { val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { @@ -163,6 +174,7 @@ private fun SettingsScreen( result.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) } selectedSoundUri = uri + onAlarmSoundChanged(uri) } } @@ -424,8 +436,10 @@ fun SettingsScreenPreview() { sessionsSliderState = rememberSliderState(value = 3f, steps = 3, valueRange = 1f..5f), alarmEnabled = true, vibrateEnabled = true, + alarmSound = Settings.System.DEFAULT_ALARM_ALERT_URI, onAlarmEnabledChange = {}, onVibrateEnabledChange = {}, + onAlarmSoundChanged = {}, modifier = Modifier.fillMaxSize() ) } diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt index af545ca..b220d81 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt @@ -7,6 +7,7 @@ package org.nsh07.pomodoro.ui.settingsScreen.viewModel +import android.net.Uri import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.SliderState @@ -47,6 +48,9 @@ class SettingsViewModel( onValueChangeFinished = ::updateSessionLength ) + private val _alarmSound = MutableStateFlow(timerRepository.alarmSoundUri) + val alarmSound: StateFlow = _alarmSound.asStateFlow() + private val _alarmEnabled: MutableStateFlow = MutableStateFlow(timerRepository.alarmEnabled) val alarmEnabled: StateFlow = _alarmEnabled.asStateFlow() @@ -119,6 +123,11 @@ class SettingsViewModel( } } + fun saveAlarmSound(uri: Uri?) { + timerRepository.alarmSoundUri = uri + _alarmSound.value = uri + } + companion object { val Factory: ViewModelProvider.Factory = viewModelFactory { initializer { diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerViewModel.kt b/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerViewModel.kt index 6149b44..145effb 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerViewModel.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerViewModel.kt @@ -77,17 +77,20 @@ class TimerViewModel( timerRepository.sessionLength ) - timerRepository.alarmEnabled = (preferenceRepository.getIntPreference("alarm_enabled") - ?: preferenceRepository.saveIntPreference( - "alarm_enabled", - 1 - )) == 1 - timerRepository.vibrateEnabled = - (preferenceRepository.getIntPreference("vibrate_enabled") - ?: preferenceRepository.saveIntPreference( - "vibrate_enabled", - 1 - )) == 1 + timerRepository.alarmEnabled = ( + preferenceRepository.getIntPreference("alarm_enabled") + ?: preferenceRepository.saveIntPreference( + "alarm_enabled", + 1 + ) + ) == 1 + timerRepository.vibrateEnabled = ( + preferenceRepository.getIntPreference("vibrate_enabled") + ?: preferenceRepository.saveIntPreference( + "vibrate_enabled", + 1 + ) + ) == 1 resetTimer() From 37c74dc844c9d849595ce4abdff0bf42e7f6281d Mon Sep 17 00:00:00 2001 From: Nishant Mishra Date: Mon, 15 Sep 2025 23:41:58 +0530 Subject: [PATCH 4/6] feat: Implement persistent storage of chosen alarm sound #36 --- .../2.json | 133 ++++++++++++++++++ .../org/nsh07/pomodoro/data/AppDatabase.kt | 8 +- .../org/nsh07/pomodoro/data/Preference.kt | 22 ++- .../org/nsh07/pomodoro/data/PreferenceDao.kt | 18 +++ .../pomodoro/data/PreferenceRepository.kt | 42 ++++++ .../viewModel/SettingsViewModel.kt | 11 +- .../timerScreen/viewModel/TimerViewModel.kt | 29 ++-- 7 files changed, 243 insertions(+), 20 deletions(-) create mode 100644 app/schemas/org.nsh07.pomodoro.data.AppDatabase/2.json diff --git a/app/schemas/org.nsh07.pomodoro.data.AppDatabase/2.json b/app/schemas/org.nsh07.pomodoro.data.AppDatabase/2.json new file mode 100644 index 0000000..54e93e3 --- /dev/null +++ b/app/schemas/org.nsh07.pomodoro.data.AppDatabase/2.json @@ -0,0 +1,133 @@ +{ + "formatVersion": 1, + "database": { + "version": 2, + "identityHash": "4691d636a1c16c8cd33dc1bf0602190c", + "entities": [ + { + "tableName": "int_preference", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `value` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "key" + ] + } + }, + { + "tableName": "boolean_preference", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `value` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "key" + ] + } + }, + { + "tableName": "string_preference", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "key" + ] + } + }, + { + "tableName": "stat", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`date` TEXT NOT NULL, `focusTimeQ1` INTEGER NOT NULL, `focusTimeQ2` INTEGER NOT NULL, `focusTimeQ3` INTEGER NOT NULL, `focusTimeQ4` INTEGER NOT NULL, `breakTime` INTEGER NOT NULL, PRIMARY KEY(`date`))", + "fields": [ + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "focusTimeQ1", + "columnName": "focusTimeQ1", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "focusTimeQ2", + "columnName": "focusTimeQ2", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "focusTimeQ3", + "columnName": "focusTimeQ3", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "focusTimeQ4", + "columnName": "focusTimeQ4", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "breakTime", + "columnName": "breakTime", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "date" + ] + } + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4691d636a1c16c8cd33dc1bf0602190c')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/org/nsh07/pomodoro/data/AppDatabase.kt b/app/src/main/java/org/nsh07/pomodoro/data/AppDatabase.kt index 8e457cf..83fa1ab 100644 --- a/app/src/main/java/org/nsh07/pomodoro/data/AppDatabase.kt +++ b/app/src/main/java/org/nsh07/pomodoro/data/AppDatabase.kt @@ -8,14 +8,18 @@ package org.nsh07.pomodoro.data import android.content.Context +import androidx.room.AutoMigration import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase import androidx.room.TypeConverters @Database( - entities = [IntPreference::class, Stat::class], - version = 1 + entities = [IntPreference::class, BooleanPreference::class, StringPreference::class, Stat::class], + version = 2, + autoMigrations = [ + AutoMigration(from = 1, to = 2) + ] ) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { diff --git a/app/src/main/java/org/nsh07/pomodoro/data/Preference.kt b/app/src/main/java/org/nsh07/pomodoro/data/Preference.kt index 101dd84..af7adb6 100644 --- a/app/src/main/java/org/nsh07/pomodoro/data/Preference.kt +++ b/app/src/main/java/org/nsh07/pomodoro/data/Preference.kt @@ -11,7 +11,17 @@ import androidx.room.Entity import androidx.room.PrimaryKey /** - * Class for storing app preferences (settings) in the app's database + * Class for storing boolean preferences in the app's database + */ +@Entity(tableName = "boolean_preference") +data class BooleanPreference( + @PrimaryKey + val key: String, + val value: Boolean +) + +/** + * Class for storing integer preferences in the app's database */ @Entity(tableName = "int_preference") data class IntPreference( @@ -19,3 +29,13 @@ data class IntPreference( val key: String, val value: Int ) + +/** + * Class for storing string preferences in the app's database + */ +@Entity(tableName = "string_preference") +data class StringPreference( + @PrimaryKey + val key: String, + val value: String +) \ No newline at end of file diff --git a/app/src/main/java/org/nsh07/pomodoro/data/PreferenceDao.kt b/app/src/main/java/org/nsh07/pomodoro/data/PreferenceDao.kt index b450b4c..a45c53e 100644 --- a/app/src/main/java/org/nsh07/pomodoro/data/PreferenceDao.kt +++ b/app/src/main/java/org/nsh07/pomodoro/data/PreferenceDao.kt @@ -17,9 +17,27 @@ interface PreferenceDao { @Insert(onConflict = REPLACE) suspend fun insertIntPreference(preference: IntPreference) + @Insert(onConflict = REPLACE) + suspend fun insertBooleanPreference(preference: BooleanPreference) + + @Insert(onConflict = REPLACE) + suspend fun insertStringPreference(preference: StringPreference) + @Query("DELETE FROM int_preference") suspend fun resetIntPreferences() + @Query("DELETE FROM boolean_preference") + suspend fun resetBooleanPreferences() + + @Query("DELETE FROM string_preference") + suspend fun resetStringPreferences() + @Query("SELECT value FROM int_preference WHERE `key` = :key") suspend fun getIntPreference(key: String): Int? + + @Query("SELECT value FROM boolean_preference WHERE `key` = :key") + suspend fun getBooleanPreference(key: String): Boolean? + + @Query("SELECT value FROM string_preference WHERE `key` = :key") + suspend fun getStringPreference(key: String): String? } \ No newline at end of file diff --git a/app/src/main/java/org/nsh07/pomodoro/data/PreferenceRepository.kt b/app/src/main/java/org/nsh07/pomodoro/data/PreferenceRepository.kt index d9abc83..c78d29d 100644 --- a/app/src/main/java/org/nsh07/pomodoro/data/PreferenceRepository.kt +++ b/app/src/main/java/org/nsh07/pomodoro/data/PreferenceRepository.kt @@ -22,11 +22,31 @@ interface PreferenceRepository { */ suspend fun saveIntPreference(key: String, value: Int): Int + /** + * Saves a boolean preference key-value pair to the database. + */ + suspend fun saveBooleanPreference(key: String, value: Boolean): Boolean + + /** + * Saves a string preference key-value pair to the database. + */ + suspend fun saveStringPreference(key: String, value: String): String + /** * Retrieves an integer preference key-value pair from the database. */ suspend fun getIntPreference(key: String): Int? + /** + * Retrieves a boolean preference key-value pair from the database. + */ + suspend fun getBooleanPreference(key: String): Boolean? + + /** + * Retrieves a string preference key-value pair from the database. + */ + suspend fun getStringPreference(key: String): String? + /** * Erases all integer preference key-value pairs in the database. Do note that the default values * will need to be rewritten manually @@ -47,11 +67,33 @@ class AppPreferenceRepository( value } + override suspend fun saveBooleanPreference(key: String, value: Boolean): Boolean = + withContext(ioDispatcher) { + preferenceDao.insertBooleanPreference(BooleanPreference(key, value)) + value + } + + override suspend fun saveStringPreference(key: String, value: String): String = + withContext(ioDispatcher) { + preferenceDao.insertStringPreference(StringPreference(key, value)) + value + } + override suspend fun getIntPreference(key: String): Int? = withContext(ioDispatcher) { preferenceDao.getIntPreference(key) } + override suspend fun getBooleanPreference(key: String): Boolean? = withContext(ioDispatcher) { + preferenceDao.getBooleanPreference(key) + } + + override suspend fun getStringPreference(key: String): String? = withContext(ioDispatcher) { + preferenceDao.getStringPreference(key) + } + override suspend fun resetSettings() = withContext(ioDispatcher) { preferenceDao.resetIntPreferences() + preferenceDao.resetBooleanPreferences() + preferenceDao.resetStringPreferences() } } \ No newline at end of file diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt index b220d81..0f9d363 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt @@ -110,7 +110,7 @@ class SettingsViewModel( fun saveAlarmEnabled(enabled: Boolean) { viewModelScope.launch { timerRepository.alarmEnabled = preferenceRepository - .saveIntPreference("alarm_enabled", if (enabled) 1 else 0) == 1 + .saveBooleanPreference("alarm_enabled", enabled) _alarmEnabled.value = enabled } } @@ -118,14 +118,17 @@ class SettingsViewModel( fun saveVibrateEnabled(enabled: Boolean) { viewModelScope.launch { timerRepository.vibrateEnabled = preferenceRepository - .saveIntPreference("vibrate_enabled", if (enabled) 1 else 0) == 1 + .saveBooleanPreference("vibrate_enabled", enabled) _vibrateEnabled.value = enabled } } fun saveAlarmSound(uri: Uri?) { - timerRepository.alarmSoundUri = uri - _alarmSound.value = uri + viewModelScope.launch { + preferenceRepository.saveStringPreference("alarm_sound", uri.toString()) + timerRepository.alarmSoundUri = uri + _alarmSound.value = uri + } } companion object { diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerViewModel.kt b/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerViewModel.kt index 145effb..f4b3985 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerViewModel.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerViewModel.kt @@ -8,7 +8,9 @@ package org.nsh07.pomodoro.ui.timerScreen.viewModel import android.app.Application +import android.provider.Settings import androidx.compose.material3.ColorScheme +import androidx.core.net.toUri import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY @@ -77,20 +79,21 @@ class TimerViewModel( timerRepository.sessionLength ) - timerRepository.alarmEnabled = ( - preferenceRepository.getIntPreference("alarm_enabled") - ?: preferenceRepository.saveIntPreference( - "alarm_enabled", - 1 + timerRepository.alarmEnabled = + preferenceRepository.getBooleanPreference("alarm_enabled") + ?: preferenceRepository.saveBooleanPreference("alarm_enabled", true) + timerRepository.vibrateEnabled = + preferenceRepository.getBooleanPreference("vibrate_enabled") + ?: preferenceRepository.saveBooleanPreference("vibrate_enabled", true) + + timerRepository.alarmSoundUri = ( + preferenceRepository.getStringPreference("alarm_sound") + ?: preferenceRepository.saveStringPreference( + "alarm_sound", + (Settings.System.DEFAULT_ALARM_ALERT_URI + ?: Settings.System.DEFAULT_RINGTONE_URI).toString() ) - ) == 1 - timerRepository.vibrateEnabled = ( - preferenceRepository.getIntPreference("vibrate_enabled") - ?: preferenceRepository.saveIntPreference( - "vibrate_enabled", - 1 - ) - ) == 1 + ).toUri() resetTimer() From 55de7589eef37210eb4b860791015c7286bc6fe4 Mon Sep 17 00:00:00 2001 From: Nishant Mishra Date: Tue, 16 Sep 2025 00:39:15 +0530 Subject: [PATCH 5/6] fix: Fix some major bugs with the URI to string (and vice versa) conversion --- .../org/nsh07/pomodoro/data/PreferenceDao.kt | 7 +++++ .../pomodoro/data/PreferenceRepository.kt | 17 ++++++++++ .../ui/settingsScreen/SettingsScreen.kt | 14 ++++----- .../viewModel/SettingsViewModel.kt | 31 +++++++------------ 4 files changed, 42 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/org/nsh07/pomodoro/data/PreferenceDao.kt b/app/src/main/java/org/nsh07/pomodoro/data/PreferenceDao.kt index a45c53e..72b01bb 100644 --- a/app/src/main/java/org/nsh07/pomodoro/data/PreferenceDao.kt +++ b/app/src/main/java/org/nsh07/pomodoro/data/PreferenceDao.kt @@ -11,6 +11,7 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy.Companion.REPLACE import androidx.room.Query +import kotlinx.coroutines.flow.Flow @Dao interface PreferenceDao { @@ -38,6 +39,12 @@ interface PreferenceDao { @Query("SELECT value FROM boolean_preference WHERE `key` = :key") suspend fun getBooleanPreference(key: String): Boolean? + @Query("SELECT value FROM boolean_preference WHERE `key` = :key") + fun getBooleanPreferenceFlow(key: String): Flow + @Query("SELECT value FROM string_preference WHERE `key` = :key") suspend fun getStringPreference(key: String): String? + + @Query("SELECT value FROM string_preference WHERE `key` = :key") + fun getStringPreferenceFlow(key: String): Flow } \ No newline at end of file diff --git a/app/src/main/java/org/nsh07/pomodoro/data/PreferenceRepository.kt b/app/src/main/java/org/nsh07/pomodoro/data/PreferenceRepository.kt index c78d29d..4d53239 100644 --- a/app/src/main/java/org/nsh07/pomodoro/data/PreferenceRepository.kt +++ b/app/src/main/java/org/nsh07/pomodoro/data/PreferenceRepository.kt @@ -9,6 +9,7 @@ package org.nsh07.pomodoro.data import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.withContext /** @@ -42,11 +43,21 @@ interface PreferenceRepository { */ suspend fun getBooleanPreference(key: String): Boolean? + /** + * Retrieves a boolean preference key-value pair as a flow from the database. + */ + fun getBooleanPreferenceFlow(key: String): Flow + /** * Retrieves a string preference key-value pair from the database. */ suspend fun getStringPreference(key: String): String? + /** + * Retrieves a string preference key-value pair as a flow from the database. + */ + fun getStringPreferenceFlow(key: String): Flow + /** * Erases all integer preference key-value pairs in the database. Do note that the default values * will need to be rewritten manually @@ -87,10 +98,16 @@ class AppPreferenceRepository( preferenceDao.getBooleanPreference(key) } + override fun getBooleanPreferenceFlow(key: String): Flow = + preferenceDao.getBooleanPreferenceFlow(key) + override suspend fun getStringPreference(key: String): String? = withContext(ioDispatcher) { preferenceDao.getStringPreference(key) } + override fun getStringPreferenceFlow(key: String): Flow = + preferenceDao.getStringPreferenceFlow(key) + override suspend fun resetSettings() = withContext(ioDispatcher) { preferenceDao.resetIntPreferences() preferenceDao.resetBooleanPreferences() diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt index c8b6ace..0a2c9af 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt @@ -71,6 +71,7 @@ import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.core.net.toUri import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import org.nsh07.pomodoro.R @@ -101,9 +102,9 @@ fun SettingsScreenRoot( viewModel.longBreakTimeTextFieldState } - val alarmEnabled by viewModel.alarmEnabled.collectAsStateWithLifecycle() - val vibrateEnabled by viewModel.vibrateEnabled.collectAsStateWithLifecycle() - val alarmSound by viewModel.alarmSound.collectAsStateWithLifecycle() + val alarmEnabled by viewModel.alarmEnabled.collectAsStateWithLifecycle(true) + val vibrateEnabled by viewModel.vibrateEnabled.collectAsStateWithLifecycle(true) + val alarmSound by viewModel.alarmSound.collectAsStateWithLifecycle("") val sessionsSliderState = rememberSaveable( saver = SliderState.Saver( @@ -144,7 +145,7 @@ private fun SettingsScreen( sessionsSliderState: SliderState, alarmEnabled: Boolean, vibrateEnabled: Boolean, - alarmSound: Uri?, + alarmSound: String, onAlarmEnabledChange: (Boolean) -> Unit, onVibrateEnabledChange: (Boolean) -> Unit, onAlarmSoundChanged: (Uri?) -> Unit, @@ -155,7 +156,7 @@ private fun SettingsScreen( checkedIconColor = colorScheme.primary, ) - var selectedSoundUri by remember { mutableStateOf(alarmSound) } + var selectedSoundUri by remember(alarmSound) { mutableStateOf(alarmSound.toUri()) } var selectedSoundName by remember { mutableStateOf("...") } val context = LocalContext.current @@ -173,7 +174,6 @@ private fun SettingsScreen( @Suppress("DEPRECATION") result.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) } - selectedSoundUri = uri onAlarmSoundChanged(uri) } } @@ -436,7 +436,7 @@ fun SettingsScreenPreview() { sessionsSliderState = rememberSliderState(value = 3f, steps = 3, valueRange = 1f..5f), alarmEnabled = true, vibrateEnabled = true, - alarmSound = Settings.System.DEFAULT_ALARM_ALERT_URI, + alarmSound = Settings.System.DEFAULT_ALARM_ALERT_URI.toString(), onAlarmEnabledChange = {}, onVibrateEnabledChange = {}, onAlarmSoundChanged = {}, diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt index 0f9d363..6c779c2 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt @@ -20,10 +20,8 @@ import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import kotlinx.coroutines.Dispatchers 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.distinctUntilChanged import kotlinx.coroutines.launch import org.nsh07.pomodoro.TomatoApplication import org.nsh07.pomodoro.data.AppPreferenceRepository @@ -48,16 +46,12 @@ class SettingsViewModel( onValueChangeFinished = ::updateSessionLength ) - private val _alarmSound = MutableStateFlow(timerRepository.alarmSoundUri) - val alarmSound: StateFlow = _alarmSound.asStateFlow() - - private val _alarmEnabled: MutableStateFlow = - MutableStateFlow(timerRepository.alarmEnabled) - val alarmEnabled: StateFlow = _alarmEnabled.asStateFlow() - - private val _vibrateEnabled: MutableStateFlow = - MutableStateFlow(timerRepository.alarmEnabled) - val vibrateEnabled: StateFlow = _vibrateEnabled.asStateFlow() + val alarmSound = + preferenceRepository.getStringPreferenceFlow("alarm_sound").distinctUntilChanged() + val alarmEnabled = + preferenceRepository.getBooleanPreferenceFlow("alarm_enabled").distinctUntilChanged() + val vibrateEnabled = + preferenceRepository.getBooleanPreferenceFlow("vibrate_enabled").distinctUntilChanged() init { viewModelScope.launch(Dispatchers.IO) { @@ -109,17 +103,15 @@ class SettingsViewModel( fun saveAlarmEnabled(enabled: Boolean) { viewModelScope.launch { - timerRepository.alarmEnabled = preferenceRepository - .saveBooleanPreference("alarm_enabled", enabled) - _alarmEnabled.value = enabled + preferenceRepository.saveBooleanPreference("alarm_enabled", enabled) + timerRepository.alarmEnabled = enabled } } fun saveVibrateEnabled(enabled: Boolean) { viewModelScope.launch { - timerRepository.vibrateEnabled = preferenceRepository - .saveBooleanPreference("vibrate_enabled", enabled) - _vibrateEnabled.value = enabled + preferenceRepository.saveBooleanPreference("vibrate_enabled", enabled) + timerRepository.vibrateEnabled = enabled } } @@ -127,7 +119,6 @@ class SettingsViewModel( viewModelScope.launch { preferenceRepository.saveStringPreference("alarm_sound", uri.toString()) timerRepository.alarmSoundUri = uri - _alarmSound.value = uri } } From 6488fea5be28591a9061ed4520019690ba475ad3 Mon Sep 17 00:00:00 2001 From: Nishant Mishra Date: Tue, 16 Sep 2025 08:01:30 +0530 Subject: [PATCH 6/6] fix: Optimize code to reduce state variables, fix alarm sound name flickering --- .../ui/settingsScreen/SettingsScreen.kt | 30 +++++++------------ .../viewModel/SettingsViewModel.kt | 4 ++- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt index 0a2c9af..6420566 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt @@ -12,7 +12,6 @@ import android.content.Intent import android.media.RingtoneManager import android.net.Uri import android.os.Build -import android.provider.Settings import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.DrawableRes @@ -54,7 +53,6 @@ import androidx.compose.material3.TopAppBar 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 import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -104,7 +102,7 @@ fun SettingsScreenRoot( val alarmEnabled by viewModel.alarmEnabled.collectAsStateWithLifecycle(true) val vibrateEnabled by viewModel.vibrateEnabled.collectAsStateWithLifecycle(true) - val alarmSound by viewModel.alarmSound.collectAsStateWithLifecycle("") + val alarmSound by viewModel.alarmSound.collectAsStateWithLifecycle(viewModel.currentAlarmSound) val sessionsSliderState = rememberSaveable( saver = SliderState.Saver( @@ -156,8 +154,6 @@ private fun SettingsScreen( checkedIconColor = colorScheme.primary, ) - var selectedSoundUri by remember(alarmSound) { mutableStateOf(alarmSound.toUri()) } - var selectedSoundName by remember { mutableStateOf("...") } val context = LocalContext.current val ringtonePickerLauncher = rememberLauncherForActivityResult( @@ -178,15 +174,10 @@ private fun SettingsScreen( } } - // 3. The Intent to launch the picker val intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply { - // We want to show only alarm sounds putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_ALARM) - // A title for the picker putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, "Alarm sound") - - // If a sound is already selected, show it as checked - putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, selectedSoundUri) + putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, alarmSound.toUri()) } val switchItems = remember(alarmEnabled, vibrateEnabled) { @@ -208,12 +199,6 @@ private fun SettingsScreen( ) } - LaunchedEffect(selectedSoundUri) { - selectedSoundName = - RingtoneManager.getRingtone(context, selectedSoundUri) - .getTitle(context) - } - Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) { TopAppBar( title = { @@ -338,7 +323,14 @@ private fun SettingsScreen( Icon(painterResource(R.drawable.alarm), null) }, headlineContent = { Text("Alarm sound") }, - supportingContent = { Text(selectedSoundName) }, + supportingContent = { + Text( + remember(alarmSound) { + RingtoneManager.getRingtone(context, alarmSound.toUri()) + .getTitle(context) + } + ) + }, colors = listItemColors, modifier = Modifier .clip(bottomListItemShape) @@ -436,7 +428,7 @@ fun SettingsScreenPreview() { sessionsSliderState = rememberSliderState(value = 3f, steps = 3, valueRange = 1f..5f), alarmEnabled = true, vibrateEnabled = true, - alarmSound = Settings.System.DEFAULT_ALARM_ALERT_URI.toString(), + alarmSound = "null", onAlarmEnabledChange = {}, onVibrateEnabledChange = {}, onAlarmSoundChanged = {}, diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt index 6c779c2..dc2439d 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt @@ -46,6 +46,8 @@ class SettingsViewModel( onValueChangeFinished = ::updateSessionLength ) + val currentAlarmSound = timerRepository.alarmSoundUri.toString() + val alarmSound = preferenceRepository.getStringPreferenceFlow("alarm_sound").distinctUntilChanged() val alarmEnabled = @@ -118,8 +120,8 @@ class SettingsViewModel( fun saveAlarmSound(uri: Uri?) { viewModelScope.launch { preferenceRepository.saveStringPreference("alarm_sound", uri.toString()) - timerRepository.alarmSoundUri = uri } + timerRepository.alarmSoundUri = uri } companion object {