Skip to content

Commit

Permalink
Merge pull request #14 from kirasok/boot_receiver
Browse files Browse the repository at this point in the history
feat(ui): re-schedule alarms on boot
  • Loading branch information
kirasok authored Apr 3, 2024
2 parents 1b57e1b + 208107d commit 442e368
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 10 deletions.
11 changes: 11 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<application
android:name=".AlarmApp"
Expand All @@ -23,6 +24,16 @@
<receiver
android:name=".presentation.receiver.AlarmReceiver"
android:exported="false" />
<receiver
android:name=".presentation.receiver.BootReceiver"
android:directBootAware="true"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>

<service
android:name=".presentation.receiver.AlarmService"
android:exported="false"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import io.github.kirasok.alarmix.domain.repository.AlarmRepository
import kotlinx.coroutines.flow.Flow

class AlarmRepositoryLocal(private val dao: AlarmDao) : AlarmRepository {
override fun getAlarms(): Flow<List<Alarm>> = dao.getAlarms()
override suspend fun getAlarms(): List<Alarm> = dao.getAlarms()

override suspend fun getAlarmById(id: Int): Alarm = dao.getAlarmById(id)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,17 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import io.github.kirasok.alarmix.domain.model.Alarm
import kotlinx.coroutines.flow.Flow

@Dao
interface AlarmDao {
@Query("SELECT * FROM alarm")
fun getAlarms(): Flow<List<Alarm>>
suspend fun getAlarms(): List<Alarm>

@Query("SELECT * FROM alarm WHERE id = :id")
suspend fun getAlarmById(id: Int): Alarm

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAlarm(alarm: Alarm) : Long
suspend fun insertAlarm(alarm: Alarm): Long

@Delete
suspend fun deleteAlarm(alarm: Alarm)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ 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)
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package io.github.kirasok.alarmix.domain.repository

import io.github.kirasok.alarmix.domain.model.Alarm
import kotlinx.coroutines.flow.Flow

interface AlarmRepository {
fun getAlarms(): Flow<List<Alarm>>
suspend fun getAlarms(): List<Alarm>

suspend fun getAlarmById(id: Int): Alarm?

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import io.github.kirasok.alarmix.domain.model.Alarm
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 kotlinx.coroutines.flow.Flow

data class AlarmUseCases(
val getAlarms: GetAlarms,
Expand All @@ -15,7 +14,7 @@ data class AlarmUseCases(
)

class GetAlarms(private val repository: AlarmRepository) {
operator fun invoke(): Flow<List<Alarm>> = repository.getAlarms()
suspend operator fun invoke(): List<Alarm> = repository.getAlarms()
}

class GetAlarmById(private val repository: AlarmRepository) {
Expand All @@ -33,7 +32,8 @@ 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
scheduler.schedule(Alarm(id = id, timestamp = alarm.timestamp))
if (alarm.enabled)
scheduler.schedule(alarm.copy(id = id))
}
}

Expand All @@ -49,7 +49,7 @@ class ValidateAlarm(private val scheduler: AlarmScheduler) {
suspend operator fun invoke(
alarm: Alarm,
): Boolean = when {
alarm.timestamp.toEpochSecond() * 1000 < System.currentTimeMillis() // accepts in milliseconds
alarm.timestamp.toEpochSecond() * 1000 < System.currentTimeMillis() && alarm.enabled // accepts in milliseconds
-> throw InvalidAlarmException("timestamp can't be less than current time")

!scheduler.canSchedule() -> throw SecurityException(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.github.kirasok.alarmix.presentation.receiver

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import dagger.hilt.android.AndroidEntryPoint
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
class BootReceiver : BroadcastReceiver() {

@Inject
lateinit var alarmUseCases: AlarmUseCases
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action != Intent.ACTION_BOOT_COMPLETED) return
// 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)) }
}
}
}
}

0 comments on commit 442e368

Please sign in to comment.