From f1830e5f6f80674897652a38298fb3643769d04a Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Tue, 30 Dec 2025 09:50:29 +0000 Subject: [PATCH] fix: properly cancel alarms using FLAG_NO_CREATE and pendingIntent.cancel() - Use FLAG_NO_CREATE to get existing PendingIntent instead of creating new one - Call both alarmManager.cancel() AND pendingIntent.cancel() (matches scheduleExactNotification pattern) - Fixes issue where cancellation failed, causing idempotence check to skip scheduling Previously, cancelNotification(): - Used FLAG_UPDATE_CURRENT which could create new PendingIntent - Only called alarmManager.cancel(), not pendingIntent.cancel() - Result: PendingIntent still existed after cancellation, causing 'duplicate schedule' skip The fix: - Use FLAG_NO_CREATE to get existing PendingIntent (don't create if missing) - Call both alarmManager.cancel() and pendingIntent.cancel() (matches pattern in scheduleExactNotification) - This ensures proper cancellation so new alarms can be scheduled This fixes Test 1 failure where alarms weren't appearing after scheduling. --- .../dailynotification/NotifyReceiver.kt | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/android/src/main/java/com/timesafari/dailynotification/NotifyReceiver.kt b/android/src/main/java/com/timesafari/dailynotification/NotifyReceiver.kt index ecd53f0..127f6ac 100644 --- a/android/src/main/java/com/timesafari/dailynotification/NotifyReceiver.kt +++ b/android/src/main/java/com/timesafari/dailynotification/NotifyReceiver.kt @@ -474,26 +474,37 @@ class NotifyReceiver : BroadcastReceiver() { return } } - val pendingIntent = PendingIntent.getBroadcast( - context, - requestCode, - intent, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE - ) - alarmManager.cancel(pendingIntent) - Log.i(TAG, "DNP-CANCEL: Notification alarm cancelled: scheduleId=$scheduleId, triggerAt=$triggerAtMillis, requestCode=$requestCode") - // Verify cancellation by checking if alarm still exists - val verifyIntent = PendingIntent.getBroadcast( + // CRITICAL: Use FLAG_NO_CREATE to get existing PendingIntent, don't create new one + // This matches the pattern used in scheduleExactNotification for proper cancellation + val existingPendingIntent = PendingIntent.getBroadcast( context, requestCode, intent, PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE ) - if (verifyIntent == null) { - Log.d(TAG, "DNP-CANCEL: ✅ Cancellation verified - no PendingIntent found for requestCode=$requestCode") + + if (existingPendingIntent != null) { + // Cancel both the alarm in AlarmManager AND the PendingIntent itself + // This matches the pattern in scheduleExactNotification (lines 311-312) + alarmManager.cancel(existingPendingIntent) + existingPendingIntent.cancel() + Log.i(TAG, "DNP-CANCEL: Notification alarm cancelled: scheduleId=$scheduleId, triggerAt=$triggerAtMillis, requestCode=$requestCode") + + // Verify cancellation by checking if alarm still exists + val verifyIntent = PendingIntent.getBroadcast( + context, + requestCode, + intent, + PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE + ) + if (verifyIntent == null) { + Log.d(TAG, "DNP-CANCEL: ✅ Cancellation verified - no PendingIntent found for requestCode=$requestCode") + } else { + Log.w(TAG, "DNP-CANCEL: ⚠️ Cancellation may have failed - PendingIntent still exists for requestCode=$requestCode") + } } else { - Log.w(TAG, "DNP-CANCEL: ⚠️ Cancellation may have failed - PendingIntent still exists for requestCode=$requestCode") + Log.d(TAG, "DNP-CANCEL: No existing PendingIntent found to cancel: scheduleId=$scheduleId, requestCode=$requestCode") } }