Rewrite the gauge from scratch as a reusable HorseshoeProgressBar with flow animations#145
Merged
Merged
Conversation
…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>
…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>
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 #144.
Replaces the third-party-derived
CircularProgressBar(ylyc/circular_progress_bar, see #141) withHorseshoeProgressBar, 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 aregauge*attributes with built-in defaults — no app resources referenced. To reuse: copy the file + itsdeclare-styleableblock. GPLv3 header travels in the file. (MainActivitymaps the OS battery status onto the flow states; the app-side style keeps the nameWidget.App.BatteryGaugeas the battery-flavored styling of the generic widget.)Motion
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
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)
🤖 Generated with Claude Code