A native Android gym tracker with a real statistics engine behind it. Local-only: the manifest declares no permissions at all, not even internet, so the data cannot leave the phone, because there is no code path for it to leave by.
Built for one user (me), on a Pixel, without Android Studio.
The projection engine. Lifting progress is not linear and a straight line through a training log flatters you. Suspender fits a saturating log-time curve with recency-weighted least squares and returns an 80% prediction band rather than a single number:
- Anchored at the latest session, not at the middle of the data. The first weeks of a movement are spent finding a working weight, not getting stronger, and a line fitted through that ramp puts "today" well below the weight that was just lifted. The interval opens from the anchor at the width session noise justifies, so pinning to one session does not pretend that session was exact.
- Student-t prediction intervals, priced by how far forward you are asking: a slope measured across ten days is not ten days' worth of evidence about the next six months.
- Population priors. Below three points there is no residual degree of freedom and so no interval. But published strength standards, placed by sex, bodyweight and training age, still say something about a movement you have only just started. Movements with no table of their own borrow the shape the tables share.
- The chart states its own limits: a projection, not a prediction; deloads, injuries and programme changes are outside anything it can see.
The analytics are a separate Gradle module with no Android dependency (its only dependency is JUnit), so the maths runs and is tested on the JVM: 282 tests across 28 files covering regression, projection, goal ETAs, one-rep-max estimation, effective load, population placement and roll-ups.
- Composable exercises: weight / reps / time / distance, mixed per movement.
- Per-side loads are entered per hand; volume metrics use the doubled total.
- Bodyweight lifts resolve against a frozen weigh-in snapshot times a per-exercise factor, plus any added weight, so a pull-up's history does not silently move when your bodyweight does.
- 1RM is Epley by default and switchable; Brzycki is undefined past 36 reps.
- Dates are epoch days, so a set logged at 23:50 stays on that day no matter where the phone is when the chart is drawn.
Kotlin · Jetpack Compose · Material 3 · Room/SQLite · Hilt · KSP · Vico charts ·
Gradle version catalog. minSdk 31, compileSdk 37.
./gradlew :app:assembleDebugSigning config is read from keystore.properties, which is not in the
repository. Without it the build still runs; the APK is simply unsigned.
In daily use for my own training. Not on Play, not intended for it.