package com.vgmlr.wedge
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
import java.util.Calendar
import java.util.Locale
class WedgeAlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
if (action == "com.vgmlr.wedge.ACTION_CHECK_ALARM" || action == Intent.ACTION_BOOT_COMPLETED) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val nextIntent = Intent(context, WedgeAlarmReceiver::class.java).apply {
this.action = "com.vgmlr.wedge.ACTION_CHECK_ALARM"
}
val pendingIntent = PendingIntent.getBroadcast(
context, 2026, nextIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val triggerAt = System.currentTimeMillis() + 60000L
try {
if (android.os.Build.VERSION.SDK_INT < 31 || alarmManager.canScheduleExactAlarms()) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAt, pendingIntent)
} else {
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAt, pendingIntent)
}
} catch (_: SecurityException) {
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAt, pendingIntent)
}
val database = AppDatabase.getInstance(context)
val preferenceManager = PreferenceManager(context)
CoroutineScope(Dispatchers.IO).launch {
NoteWidgetProvider.triggerUpdate(context)
val note = database.noteDao().getNoteSync() ?: return@launch
val currentText = note.content
val now = Calendar.getInstance()
val hour = String.format(Locale.US, "%02d", now.get(Calendar.HOUR_OF_DAY))
val minute = String.format(Locale.US, "%02d", now.get(Calendar.MINUTE))
val targetHHMM = "$hour:$minute"
val headerFormat = preferenceManager.headerFormat.firstOrNull()
?: WedgeConfig.HEADER_FORMAT_DEFAULT
val triggeredLine = getTriggeredAlarmLine(
currentText, headerFormat, now.timeInMillis, targetHHMM
)
if (triggeredLine != null) {
val soundUri = preferenceManager.alarmSoundUri.firstOrNull() ?: ""
val durationMinutes = preferenceManager.alarmDurationMinutes.firstOrNull() ?: 5
val vibrationEnabled = preferenceManager.vibrationEnabled.firstOrNull() ?: false
val serviceIntent = Intent(context, WedgeAlarmPlayback::class.java).apply {
putExtra("ALARM_SOUND_URI", soundUri)
putExtra("ALARM_DURATION_MINUTES", durationMinutes)
putExtra("ALARM_EVENT_TEXT", triggeredLine)
putExtra("ALARM_VIBRATION_ENABLED", vibrationEnabled)
}
context.startForegroundService(serviceIntent)
}
}
}
}
private fun getTriggeredAlarmLine(
text: String,
headerFormat: String,
timestamp: Long,
currentHHMM: String
): String? {
val headerRegex = WedgeHeader.dateRegex(headerFormat, timestamp)
val match = headerRegex.find(text) ?: return null
val sub = text.substring(match.range.first)
val lines = sub.split("\n")
val nextHeaderRegex = WedgeHeader.matchRegex(headerFormat)
for (i in 1 until lines.size) {
val line = lines[i]
if (line.trim().isEmpty()) continue
if (nextHeaderRegex.containsMatchIn(line.trim())) break
val alarmMatch = WedgeRegex.ALARM_TIME.find(line.trim())
if (alarmMatch != null) {
val extractedTime = "${alarmMatch.groupValues[1]}:${alarmMatch.groupValues[2]}"
if (extractedTime == currentHHMM) {
return line.trim()
}
}
}
return null
}
}