# Notification Feature Audit — Tasklist

**Source:** Full codebase trace of notification system  
**Date:** June 2026  
**Status legend:** `[ ]` Not started · `[~]` In progress · `[x]` Done · `[-]` Skipped

---

## Defects (Bugs)

### D1. Notification ID Range Collision — Adhan vs Report Reminders `[SEVERITY: High]`
- [x] **D1.1** Move report reminder ID range from `700000+` to `2000000+` in `_reportReminderId()`
- [x] **D1.2** Update `_cancelPrayerPending()` range guard to exclude the new report range explicitly
- [x] **D1.3** Verify adhan IDs (100k–800k), iqamah IDs (800k–1.5M), report IDs (2M+), non-prayer IDs (900k–980k) are all disjoint

### D2. Streak Risk Only Checks Dzikir Petang, Not Pagi `[SEVERITY: Medium]`
- [x] **D2.1** Fix `emitStreakRiskIfNeeded` to check both `!pagi` and `!petang` in dzikir risk logic
- [x] **D2.2** Emit separate inbox items for pagi vs petang dzikir risk with correct deeplinks

### D3. `_cancelPrayerPending` Cancels Non-Prayer Notifications Too `[SEVERITY: Medium]`
- [x] **D3.1** Narrow the ID range filter to only cancel adhan (100k–799k), iqamah (800k–1.5M), and report (2M+) IDs
- [x] **D3.2** Exclude non-prayer range (900k–980k) from cancellation

### D4. Notification Tap Routes All Non-Prayer to `/notifications` Instead of Deep Link `[SEVERITY: Medium]`
- [x] **D4.1** Update `routeForNotificationPayload` to parse deeplink from payload for `streak_risk` type
- [x] **D4.2** Include deeplink in notification payload through `_pushNonPrayer` → `showNonPrayerAlert` chain

### D5. Timezone Not Updated on Device TZ Change `[SEVERITY: Medium]`
- [x] **D5.1** Add `reconfigureTimeZoneIfNeeded()` method to detect and apply timezone changes
- [x] **D5.2** Reset `_lastSyncSignature` on TZ change to force prayer notification resync

### D6. `_handleLaunchNotification` May Fire Before Router is Ready `[SEVERITY: Low]`
- [x] **D6.1** Defer launch notification routing — store pending route, consume from `AppState.initState` with 800ms delay

---

## Gaps (Missing or Incomplete)

### G1. No Settings UI for Notification Preferences `[SEVERITY: High]`
- [x] **G1.1** Settings UI already existed — added missing `streakRiskEnabled` toggle to notification group
- [x] **G1.2** Added `weeklySummaryEnabled` toggle to notification group
- [x] **G1.3** All other notification settings (alerts, inbox, checklist reminder, quiet hours, push cap) were already present

### G2. No Device Reboot Reschedule `[SEVERITY: High]`
- [x] **G2.1** Verified `RECEIVE_BOOT_COMPLETED` permission in AndroidManifest.xml — already present
- [x] **G2.2** Verified `ScheduledNotificationReceiver` and `ScheduledNotificationBootReceiver` — already declared
- [x] **G2.3** `flutter_local_notifications` v21 handles reboot natively; `workmanager` periodic task resumes via `ExistingPeriodicWorkPolicy.update`

### G3. `mirrorAdzanToInbox` Setting Exists But Never Used `[SEVERITY: Medium]`
- [x] **G3.1** Removed unused `mirrorAdzanToInbox` field from `AppSettings` and generated adapter
- [x] **G3.2** Removed legacy `removeByType('prayer')` calls from `main.dart` and `notification_center_screen.dart`

### G4. No Analytics for `notif_push_opened` `[SEVERITY: Low]`
- [x] **G4.1** Added `notif_push_opened` tracking in `_handleNotificationResponse` (foreground) and `consumePendingLaunchRoute` (launch)

### G5. No Analytics for `notif_settings_changed` `[SEVERITY: Low]`
- [x] **G5.1** Added `notif_settings_changed` tracking in notification bell quick actions toggle

---

## Opportunities (Enhancements)

### O1. Rich Notification Actions — "Sudah Sholat" Button on Report Reminders
- [x] **O1.1** Added `AndroidNotificationAction` with `action_prayed` / "Sudah Sholat" button to `_scheduleShalatReportReminder`
- [x] **O1.2** Background handler (`notificationTapBackgroundHandler`) opens Hive and logs `ShalatLog(completed: true)` via `_markPrayedFromBackground`
- [x] **O1.3** Foreground handler (`_handleNotificationResponse`) logs prayer via `_markPrayedFromForeground`
- [x] **O1.4** Added `_resolvePrayerKeyFromName` to map display names back to canonical keys in background isolate

### O2. Notification Permission Check on App Resume via WidgetsBindingObserver
- [x] **O2.1** Added `_checkNotificationPermissionOnResume()` with 30-second throttle to `_AppState.didChangeAppLifecycleState`
- [x] **O2.2** Re-checks notification permissions and emits warnings via `emitPermissionWarningsIfNeeded` on resume

### O2b. Fix Stretched Notification Icon
- [x] **O2b.1** Created `@drawable/ic_notification` — white crescent moon vector drawable (Android notification-safe)
- [x] **O2b.2** Changed `AndroidInitializationSettings` from `@mipmap/ic_launcher` to `@drawable/ic_notification`
- [x] **O2b.3** Added `icon: '@drawable/ic_notification'` to all 4 notification channels

### O3. Add Expired Item Cleanup to Background Sync
- [x] **O3.1** Added `removeExpired()` call in `BackgroundSyncService.runSyncPass()`

### O4. Haptic Feedback on Quick Actions
- [x] **O4.1** Added `HapticFeedback.selectionClick()` to all three notification bell quick action taps

---

## Progress Tracker

| Category | Total | Done | Skipped | Remaining |
|----------|-------|------|---------|------------|
| Defects (D1–D6) | 11 | 11 | 0 | 0 |
| Gaps (G1–G5) | 10 | 10 | 0 | 0 |
| Opportunities (O1–O4) | 9 | 9 | 0 | 0 |
| **TOTAL** | **30** | **30** | **0** | **0** |

---

## Files Changed

| File | Changes |
|------|---------|
| `lib/data/services/notification_service.dart` | D1: ID range fix, D3: cancel range fix, D4: payload routing with deeplink, D5: TZ reconfig, D6: deferred launch routing, O1: rich notification action + background handler, O2b: icon fix |
| `lib/data/services/notification_event_producer_service.dart` | D2: pagi+petang dzikir streak risk, D4: deeplink threading |
| `lib/core/widgets/notification_bell_button.dart` | G5: analytics tracking, O4: haptic feedback |
| `lib/data/services/background_sync_service.dart` | O3: expired inbox cleanup |
| `lib/features/settings/presentation/settings_screen.dart` | G1: streak risk + weekly summary toggles |
| `lib/data/local/models/app_settings.dart` | G3: removed `mirrorAdzanToInbox` field |
| `lib/data/local/models/app_settings.g.dart` | G3: removed field 32 from adapter |
| `lib/main.dart` | G3: removed legacy `removeByType('prayer')` |
| `lib/features/notifications/presentation/notification_center_screen.dart` | G3: removed legacy cleanup |
| `lib/app/app.dart` | D6: consume pending launch route on init, O2: permission check on resume |
| `android/app/src/main/res/drawable/ic_notification.xml` | O2b: white crescent moon vector drawable for notification icon |
