Problem
NotificationService.java`` is ~1,200 lines doing four jobs (SRP): channel registry, notification building/dispatch, quiet-hours + silent-mode policy, and sound/vibration playback — plus static lifecycle state (soundExecutor, `cachedLauncherIcon`).
Suggested split (seams already exist in the code)
DRY items to fold in (all in this file)
(permalinks pinned at f4460ac; grep the method names if lines have moved)
- The builder chain is written three times:
configureNotificationContent (760-768), `sendQuietHoursAwareAlert` ([430-439](https://github.com/almothafar/SimpleBatteryNotifier/blob/f4460acc3ba99320c3efe310fb51456af0939ab0/app/src/main/java/com/almothafar/simplebatterynotifier/service/NotificationService.java#L430-L439)),`` postChargeNotification (297-307)`` — same setTicker/setContentTitle/setContentText/setWhen/setLargeIcon/setContentIntent/setVisibility/BigTextStyle sequence. `AlertSpec` is already the right abstraction; route the level alerts and the charge notification through it too.
- Charge-style resolution duplicated:
notifyChargeConnected (173-186) vs `sendSlowChargeWarning` ([381-393](https://github.com/almothafar/SimpleBatteryNotifier/blob/f4460acc3ba99320c3efe310fb51456af0939ab0/app/src/main/java/com/almothafar/simplebatterynotifier/service/NotificationService.java#L381-L393))`` — extract one "deliver per the user's charge style" helper (read pref → resolve → none/toast/notification branch).
- Vibration pattern duplicated:
VIBRATION_PATTERN (line 78) vs the inline copy in `SystemService.vibratePhone` ([SystemService.java:510](https://github.com/almothafar/SimpleBatteryNotifier/blob/f4460acc3ba99320c3efe310fb51456af0939ab0/app/src/main/java/com/almothafar/simplebatterynotifier/service/SystemService.java#L510))`` — one constant, so channel vibration and manual silent-mode vibration can't drift.
- Dead lifecycle API:
shutdown() (565-575) and `releaseCachedBitmap()` ([583-588](https://github.com/almothafar/SimpleBatteryNotifier/blob/f4460acc3ba99320c3efe310fb51456af0939ab0/app/src/main/java/com/almothafar/simplebatterynotifier/service/NotificationService.java#L583-L588))`` are never called from anywhere (there is no Application class). Either add the Application hook their javadoc promises, or delete them — record the decision.
Sequencing
Land the small targeted fixes first so this refactor is a pure move: #153 (channel versioning), #154 (time parse), #155 (charge notification ID), #160 (type enum), #165 (channel name strings). This issue then reshapes without changing behavior.
Acceptance criteria
Problem
NotificationService.java`` is ~1,200 lines doing four jobs (SRP): channel registry, notification building/dispatch, quiet-hours + silent-mode policy, and sound/vibration playback — plus static lifecycle state (
soundExecutor, `cachedLauncherIcon`).Suggested split (seams already exist in the code)
NotificationChannels—createNotificationChannels/createChannelIfNotExists/createSilentChannelIfNotExists/refreshAlertChannels/channelFor, plus the versioned-ID scheme from Vibrate toggle can't take effect: deleted notification channels resurrect with their old settings #153.QuietHours(policy) —isWithinNotificationWindow/isWithinTime(Range)/alertsAllowedNow/shouldIgnoreSilentMode, plus the parse hardening from Corrupt quiet-hours time preference crashes every alert — unhandled parse exceptions on the broadcast path #154. Already mostly pure.AlertSounds—playAlarm+soundExecutor+ DND check; owns its executor lifecycle.AlertSpec(see below).DRY items to fold in (all in this file)
(permalinks pinned at
f4460ac; grep the method names if lines have moved)configureNotificationContent(760-768),`sendQuietHoursAwareAlert` ([430-439](https://github.com/almothafar/SimpleBatteryNotifier/blob/f4460acc3ba99320c3efe310fb51456af0939ab0/app/src/main/java/com/almothafar/simplebatterynotifier/service/NotificationService.java#L430-L439)),``postChargeNotification(297-307)`` — samesetTicker/setContentTitle/setContentText/setWhen/setLargeIcon/setContentIntent/setVisibility/BigTextStylesequence. `AlertSpec` is already the right abstraction; route the level alerts and the charge notification through it too.notifyChargeConnected(173-186)vs `sendSlowChargeWarning` ([381-393](https://github.com/almothafar/SimpleBatteryNotifier/blob/f4460acc3ba99320c3efe310fb51456af0939ab0/app/src/main/java/com/almothafar/simplebatterynotifier/service/NotificationService.java#L381-L393))`` — extract one "deliver per the user's charge style" helper (read pref → resolve → none/toast/notification branch).VIBRATION_PATTERN(line 78)vs the inline copy in `SystemService.vibratePhone` ([SystemService.java:510](https://github.com/almothafar/SimpleBatteryNotifier/blob/f4460acc3ba99320c3efe310fb51456af0939ab0/app/src/main/java/com/almothafar/simplebatterynotifier/service/SystemService.java#L510))`` — one constant, so channel vibration and manual silent-mode vibration can't drift.shutdown()(565-575)and `releaseCachedBitmap()` ([583-588](https://github.com/almothafar/SimpleBatteryNotifier/blob/f4460acc3ba99320c3efe310fb51456af0939ab0/app/src/main/java/com/almothafar/simplebatterynotifier/service/NotificationService.java#L583-L588))`` are never called from anywhere (there is noApplicationclass). Either add theApplicationhook their javadoc promises, or delete them — record the decision.Sequencing
Land the small targeted fixes first so this refactor is a pure move: #153 (channel versioning), #154 (time parse), #155 (charge notification ID), #160 (type enum), #165 (channel name strings). This issue then reshapes without changing behavior.
Acceptance criteria
shutdown/releaseCachedBitmapwired to a real lifecycle or removed