dmytrodanylyk/circular-progress-button
Android Circular Progress Button
Healthy across all four use cases
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 2d ago
- ✓3 active contributors
- ✓MIT licensed
Show all 7 evidence items →Show less
- ✓CI configured
- ⚠Small team — 3 contributors active in recent commits
- ⚠Concentrated ownership — top contributor handles 57% of recent commits
- ⚠No test directory detected
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/dmytrodanylyk/circular-progress-button)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/dmytrodanylyk/circular-progress-button on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: dmytrodanylyk/circular-progress-button
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/dmytrodanylyk/circular-progress-button shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- Last commit 2d ago
- 3 active contributors
- MIT licensed
- CI configured
- ⚠ Small team — 3 contributors active in recent commits
- ⚠ Concentrated ownership — top contributor handles 57% of recent commits
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live dmytrodanylyk/circular-progress-button
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/dmytrodanylyk/circular-progress-button.
What it runs against: a local clone of dmytrodanylyk/circular-progress-button — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in dmytrodanylyk/circular-progress-button | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 32 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of dmytrodanylyk/circular-progress-button. If you don't
# have one yet, run these first:
#
# git clone https://github.com/dmytrodanylyk/circular-progress-button.git
# cd circular-progress-button
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of dmytrodanylyk/circular-progress-button and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "dmytrodanylyk/circular-progress-button(\\.git)?\\b" \\
&& ok "origin remote is dmytrodanylyk/circular-progress-button" \\
|| miss "origin remote is not dmytrodanylyk/circular-progress-button (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "library/src/main/java/com/dd/CircularProgressButton.java" \\
&& ok "library/src/main/java/com/dd/CircularProgressButton.java" \\
|| miss "missing critical file: library/src/main/java/com/dd/CircularProgressButton.java"
test -f "library/src/main/java/com/dd/StateManager.java" \\
&& ok "library/src/main/java/com/dd/StateManager.java" \\
|| miss "missing critical file: library/src/main/java/com/dd/StateManager.java"
test -f "library/src/main/java/com/dd/MorphingAnimation.java" \\
&& ok "library/src/main/java/com/dd/MorphingAnimation.java" \\
|| miss "missing critical file: library/src/main/java/com/dd/MorphingAnimation.java"
test -f "library/src/main/java/com/dd/CircularProgressDrawable.java" \\
&& ok "library/src/main/java/com/dd/CircularProgressDrawable.java" \\
|| miss "missing critical file: library/src/main/java/com/dd/CircularProgressDrawable.java"
test -f "library/build.gradle" \\
&& ok "library/build.gradle" \\
|| miss "missing critical file: library/build.gradle"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 32 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~2d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/dmytrodanylyk/circular-progress-button"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
An Android UI library that provides a reusable CircularProgressButton widget—a button that morphs from a rectangular shape into a circular progress indicator with animated stroke drawing. It handles state transitions (idle → progress → complete/error) with smooth animations, solving the problem of creating polished async operation feedback without building custom drawables and animations from scratch. Simple two-module structure: library/ contains the core drawable and animation classes (CircularProgressButton.java, CircularAnimatedDrawable.java, StateManager.java, MorphingAnimation.java) with res/ subdirs for colors and drawables; sample/ is a standalone demo app. No test directory visible, suggesting manual testing only.
👥Who it's for
Android app developers (targeting API 16+) who need visually appealing async operation feedback (form submissions, file uploads, network requests) without implementing complex custom drawable logic themselves.
🌱Maturity & risk
This is a legacy but stable library, last updated around 2014–2018 based on structure; it has 2.7K+ stars on GitHub (per JitPack badge context) and is published to Maven Central. CI/CD is configured (GitHub Actions workflows present: Android-CI.yml, Android-CI-release.yml) and ProGuard rules are documented. However, the README explicitly links to a newer maintained successor (android-morphing-button), indicating this repo is superseded but not abandoned—use it for existing projects but prefer the successor for new work.
Single-maintainer repository with no recent commits visible in the file list (last update appears to be pre-2020); no open PR/issue count is provided, but the explicit deprecation notice in README recommends migrating to dmytrodanylyk/android-morphing-button. Dependency surface is minimal (Java-only core, no external runtime deps beyond Android framework), reducing supply-chain risk. Main risk: stale maintenance means no fixes for Android 13+ compatibility issues or Material Design 3 support.
Active areas of work
Repository appears dormant. GitHub workflows (Android-CI.yml, Android-CI-release.yml) are in place for automation, and dependabot.yml indicates dependency updates are monitored, but no active feature development is evident from the file structure alone.
🚀Get running
git clone https://github.com/dmytrodanylyk/circular-progress-button.git
cd circular-progress-button
./gradlew build
To run the sample app: ./gradlew :sample:installDebug (requires an emulator or device). Gradle Wrapper (7.x implied by gradle-wrapper.properties) handles dependency download.
Daily commands:
Build the library: ./gradlew :library:assemble. Build and install the sample APK: ./gradlew :sample:installDebug. Open sample app in emulator—it demonstrates button state transitions triggered by tap.
🗺️Map of the codebase
library/src/main/java/com/dd/CircularProgressButton.java— Main public API and state machine for the button widget; all integrations depend on this classlibrary/src/main/java/com/dd/StateManager.java— Core state management logic that drives morphing animations between idle, progress, complete, and error stateslibrary/src/main/java/com/dd/MorphingAnimation.java— Animation framework that handles all morphing transitions; critical for smooth button state changeslibrary/src/main/java/com/dd/CircularProgressDrawable.java— Custom drawable for rendering circular progress; defines visual appearance during progress statelibrary/build.gradle— Library module build configuration; defines minimum API level, dependencies, and artifact publicationlibrary/src/main/res/values/styles.xml— Default theme and style attributes for CircularProgressButton; required for customizationlibrary/src/main/java/com/dd/CircularAnimatedDrawable.java— Animated drawable for continuous progress animation; handles the rotating spinner visual
🛠️How to make changes
Add a New Button State
- Define the new state constant in StateManager.java (e.g., CUSTOM_STATE = 5) (
library/src/main/java/com/dd/StateManager.java) - Add transition logic in StateManager.onStateChange() method to handle new state transitions (
library/src/main/java/com/dd/StateManager.java) - Create or modify a color selector XML file (e.g., cpb_custom_state_selector.xml) for the new state colors (
library/src/main/res/color/cpb_complete_state_selector.xml) - Update MorphingAnimation.java to define how the button morphs into/from the new state (
library/src/main/java/com/dd/MorphingAnimation.java) - Add a public method in CircularProgressButton.java to transition to the new state (e.g., setCustomState()) (
library/src/main/java/com/dd/CircularProgressButton.java)
Customize Button Colors and Styling
- Override color resources in your app's values/colors.xml (e.g., cpb_idle_color, cpb_progress_color) (
library/src/main/res/values/colors.xml) - Define custom style attributes in your app's values/attrs.xml extending CircularProgressButton theme (
library/src/main/res/values/styles.xml) - Apply the custom style to CircularProgressButton in your layout XML via style attribute (
sample/src/main/res/layout/ac_sample_3.xml) - Optionally override dimensions in your app's values/dimen.xml (button size, stroke width, etc.) (
library/src/main/res/values/dimen.xml)
Implement Custom Animation Timing
- Extend or modify MorphingAnimation.java to adjust animation duration and interpolators (
library/src/main/java/com/dd/MorphingAnimation.java) - Update dimen.xml with custom animation duration values (e.g., cpb_morphing_duration_ms) (
library/src/main/res/values/dimen.xml) - Modify CircularAnimatedDrawable.java if you need to change the progress spinner rotation speed (
library/src/main/java/com/dd/CircularAnimatedDrawable.java) - Test animation behavior in a sample Activity and verify state transitions are smooth (
sample/src/main/java/com/dd/sample/Sample1Activity.java)
Add ProGuard/R8 Obfuscation Support
- Review current ProGuard rules required for StrokeGradientDrawable reflection (
library/proguard-rules.txt) - If adding new reflective methods or callbacks, add corresponding -keepclassmembers rules (
library/proguard-rules.txt) - Update library/build.gradle consumerProguardFiles to ensure rules are consumed by dependent projects (
library/build.gradle) - Test obfuscated build in sample app to verify button functionality remains intact (
sample/build.gradle)
🔧Why these technologies
- Android View & Custom Drawable API — Provides efficient rendering of morphing button without third-party dependencies; leverages hardware acceleration
- ObjectAnimator & ValueAnimator — Built-in Android animation framework allows smooth property transitions and callbacks without external libraries
- State Machine Pattern (StateManager) — Enforces valid state transitions and prevents invalid state combinations (e.g., completing from idle state)
- Drawable Subclasses (CircularProgressDrawable, CircularAnimatedDrawable) — Separates visual rendering from view logic; enables reusable, composable animation primitives
- Gradle & JitPack — Simplifies distribution as Maven artifact; enables one-line Gradle integration for downstream projects
⚖️Trade-offs already made
-
Custom Java implementation instead of Kotlin
- Why: Codebase predates Kotlin adoption in Android; maintains backward compatibility with older projects
- Consequence: Verbose null checks and property getters/setters; no null safety or extension function benefits
-
Single monolithic CircularProgressButton class instead of composition
- Why: Simpler API surface for end-users; all button logic (state, animation, drawing) in one place
- Consequence: Large class file with mixed responsibilities (view logic + animation + drawable management)
-
No async task/coroutine wrapper built-in; state transitions are synchronous
- Why: Keeps library lightweight and UI-agnostic; delegates async concerns to caller
- Consequence: Integrators must manually manage background work and thread-safe state updates
-
Hard
- Why: undefined
- Consequence: undefined
🪤Traps & gotchas
ProGuard gotcha: Must add -keepclassmembers class com.dd.StrokeGradientDrawable { public void setStrokeColor(int); } to ProGuard config or release builds will break (reflection target). API level: minSdkVersion 16 is ancient; building on modern Android Gradle plugin (8.13.2) may require targetSdkVersion bump (currently likely API 31–32, should be 34+). No modern AndroidX: library predates androidx namespace, uses old support libs if any—migration path unclear. Deprecated successor: README points to android-morphing-button repo; features added there won't backport here.
🏗️Architecture
💡Concepts to learn
- Custom Drawable & Canvas Drawing — CircularProgressButton relies on overriding onDraw() in custom Drawable subclasses to paint circular stroke, progress arcs, and morphing shapes; essential to understand Canvas API and Drawable lifecycle
- ObjectAnimator & ValueAnimator — MorphingAnimation.java uses ObjectAnimator to animate property changes (radius, stroke width) and timing; understanding property animation framework is critical to extending animation behavior
- State Pattern (Enum-based) — StateManager.java implements the State pattern via Java enum to manage button lifecycle (IDLE, PROGRESS, COMPLETE, ERROR); each state maps to distinct visuals and transitions
- Android Color State Lists (ColorStateList) — cpb_*_state_selector.xml files use ColorStateList to apply different colors per button state (pressed, disabled, focused); key to understanding state-driven styling without code
- Interpolators & Easing Functions — MorphingAnimation likely uses AccelerateInterpolator, DecelerateInterpolator, or custom interpolators to control morphing speed curves; interpolator choice dramatically affects perceived smoothness
- ProGuard Reflection Preservation — StrokeGradientDrawable.setStrokeColor(int) is invoked via reflection by Android's property animation; ProGuard must preserve it or obfuscation breaks the library in release builds
🔗Related repos
dmytrodanylyk/android-morphing-button— Official successor repository with modern Android support, Material Design 3, and active maintenance—users should migrate herehdodenhof/CircleImageView— Similar-era circular Android widget; uses custom Drawable patterns applicable to understanding CircularProgressButton's drawing pipelinegoogle/material-components-android— Modern Material Design button components (ProgressButton-like patterns); reference for state-driven color/animation patterns in contemporary Androidsquare/okhttp— Common companion library for the network requests that trigger CircularProgressButton state transitions in real apps
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add AndroidX migration and modernize dependencies
The repository uses legacy Android support libraries and older Gradle/Kotlin versions (kotlin_version = '2.3.20' appears malformed, likely should be '1.x'). Modernizing to AndroidX, updating AGP to 8.x+ stable, and using current Kotlin stdlib would improve compatibility with modern Android projects and reduce dependency conflicts.
- [ ] Update library/build.gradle to use AndroidX artifacts (androidx.appcompat:appcompat instead of android.support.appcompat)
- [ ] Update gradle.properties and build.gradle with correct Kotlin version and stable AGP version
- [ ] Update all sample app dependencies in sample/build.gradle
- [ ] Migrate library/src/main/AndroidManifest.xml if using legacy support library components
- [ ] Test all 5 sample activities (Sample1Activity through Sample5Activity) with AndroidX builds
Add comprehensive unit tests for core animation classes
The library contains complex animation logic in CircularAnimatedDrawable.java, CircularProgressDrawable.java, MorphingAnimation.java, and StateManager.java, but there are no visible test files. Adding unit tests would catch regressions and improve code quality for contributors.
- [ ] Create library/src/test/java/com/dd/ directory structure
- [ ] Add StateManagerTest.java to test state transitions (idle, progress, complete, error states)
- [ ] Add MorphingAnimationTest.java to test animation timing and callbacks (OnAnimationEndListener)
- [ ] Add CircularProgressDrawableTest.java to test stroke gradient and drawing properties
- [ ] Include these tests in Android-CI.yml GitHub Actions workflow
Add instrumented tests for CircularProgressButton UI behavior
The main CircularProgressButton.java class controls user-facing behavior and state morphing, but no instrumented (Android) tests exist. These would validate button behavior across different Android API levels and configurations.
- [ ] Create library/src/androidTest/java/com/dd/ directory structure
- [ ] Add CircularProgressButtonTest.java with tests for: button state transitions, progress updates, animation completion callbacks
- [ ] Add layout inflation tests in library/src/androidTest/java/com/dd/ to verify XML attribute parsing from styles.xml and dimen.xml
- [ ] Configure library/build.gradle with androidTestImplementation dependencies (androidx.test:runner, espresso-core)
- [ ] Add androidTest execution step to Android-CI.yml workflow using Android emulator or Firebase Test Lab
🌿Good first issues
- Add JUnit4 unit tests for
StateManager.javastate transitions—currently no test dir exists, and state enum lacks coverage for all IDLE→PROGRESS→COMPLETE/ERROR paths - Update
library/build.gradletargetSdkVersion from ~31 to 34 and verify compatibility with Android 13+ behavior changes (scoped storage, runtime permissions if used) - Document the animation customization API with JavaDoc in
MorphingAnimation.java(duration, interpolator, morphing radius ratio)—currently undocumented and hard to customize
⭐Top contributors
Click to expand
Top contributors
- @hannesa2 — 57 commits
- @dependabot[bot] — 39 commits
- @gradle-update-robot — 4 commits
📝Recent commits
Click to expand
Recent commits
27350e2— Merge pull request #252 from dmytrodanylyk/dependabot/gradle/org.jetbrains.kotlin-kotlin-gradle-plugin-2.3.20 (hannesa2)a6cd275— Merge pull request #253 from dmytrodanylyk/dependabot/gradle/gradle-wrapper-9.4.1 (hannesa2)775d8f7— Bump gradle-wrapper from 9.3.1 to 9.4.1 (dependabot[bot])07c7199— Bump org.jetbrains.kotlin:kotlin-gradle-plugin from 2.3.0 to 2.3.20 (dependabot[bot])3d4d038— Merge pull request #247 from dmytrodanylyk/dependabot/gradle/gradle-wrapper-9.3.1 (hannesa2)4e4d408— Bump gradle-wrapper from 9.2.1 to 9.3.1 (dependabot[bot])fadac55— Merge pull request #242 from dmytrodanylyk/gradlew-update-9.2.1 (hannesa2)996755f— Merge pull request #233 from dmytrodanylyk/dependabot/github_actions/actions/setup-java-5 (hannesa2)8f669a7— Bump actions/setup-java from 4 to 5 (dependabot[bot])cbf0ac0— Merge pull request #241 from dmytrodanylyk/dependabot/github_actions/actions/checkout-6 (hannesa2)
🔒Security observations
The codebase has moderate security concerns primarily related to dependency management and build configuration. Key issues include a potentially incorrect Kotlin version format, outdated Gradle plugin, use of snapshot repositories, and unpinned dependency versions. The library itself (UI component) has low injection risk, but the build infrastructure should be hardened. No hardcoded credentials or sensitive data were found in the analyzed files. Recommendation: Update build tools, remove snapshot repositories, pin dependency versions, and verify the Kotlin version is correct.
- High · Outdated Gradle Plugin —
build.gradle (classpath 'com.android.tools.build:gradle:8.13.2'). The project uses Android Gradle Plugin version 8.13.2, which may contain known security vulnerabilities. The current stable version should be regularly updated to receive security patches. Fix: Update to the latest stable version of Android Gradle Plugin. Check https://developer.android.com/studio/releases/gradle-plugin for current versions and migrate accordingly. - High · Insecure Kotlin Gradle Plugin Version —
build.gradle (ext.kotlin_version = '2.3.20'). Kotlin version 2.3.20 specified in build.gradle appears to be a non-standard version format. Standard Kotlin versions follow semantic versioning (e.g., 1.9.x). This may indicate a typo or use of an unofficial/unreleased version. Fix: Verify the correct Kotlin version from https://kotlinlang.org/releases.html. Update to an official stable release, typically in the 1.x or current major version series. - Medium · Insecure Maven Repository Configuration —
build.gradle (maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }). The build.gradle includes a snapshot repository from Sonatype (https://oss.sonatype.org/content/repositories/snapshots/) which serves pre-release and unstable dependencies. This increases the risk of using unvetted or vulnerable code. Fix: Remove snapshot repositories from production builds. If needed, isolate them to a separate build configuration or development-only profile. Use only stable releases from Maven Central or JCenter. - Medium · Missing Dependency Version Pinning —
README.md (implementation 'com.github.dmytrodanylyk:circular-progress-button:$latest'). The README recommends using '$latest' for dependency versions, which results in unpinned transitive dependencies. This can introduce unexpected breaking changes or security vulnerabilities. Fix: Always use explicit version numbers (e.g., 'com.github.dmytrodanylyk:circular-progress-button:1.3.8'). Use dependency locking or lock files to ensure reproducible builds. - Low · Incomplete ProGuard Configuration —
library/proguard-rules.txt. The ProGuard rule in library/proguard-rules.txt appears incomplete (cut off at '-keepclassmember'). Incomplete obfuscation rules may fail to properly protect the library code. Fix: Complete and review the ProGuard/R8 configuration file. Ensure all public API classes are properly kept while obfuscating internal implementation details. - Low · Deprecated JVM Utility Import —
build.gradle (import org.gradle.internal.jvm.Jvm). The build.gradle imports 'org.gradle.internal.jvm.Jvm', which is an internal Gradle API. Internal APIs are subject to change without notice and should not be relied upon. Fix: Remove or replace with official Gradle APIs. If Java version detection is needed, use org.gradle.api.JavaVersion or build script plugins instead of internal APIs.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.