Fast-drain alert: warn when battery drains abnormally fast for a sustained time (#109)#121
Merged
Merged
Conversation
16c3619 to
225d49a
Compare
…ained time (#109) Adds the alerting layer on top of #108's smoothed drain rate. Needs no per-app data — it watches the whole-battery %/h — so it is buildable by a normal app. - FastDrainDetector fires when the (already smoothed) drain rate stays at/above the user's limit for a sustained window (default 5 min); a brief spike breaks the streak. The decision core is pure and unit-tested; the streak/hysteresis state is persisted so it survives process restarts (like BatteryHealthTracker). - Context-aware repeats with no new timer/alarm/wakelock (reminders piggyback the battery broadcasts): warn once per episode while the screen is on and unlocked, but remind every 15 min (default) while off/locked — the highest-value case. Re-arms only after the rate drops back below the limit, like the temperature alert's hysteresis. - Only while discharging. When #108 can't produce a %/h yet (warm-up), the alert sleeps rather than firing — fail-quiet, no false alarms. - Transparent message stating the real measured rate, how long it's been sustained, and the user's own limit. Own notification channel + id (never replaces a level/temperature alert); respects quiet-hours / silent / vibrate. - The limit is the single setting shared with #108's red colour. Adds enable + sustained-minutes + locked-reminder-gap settings. New strings drafted in Arabic. Screen/lock state read via PowerManager.isInteractive + KeyguardManager (no special permission). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
d357c6b to
d111454
Compare
- Bound the streak's survival across data gaps: the sleep-on-no-rate rule kept a streak alive indefinitely, so the first tick after a long gap (process death, doze) could alert immediately with a "sustained for N minutes" claim built on unobserved time. The state now records when the rate was last seen at/above the limit; a gap longer than the rate's own smoothing window (BatteryRateTracker.WINDOW_MS) lapses the episode and starts fresh. - Persist the streak state only when it changed: the common case (discharging below the limit) re-decided CLEARED and rewrote identical values into SharedPreferences on every battery broadcast. - Clamp the sustained-minutes and reminder-gap preferences to their slider ranges on read, mirroring clampDrainLimit: a corrupt 0-minute sustained window would have turned the alert into the spike alarm the design forbids. Tests: lapse-rule scenarios (long gap restarts the episode, boundary at exactly WINDOW_MS, re-alert after a lapsed episode), observation refresh, and timing clamps; existing scenarios updated for the new lastSeenAbove field. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012LC6Qt7Nr8MpJHXuszizNF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #109.
What
The alerting layer on top of #108's smoothed drain rate. No per-app data needed (it watches the whole-battery %/h), so it's fully buildable by a normal app.
FastDrainDetector— fires when the (already smoothed) drain rate stays at/above the user's limit for a sustained window (default 5 min). A brief spike breaks the streak. The decision core is a pure, unit-tested function; the streak/hysteresis state is persisted so it survives process restarts (likeBatteryHealthTracker).PowerManager.isInteractive+KeyguardManager(no special permission).Testing
FastDrainDetectorTestcovers the puredecide()core: streak start, not-yet-sustained, first-alert-fires-even-when-used, once-vs-reminder split, gap gating, sleep-on-no-rate, and re-arm-then-re-alert.testDebugUnitTest+lintDebuggreen; installed on the Kirin Mate 10 Pro.On-device TODO (maintainer)
adb shell dumpsys battery unplug+set level <n>stepping down, and verify: the streak (fires only after the sustained window), the once-vs-reminder split (lock the phone to get reminders), and that a brief spike stays silent. Restore withadb shell dumpsys battery reset.Needs review
values-ar) are my drafts.