diff --git a/app/src/main/java/io/github/kirasok/alarmix/domain/model/Alarm.kt b/app/src/main/java/io/github/kirasok/alarmix/domain/model/Alarm.kt index c9a1132..cbfd7c4 100644 --- a/app/src/main/java/io/github/kirasok/alarmix/domain/model/Alarm.kt +++ b/app/src/main/java/io/github/kirasok/alarmix/domain/model/Alarm.kt @@ -8,7 +8,11 @@ import java.time.ZonedDateTime data class Alarm( @PrimaryKey(autoGenerate = true) val id: Int = 0, // id is generated on **db.insert**, not on creation of object val timestamp: ZonedDateTime, - var enabled: Boolean = true, ) -class InvalidAlarmException(message: String) : Exception(message) \ No newline at end of file +data class InvalidAlarmException(val invalidAlarmError: InvalidAlarmError) : + Exception(invalidAlarmError.toString()) + +enum class InvalidAlarmError { + PAST_TIMESTAMP, NULL_ALARM +} \ No newline at end of file diff --git a/app/src/main/java/io/github/kirasok/alarmix/domain/use_case/AlarmUseCases.kt b/app/src/main/java/io/github/kirasok/alarmix/domain/use_case/AlarmUseCases.kt index 8dd7f6d..0dafa20 100644 --- a/app/src/main/java/io/github/kirasok/alarmix/domain/use_case/AlarmUseCases.kt +++ b/app/src/main/java/io/github/kirasok/alarmix/domain/use_case/AlarmUseCases.kt @@ -1,9 +1,11 @@ package io.github.kirasok.alarmix.domain.use_case import io.github.kirasok.alarmix.domain.model.Alarm +import io.github.kirasok.alarmix.domain.model.InvalidAlarmError import io.github.kirasok.alarmix.domain.model.InvalidAlarmException import io.github.kirasok.alarmix.domain.repository.AlarmRepository import io.github.kirasok.alarmix.domain.repository.AlarmScheduler +import java.time.ZonedDateTime data class AlarmUseCases( val getAlarms: GetAlarms, @@ -32,8 +34,7 @@ class InsertAlarm( validate(alarm) val id = repository.insertAlarm(alarm) // alarm.id is set during insertion in DB, repository return id so we can use it in scheduler - if (alarm.enabled) - scheduler.schedule(alarm.copy(id = id)) + scheduler.schedule(alarm.copy(id = id)) } } @@ -46,11 +47,11 @@ class DeleteAlarm(private val repository: AlarmRepository) { } class ValidateAlarm(private val scheduler: AlarmScheduler) { - suspend operator fun invoke( + operator fun invoke( alarm: Alarm, ): Boolean = when { - alarm.timestamp.toEpochSecond() * 1000 < System.currentTimeMillis() && alarm.enabled // accepts in milliseconds - -> throw InvalidAlarmException("timestamp can't be less than current time") + alarm.timestamp.isBefore(ZonedDateTime.now()) + -> throw InvalidAlarmException(InvalidAlarmError.PAST_TIMESTAMP) !scheduler.canSchedule() -> throw SecurityException( "Can't schedule alarm without permission from user" diff --git a/app/src/main/java/io/github/kirasok/alarmix/presentation/receiver/AlarmService.kt b/app/src/main/java/io/github/kirasok/alarmix/presentation/receiver/AlarmService.kt index 578c826..c209bee 100644 --- a/app/src/main/java/io/github/kirasok/alarmix/presentation/receiver/AlarmService.kt +++ b/app/src/main/java/io/github/kirasok/alarmix/presentation/receiver/AlarmService.kt @@ -20,13 +20,13 @@ import android.os.IBinder import android.os.VibrationAttributes import android.os.VibrationEffect import android.os.VibratorManager -import android.util.Log import androidx.core.app.NotificationCompat import androidx.core.app.ServiceCompat import dagger.hilt.android.AndroidEntryPoint import io.github.kirasok.alarmix.NotificationChannels import io.github.kirasok.alarmix.R import io.github.kirasok.alarmix.domain.model.Alarm +import io.github.kirasok.alarmix.domain.model.InvalidAlarmError import io.github.kirasok.alarmix.domain.model.InvalidAlarmException import io.github.kirasok.alarmix.domain.use_case.AlarmUseCases import io.github.kirasok.alarmix.presentation.MainActivity @@ -48,7 +48,11 @@ class AlarmService : Service(), MediaPlayer.OnPreparedListener, MediaPlayer.OnEr private val alarmActionReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { when (intent.getStringExtra(ALARM_ACTION_KEY)?.let { AlarmAction.valueOf(it) }) { - AlarmAction.DISMISS -> onDestroy() + AlarmAction.DISMISS -> { + runBlocking { useCases.deleteAlarm(alarm) } + onDestroy() + } + AlarmAction.SNOOZE -> onDestroy() // TODO: implement snooze null -> throw IllegalStateException("Alarm action can't be null") } @@ -75,7 +79,7 @@ class AlarmService : Service(), MediaPlayer.OnPreparedListener, MediaPlayer.OnEr val id = intent?.getIntExtra(AlarmReceiver.INTENT_EXTRA_ALARM_ID, -1).takeIf { it != -1 } ?: throw IllegalStateException("Alarm id can't be null") alarm = runBlocking { useCases.getAlarmById(id) } - ?: throw InvalidAlarmException("Alarm can't be null") + ?: throw InvalidAlarmException(InvalidAlarmError.NULL_ALARM) mediaPlayer = MediaPlayer() initMediaPlayer() diff --git a/app/src/main/java/io/github/kirasok/alarmix/presentation/receiver/BootReceiver.kt b/app/src/main/java/io/github/kirasok/alarmix/presentation/receiver/BootReceiver.kt index 05ad9b4..5d944f6 100644 --- a/app/src/main/java/io/github/kirasok/alarmix/presentation/receiver/BootReceiver.kt +++ b/app/src/main/java/io/github/kirasok/alarmix/presentation/receiver/BootReceiver.kt @@ -4,10 +4,11 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import dagger.hilt.android.AndroidEntryPoint +import io.github.kirasok.alarmix.domain.model.InvalidAlarmError +import io.github.kirasok.alarmix.domain.model.InvalidAlarmException import io.github.kirasok.alarmix.domain.use_case.AlarmUseCases import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking -import java.time.ZonedDateTime import javax.inject.Inject @AndroidEntryPoint @@ -20,12 +21,17 @@ class BootReceiver : BroadcastReceiver() { // Get alarms val alarms = runBlocking(Dispatchers.IO) { alarmUseCases.getAlarms() } - // Enable enabled alarms which timestamp is after now - alarms.forEach { - if (it.enabled && it.timestamp.isAfter(ZonedDateTime.now())) { - runBlocking(Dispatchers.IO) { alarmUseCases.insertAlarm(it) } // On insert, alarm will be validated, replaced in DB and scheduled - } else if (it.enabled) { // Disable alarms which timestamp is in past - runBlocking(Dispatchers.IO) { alarmUseCases.insertAlarm(it.copy(enabled = false)) } + // Insert each alarm + alarms.forEach { alarm -> + runBlocking { + try { + alarmUseCases.insertAlarm(alarm) // insertAlarm validates alarm and throws InvalidAlarmException if alarm is not valid + } catch (e: InvalidAlarmException) { + when (e.invalidAlarmError) { + InvalidAlarmError.PAST_TIMESTAMP -> alarmUseCases.deleteAlarm(alarm) // If alarm has past timestamp then we delete it + else -> {} + } + } } } }