- Demande : « les traitements inactifs sont peu discernables — les placer à la fin (tout en bas), et peut-être les rendre plus distincts ». - Helper pur treatmentsForDisplay (tri STABLE : actifs d'abord, inactifs après, ordre relatif conservé dans chaque groupe). - LazyColumn en 3 sections : actifs / en-tête inactive_section (seulement si des inactifs existent) / inactifs — cartes alpha 0.55 en plus du badge, lisibles et tappables (l'édition d'un archivé reste possible). - Miroir web : treatmentsForDisplay dans js/data/models.js + rendu sections + opacity 0.55 + i18n inactive_section FR/EN (web v1.11.0 sync). - 5 tests JVM TreatmentsDisplayTest + 4 tests web (miroir). 264 tests JVM + 14 UI + lint verts ; web 184 tests + E2E verts (check.sh). Validé émulateur sur APK RELEASE avec données réelles seedées (5 actifs en haut, en-tête + EV-old/EEn-old tout en bas, 0 crash).
HormoneTrack
Language: Français · English (this file)
🤖 AI-assisted development: this project was designed and coded with an AI assistant. The human contribution was essential: continuous feedback, user reports (testing on a real phone, bug reports with real data exports), improvement suggestions and validation of every release. The session-by-session detail is documented in docs/DEVELOPPEMENT.md §2 (French).
Hormone therapy (HRT) tracker on Android, with hour-by-hour estimated curves of estradiol (E2) and testosterone (T), calibration against blood tests, reminders mirrored on a smartwatch (Huawei Watch GT 3 via Gadgetbridge or Huawei Health) and JSON backup. 100% local, no account, no server.
⚠️ Medical disclaimer: the curves are pharmacokinetic estimates for informational purposes — they are not measurements. Always rely on your blood tests and on your endocrinologist's guidance.
- Status: v1.11.0 — Android build ✅, lint clean ✅, 264 unit tests ✅ (230 without the local test data; 6 regressions pinned on real data not committed to the repo), 14 Compose UI tests ✅ (emulator), smartwatch integration = notifications ✅, private Gitea repo + releases with APK ✅
- Changelog: docs/CHANGELOG.md (French)
- User guide: docs/GUIDE_INSTALLATION.md (French)
- Development docs (architecture, math, decisions, bugs): docs/DEVELOPPEMENT.md (French)
- Watch / Gadgetbridge: docs/MONTRE-GADGETBRIDGE.md (French)
Features
- Hour-by-hour estimated curves: E2 (pg/mL) and T (ng/mL), 24 h / 7 d / 30 d views, dose markers (forecast and actual), configurable chart timezone (v1.4.5), pan (drag to scroll back in time), zoom (pinch or − / + buttons, 6 h → 300 d, adaptive sampling), displayable peaks & troughs with their estimated values (▲▼ triangles at local extrema, toggleable)
- Two PK models, superimposable: Estrannaise (EstraNase) (hourly tables
from the
.odsspreadsheet) and Transfem Science — since v1.4.0 the TFS model is the official 3-compartment meta-analysis (exact closed form, parameters from the TFS simulator, article), covering 7 esters (EV, EU, EEn, EB, EC oil, EC suspension, PEP) — WHSAH covers 6 (no PEP). Displayed side by side with independent toggles (3 colors) — pre-checked according to your treatments' models (v1.4.7) and each model calibrated separately by your labs (v1.4.8 — scale factors adapt to each profile) - "Lab track" curve (v1.5.0, extended in v1.6.0): the curve anchored on your
blood tests (
Lab trackchip, off by default) — the PK model's shape between labs, with the amplitude rescaled to pass EXACTLY through every E2 lab value; log-linear transition from one lab to the next, keeps out-of-action-window labs (#61);Extendchip (v1.6.0): the curve extends beyond the last lab (model × last lab ratio, horizon = model extinction, estimated part drawn faded + dedicated legend) — the curve is a COMPARISON reference, never an estimate of action (it never feeds the home screen or the alerts) - Analytical Estrannaise model (v1.9.0): the ESE model now uses the 3C
closed form published by estrannaise.js (fidelity to the old tables pinned at
RMS 0) — MCMC uncertainty cloud (
Cloudchip, ESE-exclusive: 32 posterior curves showing the imprecision range, like the estrannaise website); 6 injectable esters (incl. EUCS, exclusive); the historical ODS is no longer used at runtime (fidelity = tests only); analytical terminal t½ (blood draw recommendation) - Configurable Bateman model (time to peak, half-life, bioavailability) for gel, patch and oral routes
- Forecast simulation: set a Dosage interval (days) on a treatment → upcoming doses are projected on the chart (never saved); enabling forecast does not move the chart start but EXTENDS the window to the next dose (v1.4.4), and the projection is browsed by dragging left, up to 1 year
- Dose log with exact date/time, dose in mg, per-injection ester (EV↔EU↔EEn switch like in the spreadsheet), editable (tap a row in Doses), interval in days between doses displayed
- Inactive treatments: an archived treatment disappears from input and reminders, but its whole history stays simulated and calibrated — useful for a valerate → enanthate transition; grouped at the very bottom of the Treatments page under a dedicated header, dimmed cards (v1.11.0)
- Blood tests: E2 + T in a single entry (each optional), displayed side by side when they share the same date/time, editable (tap → pick the entry); T units: ng/mL, ng/dL, ng/L, nmol/L
- Suggested next blood draw (v1.8.0, extended v1.8.1 to the Labs and Home pages): the Labs page recommends the estimated trough just before the next injection (the most comparable moment), at the first trough where your regimen is stabilized (~5 half-lives after your last change — dose, ester or interval) — derived from the curves and the Dosage interval; requires an active injectable E2 with a Dosage (a prompt suggests setting one otherwise); estimates, never medical advice
- Calibration: per-treatment scale factor = median(lab ÷ model prediction),
computed automatically (the
.ods"Scale factor", automated) — or permanent automatic calibration (option, off by default) which calibrates each ester with the labs of its own period (valerate labs → valerate doses, enanthate labs → enanthate doses) and recalibrates the T model - Empirical T estimation
T = floor + (base − floor) ÷ (1 + k·E2), with k calibrated per ester period (T suppression differs valerate vs enanthate), against the already-calibrated E2 — accepted T units: ng/mL, ng/dL, ng/L, nmol/L - Daily reminders with "Taken" / "Snooze 1 h" actions right in the notification; notifications are mirrored to the GT 3 watch (Gadgetbridge or Huawei Health); reminders follow the Dosage interval (v1.4.0): a treatment injected every 7 days only rings on injection day, not every day; a dose already logged on the day skips the notification (v1.10.0 — the next reminder resumes at the following slot)
- Configurable alert thresholds (v1.4.2): E2 (pg/mL) and T (ng/mL) high/low limits in Settings → warning card on the home screen + notification every 15 min even with the app closed (WorkManager, anti-spam, dedicated channel) — evaluated on the estimated level, opt-in
- Full JSON backup: treatments + doses + labs + T settings + parameters (language, auto-calibration, alert thresholds) (v1.4.2)
- Daily automatic backup (v1.7.0, opt-in): IN ADDITION to the manual export — every day, a full JSON backup (same format, directly importable) is written to the folder you choose (e.g. a synchronized Owncloud folder). No storage permission (SAF, folder picked once, persistent revocable permission), configurable retention (1–30 copies, default 7 — timestamped file per run, never overwritten; manual exports and foreign files never touched), last run status in Settings, first backup immediately on activation
- What's new on every update: automatic changelog dialog (dismissed = won't show again before the next version)
- Time on HRT displayed at the top of the Doses page (since the first dose); next dose in days on the home screen when it is more than 24 h away (v1.4.1)
- Exportable diagnostic logs (Settings) — useful for support
- Calendar events: dose reminders can create a recurring (posology) event in a "HormoneTrack" calendar on your phone
- Full JSON backup/restore (treatments + doses + labs + T settings + parameters, v1.4.2)
- FR + EN (per-app language, independent of the system)
- Jetpack Compose UI (BOM 2026.08, Material You); 100% local, no account
Quick start (building from source)
Prerequisites: JDK 17+ (Java 21 OK), Android SDK (platform 37 will be auto-downloaded by AGP if the licenses are signed). The wrapper downloads Gradle 9.7.1.
git clone <repo> && cd HormoneTrack
echo "sdk.dir=/path/to/android-sdk" > local.properties # or ANDROID_HOME
./gradlew assembleDebug # APK: app/build/outputs/apk/debug/app-debug.apk
./gradlew testDebugUnitTest # 264 tests (230 without local test data)
./gradlew connectedDebugAndroidTest # 14 UI tests (emulator/device required)
./gradlew lint # clean lint required before a release
Installing on a phone: developer mode + USB debugging, then Android Studio
(Run ▶️) or adb install -r app/build/outputs/apk/debug/app-debug.apk.
No Play Store: the app is sideloaded. Step-by-step details (French):
docs/GUIDE_INSTALLATION.md.
Git
Repositories: gitea.cloudyfy.fr and gitea.farewell.dev (mirror) —
Siphonight/HormoneTrack on both (private), with tagged releases
(v1.1.0 → v1.10.0) and two APKs per release (since v1.2.5)
(downloadable without building, cf
docs/DEVELOPPEMENT.md §16.bis):
-release.apk (recommended, R8-optimized, 2.7 MB) and -debug.apk (20 MB):
git tag # list releases
git log --oneline # layer-by-layer history (toolchain / engine / UI / docs)
git push -u origin main --tags
Web port: since v1.4.10, the browser version lives in its own repository
Siphonight/HormoneTrack-web (same instances) — aligned versions
(web v1.4.10 = port of Android v1.4.10), synced releases (checklist §16 step
4.bis), separate changelogs.
Every release commit passes ./gradlew testDebugUnitTest (green required) and
is an annotated tag. See docs/DEVELOPPEMENT.md §16.
Release signing: the release APK is signed with the debug key as long as
no keystore.properties exists at the repo root; once created via
scripts/make-release-keystore.sh (both files gitignored), assembleRelease
signs with that keystore — phone migration documented in
docs/DEVELOPPEMENT.md §16.quater.
The models in short
Each injection contributes dose_mg × profile(dt) where profile is the
normalized response (pg/mL per mg); contributions superimpose. Three
superimposable models:
- Estrannaise = hourly tables from the
.ods(8001 h), EV/EU/EEn - Transfem Science = 3-compartment meta-analysis (V3C, closed form, article), 7 esters (EV, EU, EEn, EB, EC, EC suspension, PEP)
- WHSAH (v1.4.6) = "license-free" fit of the WHSAH Collective via Mona — same mathematical family but independent parameters with explicit bioavailability F < 1: faster rise at D+1 (EEn ~70 pg/mL at D+1 for 5 mg vs ~22 for TFS) and longer decay (EEn t½ 7.3 d vs 4.5 d). 6 esters (no PEP)
Reference peaks (pg/mL per mg):
| Profile | Model | Peak (pg/mL/mg) | Tmax | Terminal t½ |
|---|---|---|---|---|
| EV | Estrannaise | 61.1 | ~45 h | — |
| EU | Estrannaise | 3.4 | ~55 h (long plateau) | — |
| EEn | Estrannaise | 31.4 | ~152 h | — |
| EV | Transfem Science | 59.0 | ~51 h | 3.0 d |
| EU | Transfem Science | 10.1 | ~198 h | — |
| EEn | Transfem Science | 32.0 | ~156 h | 4.5 d |
| EB | Transfem Science | 194.2 | ~16 h | 1.2 d |
| EC (oil) | Transfem Science | 31.1 | ~103 h | 6.7 d |
| EC (susp.) | Transfem Science | 48.2 | ~29 h | 5.1 d |
| PEP | Transfem Science | 1.03 (~6.5× dose) | ~18 d | 28.4 d |
| EV | WHSAH | 73.5 | ~41 h | 3.1 d |
| EEn | WHSAH | 37.6 | ~120 h | 7.3 d |
| EB | WHSAH | 260.1 | ~12 h | 1.3 d |
| EC (oil) | WHSAH | 25.0 | ~81 h | 7.9 d |
| EC (susp.) | WHSAH | 53.5 | ~16 h | 7.1 d |
| EU | WHSAH | 4.9 | ~67 h | 31.7 d |
Calibration (v1.4.8): the scale factor applies per ESTER and per injection PERIOD (median of lab ÷ prediction ratios, like the original spreadsheet's "Scale factor" column) — and per MODEL: every displayed curve (Estrannaise / Transfem Science / WHSAH) is calibrated with ITS OWN model's prediction → they all stick to your labs, whatever their shape. Auto-calibration is optional (off by default).
Privacy
- The app is 100% local: Room database on the phone, no server, no telemetry; Gadgetbridge-compatible (FOSS) with no Huawei Health dependency
- The repository contains no health data: code and docs are generic; the
regression tests that use real exports load their data from
local-test-data/(gitignored, outside the repo — and the history was cleaned before the first push, cf docs/DEVELOPPEMENT.md §8.bis) - Backups = JSON files you store wherever you want (Owncloud, etc.)
- The daily auto-backup (v1.7.0) only writes to the folder YOU picked yourself (system SAF picker, revocable permission) — nothing ever leaves the phone, no server
allowBackup=false(sensitive data); biometric lock planned for Phase 2- The repository is private: release APKs download while logged in; making the repo public would make the APKs downloadable without an account (no data risk, see above)
Repository structure
HormoneTrack/
├── README.md ← this file's French twin
├── docs/
│ ├── GUIDE_INSTALLATION.md user guide (phone + watch, French)
│ ├── DEVELOPPEMENT.md full dev docs (architecture, math, bugs, tests, French)
│ ├── CHANGELOG.md detailed version history (French)
│ └── MONTRE-GADGETBRIDGE.md Huawei GT 3 watch: options + limits (French)
├── scripts/
│ ├── gitea-release.py publishes a release body (CHANGELOG + APK)
│ ├── publish-release.py publishes a tag's 2 APKs (verified by download)
│ ├── seed-emulator.py injects a JSON backup into an emulator DB
│ └── make-release-keystore.sh generates the release keystore + properties (v1.9.8)
├── local-test-data/ ← gitignored: real backups for regression
│ tests (NEVER in the repo, cf §8.bis)
├── keystore.properties ← gitignored: release signing (optional, §16.quater)
├── build.gradle.kts root Gradle config (AGP/Kotlin/KSP pinned)
├── settings.gradle.kts
├── gradle.properties
├── gradle/wrapper/ Gradle 9.7.1 wrapper (jar + properties)
├── gradlew / gradlew.bat
└── app/
├── build.gradle.kts dependencies (Compose, Room, DataStore, Gson…)
├── proguard-rules.pro
└── src/
├── main/
│ ├── AndroidManifest.xml
│ ├── assets/pk_profiles.json ← hourly tables (Estrannaise/TFS)
│ ├── java/com/hormonetrack/
│ │ ├── data/ (Room: models, DAOs, repository, backup)
│ │ ├── pk/ (PK engine + profiles: Estrannaise/TFS/WHSAH, alerts, export)
│ │ ├── reminder/ (exact alarms, notifs + actions, boot, alert worker)
│ │ ├── settings/ (DataStore: TConfig, language)
│ │ ├── ui/ (Compose: screens, components, theme)
│ │ │ └── screens/settings/ ← Settings cards (v1.9.8 split)
│ │ ├── HormoneTrackApp.kt
│ │ └── MainActivity.kt
│ └── res/ (FR/EN strings, theme, icons)
├── test/java/com/hormonetrack/ ← JVM unit tests
│ ├── pk/ (engine, ESE/TFS/WHSAH profiles, per-model
│ │ calibration, reminders, data-driven regressions)
│ ├── data/backup/ (Gson round-trip + parameters)
│ ├── ui/ (chart window, source guard)
│ ├── util/ (export file names, durations)
│ └── reminder/ + settings/ (calendar RRULE, changelog)
└── androidTest/java/com/hormonetrack/ ← Compose UI tests (v1.9.8)
└── uitest/ (navigation, changelog dialog, settings)
Roadmap
- v1: E2/T curves, dose log, labs + calibration, reminders, JSON backup, FR/EN
- Compose UI tests (13) + dedicated release signing infrastructure (v1.9.8)
- Biometric lock, widget, CSV export
- Watch Phase 2: custom watchface and/or Lite Wearable mini-app (see docs/MONTRE-GADGETBRIDGE.md)
License
GPL-3.0 — see LICENSE. Consistent with the Gadgetbridge ecosystem. The PK models belong to their respective authors (Estrannaise, Transfem Science).