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/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/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..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,15 +11,40 @@ 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 { @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 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 d9abc83..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 /** @@ -22,11 +23,41 @@ 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 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 @@ -47,11 +78,39 @@ 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 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() + preferenceDao.resetStringPreferences() } } \ No newline at end of file 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 b3683fe..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 @@ -7,9 +7,17 @@ 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 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 @@ -54,21 +62,23 @@ 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 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 +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 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 @@ -79,6 +89,7 @@ fun SettingsScreenRoot( modifier: Modifier = Modifier, viewModel: SettingsViewModel = viewModel(factory = SettingsViewModel.Factory) ) { + val context = LocalContext.current val focusTimeInputFieldState = rememberSaveable(saver = TextFieldState.Saver) { viewModel.focusTimeTextFieldState } @@ -89,8 +100,9 @@ fun SettingsScreenRoot( viewModel.longBreakTimeTextFieldState } - val alarmEnabled = viewModel.alarmEnabled.collectAsStateWithLifecycle() - val vibrateEnabled = viewModel.vibrateEnabled.collectAsStateWithLifecycle() + val alarmEnabled by viewModel.alarmEnabled.collectAsStateWithLifecycle(true) + val vibrateEnabled by viewModel.vibrateEnabled.collectAsStateWithLifecycle(true) + val alarmSound by viewModel.alarmSound.collectAsStateWithLifecycle(viewModel.currentAlarmSound) val sessionsSliderState = rememberSaveable( saver = SliderState.Saver( @@ -106,10 +118,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 ) } @@ -123,8 +143,10 @@ private fun SettingsScreen( sessionsSliderState: SliderState, alarmEnabled: Boolean, vibrateEnabled: Boolean, + alarmSound: String, onAlarmEnabledChange: (Boolean) -> Unit, onVibrateEnabledChange: (Boolean) -> Unit, + onAlarmSoundChanged: (Uri?) -> Unit, modifier: Modifier = Modifier ) { val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() @@ -132,20 +154,46 @@ private fun SettingsScreen( checkedIconColor = colorScheme.primary, ) + val context = LocalContext.current + + val ringtonePickerLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.StartActivityForResult() + ) { result -> + 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) + } + onAlarmSoundChanged(uri) + } + } + + val intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply { + putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_ALARM) + putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, "Alarm sound") + putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, alarmSound.toUri()) + } + val switchItems = remember(alarmEnabled, vibrateEnabled) { listOf( SettingsSwitchItem( 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 ) ) @@ -266,7 +314,27 @@ 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( + remember(alarmSound) { + RingtoneManager.getRingtone(context, alarmSound.toUri()) + .getTitle(context) + } + ) + }, + colors = listItemColors, + modifier = Modifier + .clip(bottomListItemShape) + .clickable(onClick = { ringtonePickerLauncher.launch(intent) }) ) } item { Spacer(Modifier.height(12.dp)) } @@ -360,8 +428,10 @@ fun SettingsScreenPreview() { sessionsSliderState = rememberSliderState(value = 3f, steps = 3, valueRange = 1f..5f), alarmEnabled = true, vibrateEnabled = true, + alarmSound = "null", 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..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 @@ -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 @@ -19,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 @@ -47,13 +46,14 @@ class SettingsViewModel( onValueChangeFinished = ::updateSessionLength ) - private val _alarmEnabled: MutableStateFlow = - MutableStateFlow(timerRepository.alarmEnabled) - val alarmEnabled: StateFlow = _alarmEnabled.asStateFlow() + val currentAlarmSound = timerRepository.alarmSoundUri.toString() - 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) { @@ -105,20 +105,25 @@ class SettingsViewModel( fun saveAlarmEnabled(enabled: Boolean) { viewModelScope.launch { - timerRepository.alarmEnabled = preferenceRepository - .saveIntPreference("alarm_enabled", if (enabled) 1 else 0) == 1 - _alarmEnabled.value = enabled + preferenceRepository.saveBooleanPreference("alarm_enabled", enabled) + timerRepository.alarmEnabled = enabled } } fun saveVibrateEnabled(enabled: Boolean) { viewModelScope.launch { - timerRepository.vibrateEnabled = preferenceRepository - .saveIntPreference("vibrate_enabled", if (enabled) 1 else 0) == 1 - _vibrateEnabled.value = enabled + preferenceRepository.saveBooleanPreference("vibrate_enabled", enabled) + timerRepository.vibrateEnabled = enabled } } + fun saveAlarmSound(uri: Uri?) { + viewModelScope.launch { + preferenceRepository.saveStringPreference("alarm_sound", uri.toString()) + } + timerRepository.alarmSoundUri = 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..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,17 +79,21 @@ class TimerViewModel( timerRepository.sessionLength ) - timerRepository.alarmEnabled = (preferenceRepository.getIntPreference("alarm_enabled") - ?: preferenceRepository.saveIntPreference( - "alarm_enabled", - 1 - )) == 1 + timerRepository.alarmEnabled = + preferenceRepository.getBooleanPreference("alarm_enabled") + ?: preferenceRepository.saveBooleanPreference("alarm_enabled", true) timerRepository.vibrateEnabled = - (preferenceRepository.getIntPreference("vibrate_enabled") - ?: preferenceRepository.saveIntPreference( - "vibrate_enabled", - 1 - )) == 1 + 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() + ) + ).toUri() resetTimer() 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 @@ + + +