diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/AlarmDialog.kt b/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/AlarmDialog.kt new file mode 100644 index 0000000..556e88d --- /dev/null +++ b/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/AlarmDialog.kt @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2025 Nishant Mishra + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.nsh07.pomodoro.ui.timerScreen + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.material3.AlertDialogDefaults +import androidx.compose.material3.BasicAlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.MaterialTheme.typography +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import org.nsh07.pomodoro.R + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AlarmDialog( + modifier: Modifier = Modifier, + stopAlarm: () -> Unit +) { + BasicAlertDialog( + onDismissRequest = stopAlarm, + modifier = modifier + ) { + Surface( + modifier = Modifier + .wrapContentWidth() + .wrapContentHeight() + .clickable(onClick = stopAlarm), + shape = MaterialTheme.shapes.extraLarge, + tonalElevation = AlertDialogDefaults.TonalElevation, + ) { + Column(modifier = Modifier.padding(24.dp)) { + Icon( + painter = painterResource(R.drawable.alarm), + contentDescription = "Alarm", + modifier = Modifier.align(Alignment.CenterHorizontally) + ) + Spacer(Modifier.height(16.dp)) + Text( + text = "Stop Alarm?", + style = typography.headlineSmall, + modifier = Modifier.align(Alignment.CenterHorizontally) + ) + Spacer(Modifier.height(16.dp)) + Text( + text = "Current timer session is complete. Tap anywhere to stop the alarm." + ) + Spacer(modifier = Modifier.height(24.dp)) + Button( + onClick = stopAlarm, + modifier = Modifier.align(Alignment.End), + ) { + Text("Ok") + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/TimerScreen.kt b/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/TimerScreen.kt index 4dbb7c8..a31724e 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/TimerScreen.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/TimerScreen.kt @@ -111,6 +111,9 @@ fun TimerScreen( onResult = {} ) + if (timerState.alarmRinging) + AlarmDialog { onAction(TimerAction.StopAlarm) } + Column(modifier = modifier) { TopAppBar( title = { diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerAction.kt b/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerAction.kt index 633c5cb..46df0d5 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerAction.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerAction.kt @@ -10,5 +10,6 @@ package org.nsh07.pomodoro.ui.timerScreen.viewModel sealed interface TimerAction { data object ResetTimer : TimerAction data object SkipTimer : TimerAction + data object StopAlarm : TimerAction data object ToggleTimer : TimerAction } \ No newline at end of file diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerState.kt b/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerState.kt index 4a9c3d9..3a3cd3b 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerState.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerState.kt @@ -16,7 +16,8 @@ data class TimerState( val nextTimeStr: String = "5:00", val showBrandTitle: Boolean = true, val currentFocusCount: Int = 1, - val totalFocusCount: Int = 4 + val totalFocusCount: Int = 4, + val alarmRinging: Boolean = false ) enum class TimerMode { 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 8f7d025..80ed086 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,19 +8,23 @@ package org.nsh07.pomodoro.ui.timerScreen.viewModel import android.annotation.SuppressLint +import android.app.Application import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.content.Intent +import android.media.MediaPlayer import android.os.SystemClock +import android.provider.Settings import androidx.compose.material3.ColorScheme import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat -import androidx.lifecycle.ViewModel +import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY +import androidx.lifecycle.application import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory @@ -46,12 +50,13 @@ import kotlin.text.Typography.middleDot @OptIn(FlowPreview::class) class TimerViewModel( + application: Application, private val preferenceRepository: PreferenceRepository, private val statRepository: StatRepository, private val timerRepository: TimerRepository, private val notificationBuilder: NotificationCompat.Builder, private val notificationManager: NotificationManagerCompat -) : ViewModel() { +) : AndroidViewModel(application) { private val _timerState = MutableStateFlow( TimerState( totalTime = timerRepository.focusTime, @@ -73,6 +78,11 @@ class TimerViewModel( private lateinit var cs: ColorScheme + private val alarm = MediaPlayer.create( + this.application, + Settings.System.DEFAULT_ALARM_ALERT_URI ?: Settings.System.DEFAULT_RINGTONE_URI + ) + init { viewModelScope.launch(Dispatchers.IO) { timerRepository.focusTime = @@ -126,6 +136,7 @@ class TimerViewModel( when (action) { TimerAction.ResetTimer -> resetTimer() TimerAction.SkipTimer -> skipTimer() + TimerAction.StopAlarm -> stopAlarm() TimerAction.ToggleTimer -> toggleTimer() } } @@ -331,9 +342,24 @@ class TimerViewModel( ) .setShowWhen(true) .setWhen(System.currentTimeMillis() + remainingTime) // Sets the Live Activity/Now Bar chip time - .setSilent(!complete) + .setSilent(true) .build() ) + + if (complete) { + alarm.start() + _timerState.update { currentState -> + currentState.copy(alarmRinging = true) + } + } + } + + fun stopAlarm() { + alarm.pause() + alarm.seekTo(0) + _timerState.update { currentState -> + currentState.copy(alarmRinging = false) + } } companion object { @@ -374,6 +400,7 @@ class TimerViewModel( .setOngoing(true) TimerViewModel( + application = application, preferenceRepository = appPreferenceRepository, statRepository = appStatRepository, timerRepository = appTimerRepository, diff --git a/app/src/main/res/drawable/alarm.xml b/app/src/main/res/drawable/alarm.xml new file mode 100644 index 0000000..500f240 --- /dev/null +++ b/app/src/main/res/drawable/alarm.xml @@ -0,0 +1,16 @@ + + + + +