Skip to content

Rewrite the gauge from scratch as a reusable HorseshoeProgressBar with flow animations#145

Merged
almothafar merged 5 commits into
masterfrom
feat/144-gauge-rewrite-wave
Jul 13, 2026
Merged

Rewrite the gauge from scratch as a reusable HorseshoeProgressBar with flow animations#145
almothafar merged 5 commits into
masterfrom
feat/144-gauge-rewrite-wave

Conversation

@almothafar

@almothafar almothafar commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Closes #144.

Replaces the third-party-derived CircularProgressBar (ylyc/circular_progress_bar, see #141) with HorseshoeProgressBar, written from scratch for this project — the widget is now original work under the project's GPLv3, so all Apache attribution artifacts are removed.

Same look

270° horseshoe open at the bottom, same colors/stroke/shadow, auto-fitting title + status text, ring color driven by the critical/warning thresholds.

Generic, reusable widget

Named for its shape, not this app's use case — it fits anything that fills and drains. The API is battery-agnostic: setFlow(FILLING | FULL | DRAINING), setLevel, setThresholds(critical, warning), animateLevelTo(target, perStep). Colors, text sizes, and the screen-reader description template are gauge* attributes with built-in defaults — no app resources referenced. To reuse: copy the file + its declare-styleable block. GPLv3 header travels in the file. (MainActivity maps the OS battery status onto the flow states; the app-side style keeps the name Widget.App.BatteryGauge as the battery-flavored styling of the generic widget.)

Motion

Flow state Motion
FILLING (charging) highlight wave travels start → tip (~2.4s per trip)
DRAINING (on battery) slower wave tip → start (~8s); at/below critical: urgent breathing pulse instead
FULL (on charger) one gentle pulse every 3s; truly idle (zero redraws) between pulses

setWaveEnabled(false) remains for battery-sensitive hosts: filling falls back to a breathing pulse, draining goes still. All motion pauses when the activity is backgrounded.

Performance

  • Hardware-accelerated rendering on Android 9+ (arc shadows are GPU-capable there); only Android 8 keeps the software-layer fallback. Fixes the idle-temperature rise observed on the Mate 10 Pro.
  • Wave frames that would not move the highlight a visible step are skipped; the full-state pulse draws nothing at all between pulses.

Removed

CircularProgressBar.java, THIRD-PARTY-NOTICES.md, licenses/Apache-2.0.txt, README acknowledgement, unused Holo style variant.

Verified on device (Mate 10 Pro, Android 10)

  • Wave frames move along the arc between screenshots.
  • Full-on-charger: 10-frame burst shows identical resting frames, distinct frames only during the periodic pulse — confirming zero redraws at rest.
  • Re-verified after the rename: gauge renders identically.

🤖 Generated with Claude Code

almothafar and others added 4 commits July 13, 2026 09:14
…144)

Replace the third-party-derived CircularProgressBar (ylyc/circular_progress_bar,
see #141) with BatteryGaugeView, written from scratch for this project, so the
gauge is original work under the project's GPLv3 and the Apache attribution
artifacts can go away.

New widget, same look: a View (not ProgressBar) drawing the same 270-degree
horseshoe (gap parameterized instead of magic start/sweep), same colors, stroke,
shadow, auto-fitting title/status text, threshold-driven ring color, and the
critical breathing pulse. Fresh architecture: instance thresholds set in one
call, dimension-typed gauge* attributes replacing the cpb_* integers, geometry
computed in onSizeChanged, one motion animator driven by a small state enum,
and a leaner API (setLevel/setThresholds/animateLevelTo with an IntConsumer
step callback instead of a 3-method listener).

New behavior: a soft highlight wave travels along the lit arc - start to tip
while charging (2.4s trip), slower tip to start while discharging (8s trip).
Critical (not charging) keeps the breathing pulse instead. All motion pauses
when the activity is backgrounded (same hooks as before, renamed
pauseAnimations/resumeAnimations).

Removed: CircularProgressBar.java, THIRD-PARTY-NOTICES.md,
licenses/Apache-2.0.txt, the README acknowledgement, and the unused
Widget.ProgressBar.Holo.CircularProgressBar style.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On-device testing (Mate 10 Pro) showed the always-on wave kept the CPU busy:
the gauge sits on a software layer (arc shadows need one) and the wave
invalidates every frame, so idle temperature rose noticeably. It also ran the
reverse wave while the battery was full on the charger, which reads wrong.

The wave stays in BatteryGaugeView as a documented capability for screens that
want it, but is now opt-in via setWaveEnabled() and off by default. Without it
the gauge matches the old widget's profile exactly:

- charging: gentle 2s breathing pulse (glow alpha 40)
- critical, not charging: faster 1.5s breathing (glow alpha 60)
- anything else (full, idle, discharging): completely still - zero redraws

Verified on device: two gauge-region screenshots 2s apart hash identical while
full on the charger.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nder (#144)

Motion now follows a three-state power model (setPowerState replaces the
boolean setCharging; MainActivity maps the OS battery status onto it):

- CHARGING: highlight wave travels start -> tip (~2.4s trips)
- ON_BATTERY: slower wave tip -> start (~8s trips); at/below critical the
  urgent breathing pulse takes precedence
- FULL (on charger): one gentle pulse every 3s; between pulses the update
  listener bails out before invalidating, so the view truly draws nothing

Waves are on by default now; setWaveEnabled(false) remains for
battery-sensitive hosts (charging falls back to the breathing pulse and
discharging goes still).

Rendering cost: hardware-accelerated on Android 9+ (Skia hardware canvases
draw arc shadows fine; only Android 8 keeps the software-layer fallback), and
wave frames that would move the band less than a visible step are skipped.
This addresses the idle-temperature rise seen on the Mate 10 Pro with the
always-software-layer version.

Reusability: the class no longer references app colors or strings - ring/track/
text colors and the screen-reader description template are now gauge*
attributes with built-in defaults (the app style supplies its palette and
localized string), so the file plus its declare-styleable block can be dropped
into any project. GPLv3 header already travels in the file.

Verified on device (full on charger): a 10-frame burst shows identical resting
frames with distinct frames only during the periodic pulse.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#144)

The widget is a generic fill/drain progress gauge - nothing in it is battery
specific - so give it a name that says what it looks like rather than what this
app uses it for. Along with the class rename:

- Power { CHARGING, FULL, ON_BATTERY } becomes Flow { FILLING, FULL, DRAINING }
  and setPowerState() becomes setFlow(); MainActivity maps battery status onto
  the generic flow states.
- Battery-flavored constant names and doc wording genericized; the default
  screen-reader template becomes "Level at %1$d percent" (the app style still
  supplies the localized battery string).
- The styleable follows the class name (HorseshoeProgressBar); the app-side
  style stays Widget.App.BatteryGauge since it is the battery-flavored styling
  of the generic widget.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@almothafar almothafar changed the title Rewrite the battery gauge from scratch; add directional charge wave Rewrite the gauge from scratch as a reusable HorseshoeProgressBar with flow animations Jul 13, 2026
…es (#144)

HorseshoeProgressBar is meant to be copied into other projects, but its header
only named the app, so a copied file would lose its provenance. Name the widget
itself, link the source repo and author, and ask copiers to keep the header
intact since it is the file's GPLv3 license and attribution notice.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@almothafar almothafar merged commit 2ec4414 into master Jul 13, 2026
2 checks passed
@almothafar almothafar deleted the feat/144-gauge-rewrite-wave branch July 13, 2026 08:18
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.

Rewrite the battery gauge from scratch (drop third-party lineage) + directional wave animation

1 participant