fix(android): avoid overwriting app schedule when rollover uses daily_rollover_ id

NotifyReceiver's post-schedule DB update no longer uses the "first enabled
notify schedule" fallback when stableScheduleId starts with "daily_rollover_".
That fallback was updating the app's schedule row (e.g. daily_timesafari_reminder)
with the rollover time and could leave the app's next alarm in a bad state after
a notification fired.

Add docs/CONSUMING_APP_ANDROID_NOTES.md with notes for consuming apps: debounce
double scheduleDailyNotification calls, and include DailyNotificationReceiver
in logcat when debugging alarms that are scheduled but do not fire.
This commit is contained in:
Jose Olarte III
2026-02-16 18:16:20 +08:00
parent 02a44a3e7b
commit 0b61d33f21
2 changed files with 42 additions and 3 deletions

View File

@@ -420,9 +420,11 @@ class NotifyReceiver : BroadcastReceiver() {
// First, try to find schedule by the provided stableScheduleId // First, try to find schedule by the provided stableScheduleId
var scheduleToUpdate = db.scheduleDao().getById(stableScheduleId) var scheduleToUpdate = db.scheduleDao().getById(stableScheduleId)
// If not found by ID, find the existing enabled notify schedule (for rollover scenarios) // If not found by ID, only use "first enabled notify" fallback when this is NOT
// getNotificationStatus() finds schedules with kind="notify" && enabled=true // a rollover id (daily_rollover_*). Rollover work may use a different notification_id
if (scheduleToUpdate == null) { // (e.g. from recovery); updating the app's schedule row here would overwrite
// nextRunAt with the rollover time and can leave the app's alarm in a bad state.
if (scheduleToUpdate == null && !stableScheduleId.startsWith("daily_rollover_")) {
val allSchedules = db.scheduleDao().getAll() val allSchedules = db.scheduleDao().getAll()
scheduleToUpdate = allSchedules.firstOrNull { it.kind == "notify" && it.enabled } scheduleToUpdate = allSchedules.firstOrNull { it.kind == "notify" && it.enabled }
} }

View File

@@ -0,0 +1,37 @@
# Consuming App Notes — Android Daily Notifications
Brief notes for apps that integrate the daily notification plugin on Android.
---
## Double schedule (rapid successive calls)
If your app calls `scheduleDailyNotification` twice in quick succession (e.g. within a few hundred ms) for the same reminder, the second call cancels the alarm just set and reschedules. On some devices or OEMs this can contribute to the alarm not firing.
**Recommendation:** Debounce or guard in the edit-reminder success path so you only call `scheduleDailyNotification` once per user action (e.g. wait for the first call to resolve before allowing another, or coalesce rapid calls).
---
## Alarm scheduled but not firing (e.g. 6:04)
When logs show "Scheduling OS alarm" and "Updated schedule in database" but the notification never appears:
1. **Confirm the broadcast is delivered**
Run logcat including the receiver:
```bash
adb logcat -v time -s DNP-SCHEDULE:V DailyNotificationWorker:V DailyNotificationReceiver:V
```
At the scheduled time, check whether `DailyNotificationReceiver` logs anything. If the Receiver runs, the issue is downstream (WorkManager / display). If it does not run, the OS did not deliver the alarm (Doze, OEM, or alarm replacement).
2. **Avoid double schedule**
Ensure the app is not calling `scheduleDailyNotification` twice in quick succession for the same reminder (see above).
3. **Plugin fix (v1.1.5+)**
The plugin no longer overwrites the apps schedule row when handling rollover work that uses a `daily_rollover_*` id, so the apps `nextRunAt` stays correct after a notification fires.
---
## References
- [ACTION_PLAN_INTEGRATION_FIXES.md](./ACTION_PLAN_INTEGRATION_FIXES.md) — plugin and app integration checklist
- [CONSUMING_APP_OPTIONAL_ANDROID_ID_CLEANUP.md](./CONSUMING_APP_OPTIONAL_ANDROID_ID_CLEANUP.md) — optional cleanup of stale schedule rows