feat: Implement the picked alarm sound to be played

#36
This commit is contained in:
Nishant Mishra
2025-09-15 23:23:05 +05:30
parent fdc1c3f6c5
commit 9c33406151
5 changed files with 71 additions and 28 deletions

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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<Uri?>(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()
)
}

View File

@@ -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<Uri?> = _alarmSound.asStateFlow()
private val _alarmEnabled: MutableStateFlow<Boolean> =
MutableStateFlow(timerRepository.alarmEnabled)
val alarmEnabled: StateFlow<Boolean> = _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 {

View File

@@ -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()