Slow-charge warning: detect frayed cable / dying charger (#123)#150
Merged
Conversation
1e2715a to
3f5d8e3
Compare
01f533e to
2b01566
Compare
A single "Time to full · ~1h 20m" row directly below the charge rate, shown only while charging with a trustworthy rate and below the taper top (level < 99%) — hidden otherwise, so it never shows a garbage 0m. Capacity-free linear projection from #108's smoothed charge rate: (100 - level) / ratePercentPerHour, so it works on devices with an unreliable charge counter (Kirin) too. Deliberately simple: no taper modeling, precision is a non-goal (the OS owns the accurate figure). New pure BatteryRateTracker.estimateMinutesToFull + a Western-digit duration formatter. The estimate is unit-tested (level 99/100, rate 0/1, rounding); the formatter is tested under Robolectric (~1h 20m / ~45m, and Western digits even under the Arabic locale). New strings localized in values-ar (units stay Latin like the other rate values). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
While charging, the ongoing status notification now shows the estimated charge speed instead of %/h — "85% · Fast charging · ~18 W · 32.0 °C". Charger speed is what people think in while plugged in; %/h stays the right unit while discharging (unchanged). statusWithRate gains a charging-only branch that reads getChargeSpeed (CURRENT_NOW × voltage, from #122) and, when known, renders the tier + wattage via the existing tierLabelRes mapping. The tier label already reads as "… charging", so it replaces the plain "Charging" word rather than doubling it. Falls back cleanly to the existing %/h → mA → plain chain when the speed can't be estimated (e.g. a device without CURRENT_NOW), so the discharging hot path is untouched. Still gated by the existing show-rate preference — no new setting. One new string (Western digits, #96) localized in values-ar. The ChargeSpeed math is already covered by ChargeSpeedTest. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
New SlowChargeDetector warns once per charge session when the estimated charging power (CURRENT_NOW x voltage, from #122) stays below a conservative trickle floor (~2.5 W wired) for a sustained window (3 min) — a strong signal of a frayed cable, dirty/loose port, or dying charger. Mirrors FastDrainDetector (#109): a pure, unit-tested decide() core plus persisted streak/hysteresis state, evaluated on the battery broadcasts (no new timer). Three guards keep it nearly false-positive-free: - level < 80% (every charger deliberately tapers above ~80%) - status == CHARGING (thermal/battery-protect pauses report NOT_CHARGING and are skipped, not cleared) - wired only in v1 (wireless excluded) Plus the #121 observation-gap lapse rule so a data gap can't inflate the "sustained" claim. Re-arms when power recovers above the floor or on disconnect. Surfaced via the #122 charge-notification style (toast / notification / none); the notification path uses the shared quiet-hours helper (#131) on its own channel + id 1641991. Own enable toggle (default on) in the Charging settings category. Transparent message shows the measured wattage. New strings localized in values-ar. v2 (session-relative collapse) stays in #132, not built here. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
3f5d8e3 to
0181454
Compare
2b01566 to
6fddea8
Compare
Base automatically changed from
feat/125-charging-wattage-notification
to
master
July 14, 2026 23:05
4 tasks
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 #123. The headline feature of the charger-notification redesign.
What
Warns once per charge session when charging power stays abnormally low for a sustained time — a strong signal of a frayed cable, dirty/loose port, or dying charger.
How
New
SlowChargeDetector, a pattern-copy ofFastDrainDetector(#109): a pure, unit-testeddecide()core + persisted streak/hysteresis state, evaluated on the battery broadcasts that already flow (no new timer). The signal is the estimated wattage fromChargeSpeed(#122,CURRENT_NOW × voltage).Trigger: estimated power stays below a conservative trickle floor (~2.5 W wired) for a sustained window (3 min, a constant in v1) while charging below 80%.
Three guards keep it nearly false-positive-free (the traps from the issue):
NOT_CHARGING; skipped (they sleep, they dont clear the streak)Re-arms when power recovers above the floor or on disconnect.
Surfacing
Respects the #122 charge-notification style (
toast/notification/none); the notification path uses the shared quiet-hours helper (#131) on its own channel + id 1641991. New enable toggle (default on) in the Charging settings category. The message is transparent — it states the measured wattage — and deliberately cant name cable vs port vs brick (indistinguishable via public APIs).Verified
testDebugUnitTest+assembleDebug+lintDebugall green. NewSlowChargeDetectorTestmirrorsFastDrainDetectorTest(streak, once-per-session, re-arm, observation-gap lapse, floor boundary).On-device check (your step)
Trickier to see naturally. Easiest with a bad/thin cable, or simulate via⚠️ On a device that doesnt report a usable
adb shell dumpsys battery set(level < 80, status charging) — but note the wattage comes from a realCURRENT_NOW, whichdumpsysdoesnt fake, so a real weak cable is the true test.CURRENT_NOWwhile charging, the power is unknown and it correctly stays silent (fail-quiet).Not here
v2 — session-relative collapse (18 W → 4 W and stuck, which this absolute floor cant see) — stays in #132, which was waiting on real-device data from this v1.
🤖 Generated with Claude Code