You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
5.5 KiB
153 lines
5.5 KiB
package com.timesafari.dailynotification
|
|
|
|
import android.content.BroadcastReceiver
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.util.Log
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.launch
|
|
|
|
/**
|
|
* Boot recovery receiver to reschedule notifications after device reboot
|
|
* Implements RECEIVE_BOOT_COMPLETED functionality
|
|
*
|
|
* @author Matthew Raymer
|
|
* @version 1.1.0
|
|
*/
|
|
class BootReceiver : BroadcastReceiver() {
|
|
|
|
companion object {
|
|
private const val TAG = "DNP-BOOT"
|
|
}
|
|
|
|
override fun onReceive(context: Context, intent: Intent?) {
|
|
if (intent?.action == Intent.ACTION_BOOT_COMPLETED) {
|
|
Log.i(TAG, "Boot completed, rescheduling notifications")
|
|
|
|
CoroutineScope(Dispatchers.IO).launch {
|
|
try {
|
|
rescheduleNotifications(context)
|
|
} catch (e: Exception) {
|
|
Log.e(TAG, "Failed to reschedule notifications after boot", e)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private suspend fun rescheduleNotifications(context: Context) {
|
|
val db = DailyNotificationDatabase.getDatabase(context)
|
|
val enabledSchedules = db.scheduleDao().getEnabled()
|
|
|
|
Log.i(TAG, "Found ${enabledSchedules.size} enabled schedules to reschedule")
|
|
|
|
enabledSchedules.forEach { schedule ->
|
|
try {
|
|
when (schedule.kind) {
|
|
"fetch" -> {
|
|
// Reschedule WorkManager fetch
|
|
val config = ContentFetchConfig(
|
|
enabled = schedule.enabled,
|
|
schedule = schedule.cron ?: schedule.clockTime ?: "0 9 * * *",
|
|
url = null, // Will use mock content
|
|
timeout = 30000,
|
|
retryAttempts = 3,
|
|
retryDelay = 1000,
|
|
callbacks = CallbackConfig()
|
|
)
|
|
FetchWorker.scheduleFetch(context, config)
|
|
Log.i(TAG, "Rescheduled fetch for schedule: ${schedule.id}")
|
|
}
|
|
"notify" -> {
|
|
// Reschedule AlarmManager notification
|
|
val nextRunTime = calculateNextRunTime(schedule)
|
|
if (nextRunTime > System.currentTimeMillis()) {
|
|
val config = UserNotificationConfig(
|
|
enabled = schedule.enabled,
|
|
schedule = schedule.cron ?: schedule.clockTime ?: "0 9 * * *",
|
|
title = "Daily Notification",
|
|
body = "Your daily update is ready",
|
|
sound = true,
|
|
vibration = true,
|
|
priority = "normal"
|
|
)
|
|
NotifyReceiver.scheduleExactNotification(context, nextRunTime, config)
|
|
Log.i(TAG, "Rescheduled notification for schedule: ${schedule.id}")
|
|
}
|
|
}
|
|
else -> {
|
|
Log.w(TAG, "Unknown schedule kind: ${schedule.kind}")
|
|
}
|
|
}
|
|
} catch (e: Exception) {
|
|
Log.e(TAG, "Failed to reschedule ${schedule.kind} for ${schedule.id}", e)
|
|
}
|
|
}
|
|
|
|
// Record boot recovery in history
|
|
try {
|
|
db.historyDao().insert(
|
|
History(
|
|
refId = "boot_recovery_${System.currentTimeMillis()}",
|
|
kind = "boot_recovery",
|
|
occurredAt = System.currentTimeMillis(),
|
|
outcome = "success",
|
|
diagJson = "{\"schedules_rescheduled\": ${enabledSchedules.size}}"
|
|
)
|
|
)
|
|
} catch (e: Exception) {
|
|
Log.e(TAG, "Failed to record boot recovery", e)
|
|
}
|
|
}
|
|
|
|
private fun calculateNextRunTime(schedule: Schedule): Long {
|
|
val now = System.currentTimeMillis()
|
|
|
|
// Simple implementation - for production, use proper cron parsing
|
|
return when {
|
|
schedule.cron != null -> {
|
|
// Parse cron expression and calculate next run
|
|
// For now, return next day at 9 AM
|
|
now + (24 * 60 * 60 * 1000L)
|
|
}
|
|
schedule.clockTime != null -> {
|
|
// Parse HH:mm and calculate next run
|
|
// For now, return next day at specified time
|
|
now + (24 * 60 * 60 * 1000L)
|
|
}
|
|
else -> {
|
|
// Default to next day at 9 AM
|
|
now + (24 * 60 * 60 * 1000L)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Data classes for configuration (simplified versions)
|
|
*/
|
|
data class ContentFetchConfig(
|
|
val enabled: Boolean,
|
|
val schedule: String,
|
|
val url: String? = null,
|
|
val timeout: Int? = null,
|
|
val retryAttempts: Int? = null,
|
|
val retryDelay: Int? = null,
|
|
val callbacks: CallbackConfig
|
|
)
|
|
|
|
data class UserNotificationConfig(
|
|
val enabled: Boolean,
|
|
val schedule: String,
|
|
val title: String? = null,
|
|
val body: String? = null,
|
|
val sound: Boolean? = null,
|
|
val vibration: Boolean? = null,
|
|
val priority: String? = null
|
|
)
|
|
|
|
data class CallbackConfig(
|
|
val apiService: String? = null,
|
|
val database: String? = null,
|
|
val reporting: String? = null
|
|
)
|
|
|