Skip to content

Repository files navigation

Suspender

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.

What is interesting about it

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.

Model notes

  • 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.

Stack

Kotlin · Jetpack Compose · Material 3 · Room/SQLite · Hilt · KSP · Vico charts · Gradle version catalog. minSdk 31, compileSdk 37.

Build

./gradlew :app:assembleDebug

Signing config is read from keystore.properties, which is not in the repository. Without it the build still runs; the APK is simply unsigned.

Status

In daily use for my own training. Not on Play, not intended for it.

About

Native Android gym tracker with a real statistics engine: weighted regression, prediction bands, and population strength standards, all computed on-device with no permissions at all.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages