Skip to content

Show battery charge/drain rate ("9%/h") in details table and status notification (#108)#120

Merged
almothafar merged 2 commits into
masterfrom
feat/108-drain-rate
Jul 11, 2026
Merged

Show battery charge/drain rate ("9%/h") in details table and status notification (#108)#120
almothafar merged 2 commits into
masterfrom
feat/108-drain-rate

Conversation

@almothafar

Copy link
Copy Markdown
Owner

Closes #108.

What

Adds a live, smoothed charge/drain rate (%/h) and a signed instantaneous current.

Testing

  • Unit tests for all pure helpers (rate computation & source selection, warm-up/static gates, plausibility ceiling, current sign & OEM-sign independence, windowing throttle/trim, serialization) — testDebugUnitTest green.
  • lintDebug clean; installed on the Kirin Mate 10 Pro.

On-device notes

  • Confirmed the Kirin charge-counter quirk on the Mate 10 Pro (dumpsys batteryCharge counter: 99000 ≈ 99 mAh), so getBatteryCapacity returns 0 and source B (level-delta) carries the rate there — as the issue anticipated.
  • Still to verify on-device (device was at 100%/plugged): whether BATTERY_PROPERTY_CURRENT_NOW is trustworthy on the Mate 10 Pro for the Current row, and the drain-rate row + colouring after unplugging under real use.

Needs review

  • Arabic strings (values-ar) are my drafts — please review the wording for the new rows/settings.

#109 (fast-drain alert) stacks on this branch and reuses the shared limit.

…tus notification (#108)

Adds a smoothed charge/drain rate (%/h) and a signed instantaneous current,
derived best-effort and degrading gracefully:

- BatteryRateTracker computes the rate over a trailing, persisted sample window
  (no polling timer — samples piggyback the existing battery broadcasts and the
  foreground refresh). Source A = averaged current / capacity; source B =
  level-change over time (capacity-free, so it still works on Kirin/HiSilicon
  devices where the charge counter is unreliable, #69/#94). Each output is gated
  on its own merit.
- Details table: "Drain rate"/"Charge rate" and "Current" rows at the top, each
  shown only when trustworthy. The drain rate turns amber near the user's limit
  and red at/above it (discharging only); charging is left uncoloured in v1.
- Ongoing notification: appends the rate to the status line
  ("85% . Discharging 9%/h . 32.0 C"), falling back to mA then the plain label.
  Gated by one new setting (default on).
- Settings: the shared "high drain" limit (default 20%/h) — the red line here and
  the fast-drain alert trigger in #109 — plus the notification toggle.
- CONTEXT.md records the vocabulary (Drain rate, Charge rate, Instantaneous
  current, Design capacity). New strings drafted in Arabic (values-ar) for review.

Pure helpers (rate computation, current sign, plausibility, windowing,
serialization) are unit-tested.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@almothafar almothafar force-pushed the feat/108-drain-rate branch from 16c3619 to 225d49a Compare July 11, 2026 19:35
Fixes from a review pass over the drain-rate feature:

- parseSamples: close the overflow hole in the digit-length guards. A
  10-digit int or 19-digit long passed validation but made parseInt/
  parseLong throw, crashing the receiver on every broadcast while the
  pref was corrupted. Ints are now range-checked through a long (the
  full int range must stay accepted: the "no current" sentinel is
  Integer.MIN_VALUE itself); longs are capped at 18 digits.
- record(): skip snapshots whose level/scale extras are invalid (-1),
  which read as 0% and would plant a bogus sample yielding a huge false
  drain rate against the next real reading.
- record(): persist only when the window or direction actually changed,
  instead of rewriting the preferences file on every ACTION_BATTERY_
  CHANGED (needless disk I/O in a battery app).
- getRate(): age-trim the loaded window so a stale pre-restart window
  can't briefly surface as a current rate at service start.
- Extract shared sameDirection/loadWindow/trimToWindow helpers so the
  record and read paths can't diverge on direction or freshness rules.
- Reuse the rate computed by record() in the ongoing notification
  instead of re-reading and re-parsing the window in the same broadcast.
- Clamp the stored drain limit to the slider's range on read; the
  bounds constants and pref_notification.xml now reference each other.

Tests: overflow entries skipped without throwing, int boundary
round-trip, invalid-level gating, age-trim (stale/future samples),
and limit clamping.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012LC6Qt7Nr8MpJHXuszizNF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Show battery charge/drain rate (e.g. "9%/h") in details table and status notification

2 participants