feat(android): add exact alarm permission request flow and fix receiver mismatch
Add comprehensive exact alarm permission handling for Android 12+ (API 31+) and fix critical bugs preventing notifications from triggering. Features: - Add checkExactAlarmPermission() and requestExactAlarmPermission() plugin methods - Add canScheduleExactAlarms() and canRequestExactAlarmPermission() helper methods - Update all scheduling methods to check/request permission before scheduling - Use reflection for canRequestScheduleExactAlarms() to avoid compilation issues Bug Fixes: - Fix receiver mismatch: change alarm intents from NotifyReceiver to DailyNotificationReceiver - Fix coroutine compilation error: wrap getLatest() suspend call in runBlocking - Store notification content in database before scheduling alarms - Update intent action to match manifest registration The permission request flow opens Settings intent when SCHEDULE_EXACT_ALARM permission is not granted, providing clear user guidance. All scheduling methods now check permission status and request it if needed before proceeding. Version bumped to 1.0.8
This commit is contained in:
@@ -8,6 +8,7 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
@@ -609,8 +610,11 @@ open class DailyNotificationPlugin : Plugin() {
|
||||
|
||||
// Also try to cancel any alarms that might not be in database
|
||||
// Cancel by attempting to cancel with a generic intent
|
||||
// FIX: Use DailyNotificationReceiver to match alarm scheduling
|
||||
try {
|
||||
val intent = Intent(context, NotifyReceiver::class.java)
|
||||
val intent = Intent(context, com.timesafari.dailynotification.DailyNotificationReceiver::class.java).apply {
|
||||
action = "com.timesafari.daily.NOTIFICATION"
|
||||
}
|
||||
// Try cancelling with common request codes (0-65535)
|
||||
// This is a fallback for any orphaned alarms
|
||||
for (requestCode in 0..100 step 10) {
|
||||
@@ -695,6 +699,53 @@ open class DailyNotificationPlugin : Plugin() {
|
||||
// Alias for scheduleDailyNotification for backward compatibility
|
||||
// scheduleDailyReminder accepts same parameters as scheduleDailyNotification
|
||||
try {
|
||||
if (context == null) {
|
||||
return call.reject("Context not available")
|
||||
}
|
||||
|
||||
// Check if exact alarms can be scheduled
|
||||
if (!canScheduleExactAlarms(context)) {
|
||||
// Permission not granted - request it
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && canRequestExactAlarmPermission(context)) {
|
||||
try {
|
||||
val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM).apply {
|
||||
data = android.net.Uri.parse("package:${context.packageName}")
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
Log.w(TAG, "Exact alarm permission required. Opened Settings for user to grant permission.")
|
||||
call.reject(
|
||||
"Exact alarm permission required. Please grant 'Alarms & reminders' permission in Settings, then try again.",
|
||||
"EXACT_ALARM_PERMISSION_REQUIRED"
|
||||
)
|
||||
return
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to open exact alarm settings", e)
|
||||
call.reject("Failed to open exact alarm settings: ${e.message}")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||
data = android.net.Uri.parse("package:${context.packageName}")
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
Log.w(TAG, "Exact alarm permission denied. Directing user to app settings.")
|
||||
call.reject(
|
||||
"Exact alarm permission denied. Please enable 'Alarms & reminders' in app settings.",
|
||||
"PERMISSION_DENIED"
|
||||
)
|
||||
return
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to open app settings", e)
|
||||
call.reject("Failed to open app settings: ${e.message}")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Permission granted - proceed with scheduling
|
||||
// Capacitor passes the object directly via call.data
|
||||
val options = call.data ?: return call.reject("Options are required")
|
||||
|
||||
@@ -751,13 +802,185 @@ open class DailyNotificationPlugin : Plugin() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if exact alarms can be scheduled
|
||||
* Helper method for internal use
|
||||
*
|
||||
* @param context Application context
|
||||
* @return true if exact alarms can be scheduled, false otherwise
|
||||
*/
|
||||
private fun canScheduleExactAlarms(context: Context): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
|
||||
alarmManager?.canScheduleExactAlarms() ?: false
|
||||
} else {
|
||||
// Pre-Android 12: exact alarms are always allowed
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if exact alarm permission can be requested
|
||||
* Helper method that handles API level differences
|
||||
*
|
||||
* Uses reflection to call Settings.canRequestScheduleExactAlarms() on Android 13+
|
||||
* to avoid compilation issues with newer APIs.
|
||||
*
|
||||
* @param context Application context
|
||||
* @return true if permission can be requested, false if permanently denied
|
||||
*/
|
||||
private fun canRequestExactAlarmPermission(context: Context): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
// Android 12+ (API 31+)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
// Android 13+ (API 33+) - use reflection to call canRequestScheduleExactAlarms
|
||||
try {
|
||||
val method = Settings::class.java.getMethod(
|
||||
"canRequestScheduleExactAlarms",
|
||||
Context::class.java
|
||||
)
|
||||
method.invoke(null, context) as Boolean
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to check exact alarm permission using reflection", e)
|
||||
// Fallback to allowing request (safe default)
|
||||
true
|
||||
}
|
||||
} else {
|
||||
// Android 12 (API 31-32) - permission can always be requested
|
||||
// (user hasn't permanently denied it yet)
|
||||
true
|
||||
}
|
||||
} else {
|
||||
// Android 11 and below - permission not needed
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check exact alarm permission status
|
||||
* Returns detailed information about permission status and whether it can be requested
|
||||
*
|
||||
* @param call Plugin call with no parameters
|
||||
*/
|
||||
@PluginMethod
|
||||
fun checkExactAlarmPermission(call: PluginCall) {
|
||||
try {
|
||||
if (context == null) {
|
||||
return call.reject("Context not available")
|
||||
}
|
||||
|
||||
val canSchedule = canScheduleExactAlarms(context)
|
||||
val canRequest = canRequestExactAlarmPermission(context)
|
||||
|
||||
val result = JSObject().apply {
|
||||
put("canSchedule", canSchedule)
|
||||
put("canRequest", canRequest)
|
||||
put("required", Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||
}
|
||||
|
||||
Log.i(TAG, "Exact alarm permission check: canSchedule=$canSchedule, canRequest=$canRequest")
|
||||
call.resolve(result)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to check exact alarm permission", e)
|
||||
call.reject("Permission check failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request exact alarm permission
|
||||
* Opens Settings intent to let user grant the permission
|
||||
*
|
||||
* On Android 12+ (API 31+), SCHEDULE_EXACT_ALARM is a special permission that
|
||||
* cannot be requested through the normal permission request flow. Users must
|
||||
* grant it manually in Settings.
|
||||
*
|
||||
* @param call Plugin call with no parameters
|
||||
*/
|
||||
@PluginMethod
|
||||
fun requestExactAlarmPermission(call: PluginCall) {
|
||||
try {
|
||||
if (context == null) {
|
||||
return call.reject("Context not available")
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||
// Android 11 and below don't need this permission
|
||||
Log.i(TAG, "Exact alarm permission not required on Android ${Build.VERSION.SDK_INT}")
|
||||
call.resolve(JSObject().apply {
|
||||
put("success", true)
|
||||
put("message", "Exact alarm permission not required on this Android version")
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (canScheduleExactAlarms(context)) {
|
||||
// Permission already granted
|
||||
Log.i(TAG, "Exact alarm permission already granted")
|
||||
call.resolve(JSObject().apply {
|
||||
put("success", true)
|
||||
put("message", "Exact alarm permission already granted")
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Check if app can request the permission
|
||||
if (canRequestExactAlarmPermission(context)) {
|
||||
// Open Settings to let user grant permission
|
||||
try {
|
||||
val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM).apply {
|
||||
data = android.net.Uri.parse("package:${context.packageName}")
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
Log.i(TAG, "Opened exact alarm permission settings")
|
||||
call.resolve(JSObject().apply {
|
||||
put("success", true)
|
||||
put("message", "Please grant 'Alarms & reminders' permission in Settings")
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to open exact alarm settings", e)
|
||||
call.reject("Failed to open exact alarm settings: ${e.message}")
|
||||
}
|
||||
} else {
|
||||
// User has already denied or permission is permanently denied
|
||||
// Direct user to app settings
|
||||
try {
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||
data = android.net.Uri.parse("package:${context.packageName}")
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
Log.w(TAG, "Permission denied. Directing user to app settings")
|
||||
call.reject(
|
||||
"Permission denied. Please enable 'Alarms & reminders' in app settings.",
|
||||
"PERMISSION_DENIED"
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to open app settings", e)
|
||||
call.reject("Failed to open app settings: ${e.message}")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to request exact alarm permission", e)
|
||||
call.reject("Permission request failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open exact alarm settings (legacy method, kept for backward compatibility)
|
||||
* Use requestExactAlarmPermission() for better error handling
|
||||
*
|
||||
* @param call Plugin call with no parameters
|
||||
*/
|
||||
@PluginMethod
|
||||
fun openExactAlarmSettings(call: PluginCall) {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
val intent = Intent(android.provider.Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
activity?.startActivity(intent)
|
||||
val intent = Intent(android.provider.Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM).apply {
|
||||
data = android.net.Uri.parse("package:${context?.packageName}")
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
activity?.startActivity(intent) ?: context?.startActivity(intent)
|
||||
call.resolve()
|
||||
} else {
|
||||
call.reject("Exact alarm settings are only available on Android 12+")
|
||||
@@ -920,6 +1143,55 @@ open class DailyNotificationPlugin : Plugin() {
|
||||
@PluginMethod
|
||||
fun scheduleDailyNotification(call: PluginCall) {
|
||||
try {
|
||||
if (context == null) {
|
||||
return call.reject("Context not available")
|
||||
}
|
||||
|
||||
// Check if exact alarms can be scheduled
|
||||
if (!canScheduleExactAlarms(context)) {
|
||||
// Permission not granted - request it
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && canRequestExactAlarmPermission(context)) {
|
||||
// Open Settings to let user grant permission
|
||||
try {
|
||||
val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM).apply {
|
||||
data = android.net.Uri.parse("package:${context.packageName}")
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
Log.w(TAG, "Exact alarm permission required. Opened Settings for user to grant permission.")
|
||||
call.reject(
|
||||
"Exact alarm permission required. Please grant 'Alarms & reminders' permission in Settings, then try again.",
|
||||
"EXACT_ALARM_PERMISSION_REQUIRED"
|
||||
)
|
||||
return
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to open exact alarm settings", e)
|
||||
call.reject("Failed to open exact alarm settings: ${e.message}")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// Permission permanently denied - direct to app settings
|
||||
try {
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||
data = android.net.Uri.parse("package:${context.packageName}")
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
Log.w(TAG, "Exact alarm permission denied. Directing user to app settings.")
|
||||
call.reject(
|
||||
"Exact alarm permission denied. Please enable 'Alarms & reminders' in app settings.",
|
||||
"PERMISSION_DENIED"
|
||||
)
|
||||
return
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to open app settings", e)
|
||||
call.reject("Failed to open app settings: ${e.message}")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Permission granted - proceed with exact alarm scheduling
|
||||
// Capacitor passes the object directly via call.data
|
||||
val options = call.data ?: return call.reject("Options are required")
|
||||
|
||||
@@ -1076,6 +1348,53 @@ open class DailyNotificationPlugin : Plugin() {
|
||||
@PluginMethod
|
||||
fun scheduleUserNotification(call: PluginCall) {
|
||||
try {
|
||||
if (context == null) {
|
||||
return call.reject("Context not available")
|
||||
}
|
||||
|
||||
// Check if exact alarms can be scheduled
|
||||
if (!canScheduleExactAlarms(context)) {
|
||||
// Permission not granted - request it
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && canRequestExactAlarmPermission(context)) {
|
||||
try {
|
||||
val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM).apply {
|
||||
data = android.net.Uri.parse("package:${context.packageName}")
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
Log.w(TAG, "Exact alarm permission required. Opened Settings for user to grant permission.")
|
||||
call.reject(
|
||||
"Exact alarm permission required. Please grant 'Alarms & reminders' permission in Settings, then try again.",
|
||||
"EXACT_ALARM_PERMISSION_REQUIRED"
|
||||
)
|
||||
return
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to open exact alarm settings", e)
|
||||
call.reject("Failed to open exact alarm settings: ${e.message}")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||
data = android.net.Uri.parse("package:${context.packageName}")
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
Log.w(TAG, "Exact alarm permission denied. Directing user to app settings.")
|
||||
call.reject(
|
||||
"Exact alarm permission denied. Please enable 'Alarms & reminders' in app settings.",
|
||||
"PERMISSION_DENIED"
|
||||
)
|
||||
return
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to open app settings", e)
|
||||
call.reject("Failed to open app settings: ${e.message}")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Permission granted - proceed with scheduling
|
||||
val configJson = call.getObject("config")
|
||||
val config = parseUserNotificationConfig(configJson)
|
||||
|
||||
@@ -1113,6 +1432,53 @@ open class DailyNotificationPlugin : Plugin() {
|
||||
@PluginMethod
|
||||
fun scheduleDualNotification(call: PluginCall) {
|
||||
try {
|
||||
if (context == null) {
|
||||
return call.reject("Context not available")
|
||||
}
|
||||
|
||||
// Check if exact alarms can be scheduled
|
||||
if (!canScheduleExactAlarms(context)) {
|
||||
// Permission not granted - request it
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && canRequestExactAlarmPermission(context)) {
|
||||
try {
|
||||
val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM).apply {
|
||||
data = android.net.Uri.parse("package:${context.packageName}")
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
Log.w(TAG, "Exact alarm permission required. Opened Settings for user to grant permission.")
|
||||
call.reject(
|
||||
"Exact alarm permission required. Please grant 'Alarms & reminders' permission in Settings, then try again.",
|
||||
"EXACT_ALARM_PERMISSION_REQUIRED"
|
||||
)
|
||||
return
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to open exact alarm settings", e)
|
||||
call.reject("Failed to open exact alarm settings: ${e.message}")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||
data = android.net.Uri.parse("package:${context.packageName}")
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
Log.w(TAG, "Exact alarm permission denied. Directing user to app settings.")
|
||||
call.reject(
|
||||
"Exact alarm permission denied. Please enable 'Alarms & reminders' in app settings.",
|
||||
"PERMISSION_DENIED"
|
||||
)
|
||||
return
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to open app settings", e)
|
||||
call.reject("Failed to open app settings: ${e.message}")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Permission granted - proceed with scheduling
|
||||
val configJson = call.getObject("config") ?: return call.reject("Config is required")
|
||||
val contentFetchObj = configJson.getJSObject("contentFetch") ?: return call.reject("contentFetch config is required")
|
||||
val userNotificationObj = configJson.getJSObject("userNotification") ?: return call.reject("userNotification config is required")
|
||||
|
||||
@@ -14,6 +14,7 @@ import androidx.core.app.NotificationCompat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
/**
|
||||
* AlarmManager implementation for user notifications
|
||||
@@ -79,6 +80,9 @@ class NotifyReceiver : BroadcastReceiver() {
|
||||
* Uses setAlarmClock() for Android 5.0+ for better reliability
|
||||
* Falls back to setExactAndAllowWhileIdle for older versions
|
||||
*
|
||||
* FIX: Uses DailyNotificationReceiver (registered in manifest) instead of NotifyReceiver
|
||||
* Stores notification content in database and passes notification ID to receiver
|
||||
*
|
||||
* @param context Application context
|
||||
* @param triggerAtMillis When to trigger the notification (UTC milliseconds)
|
||||
* @param config Notification configuration
|
||||
@@ -93,7 +97,63 @@ class NotifyReceiver : BroadcastReceiver() {
|
||||
reminderId: String? = null
|
||||
) {
|
||||
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
val intent = Intent(context, NotifyReceiver::class.java).apply {
|
||||
|
||||
// Generate notification ID (use reminderId if provided, otherwise generate from trigger time)
|
||||
val notificationId = reminderId ?: "notify_${triggerAtMillis}"
|
||||
|
||||
// Store notification content in database before scheduling alarm
|
||||
// This allows DailyNotificationReceiver to retrieve content via notification ID
|
||||
// FIX: Wrap suspend function calls in coroutine
|
||||
if (!isStaticReminder) {
|
||||
try {
|
||||
// Use runBlocking to call suspend function from non-suspend context
|
||||
// This is acceptable here because we're not in a UI thread and need to ensure
|
||||
// content is stored before scheduling the alarm
|
||||
runBlocking {
|
||||
val db = DailyNotificationDatabase.getDatabase(context)
|
||||
val contentCache = db.contentCacheDao().getLatest()
|
||||
|
||||
// If we have cached content, create a notification content entity
|
||||
if (contentCache != null) {
|
||||
val roomStorage = com.timesafari.dailynotification.storage.DailyNotificationStorageRoom(context)
|
||||
val entity = com.timesafari.dailynotification.entities.NotificationContentEntity(
|
||||
notificationId,
|
||||
"1.0.2", // Plugin version
|
||||
null, // timesafariDid - can be set if available
|
||||
"daily",
|
||||
config.title,
|
||||
config.body ?: String(contentCache.payload),
|
||||
triggerAtMillis,
|
||||
java.time.ZoneId.systemDefault().id
|
||||
)
|
||||
entity.priority = when (config.priority) {
|
||||
"high", "max" -> 2
|
||||
"low", "min" -> -1
|
||||
else -> 0
|
||||
}
|
||||
entity.vibrationEnabled = config.vibration ?: true
|
||||
entity.soundEnabled = config.sound ?: true
|
||||
entity.deliveryStatus = "pending"
|
||||
entity.createdAt = System.currentTimeMillis()
|
||||
entity.updatedAt = System.currentTimeMillis()
|
||||
entity.ttlSeconds = contentCache.ttlSeconds.toLong()
|
||||
|
||||
// saveNotificationContent returns CompletableFuture, so we need to wait for it
|
||||
roomStorage.saveNotificationContent(entity).get()
|
||||
Log.d(TAG, "Stored notification content in database: id=$notificationId")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to store notification content in database, continuing with alarm scheduling", e)
|
||||
}
|
||||
}
|
||||
|
||||
// FIX: Use DailyNotificationReceiver (registered in manifest) instead of NotifyReceiver
|
||||
// FIX: Set action to match manifest registration
|
||||
val intent = Intent(context, com.timesafari.dailynotification.DailyNotificationReceiver::class.java).apply {
|
||||
action = "com.timesafari.daily.NOTIFICATION" // Must match manifest intent-filter action
|
||||
putExtra("notification_id", notificationId) // DailyNotificationReceiver expects this extra
|
||||
// Also preserve original extras for backward compatibility if needed
|
||||
putExtra("title", config.title)
|
||||
putExtra("body", config.body)
|
||||
putExtra("sound", config.sound ?: true)
|
||||
@@ -188,12 +248,16 @@ class NotifyReceiver : BroadcastReceiver() {
|
||||
|
||||
/**
|
||||
* Cancel a scheduled notification alarm
|
||||
* FIX: Uses DailyNotificationReceiver to match alarm scheduling
|
||||
* @param context Application context
|
||||
* @param triggerAtMillis The trigger time of the alarm to cancel (required for unique request code)
|
||||
*/
|
||||
fun cancelNotification(context: Context, triggerAtMillis: Long) {
|
||||
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
val intent = Intent(context, NotifyReceiver::class.java)
|
||||
// FIX: Use DailyNotificationReceiver to match what was scheduled
|
||||
val intent = Intent(context, com.timesafari.dailynotification.DailyNotificationReceiver::class.java).apply {
|
||||
action = "com.timesafari.daily.NOTIFICATION"
|
||||
}
|
||||
val requestCode = getRequestCode(triggerAtMillis)
|
||||
val pendingIntent = PendingIntent.getBroadcast(
|
||||
context,
|
||||
@@ -207,12 +271,16 @@ class NotifyReceiver : BroadcastReceiver() {
|
||||
|
||||
/**
|
||||
* Check if an alarm is scheduled for the given trigger time
|
||||
* FIX: Uses DailyNotificationReceiver to match alarm scheduling
|
||||
* @param context Application context
|
||||
* @param triggerAtMillis The trigger time to check
|
||||
* @return true if alarm is scheduled, false otherwise
|
||||
*/
|
||||
fun isAlarmScheduled(context: Context, triggerAtMillis: Long): Boolean {
|
||||
val intent = Intent(context, NotifyReceiver::class.java)
|
||||
// FIX: Use DailyNotificationReceiver to match what was scheduled
|
||||
val intent = Intent(context, com.timesafari.dailynotification.DailyNotificationReceiver::class.java).apply {
|
||||
action = "com.timesafari.daily.NOTIFICATION"
|
||||
}
|
||||
val requestCode = getRequestCode(triggerAtMillis)
|
||||
val pendingIntent = PendingIntent.getBroadcast(
|
||||
context,
|
||||
|
||||
Reference in New Issue
Block a user