A menstrual cycle tracker for Android, built around one rule: what you recorded and what the app calculated are never presented as the same thing.
Recorded observations are labelled as recorded. Predictions are labelled as estimates, shown as a range rather than a confident date, and the app says what the range was derived from. Luteal makes no diagnosis.
The app is French-first; English is also shipped. It works entirely offline by default, with optional end-to-end encrypted sync.
- Tracking data lives in a local Room database. Nothing leaves the device until
sync is explicitly enabled -
SyncModedefaults toOFFLINE_LOCAL. - Android cloud backup is disabled for the app's data (
allowBackup=false). - Home-screen widgets start concealed and never show private notes or detailed observations. Revealing a widget sends its visible content to the launcher, which may retain previews or screenshots outside Luteal's control.
- No advertising, no analytics, no Google Play Services, no Firebase.
- Crash reporting is ACRA with a consent dialog: nothing is sent unless the user reads it and chooses to send. Tracking data is not included.
- When sync is on, records are encrypted on this device before upload. The server stores ciphertext and routing metadata. It also issued the account code and receives that code again when a device is added, so the app does not claim the operator has never seen the key. See docs/architecture/E2EE_DESIGN.md.
Requires JDK 21 and the Android SDK (compileSdk 37, minSdk 26).
./gradlew assembleDebug # debug build
./gradlew testDebugUnitTest # unit tests
./gradlew assembleRelease # release buildA plain clone builds with no other checkout present: the folicular API contract
is vendored under contract/.
Release signing reads keystore.properties at the repository root - copy
keystore.properties.example and fill it in. Without that file the release
build is left unsigned rather than falling back to the debug key.
| Path | Contents |
|---|---|
app/src/main/java/fr/luteal/app |
Screens, navigation, view models, DI wiring |
app/src/main/java/fr/luteal/core |
Domain model, Room storage, sync, crypto |
contract/ |
Vendored folicular OpenAPI spec and conformance fixtures |
docs/architecture |
Sync boundary, E2EE design, backend integration |
docs/product |
Feature research, roadmap, and widget behavior |
docs/research |
Cycle research notes and source register |
fastlane/ |
F-Droid listing metadata |
Sync talks to folicular, a Go
service. It is optional: the app is fully usable without it. The server address
defaults to the official Luteal service (BuildConfig.SYNC_BASE_URL) and can be
pointed to any self-hosted folicular instance in Settings.
GNU General Public License v3.0 or later. See LICENSE.
Luteal is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.