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 )