# HormoneTrack > **Language**: [FranΓ§ais](README.md) Β· **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](docs/DEVELOPPEMENT.md) (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.10.0 β€” Android build βœ…, **lint clean** βœ…, **259 unit tests** βœ… (225 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](docs/CHANGELOG.md) (French) - **User guide**: [docs/GUIDE_INSTALLATION.md](docs/GUIDE_INSTALLATION.md) (French) - **Development docs** (architecture, math, decisions, bugs): [docs/DEVELOPPEMENT.md](docs/DEVELOPPEMENT.md) (French) - **Watch / Gadgetbridge**: [docs/MONTRE-GADGETBRIDGE.md](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 `.ods` spreadsheet) 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](https://transfemscience.org/misc/injectable-e2-simulator/), [article](https://transfemscience.org/articles/injectable-e2-meta-analysis/)), 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 track` chip, 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); **`Extend` chip (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** (`Cloud` chip, 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 - **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. ```bash git clone && 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 # 259 tests (225 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](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.9.8`) and **two APKs per release** (since v1.2.5) (downloadable without building, cf [docs/DEVELOPPEMENT.md Β§16.bis](docs/DEVELOPPEMENT.md)): `-release.apk` (**recommended**, R8-optimized, 2.7 MB) and `-debug.apk` (20 MB): ```bash 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](docs/DEVELOPPEMENT.md). **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](docs/DEVELOPPEMENT.md). ## 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](https://transfemscience.org/articles/injectable-e2-meta-analysis/)), 7 esters (EV, EU, EEn, EB, EC, EC suspension, PEP) - **WHSAH** (v1.4.6) = "license-free" fit of the [WHSAH Collective via Mona](https://github.com/mona-hrt/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](docs/DEVELOPPEMENT.md)) - 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 - [x] v1: E2/T curves, dose log, labs + calibration, reminders, JSON backup, FR/EN - [x] 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](docs/MONTRE-GADGETBRIDGE.md)) ## License **GPL-3.0** β€” see [LICENSE](LICENSE). Consistent with the Gadgetbridge ecosystem. The PK models belong to their respective authors ([Estrannaise](https://estrannaise.github.io/), [Transfem Science](https://transfemscience.org)).