Files
daily-notification-plugin/docs/CONSUMING_APP_OPTIONAL_ANDROID_ID_CLEANUP.md
Jose Olarte III 7702bd3b81 fix(android): second daily notification not firing after reschedule
Cancel-then-schedule was skipped because the idempotence check still
found the cancelled PendingIntent in Android's cache. Skip
PendingIntent idempotence on the cancel-then-schedule path so the
new schedule is always set.

- NotifyReceiver.scheduleExactNotification: add
  skipPendingIntentIdempotence (used only from scheduleDailyNotification)
- ScheduleHelper: pass skipPendingIntentIdempotence=true after
  cancelNotification(scheduleId)
- Version 1.1.2: package.json, CHANGELOG, README, TS/Android refs
- docs/CONSUMING_APP_OPTIONAL_ANDROID_ID_CLEANUP.md: optional app
  cleanup to use one stable id on both platforms
2026-02-13 19:26:09 +08:00

4.9 KiB
Raw Blame History

Optional: Use a Single Stable Schedule ID on iOS and Android

Audience: Consuming apps (e.g. TimeSafari / crowd-funder-for-time-pwa) that use @timesafari/daily-notification-plugin.
Purpose: Describe an optional app-side cleanup now that the plugins Android second-schedule bug is fixed (plugin v1.1.2+).
Use: Feed this doc into Cursor (or any editor) in the consuming app repo when implementing the cleanup.


Context

  • Plugin fix (v1.1.2): After cancel-then-schedule on Android, the plugin no longer skips the new schedule due to PendingIntent cache. Rescheduling works reliably whether or not the app passes an explicit id to scheduleDailyNotification.
  • Previous workaround: Some apps avoided passing id on Android and used the plugin default "daily_notification" so that the (now-fixed) second-schedule bug would not trigger. On iOS they passed a stable id (e.g. "daily_timesafari_reminder") for getStatus/cancel and verification.
  • Optional cleanup: You can use the same stable schedule id on both iOS and Android. That simplifies code (one id everywhere), makes getStatus/cancel and verification consistent across platforms, and is safe with plugin v1.1.2+.

Prerequisites

  • Depend on @timesafari/daily-notification-plugin@1.1.2 (or ^1.1.2) so the Android fix is in effect.
  • No other code changes are required for the bug fix; this doc is only for the optional id cleanup.

What to Change in the Consuming App

1. Single stable reminder ID (both platforms)

Use one reminder id for schedule, cancel, and getStatus on both iOS and Android.

Example (current pattern):

// Before: different id per platform
private get reminderId(): string {
  return Capacitor.getPlatform() === "ios"
    ? "daily_timesafari_reminder"
    : "daily_notification";
}

After (optional cleanup):

// After: same stable id on both platforms (requires plugin >= 1.1.2)
private readonly reminderId = "daily_timesafari_reminder";

Or keep a getter if you prefer:

private get reminderId(): string {
  return "daily_timesafari_reminder";
}

Use whatever stable string your app already uses on iOS (e.g. "daily_timesafari_reminder"); no need to change the value.


2. Pass id when scheduling on Android

Today you may only add scheduleOptions.id on iOS. Add it for Android too so the plugin stores and returns this id (getStatus, getScheduledReminders, cancel all use it).

Example (current pattern):

const scheduleOptions = {
  time: options.time,
  title: options.title,
  body: options.body,
  sound: true,
  priority: (options.priority || "normal") as "low" | "default" | "high",
};
if (Capacitor.getPlatform() === "ios") {
  scheduleOptions.id = this.reminderId;
}
await DailyNotification.scheduleDailyNotification(scheduleOptions);

After (optional cleanup):

const scheduleOptions = {
  time: options.time,
  title: options.title,
  body: options.body,
  sound: true,
  priority: (options.priority || "normal") as "low" | "default" | "high",
  id: this.reminderId, // same id on iOS and Android (plugin >= 1.1.2)
};
await DailyNotification.scheduleDailyNotification(scheduleOptions);

So: always pass id: this.reminderId (or your chosen constant) for both platforms.


3. Update comments

Remove or update comments that say Android must not receive an id to avoid the second-schedule bug, and that the plugin uses "daily_notification" on Android. Replace with a short note that a single stable id is used on both platforms and requires plugin v1.1.2+.

Example comment to add/update:

/**
 * Stable schedule/reminder ID used for schedule, cancel, and getStatus.
 * Same value on iOS and Android (plugin v1.1.2+ fixes Android reschedule with custom id).
 */
private readonly reminderId = "daily_timesafari_reminder";

Files to Touch (typical)

  • Native notification service (e.g. src/services/notifications/NativeNotificationService.ts):
    • reminderId: use single value for both platforms.
    • scheduleDailyNotification: always pass id in scheduleOptions (include Android).
    • Adjust comments as above.

No changes are required to cancel or getStatus if they already use this.reminderId; they will now resolve the same schedule on Android as on iOS.


Verification

  1. Android: Schedule a daily notification, then change time and save again (reschedule). The second scheduled time should fire; no need to reinstall.
  2. getStatus: After scheduling on Android, getStatus should return the scheduled reminder with the same id you pass (e.g. daily_timesafari_reminder).
  3. Cancel: Cancelling by that id on Android should clear the scheduled notification.

References

  • Plugin CHANGELOG: [1.1.2] - 2026-02-13 — Android second daily notification not firing after reschedule.
  • Issue context (if present in consuming app): doc/android-daily-notification-second-schedule-issue.md.