From 9c33406151c476d60397d3d647169f0ccb1ab3cd Mon Sep 17 00:00:00 2001 From: Nishant Mishra Date: Mon, 15 Sep 2025 23:23:05 +0530 Subject: [PATCH] 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()