@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
package org.nsh07.pomodoro.data
|
package org.nsh07.pomodoro.data
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.Settings
|
||||||
import androidx.compose.material3.ColorScheme
|
import androidx.compose.material3.ColorScheme
|
||||||
import androidx.compose.material3.lightColorScheme
|
import androidx.compose.material3.lightColorScheme
|
||||||
|
|
||||||
@@ -18,12 +20,17 @@ interface TimerRepository {
|
|||||||
var focusTime: Long
|
var focusTime: Long
|
||||||
var shortBreakTime: Long
|
var shortBreakTime: Long
|
||||||
var longBreakTime: Long
|
var longBreakTime: Long
|
||||||
|
|
||||||
var sessionLength: Int
|
var sessionLength: Int
|
||||||
|
|
||||||
var timerFrequency: Float
|
var timerFrequency: Float
|
||||||
|
|
||||||
var alarmEnabled: Boolean
|
var alarmEnabled: Boolean
|
||||||
var vibrateEnabled: Boolean
|
var vibrateEnabled: Boolean
|
||||||
|
|
||||||
var colorScheme: ColorScheme
|
var colorScheme: ColorScheme
|
||||||
|
|
||||||
|
var alarmSoundUri: Uri?
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,4 +45,6 @@ class AppTimerRepository : TimerRepository {
|
|||||||
override var alarmEnabled = true
|
override var alarmEnabled = true
|
||||||
override var vibrateEnabled = true
|
override var vibrateEnabled = true
|
||||||
override var colorScheme = lightColorScheme()
|
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.VibrationEffect
|
||||||
import android.os.Vibrator
|
import android.os.Vibrator
|
||||||
import android.os.VibratorManager
|
import android.os.VibratorManager
|
||||||
import android.provider.Settings
|
|
||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.graphics.toArgb
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
@@ -57,11 +56,7 @@ class TimerService : Service() {
|
|||||||
private val scope = CoroutineScope(Dispatchers.IO + job)
|
private val scope = CoroutineScope(Dispatchers.IO + job)
|
||||||
private val skipScope = CoroutineScope(Dispatchers.IO + job)
|
private val skipScope = CoroutineScope(Dispatchers.IO + job)
|
||||||
|
|
||||||
private val alarm by lazy {
|
private var alarm: MediaPlayer? = null
|
||||||
MediaPlayer.create(
|
|
||||||
this, Settings.System.DEFAULT_ALARM_ALERT_URI ?: Settings.System.DEFAULT_RINGTONE_URI
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val vibrator by lazy {
|
private val vibrator by lazy {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
@@ -78,6 +73,11 @@ class TimerService : Service() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
alarm = MediaPlayer.create(this, timerRepository.alarmSoundUri)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
when (intent?.action) {
|
when (intent?.action) {
|
||||||
Actions.TOGGLE.toString() -> {
|
Actions.TOGGLE.toString() -> {
|
||||||
@@ -94,6 +94,8 @@ class TimerService : Service() {
|
|||||||
Actions.SKIP.toString() -> skipTimer(true)
|
Actions.SKIP.toString() -> skipTimer(true)
|
||||||
|
|
||||||
Actions.STOP_ALARM.toString() -> stopAlarm()
|
Actions.STOP_ALARM.toString() -> stopAlarm()
|
||||||
|
|
||||||
|
Actions.UPDATE_ALARM_TONE.toString() -> updateAlarmTone()
|
||||||
}
|
}
|
||||||
return super.onStartCommand(intent, flags, startId)
|
return super.onStartCommand(intent, flags, startId)
|
||||||
}
|
}
|
||||||
@@ -315,7 +317,7 @@ class TimerService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun startAlarm() {
|
fun startAlarm() {
|
||||||
if (timerRepository.alarmEnabled) alarm.start()
|
if (timerRepository.alarmEnabled) alarm?.start()
|
||||||
|
|
||||||
if (timerRepository.vibrateEnabled) {
|
if (timerRepository.vibrateEnabled) {
|
||||||
if (!vibrator.hasVibrator()) {
|
if (!vibrator.hasVibrator()) {
|
||||||
@@ -330,8 +332,8 @@ class TimerService : Service() {
|
|||||||
|
|
||||||
fun stopAlarm() {
|
fun stopAlarm() {
|
||||||
if (timerRepository.alarmEnabled) {
|
if (timerRepository.alarmEnabled) {
|
||||||
alarm.pause()
|
alarm?.pause()
|
||||||
alarm.seekTo(0)
|
alarm?.seekTo(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timerRepository.vibrateEnabled) {
|
if (timerRepository.vibrateEnabled) {
|
||||||
@@ -351,6 +353,11 @@ class TimerService : Service() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateAlarmTone() {
|
||||||
|
alarm?.release()
|
||||||
|
alarm = MediaPlayer.create(this, timerRepository.alarmSoundUri)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun saveTimeToDb() {
|
suspend fun saveTimeToDb() {
|
||||||
when (timerState.value.timerMode) {
|
when (timerState.value.timerMode) {
|
||||||
TimerMode.FOCUS -> statRepository.addFocusTime(
|
TimerMode.FOCUS -> statRepository.addFocusTime(
|
||||||
@@ -379,10 +386,11 @@ class TimerService : Service() {
|
|||||||
job.cancel()
|
job.cancel()
|
||||||
saveTimeToDb()
|
saveTimeToDb()
|
||||||
notificationManager.cancel(1)
|
notificationManager.cancel(1)
|
||||||
|
alarm?.release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Actions {
|
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.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import org.nsh07.pomodoro.R
|
import org.nsh07.pomodoro.R
|
||||||
|
import org.nsh07.pomodoro.service.TimerService
|
||||||
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsViewModel
|
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsViewModel
|
||||||
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
|
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
|
||||||
import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
|
import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
|
||||||
@@ -89,6 +90,7 @@ fun SettingsScreenRoot(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: SettingsViewModel = viewModel(factory = SettingsViewModel.Factory)
|
viewModel: SettingsViewModel = viewModel(factory = SettingsViewModel.Factory)
|
||||||
) {
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
val focusTimeInputFieldState = rememberSaveable(saver = TextFieldState.Saver) {
|
val focusTimeInputFieldState = rememberSaveable(saver = TextFieldState.Saver) {
|
||||||
viewModel.focusTimeTextFieldState
|
viewModel.focusTimeTextFieldState
|
||||||
}
|
}
|
||||||
@@ -99,8 +101,9 @@ fun SettingsScreenRoot(
|
|||||||
viewModel.longBreakTimeTextFieldState
|
viewModel.longBreakTimeTextFieldState
|
||||||
}
|
}
|
||||||
|
|
||||||
val alarmEnabled = viewModel.alarmEnabled.collectAsStateWithLifecycle()
|
val alarmEnabled by viewModel.alarmEnabled.collectAsStateWithLifecycle()
|
||||||
val vibrateEnabled = viewModel.vibrateEnabled.collectAsStateWithLifecycle()
|
val vibrateEnabled by viewModel.vibrateEnabled.collectAsStateWithLifecycle()
|
||||||
|
val alarmSound by viewModel.alarmSound.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val sessionsSliderState = rememberSaveable(
|
val sessionsSliderState = rememberSaveable(
|
||||||
saver = SliderState.Saver(
|
saver = SliderState.Saver(
|
||||||
@@ -116,10 +119,18 @@ fun SettingsScreenRoot(
|
|||||||
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
|
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
|
||||||
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
|
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
|
||||||
sessionsSliderState = sessionsSliderState,
|
sessionsSliderState = sessionsSliderState,
|
||||||
alarmEnabled = alarmEnabled.value,
|
alarmEnabled = alarmEnabled,
|
||||||
vibrateEnabled = vibrateEnabled.value,
|
vibrateEnabled = vibrateEnabled,
|
||||||
|
alarmSound = alarmSound,
|
||||||
onAlarmEnabledChange = viewModel::saveAlarmEnabled,
|
onAlarmEnabledChange = viewModel::saveAlarmEnabled,
|
||||||
onVibrateEnabledChange = viewModel::saveVibrateEnabled,
|
onVibrateEnabledChange = viewModel::saveVibrateEnabled,
|
||||||
|
onAlarmSoundChanged = {
|
||||||
|
viewModel.saveAlarmSound(it)
|
||||||
|
Intent(context, TimerService::class.java).apply {
|
||||||
|
action = TimerService.Actions.RESET.toString()
|
||||||
|
context.startService(this)
|
||||||
|
}
|
||||||
|
},
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -133,8 +144,10 @@ private fun SettingsScreen(
|
|||||||
sessionsSliderState: SliderState,
|
sessionsSliderState: SliderState,
|
||||||
alarmEnabled: Boolean,
|
alarmEnabled: Boolean,
|
||||||
vibrateEnabled: Boolean,
|
vibrateEnabled: Boolean,
|
||||||
|
alarmSound: Uri?,
|
||||||
onAlarmEnabledChange: (Boolean) -> Unit,
|
onAlarmEnabledChange: (Boolean) -> Unit,
|
||||||
onVibrateEnabledChange: (Boolean) -> Unit,
|
onVibrateEnabledChange: (Boolean) -> Unit,
|
||||||
|
onAlarmSoundChanged: (Uri?) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||||
@@ -142,15 +155,13 @@ private fun SettingsScreen(
|
|||||||
checkedIconColor = colorScheme.primary,
|
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("...") }
|
var selectedSoundName by remember { mutableStateOf("...") }
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
// 2. The Activity Result Launcher
|
|
||||||
val ringtonePickerLauncher = rememberLauncherForActivityResult(
|
val ringtonePickerLauncher = rememberLauncherForActivityResult(
|
||||||
contract = ActivityResultContracts.StartActivityForResult()
|
contract = ActivityResultContracts.StartActivityForResult()
|
||||||
) { result ->
|
) { result ->
|
||||||
// This block is executed when the user picks a sound and returns to the app
|
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
val uri =
|
val uri =
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
@@ -163,6 +174,7 @@ private fun SettingsScreen(
|
|||||||
result.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
|
result.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
|
||||||
}
|
}
|
||||||
selectedSoundUri = uri
|
selectedSoundUri = uri
|
||||||
|
onAlarmSoundChanged(uri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,8 +436,10 @@ fun SettingsScreenPreview() {
|
|||||||
sessionsSliderState = rememberSliderState(value = 3f, steps = 3, valueRange = 1f..5f),
|
sessionsSliderState = rememberSliderState(value = 3f, steps = 3, valueRange = 1f..5f),
|
||||||
alarmEnabled = true,
|
alarmEnabled = true,
|
||||||
vibrateEnabled = true,
|
vibrateEnabled = true,
|
||||||
|
alarmSound = Settings.System.DEFAULT_ALARM_ALERT_URI,
|
||||||
onAlarmEnabledChange = {},
|
onAlarmEnabledChange = {},
|
||||||
onVibrateEnabledChange = {},
|
onVibrateEnabledChange = {},
|
||||||
|
onAlarmSoundChanged = {},
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package org.nsh07.pomodoro.ui.settingsScreen.viewModel
|
package org.nsh07.pomodoro.ui.settingsScreen.viewModel
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
import androidx.compose.foundation.text.input.TextFieldState
|
import androidx.compose.foundation.text.input.TextFieldState
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.SliderState
|
import androidx.compose.material3.SliderState
|
||||||
@@ -47,6 +48,9 @@ class SettingsViewModel(
|
|||||||
onValueChangeFinished = ::updateSessionLength
|
onValueChangeFinished = ::updateSessionLength
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val _alarmSound = MutableStateFlow(timerRepository.alarmSoundUri)
|
||||||
|
val alarmSound: StateFlow<Uri?> = _alarmSound.asStateFlow()
|
||||||
|
|
||||||
private val _alarmEnabled: MutableStateFlow<Boolean> =
|
private val _alarmEnabled: MutableStateFlow<Boolean> =
|
||||||
MutableStateFlow(timerRepository.alarmEnabled)
|
MutableStateFlow(timerRepository.alarmEnabled)
|
||||||
val alarmEnabled: StateFlow<Boolean> = _alarmEnabled.asStateFlow()
|
val alarmEnabled: StateFlow<Boolean> = _alarmEnabled.asStateFlow()
|
||||||
@@ -119,6 +123,11 @@ class SettingsViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveAlarmSound(uri: Uri?) {
|
||||||
|
timerRepository.alarmSoundUri = uri
|
||||||
|
_alarmSound.value = uri
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val Factory: ViewModelProvider.Factory = viewModelFactory {
|
val Factory: ViewModelProvider.Factory = viewModelFactory {
|
||||||
initializer {
|
initializer {
|
||||||
|
|||||||
@@ -77,17 +77,20 @@ class TimerViewModel(
|
|||||||
timerRepository.sessionLength
|
timerRepository.sessionLength
|
||||||
)
|
)
|
||||||
|
|
||||||
timerRepository.alarmEnabled = (preferenceRepository.getIntPreference("alarm_enabled")
|
timerRepository.alarmEnabled = (
|
||||||
?: preferenceRepository.saveIntPreference(
|
preferenceRepository.getIntPreference("alarm_enabled")
|
||||||
"alarm_enabled",
|
?: preferenceRepository.saveIntPreference(
|
||||||
1
|
"alarm_enabled",
|
||||||
)) == 1
|
1
|
||||||
timerRepository.vibrateEnabled =
|
)
|
||||||
(preferenceRepository.getIntPreference("vibrate_enabled")
|
) == 1
|
||||||
?: preferenceRepository.saveIntPreference(
|
timerRepository.vibrateEnabled = (
|
||||||
"vibrate_enabled",
|
preferenceRepository.getIntPreference("vibrate_enabled")
|
||||||
1
|
?: preferenceRepository.saveIntPreference(
|
||||||
)) == 1
|
"vibrate_enabled",
|
||||||
|
1
|
||||||
|
)
|
||||||
|
) == 1
|
||||||
|
|
||||||
resetTimer()
|
resetTimer()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user