Compare commits
No commits in common. "e5fefc90dfc0336fd5294137e838ad8ed40015ce" and "88c365f4d064ef682f6a19a7224ffd2add9caa55" have entirely different histories.
e5fefc90df
...
88c365f4d0
@ -16,7 +16,7 @@ sauvegarde JSON. **100 % local, aucun compte, aucun serveur.**
|
|||||||
> à titre informatif — ce ne sont pas des mesures. Fie-toi toujours à tes analyses de
|
> à titre informatif — ce ne sont pas des mesures. Fie-toi toujours à tes analyses de
|
||||||
> sang et aux consignes de ton endocrinologue.
|
> sang et aux consignes de ton endocrinologue.
|
||||||
|
|
||||||
- **Statut** : v1.3.3 — build Android ✅, **87 tests unitaires** ✅ (44 sans les données de test locales ; 3 régressions épinglées sur données réelles **non versionnées**), intégration montre = notifications ✅, **dépôt Gitea privé + releases avec APK** ✅
|
- **Statut** : v1.3.2 — build Android ✅, **87 tests unitaires** ✅ (44 sans les données de test locales ; 3 régressions épinglées sur données réelles **non versionnées**), intégration montre = notifications ✅, **dépôt Gitea privé + releases avec APK** ✅
|
||||||
- **Journal des versions** : [docs/CHANGELOG.md](docs/CHANGELOG.md)
|
- **Journal des versions** : [docs/CHANGELOG.md](docs/CHANGELOG.md)
|
||||||
- **Guide utilisateur** : [docs/GUIDE_INSTALLATION.md](docs/GUIDE_INSTALLATION.md)
|
- **Guide utilisateur** : [docs/GUIDE_INSTALLATION.md](docs/GUIDE_INSTALLATION.md)
|
||||||
- **Doc de développement** (architecture, maths, décisions, bugs) : [docs/DEVELOPPEMENT.md](docs/DEVELOPPEMENT.md)
|
- **Doc de développement** (architecture, maths, décisions, bugs) : [docs/DEVELOPPEMENT.md](docs/DEVELOPPEMENT.md)
|
||||||
@ -73,7 +73,7 @@ par AGP si les licences sont signées). Le wrapper télécharge Gradle 9.7.1.
|
|||||||
git clone <repo> && cd HormoneTrack
|
git clone <repo> && cd HormoneTrack
|
||||||
echo "sdk.dir=/chemin/vers/android-sdk" > local.properties # ou ANDROID_HOME
|
echo "sdk.dir=/chemin/vers/android-sdk" > local.properties # ou ANDROID_HOME
|
||||||
./gradlew assembleDebug # APK : app/build/outputs/apk/debug/app-debug.apk
|
./gradlew assembleDebug # APK : app/build/outputs/apk/debug/app-debug.apk
|
||||||
./gradlew testDebugUnitTest # 87 tests (44 sans les données locales)
|
./gradlew testDebugUnitTest # 36 tests (moteur PK, profils, backup, régression)
|
||||||
```
|
```
|
||||||
|
|
||||||
Installation sur un téléphone : mode développeur + Débogage USB, puis Android Studio
|
Installation sur un téléphone : mode développeur + Débogage USB, puis Android Studio
|
||||||
@ -84,7 +84,7 @@ Pas de Play Store : l'app est sideloadée. Détails pas-à-pas : [docs/GUIDE_INS
|
|||||||
|
|
||||||
Dépôts : **gitea.cloudyfy.fr** et **gitea.farewell.dev** (miroir) —
|
Dépôts : **gitea.cloudyfy.fr** et **gitea.farewell.dev** (miroir) —
|
||||||
`Siphonight/HormoneTrack` sur les deux (privé), avec
|
`Siphonight/HormoneTrack` sur les deux (privé), avec
|
||||||
**releases taguées** (`v1.1.0` → `v1.3.3`) et **deux APK par release** (depuis v1.2.5)
|
**releases taguées** (`v1.1.0` → `v1.3.1`) et **deux APK par release** (depuis v1.2.5)
|
||||||
(téléchargeables sans compiler, cf [docs/DEVELOPPEMENT.md §16.bis](docs/DEVELOPPEMENT.md)) :
|
(téléchargeables sans compiler, cf [docs/DEVELOPPEMENT.md §16.bis](docs/DEVELOPPEMENT.md)) :
|
||||||
`-release.apk` (**recommandé**, optimisé R8, 2,4 Mo) et `-debug.apk` (20 Mo) :
|
`-release.apk` (**recommandé**, optimisé R8, 2,4 Mo) et `-debug.apk` (20 Mo) :
|
||||||
|
|
||||||
|
|||||||
@ -12,13 +12,8 @@ android {
|
|||||||
applicationId = "com.hormonetrack"
|
applicationId = "com.hormonetrack"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
// ⚠️ MÉTADONNÉES DE VERSION — bump obligatoire à CHAQUE release
|
versionCode = 14
|
||||||
// (checklist §16 étape 1 de docs/DEVELOPPEMENT.md), committé AVANT le
|
versionName = "1.3.0"
|
||||||
// tag : v1.3.0→v1.3.2 contenaient tous versionCode 14 / "1.3.0"
|
|
||||||
// (bump jamais commité) → BuildConfig.VERSION_NAME était faux dans
|
|
||||||
// les APK publiés (Paramètres + titre du dialog « Nouveautés »).
|
|
||||||
versionCode = 17
|
|
||||||
versionName = "1.3.3"
|
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
|
|||||||
@ -6,15 +6,6 @@
|
|||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||||
|
|
||||||
<!-- Événements d'agenda récurrents (v1.3.0, calendrier local « HormoneTrack »).
|
|
||||||
⚠️ Ces permissions DANGEREUSES doivent être déclarées ICI en plus d'être
|
|
||||||
demandées à l'exécution par l'éditeur de traitement : sans cette
|
|
||||||
déclaration la demande runtime est refusée d'office et CalendarEvents
|
|
||||||
lève SecurityException → la fonctionnalité agenda était inopérante
|
|
||||||
depuis v1.3.0 (cause racine manquante, cf §14 #44 de la doc de dev). -->
|
|
||||||
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
|
|
||||||
<uses-permission android:name="android.permission.READ_CALENDAR" />
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".HormoneTrackApp"
|
android:name=".HormoneTrackApp"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
|
|||||||
@ -78,56 +78,33 @@ fun SettingsScreen(onBack: () -> Unit) {
|
|||||||
|
|
||||||
var showImportConfirm by remember { mutableStateOf(false) }
|
var showImportConfirm by remember { mutableStateOf(false) }
|
||||||
var logLineCount by remember { mutableIntStateOf(AppLog.count()) }
|
var logLineCount by remember { mutableIntStateOf(AppLog.count()) }
|
||||||
// Message de feedback affiché en haut de l'écran (succès/échec des exports,
|
|
||||||
// imports, calibration T…) — déclaré AVANT les launchers : le callback de
|
|
||||||
// logExportLauncher l'écrit (un state local doit être déclaré avant le
|
|
||||||
// lambda qui le capture).
|
|
||||||
var message by remember { mutableStateOf<String?>(null) }
|
|
||||||
val logExportLauncher = rememberLauncherForActivityResult(
|
val logExportLauncher = rememberLauncherForActivityResult(
|
||||||
ActivityResultContracts.CreateDocument("text/plain")
|
ActivityResultContracts.CreateDocument("text/plain")
|
||||||
) { uri ->
|
) { uri ->
|
||||||
// ── Export des LOGS — même PROCESSUS que l'export JSON (cf §14 #45) ──────
|
// Même pattern que l'export JSON (BackupManager.writeBackup) : écriture
|
||||||
// Historique : v1.3.1 plantait (écriture inline), le « fix » v1.3.2 a
|
// synchronisée dans une coroutine IO, AppLog toutes les étapes
|
||||||
// réécrit inline AUSSI (et laissait des AppLog.log hors try/catch) —
|
|
||||||
// l'app plantait toujours. Leçon §14 #41 : quand un pattern d'IO marche,
|
|
||||||
// le réutiliser TEL QUEL. L'export JSON (BackupManager.writeBackup)
|
|
||||||
// fonctionne sur le téléphone → l'export des logs passe par LA MÊME
|
|
||||||
// fonction (elle prend un String, peu importe son contenu).
|
|
||||||
//
|
|
||||||
// Points de robustesse :
|
|
||||||
// - le résultat SAF arrive sur le thread UI ; l'IO part dans une
|
|
||||||
// coroutine à SCOPE RACINE (CoroutineScope standalone, jamais
|
|
||||||
// rememberCoroutineScope : elle serait annulée si l'écran quittait la
|
|
||||||
// composition pendant l'écriture) ;
|
|
||||||
// - TOUT ce qui peut lever est sous try/catch : une exception non
|
|
||||||
// interceptée dans un scope racine remonte au handler de la thread
|
|
||||||
// = crash de l'app. (AppLog.log est de toute façon fail-safe depuis
|
|
||||||
// v1.3.3, double filet.)
|
|
||||||
uri ?: return@rememberLauncherForActivityResult
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val ok = try {
|
AppLog.log("logs", "export démarré")
|
||||||
|
try {
|
||||||
val logs = AppLog.all()
|
val logs = AppLog.all()
|
||||||
AppLog.log("logs", "export démarré (${logs.length} caractères)")
|
val ok = uri != null && try {
|
||||||
// ⚠️ Code path IDENTIQUE à l'export JSON : BackupManager
|
context.contentResolver.openOutputStream(uri, "wt")?.use { os ->
|
||||||
// .writeBackup (openOutputStream "wt", exceptions capturées).
|
os.write(logs.toByteArray(Charsets.UTF_8))
|
||||||
BackupManager.writeBackup(context, uri, logs)
|
os.flush()
|
||||||
|
} ?: false
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
AppLog.log("logs", "ERREUR export : ${e.message}")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
AppLog.log("logs", "export terminé : ok=$ok, ${logs.length} caractères")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
AppLog.log("logs", "ERREUR export : ${e.message}")
|
AppLog.log("logs", "ERREUR export (outer) : ${e.message}")
|
||||||
false
|
|
||||||
}
|
|
||||||
AppLog.log("logs", "export terminé : ok=$ok")
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
// Feedback visible comme l'export JSON + compteur rafraîchi
|
|
||||||
// (l'export vient d'ajouter des lignes de journal). Écriture
|
|
||||||
// d'état Compose UNIQUEMENT sur le thread principal.
|
|
||||||
logLineCount = AppLog.count()
|
|
||||||
message = context.getString(
|
|
||||||
if (ok) R.string.logs_export_ok else R.string.logs_export_fail
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var importJson by remember { mutableStateOf<String?>(null) }
|
var importJson by remember { mutableStateOf<String?>(null) }
|
||||||
|
var message by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
val exportLauncher = rememberLauncherForActivityResult(
|
val exportLauncher = rememberLauncherForActivityResult(
|
||||||
ActivityResultContracts.CreateDocument("application/json")
|
ActivityResultContracts.CreateDocument("application/json")
|
||||||
|
|||||||
@ -24,50 +24,26 @@ object AppLog {
|
|||||||
private var file: File? = null
|
private var file: File? = null
|
||||||
private val lock = Any()
|
private val lock = Any()
|
||||||
|
|
||||||
/**
|
/** À appeler une fois dans Application.onCreate. */
|
||||||
* À appeler une fois dans Application.onCreate.
|
|
||||||
*
|
|
||||||
* ⚠️ La lecture du fichier existant est GARDÉE (try/catch) : un fichier de log
|
|
||||||
* corrompu ou illisible ne doit jamais empêcher l'app de démarrer — on perd
|
|
||||||
* l'historique, pas le process (même famille que le fix v1.3.3 de log()).
|
|
||||||
*/
|
|
||||||
fun init(context: android.content.Context) {
|
fun init(context: android.content.Context) {
|
||||||
synchronized(lock) {
|
synchronized(lock) {
|
||||||
val f = File(context.filesDir, "debug-log.txt")
|
val f = File(context.filesDir, "debug-log.txt")
|
||||||
file = f
|
file = f
|
||||||
if (f.exists()) {
|
if (f.exists()) {
|
||||||
buffer.clear()
|
buffer.clear()
|
||||||
val loaded = try {
|
buffer.addAll(f.readLines().takeLast(MAX_LINES))
|
||||||
f.readLines().takeLast(MAX_LINES)
|
|
||||||
} catch (_: Exception) {
|
|
||||||
emptyList() // fichier illisible → démarrer avec un buffer vide
|
|
||||||
}
|
|
||||||
buffer.addAll(loaded)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log("app", "démarrage (v init) — ${buffer.size} lignes de logs existantes")
|
log("app", "démarrage (v init) — ${buffer.size} lignes de logs existantes")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Journalise une ligne (buffer mémoire + append fichier).
|
|
||||||
*
|
|
||||||
* ⚠️ NE JAMAIS PROPAGER D'EXCEPTION (fix v1.3.3) : log() est appelé depuis
|
|
||||||
* des dizaines de sites, y compris dans des coroutines à scope racine où une
|
|
||||||
* exception non interceptée = crash du process (cf export des logs, §14 #45).
|
|
||||||
* L'IO fichier (appendText) peut lever IOException (disque plein, I/O error) :
|
|
||||||
* elle est avalée ici — on perd au pire UNE ligne de journal, jamais l'app.
|
|
||||||
*/
|
|
||||||
fun log(tag: String, message: String) = synchronized(lock) {
|
fun log(tag: String, message: String) = synchronized(lock) {
|
||||||
val line = formatLine(System.currentTimeMillis(), tag, message)
|
val line = formatLine(System.currentTimeMillis(), tag, message)
|
||||||
buffer.addLast(line)
|
buffer.addLast(line)
|
||||||
val trimmed = trimLines(buffer.toList(), MAX_LINES)
|
val trimmed = trimLines(buffer.toList(), MAX_LINES)
|
||||||
buffer.clear()
|
buffer.clear()
|
||||||
buffer.addAll(trimmed)
|
buffer.addAll(trimmed)
|
||||||
try {
|
file?.appendText(line + "\n")
|
||||||
file?.appendText(line + "\n")
|
|
||||||
} catch (_: Exception) {
|
|
||||||
// Journalisation best-effort : ne jamais faire planter l'app pour un log
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun all(): String = synchronized(lock) { buffer.joinToString("\n") }
|
fun all(): String = synchronized(lock) { buffer.joinToString("\n") }
|
||||||
|
|||||||
@ -40,8 +40,6 @@
|
|||||||
<string name="logs_count">%1$d lignes (rappels, agenda, import/export, erreurs)</string>
|
<string name="logs_count">%1$d lignes (rappels, agenda, import/export, erreurs)</string>
|
||||||
<string name="logs_export">Exporter</string>
|
<string name="logs_export">Exporter</string>
|
||||||
<string name="logs_clear">Effacer</string>
|
<string name="logs_clear">Effacer</string>
|
||||||
<string name="logs_export_ok">Logs de diagnostic exportés</string>
|
|
||||||
<string name="logs_export_fail">Échec de l\'export des logs de diagnostic</string>
|
|
||||||
<string name="changelog_title">Nouveautés de la version %1$s</string>
|
<string name="changelog_title">Nouveautés de la version %1$s</string>
|
||||||
<string name="settings_version">Version installée : %1$s</string>
|
<string name="settings_version">Version installée : %1$s</string>
|
||||||
<string name="releases_link">Dernières versions sur Gitea (télécharger les APK)</string>
|
<string name="releases_link">Dernières versions sur Gitea (télécharger les APK)</string>
|
||||||
|
|||||||
@ -40,8 +40,6 @@
|
|||||||
<string name="logs_count">%1$d lines (reminders, calendar, import/export, errors)</string>
|
<string name="logs_count">%1$d lines (reminders, calendar, import/export, errors)</string>
|
||||||
<string name="logs_export">Export</string>
|
<string name="logs_export">Export</string>
|
||||||
<string name="logs_clear">Clear</string>
|
<string name="logs_clear">Clear</string>
|
||||||
<string name="logs_export_ok">Diagnostic logs exported</string>
|
|
||||||
<string name="logs_export_fail">Failed to export the diagnostic logs</string>
|
|
||||||
<string name="changelog_title">What\'s new in %1$s</string>
|
<string name="changelog_title">What\'s new in %1$s</string>
|
||||||
<string name="settings_version">Installed version: %1$s</string>
|
<string name="settings_version">Installed version: %1$s</string>
|
||||||
<string name="releases_link">Latest releases on Gitea (download APKs)</string>
|
<string name="releases_link">Latest releases on Gitea (download APKs)</string>
|
||||||
|
|||||||
@ -1,41 +1,7 @@
|
|||||||
# Changelog — HormoneTrack
|
# Changelog — HormoneTrack
|
||||||
|
|
||||||
Format : [Keep a Changelog](https://keepachangelog.com/fr-FR/1.1.0/).
|
Format : [Keep a Changelog](https://keepachangelog.com/fr-FR/1.1.0/).
|
||||||
Versionnage : [SemVer](https://semver.org/). Releases taguées dans git (`v1.3.3`, …).
|
Versionnage : [SemVer](https://semver.org/). Releases taguées dans git (`v1.3.2`, …).
|
||||||
|
|
||||||
## [1.3.3] — 2026-09-06 (versionCode 17)
|
|
||||||
|
|
||||||
### Corrigé
|
|
||||||
- **Export des logs plantait TOUJOURS l'app** (remontée ; le « fix » v1.3.2
|
|
||||||
était insuffisant) : l'écriture était réimplémentée en inline au lieu de
|
|
||||||
réutiliser l'IO éprouvée de l'export JSON, et les appels `AppLog.log` du
|
|
||||||
callback étaient hors try/catch — une exception dans une coroutine à scope
|
|
||||||
racine (CoroutineScope standalone) remonte au handler de la thread =
|
|
||||||
crash du process. FIX : l'export passe désormais par
|
|
||||||
**`BackupManager.writeBackup` tel quel** (le même code path que l'export
|
|
||||||
JSON qui fonctionne sur le téléphone), tout le callback est gardé, et
|
|
||||||
`AppLog.log` ne lève plus jamais d'exception (IO fichier avalée en interne,
|
|
||||||
on perd au pire une ligne de journal). Feedback visible succès/échec
|
|
||||||
(comme l'export JSON) + compteur de lignes rafraîchi après l'export.
|
|
||||||
- **Événements d'agenda inopérants depuis v1.3.0** : les permissions
|
|
||||||
WRITE_CALENDAR / READ_CALENDAR n'étaient **pas déclarées dans le manifest**
|
|
||||||
(jamais présentes dans tout l'historique git, bien que documentées §9) —
|
|
||||||
la demande à l'exécution était donc refusée d'office et
|
|
||||||
`CalendarEvents` lève SecurityException. Cause racine manquante des bugs
|
|
||||||
« toggle agenda » « corrigés » en v1.3.1/v1.3.2 : la logique UI était
|
|
||||||
correcte, il ne venait à l'esprit de personne que la déclaration manquait.
|
|
||||||
- **Métadonnées de version fausses** : le bump de version n'avait jamais
|
|
||||||
été commité — les tags v1.3.0, v1.3.1 **et** v1.3.2 contenaient tous
|
|
||||||
`versionCode = 14 / versionName = "1.3.0"`, donc les APK publiés
|
|
||||||
affichaient « 1.3.0 » dans Paramètres et en titre du dialog « Nouveautés ».
|
|
||||||
Désormais bumpé + commité avant chaque tag (checklist §16 étape 1).
|
|
||||||
|
|
||||||
### Technique
|
|
||||||
- Doc de développement mise à jour (DB v3, targetSdk 36, fichiers de
|
|
||||||
données de régression, bugs #43–#45) ; téléphone de test documenté :
|
|
||||||
**Google Pixel 9 /e/OS** (AOSP, DocumentsUI standard — l'export JSON y
|
|
||||||
fonctionne, l'export logs réutilise ce même path).
|
|
||||||
- Compteur de tests inchangé : 87 (44 sans les données locales, gitignorées).
|
|
||||||
|
|
||||||
## [1.3.2] — 2026-09-06 (versionCode 16)
|
## [1.3.2] — 2026-09-06 (versionCode 16)
|
||||||
|
|
||||||
|
|||||||
@ -79,7 +79,6 @@ Fonctionnalités v1 :
|
|||||||
| 6 sept. 2026 (session v1.3.1) | En-tête « temps sous THS » sur Doses ; **logs de diagnostic** (AppLog : buffer 500 lignes, persisté, exportable de Paramètres) ; **fix toggle agenda** (callback async) ; **87 tests verts**, APK v1.3.1 + releases. |
|
| 6 sept. 2026 (session v1.3.1) | En-tête « temps sous THS » sur Doses ; **logs de diagnostic** (AppLog : buffer 500 lignes, persisté, exportable de Paramètres) ; **fix toggle agenda** (callback async) ; **87 tests verts**, APK v1.3.1 + releases. |
|
||||||
| 6 sept. 2026 (session v1.3.2) | 3 bugs remontés : toggle agenda (callback async fix + ContextCompat au save + Posologie requise), export logs plantait (pattern JSON réutilisé + AppLog), dialog changelog récurrent (version vue mémorisée avant affichage + titre BuildConfig documenté) ; export réel mis à jour (3 traitements : EV inactif + EEn actif + CPA oral) ; **87 tests verts**, APK v1.3.2 + releases. |
|
| 6 sept. 2026 (session v1.3.2) | 3 bugs remontés : toggle agenda (callback async fix + ContextCompat au save + Posologie requise), export logs plantait (pattern JSON réutilisé + AppLog), dialog changelog récurrent (version vue mémorisée avant affichage + titre BuildConfig documenté) ; export réel mis à jour (3 traitements : EV inactif + EEn actif + CPA oral) ; **87 tests verts**, APK v1.3.2 + releases. |
|
||||||
| 6 sept. 2026 (session farewell) | Repo créé côté farewell → **push SSH (alias `farewell` : giteassh:2222) + 12 releases publiées avec APK vérifiés par téléchargement** ; piège lexicographique `v1.2.10 < v1.2.5` en comparaison de chaînes épinglé (§14 #38) ; les deux instances Gitea sont synchrones. |
|
| 6 sept. 2026 (session farewell) | Repo créé côté farewell → **push SSH (alias `farewell` : giteassh:2222) + 12 releases publiées avec APK vérifiés par téléchargement** ; piège lexicographique `v1.2.10 < v1.2.5` en comparaison de chaînes épinglé (§14 #38) ; les deux instances Gitea sont synchrones. |
|
||||||
| 6 sept. 2026 (session v1.3.3) | **Audit de reprise de maintenance** (nouvelle session IA) : 3 bugs racines trouvés — permissions agenda ABSENTES du manifest (jamais déclarées, §14 #44), export logs plantait TOUJOURS (le fix v1.3.2 réimplémentait l'IO au lieu de réutiliser BackupManager, §14 #45), bump de version JAMAIS commité (tags v1.3.0–1.3.2 tous `versionCode 14 / "1.3.0"`, §14 #46). Corrigés + doc rafraîchie (DB v3, targetSdk 36, régression 3 = export v1.3.1, §19/§20 staleness) ; **téléphone de test documenté : Google Pixel 9 /e/OS** (AOSP ; SAF DocumentsUI standard). |
|
|
||||||
| 6 sept. 2026 (session v1.3.0) | Dialog « Nouveautés » après mise à jour (CHANGELOG embarqué en asset, tâche `copyChangelog`, version vue en DataStore) ; **événements d'agenda récurrents** (calendrier local HormoneTrack, RRULE posologie, permission runtime, Room v3 `calendarEventId`) ; Paramètres : version + lien releases ; **78 tests verts**, APK v1.3.0 + releases. |
|
| 6 sept. 2026 (session v1.3.0) | Dialog « Nouveautés » après mise à jour (CHANGELOG embarqué en asset, tâche `copyChangelog`, version vue en DataStore) ; **événements d'agenda récurrents** (calendrier local HormoneTrack, RRULE posologie, permission runtime, Room v3 `calendarEventId`) ; Paramètres : version + lien releases ; **78 tests verts**, APK v1.3.0 + releases. |
|
||||||
| 5 sept. 2026 (session v1.2.10) | Sens des boutons de zoom inversé (+ = zoom avant, convention carte — retour utilisateur) ; 67 tests verts, APK v1.2.10 + releases. |
|
| 5 sept. 2026 (session v1.2.10) | Sens des boutons de zoom inversé (+ = zoom avant, convention carte — retour utilisateur) ; 67 tests verts, APK v1.2.10 + releases. |
|
||||||
| 5 sept. 2026 (session v1.2.9) | **Zoom du graphique** (pinch + boutons, 6 h → 300 j, focal stable, échantillonnage adaptatif `stepForRange`, labels X 1 h/3 h) ; **README : disclaimer IA-assisté** en en-tête ; 3 tests ; **67 tests verts**, APK v1.2.9 + releases. |
|
| 5 sept. 2026 (session v1.2.9) | **Zoom du graphique** (pinch + boutons, 6 h → 300 j, focal stable, échantillonnage adaptatif `stepForRange`, labels X 1 h/3 h) ; **README : disclaimer IA-assisté** en en-tête ; 3 tests ; **67 tests verts**, APK v1.2.9 + releases. |
|
||||||
@ -99,14 +98,14 @@ et en testant** — aucun build n'avait été lancé avant la session 3.
|
|||||||
| Kotlin | 2.3.21 (plugin compose 2.3.21) | idem |
|
| Kotlin | 2.3.21 (plugin compose 2.3.21) | idem |
|
||||||
| KSP | 2.3.11 (versionnage indépendant depuis KSP2) | idem |
|
| KSP | 2.3.11 (versionnage indépendant depuis KSP2) | idem |
|
||||||
| Compose BOM | **2026.08.00** (Compose 1.12 ; material3 pinné par le BOM) | `app/build.gradle.kts` |
|
| Compose BOM | **2026.08.00** (Compose 1.12 ; material3 pinné par le BOM) | `app/build.gradle.kts` |
|
||||||
| Room | 2.8.4 (KSP) — **DB v3 + MIGRATION_1_2 (forecastIntervalDays) + MIGRATION_2_3 (calendarEventId)**, `fallbackToDestructiveMigration` retiré | idem + `AppDatabase.kt` |
|
| Room | 2.8.4 (KSP) — **DB v2 + MIGRATION_1_2**, `fallbackToDestructiveMigration` retiré | idem + `AppDatabase.kt` |
|
||||||
| Navigation Compose | 2.10.0 | idem |
|
| Navigation Compose | 2.10.0 | idem |
|
||||||
| AppCompat | 1.8.0 (langue par app) | idem |
|
| AppCompat | 1.8.0 (langue par app) | idem |
|
||||||
| DataStore Preferences | 1.2.1 | idem |
|
| DataStore Preferences | 1.2.1 | idem |
|
||||||
| Gson | 2.14.0 | idem |
|
| Gson | 2.14.0 | idem |
|
||||||
| JUnit | 4.13.2 (testImplementation) | idem |
|
| JUnit | 4.13.2 (testImplementation) | idem |
|
||||||
| WorkManager | 2.11.2 (déclaré, non utilisé — supprimable) | idem |
|
| WorkManager | 2.11.2 (déclaré, non utilisé — supprimable) | idem |
|
||||||
| compileSdk / targetSdk | **37** / **36** ; minSdk 26 ; Java target 17 | app |
|
| compileSdk / targetSdk | **37** / 37 ; minSdk 26 ; Java target 17 | app |
|
||||||
| buildFeatures | compose + **buildConfig** (VERSION_NAME pour l'app) | app |
|
| buildFeatures | compose + **buildConfig** (VERSION_NAME pour l'app) | app |
|
||||||
|
|
||||||
Notes importantes (v1.2.0) :
|
Notes importantes (v1.2.0) :
|
||||||
@ -130,12 +129,6 @@ Notes importantes (v1.2.0) :
|
|||||||
## 4. Environnement de build (cette machine)
|
## 4. Environnement de build (cette machine)
|
||||||
|
|
||||||
- **macOS (Apple Silicon), brew présent, Java 21 (Microsoft OpenJDK) sur `/usr/bin/java`** ✓
|
- **macOS (Apple Silicon), brew présent, Java 21 (Microsoft OpenJDK) sur `/usr/bin/java`** ✓
|
||||||
- **Téléphone de test** : **Google Pixel 9 sous /e/OS** (ROM dé-Googlée, base AOSP) —
|
|
||||||
SAF/DocumentsUI standard (l'export JSON via `CreateDocument` y fonctionne → c'est LE
|
|
||||||
pattern d'IO de référence pour tout export de fichier) ; pas de Play Store, sideload
|
|
||||||
par `adb install` ou APK direct. Les retours utilisateur (bugs, exports JSON) viennent
|
|
||||||
de ce téléphone. (Historique : la montre est une Huawei Watch GT 3 via Gadgetbridge,
|
|
||||||
cf §17 — ce n'est PAS le téléphone.)
|
|
||||||
- **SDK Android** : installé via `brew install --cask android-commandlinetools`
|
- **SDK Android** : installé via `brew install --cask android-commandlinetools`
|
||||||
→ `/opt/homebrew/share/android-commandlinetools` (524 MB)
|
→ `/opt/homebrew/share/android-commandlinetools` (524 MB)
|
||||||
- licences acceptées : `yes | sdkmanager --licenses`
|
- licences acceptées : `yes | sdkmanager --licenses`
|
||||||
@ -173,12 +166,9 @@ Points clés :
|
|||||||
|
|
||||||
## 6. Modèle de données (Room)
|
## 6. Modèle de données (Room)
|
||||||
|
|
||||||
DB `hormonetrack.db`, **version 3**, migrations explicites (⚠️ plus de
|
DB `hormonetrack.db`, **version 2**, migrations explicites (⚠️ plus de
|
||||||
`fallbackToDestructiveMigration` — retiré en v1.2.0 car l'utilisatrice a des données
|
`fallbackToDestructiveMigration` — retiré en v1.2.0 car l'utilisatrice a des données
|
||||||
réelles ; toute évolution de schéma = `Migration(x, y)` + ALTER TABLE) :
|
réelles ; toute évolution de schéma = `Migration(x, y)` + ALTER TABLE).
|
||||||
|
|
||||||
- **v1 → v2** : `forecastIntervalDays REAL` (prévision, v1.2.0)
|
|
||||||
- **v2 → v3** : `calendarEventId INTEGER` (agenda récurrent, v1.3.0)
|
|
||||||
|
|
||||||
### `Treatment` (treatments)
|
### `Treatment` (treatments)
|
||||||
- base : `id`, `name`, `type` (ESTRADIOL/ANTI_ANDROGEN/PROGESTOGEN/OTHER), `route`
|
- base : `id`, `name`, `type` (ESTRADIOL/ANTI_ANDROGEN/PROGESTOGEN/OTHER), `route`
|
||||||
@ -399,14 +389,12 @@ Emplacement : `app/src/test/java/com/hormonetrack/`. Répertoire de travail d'ex
|
|||||||
non calibré ≈ 367 = les « 375 » rapportés), facteur unique EEN plausible, lab T en
|
non calibré ≈ 367 = les « 375 » rapportés), facteur unique EEN plausible, lab T en
|
||||||
unité aberrante neutralisé, prévision 6 j exacte, auto-cal cohérente.
|
unité aberrante neutralisé, prévision 6 j exacte, auto-cal cohérente.
|
||||||
**Tout nouvel export utilisateur = un nouveau test de régression.**
|
**Tout nouvel export utilisateur = un nouveau test de régression.**
|
||||||
- **`RegressionUserCase3Test`** (6) : 3ᵉ régression (export **v1.3.1** — mis à
|
- **`RegressionUserCase3Test`** (6) : 3ᵉ régression (export v1.2.3) — le scénario
|
||||||
jour à la v1.3.2) — le scénario **transition** : traitement **EV inactif**
|
**transition** : traitement **EV inactif** (29 doses 2–8 mg, janvier→juillet) +
|
||||||
(29 doses 2–8 mg, janvier→juillet) + traitement **EEn actif** (9 doses) +
|
traitement **EEn actif** (9 doses), 22 labs sur les deux périodes. Vérifie :
|
||||||
**CPA oral** (anti-androgène), soit 3 traitements, 51 doses, 26 labs.
|
parsing, **l'inactif reste simulé** (bug v1.2.4 — le moteur reçoit TOUS les
|
||||||
Vérifie : parsing, **l'inactif reste simulé** (bug v1.2.4 — le moteur reçoit
|
traitements), calibration par période couvrant EV **et** EEN, k T par ester,
|
||||||
TOUS les traitements), calibration par période couvrant EV **et** EEN,
|
continuité de la courbe pendant la transition, niveau actuel ~EEn équilibre.
|
||||||
k T par ester, continuité de la courbe pendant la transition, niveau
|
|
||||||
actuel ~EEn équilibre.
|
|
||||||
|
|
||||||
### 8.bis Données de test réelles : HORS dépôt (`local-test-data/`)
|
### 8.bis Données de test réelles : HORS dépôt (`local-test-data/`)
|
||||||
|
|
||||||
@ -415,11 +403,8 @@ du moteur sur les **exports réels** de l'utilisatrice. Ce sont des **données d
|
|||||||
personnelles** : elles ne sont **pas versionnées**, pour ne rien divulguer dans le
|
personnelles** : elles ne sont **pas versionnées**, pour ne rien divulguer dans le
|
||||||
dépôt (ni maintenant, ni si le repo devient public un jour).
|
dépôt (ni maintenant, ni si le repo devient public un jour).
|
||||||
|
|
||||||
- emplacement : `local-test-data/backup-v1.0.0.json`, `backup-v1.2.0.json`,
|
- emplacement : `local-test-data/backup-v1.0.0.json` et `backup-v1.2.0.json`
|
||||||
`backup-v1.2.3.json` (ancien export, plus consommé par un test) et
|
(copiés tels quels depuis l'export JSON de l'app) ;
|
||||||
`backup-v1.3.1.json` (copiés tels quels depuis l'export JSON de l'app) ;
|
|
||||||
`RegressionUserCaseTest` lit v1.0.0, `RegressionUserCase2Test` lit v1.2.0,
|
|
||||||
`RegressionUserCase3Test` lit **v1.3.1** ;
|
|
||||||
- `.gitignore` contient `local-test-data/` → jamais commités ;
|
- `.gitignore` contient `local-test-data/` → jamais commités ;
|
||||||
- les tests font `Assume.assumeTrue(file.exists())` dans le `@Before` : **sans le
|
- les tests font `Assume.assumeTrue(file.exists())` dans le `@Before` : **sans le
|
||||||
fichier, la classe est IGNORÉE** (skipped, pas failed) — un clone neuf ou une CI
|
fichier, la classe est IGNORÉE** (skipped, pas failed) — un clone neuf ou une CI
|
||||||
@ -433,20 +418,8 @@ dépôt (ni maintenant, ni si le repo devient public un jour).
|
|||||||
- **l'historique a été nettoyé avant le premier push** : les premiers commits
|
- **l'historique a été nettoyé avant le premier push** : les premiers commits
|
||||||
embarquaient les valeurs (tests + doc) → `git filter-branch --tree-filter` avec
|
embarquaient les valeurs (tests + doc) → `git filter-branch --tree-filter` avec
|
||||||
un script d'anonymisation (timestamps décalés de +30 j, valeurs perturbées dans
|
un script d'anonymisation (timestamps décalés de +30 j, valeurs perturbées dans
|
||||||
la prose des docs), tags réécrits, refs purgeées.
|
la prose des docs), tags réécrits, refs purgeées. `git grep` sur **toutes** les
|
||||||
⚠️ **Lint de dette connue (constat v1.3.3)** : le scanner travaillait sur une liste
|
révisions ne trouve aucune donnée réelle.
|
||||||
de motifs figée ; depuis que `local-test-data/` s'enrichit de NOUVEAUX exports
|
|
||||||
(ex. backup-v1.3.1.json ajouté à la v1.3.2), la ré-extraction des valeurs fait
|
|
||||||
que le scan `--history` détecte des fragments JSON de labs **encore présents
|
|
||||||
dans l'historique des révisions v1.1.0 → v1.2.3** (tests `RegressionUserCase{,2}`
|
|
||||||
data-driven AVANT la migration fichiers gitignorés ; fragments de la forme
|
|
||||||
« value » + timestamp). Amplitude : quelques valeurs numériques de labs — les
|
|
||||||
mêmes ordres de grandeur figurent déjà en PROSE dans les docs publiés (§14 #23,
|
|
||||||
CHANGELOG 1.1.0). Le working tree est propre. **Décision utilisateur (6 sept.
|
|
||||||
2026)** : purge complète de l'historique NON relancée (les révisions sont déjà
|
|
||||||
répliquées sur les deux Gitea, aucune fuite nouvelle par push) ; la dette
|
|
||||||
(réécriture d'historique + re-tags + force-push des 2 remotes + re-publish des
|
|
||||||
releases) reste ouverte et doit être re-décisionnée si le dépôt passe PUBLIC.
|
|
||||||
- **`LabsGroupingTest`** (4) : v1.2.2 — regroupement de l'écran Analyses (paire E2+T
|
- **`LabsGroupingTest`** (4) : v1.2.2 — regroupement de l'écran Analyses (paire E2+T
|
||||||
même timestamp ; timestamps différents séparés ; tri E2 avant T avant autres ;
|
même timestamp ; timestamps différents séparés ; tri E2 avant T avant autres ;
|
||||||
ordre chronologique décroissant).
|
ordre chronologique décroissant).
|
||||||
@ -501,12 +474,7 @@ l'entier PAIR !), début = prochaine occurrence à l'heure de rappel (ou 12:00).
|
|||||||
L'id est stocké sur le traitement (`calendarEventId`, Room v3) ; au save :
|
L'id est stocké sur le traitement (`calendarEventId`, Room v3) ; au save :
|
||||||
supprimer + recréer (fiable) si changé, supprimer si désactivé. Permissions
|
supprimer + recréer (fiable) si changé, supprimer si désactivé. Permissions
|
||||||
WRITE_CALENDAR + READ_CALENDAR demandées à l'activation du switch.
|
WRITE_CALENDAR + READ_CALENDAR demandées à l'activation du switch.
|
||||||
Manifest : `POST_NOTIFICATIONS`, `SCHEDULE_EXACT_ALARM`, `RECEIVE_BOOT_COMPLETED`,
|
Manifest : `POST_NOTIFICATIONS`, `SCHEDULE_EXACT_ALARM`, `RECEIVE_BOOT_COMPLETED`, `VIBRATE`, `WRITE_CALENDAR`, `READ_CALENDAR`.
|
||||||
`VIBRATE`, `WRITE_CALENDAR`, `READ_CALENDAR`.
|
|
||||||
⚠️ **Permissions agenda (v1.3.3)** : WRITE_CALENDAR + READ_CALENDAR ont été
|
|
||||||
ajoutées au manifest seulement en v1.3.3 — elles étaient documentées ici
|
|
||||||
depuis v1.3.0 mais **jamais déclarées dans le XML** (bug #44, §14) : tout
|
|
||||||
jeton « le manifest contient X » doit être VÉRIFIÉ dans le fichier réel.
|
|
||||||
Sur la montre : remontée par Gadgetbridge **ou** Huawei Health (cf §17).
|
Sur la montre : remontée par Gadgetbridge **ou** Huawei Health (cf §17).
|
||||||
|
|
||||||
## 10. UI & navigation
|
## 10. UI & navigation
|
||||||
@ -767,18 +735,12 @@ Historique complet — **à ne pas réintroduire** (utile pour diff/revert) :
|
|||||||
(openOutputStream "wt") + journalisation AppLog à chaque étape.
|
(openOutputStream "wt") + journalisation AppLog à chaque étape.
|
||||||
Leçon : quand un pattern d'IO marche (export JSON), le réutiliser
|
Leçon : quand un pattern d'IO marche (export JSON), le réutiliser
|
||||||
tel quel plutôt que d'en écrire un nouveau.
|
tel quel plutôt que d'en écrire un nouveau.
|
||||||
38. **Piège lexicographique de tags/versions** (v1.3.0, publication
|
41. **Comparaison lexicographique de tags/versions** (v1.3.0, publication
|
||||||
farewell) : `tag >= "v1.2.5"` en comparaison de CHAÎNES fait `v1.2.10 <
|
farewell) : `tag >= "v1.2.5"` en comparaison de CHAÎNES fait `v1.2.10 <
|
||||||
v1.2.5` ("1" < "5") → la boucle de publication a sauté v1.2.10. →
|
v1.2.5` ("1" < "5") → la boucle de publication a sauté v1.2.10. →
|
||||||
toujours comparer les versions STRUCTURÉEMENT (tuple numérique, cf
|
toujours comparer les versions STRUCTURÉEMENT (tuple numérique, cf
|
||||||
ChangelogHelper.isVersionNewer) ; même famille que le bug #22 (casse
|
ChangelogHelper.isVersionNewer) ; même famille que le bug #22 (casse
|
||||||
EEn) : identifiants « presque pareils ». (Numéro corrigé en v1.3.3 :
|
EEn) : identifiants « presque pareils ».
|
||||||
l'entrée était dupliquée sous « #41 » alors que §2/§16 référencent #38.)
|
|
||||||
43. **Course entre uploads rapprochés** (v1.2.6→v1.3.2, confirmé 4 fois) :
|
|
||||||
publier les 2 APK par DEUX INVOCATIONS rapprochées de gitea-release.py
|
|
||||||
→ le second upload écrase le premier (APK release disparu de la
|
|
||||||
release). → `scripts/publish-release.py` : UNE invocation fait purge +
|
|
||||||
upload des 2 APK + vérification PAR TÉLÉCHARGEMENT de chacun (§16.bis).
|
|
||||||
36. **Sens des boutons de zoom inversé** (v1.2.10, retour utilisateur) :
|
36. **Sens des boutons de zoom inversé** (v1.2.10, retour utilisateur) :
|
||||||
le « + » dézoomait (fenêtre plus longue) et le « − » zoomait — contre
|
le « + » dézoomait (fenêtre plus longue) et le « − » zoomait — contre
|
||||||
la convention carte. → « + » = zoom avant (fenêtre courte), « − » =
|
la convention carte. → « + » = zoom avant (fenêtre courte), « − » =
|
||||||
@ -802,41 +764,6 @@ Historique complet — **à ne pas réintroduire** (utile pour diff/revert) :
|
|||||||
(chips, dropdown création, rappels) ; simulation et calibration reçoivent
|
(chips, dropdown création, rappels) ; simulation et calibration reçoivent
|
||||||
TOUS les traitements. Épinglé par la régression n°3.
|
TOUS les traitements. Épinglé par la régression n°3.
|
||||||
|
|
||||||
**Session v1.3.3 (audit de reprise de maintenance — session IA démarrant sans
|
|
||||||
aucun contexte, dans l'esprit de cette doc) :**
|
|
||||||
|
|
||||||
44. **Permissions agenda absentes du manifest** (v1.3.0 → v1.3.2) : la
|
|
||||||
fonctionnalité « événement d'agenda récurrent » ne pouvait JAMAIS
|
|
||||||
fonctionner — `WRITE_CALENDAR`/`READ_CALENDAR` n'étaient **pas déclarées
|
|
||||||
dans AndroidManifest.xml** (la demande limitée à l'exécution est refusée
|
|
||||||
d'office sans déclaration, et `CalendarEvents.ensureCalendar/upsert`
|
|
||||||
lèvent SecurityException). La doc §9 les listait pourtant depuis v1.3.0
|
|
||||||
→ leçon : « documenté » ≠ « implémenté » ; toute affirmation « le
|
|
||||||
manifest contient X » doit être vérifiée dans le XML réel (30 s de
|
|
||||||
`grep uses-permission`). Les fixes v1.3.1/v1.3.2 du « toggle agenda »
|
|
||||||
réparaient la logique UI mais pas la cause racine. FIX : déclaration +
|
|
||||||
commentaire dans le manifest.
|
|
||||||
45. **Export des logs plantait TOUJOURS l'app** (le « fix » v1.3.2, #41
|
|
||||||
plus haut, était insuffisant) : l'IO restait réimplémentée inline
|
|
||||||
(leçon du #41 : réutiliser BackupManager — PAS appliquée) et 3 appels
|
|
||||||
`AppLog.log` du callback étaient HORS try/catch — or une exception non
|
|
||||||
interceptée dans une coroutine à scope racine (`CoroutineScope
|
|
||||||
(Dispatchers.IO)`) remonte au handler de la thread = crash du process.
|
|
||||||
FIX : (a) l'export réutilise `BackupManager.writeBackup` TEL QUEL —
|
|
||||||
le même code path que l'export JSON qui marche sur le Pixel 9 /e/OS ;
|
|
||||||
(b) tout le callback est gardé ; (c) `AppLog.log` est fail-safe
|
|
||||||
(l'IO fichier est avalée dans le wrapper — une erreur disque ne doit
|
|
||||||
jamais tuer l'app pour une ligne de journal) ; (d) feedback visible
|
|
||||||
succès/échec + `logLineCount` rafraîchi (sur Main).
|
|
||||||
46. **Bump de version jamais commité** (v1.3.0 → v1.3.2) : les tags
|
|
||||||
v1.3.0/v1.3.1/v1.3.2 contenaient TOUS `versionCode = 14`
|
|
||||||
/ `versionName = "1.3.0"` → `BuildConfig.VERSION_NAME` était faux dans
|
|
||||||
les APK publiés (Paramètres, titre du dialog « Nouveautés »,
|
|
||||||
comparaison `isVersionNewer`). La checklist §16 étape 1 existait mais
|
|
||||||
n'a jamais été appliquée. FIX : versionCode 17 / « 1.3.3 » commité
|
|
||||||
AVANT le tag ; le bump fait partie du commit de release (jamais un
|
|
||||||
état local non commité au moment du build).
|
|
||||||
|
|
||||||
**Leçons** : (a) ne jamais croire un build « probablement bon » sans l'avoir lancé ;
|
**Leçons** : (a) ne jamais croire un build « probablement bon » sans l'avoir lancé ;
|
||||||
(b) les tests sémantiques attrapent ce que la compilation ne voit pas ; (c) se méfier des
|
(b) les tests sémantiques attrapent ce que la compilation ne voit pas ; (c) se méfier des
|
||||||
constantes stdlib « de mémoire » (`ln2`), des mélanges Float/Double, et des APIs M3
|
constantes stdlib « de mémoire » (`ln2`), des mélanges Float/Double, et des APIs M3
|
||||||
@ -846,16 +773,13 @@ synthétiques ratent — mais garde ces données **hors du dépôt** (§8.bis) ;
|
|||||||
(e) attention aux identifiants « presque pareils » entre sources (constantes app
|
(e) attention aux identifiants « presque pareils » entre sources (constantes app
|
||||||
vs clés d'asset) ; (f) après un échec de COMPILATION, jeter les résultats de tests
|
vs clés d'asset) ; (f) après un échec de COMPILATION, jeter les résultats de tests
|
||||||
de la même passe (XML périmés) ; (g) tout script de réécriture d'historique doit
|
de la même passe (XML périmés) ; (g) tout script de réécriture d'historique doit
|
||||||
être idempotent ; (h) dans un KDoc, `/**` imbrique ; (i) quand un pattern d'IO
|
être idempotent ; (h) dans un KDoc, `/**` imbrique. (a) ne jamais croire un build « probablement bon » sans l'avoir lancé ;
|
||||||
marche (export JSON), le réutiliser TEL QUEL — une réimplémentation « équivalente »
|
(b) les tests sémantiques attrapent ce que la compilation ne voit pas ; (c) se méfier des
|
||||||
perd les garde-fous acquis à l'usage (cf #45) ; (j) vérifier les POSTULATS dans le
|
constantes stdlib « de mémoire » (`ln2`), des mélanges Float/Double, et des APIs M3
|
||||||
code réel, pas dans la doc (« le manifest contient WRITE_CALENDAR », cf #44) ;
|
expérimentales sans `@OptIn` ; (d) **un test de régression sur les VRAIES données
|
||||||
(k) un bump de version non commité = métadonnées fausses dans les APK publiés
|
utilisateur** (`RegressionUserCaseTest` = export JSON réel) attrape les bugs de
|
||||||
(cf #46) — le bump fait partie du commit de release ; (l) le scan de
|
convention (casse, unités) que les tests synthétiques ratent ; (e) attention aux
|
||||||
confidentialité s'ADAPTE aux nouveaux exports (`local-test-data/`) : se régénère
|
identifiants « presque pareils » entre sources (constantes app vs clés d'asset).
|
||||||
donc à CHECK chaque push — un historique « propre hier » peut devenir hit dès
|
|
||||||
qu'un nouvel export introduit des motifs qui collent ; juger les hits avec
|
|
||||||
l'utilisatrice (cf dette documentée §8.bis).
|
|
||||||
|
|
||||||
## 15. Comment régénérer l'asset pk_profiles.json
|
## 15. Comment régénérer l'asset pk_profiles.json
|
||||||
|
|
||||||
@ -1051,6 +975,7 @@ Volontaires (v1) :
|
|||||||
- Pas de ViewModel/DI (couplage UI↔repo via CompositionLocal)
|
- Pas de ViewModel/DI (couplage UI↔repo via CompositionLocal)
|
||||||
- Modèle T empirique (non publié) — étiqueté estimation partout
|
- Modèle T empirique (non publié) — étiqueté estimation partout
|
||||||
- Import JSON = **écrasement** depuis v1.2.6 (mode fusion non implémenté)
|
- Import JSON = **écrasement** depuis v1.2.6 (mode fusion non implémenté)
|
||||||
|
- `fallbackToDestructiveMigration()` — à retirer à la migration v2 du schéma
|
||||||
- WorkManager déclaré non utilisé
|
- WorkManager déclaré non utilisé
|
||||||
- Profils par **tables** (pas par formule) : les D/k1–k3 de l'ODS ne sont pas consommés —
|
- Profils par **tables** (pas par formule) : les D/k1–k3 de l'ODS ne sont pas consommés —
|
||||||
rétro-ingénierie des fits non tentée ; les tables sont exactes
|
rétro-ingénierie des fits non tentée ; les tables sont exactes
|
||||||
@ -1070,17 +995,16 @@ Volontaires (v1) :
|
|||||||
3. Mode « planifier les injections » (schedule récurrent → pré-remplir le log)
|
3. Mode « planifier les injections » (schedule récurrent → pré-remplir le log)
|
||||||
4. Import JSON : détection de doublons / mode fusion optionnel (l'écrasement est fait, v1.2.6)
|
4. Import JSON : détection de doublons / mode fusion optionnel (l'écrasement est fait, v1.2.6)
|
||||||
5. Verrou biométrique (BiometricPrompt), widget, export CSV
|
5. Verrou biométrique (BiometricPrompt), widget, export CSV
|
||||||
6. Charts : tooltip au toucher (le pan est fait v1.2.0, **le zoom v1.2.9**) ;
|
6. Charts : **zoom** + tooltip au toucher (le pan est fait, v1.2.0) ;
|
||||||
MaterialExpressiveTheme quand l'API passera publique (cf §3)
|
MaterialExpressiveTheme quand l'API passera publique (cf §3)
|
||||||
7. **Vrai keystore de distribution** (signature dédiée ≠ clé debug → nécessite
|
7. **Vrai keystore de distribution** (signature dédiée ≠ clé debug → nécessite
|
||||||
une migration : backup → désinstallation → installation signée → réimport)
|
une migration : backup → désinstallation → installation signée → réimport)
|
||||||
|
|
||||||
Fait (à ne pas refaire) : pan du chart (v1.2.0), **zoom du chart (v1.2.9)**,
|
Fait (à ne pas refaire) : pan du chart (v1.2.0), pics/creux (v1.2.3),
|
||||||
pics/creux (v1.2.3), prévision par fréquence (v1.2.0), calibration par période
|
prévision par fréquence (v1.2.0), calibration par période d'ester E2 **et** T
|
||||||
d'ester E2 **et** T (v1.2.1/v1.2.3), édition doses (v1.1.0) et labs (v1.2.2),
|
(v1.2.1/v1.2.3), édition doses (v1.1.0) et labs (v1.2.2), E2+T en une entrée
|
||||||
E2+T en une entrée (v1.2.2), migration Room v1→v2 sans fallback destructif
|
(v1.2.2), migration Room v1→v2 sans fallback destructif (v1.2.0), dépôt Gitea
|
||||||
(v1.2.0), rooms v2→v3 (calendarEventId, v1.3.0), dépôt Gitea + releases APK
|
+ releases APK (push session).
|
||||||
(push session).
|
|
||||||
8. Phase 2 montre : watchface `.hwt` custom, puis mini-app Lite Wearable (cf §17)
|
8. Phase 2 montre : watchface `.hwt` custom, puis mini-app Lite Wearable (cf §17)
|
||||||
9. Retirer WorkManager ou l'utiliser (reschedule de sécurité quotidien)
|
9. Retirer WorkManager ou l'utiliser (reschedule de sécurité quotidien)
|
||||||
|
|
||||||
@ -1121,25 +1045,6 @@ Sur le téléphone de test (à compléter par l'utilisatrice) :
|
|||||||
- [ ] **v1.2.4** : passer un traitement à inactif → retiré de « Log rapide » et
|
- [ ] **v1.2.4** : passer un traitement à inactif → retiré de « Log rapide » et
|
||||||
du dropdown des nouvelles doses, rappel annulé, **mais sa simulation reste
|
du dropdown des nouvelles doses, rappel annulé, **mais sa simulation reste
|
||||||
sur le graphique** et la calibration couvre toujours ses périodes
|
sur le graphique** et la calibration couvre toujours ses périodes
|
||||||
- [ ] **v1.3.3** : **export des logs** → le gestionnaire de fichiers s'ouvre,
|
|
||||||
le .txt est écrit, un message « Logs de diagnostic exportés » s'affiche ;
|
|
||||||
cas d'échec (annulation du chooser = silencieux, échec d'écriture =
|
|
||||||
message d'erreur, l'app ne doit JAMAIS planter — bug #45, Pixel 9 /e/OS)
|
|
||||||
- [ ] **v1.3.3** : **version affichée** « 1.3.3 » dans Paramètres et en titre du
|
|
||||||
dialog « Nouveautés » (bug #46 : les APK v1.3.0–1.3.2 affichaient 1.3.0) ;
|
|
||||||
au premier lancement de v1.3.3, le dialog liste 1.3.1→1.3.3 (dernière vue
|
|
||||||
mémorisée = « 1.3.0 » sur les anciens APK)
|
|
||||||
- [ ] **v1.3.3** : **événement d'agenda** : le switch demande la permission
|
|
||||||
agenda (cette fois le système doit vraiment montrer le dialogue de
|
|
||||||
permission — elle est désormais dans le manifest, bug #44) ; après
|
|
||||||
accord, l'événement récurrent apparaît dans l'app d'agenda du Pixel 9 ;
|
|
||||||
le smoke-test R8 de la checklist §16 doit aussi couvrir export/import
|
|
||||||
de backup + graphiques (réflexion Gson sous R8)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
*Doc mise à jour le 6 sept. 2026 (v1.3.3) — build OK, 87/87 tests verts (44 sans les
|
*Doc mise à jour le 6 sept. 2026 (v1.3.2) — build OK, 87/87 tests verts (44 sans les données locales), dépôts Gitea (cloudyfy + farewell) avec releases APK, aucune donnée de santé dans le dépôt ni l'historique.*
|
||||||
données locales), dépôts Gitea (cloudyfy + farewell) avec releases APK, aucune donnée
|
|
||||||
de santé dans le dépôt ni l'historique. v1.3.3 = session de reprise de maintenance :
|
|
||||||
3 bugs racines corrigés (permissions agenda, export logs, bump de version) ; doc
|
|
||||||
raccourcie (DB v3, targetSdk 36, régression 3 = export v1.3.1, numéros #38/#43 rétablis,
|
|
||||||
staleness §19/§20 nettoyé).*
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user