@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user