diff --git a/Home.md b/Home.md index 84e195c..40fc8c4 100644 --- a/Home.md +++ b/Home.md @@ -4,57 +4,77 @@ Android-klient (Android 12+) för gym-API:t — samma backend som gym-delen i [brasse-pc.eu-v2](https://gitea.brasse-pc.eu/brasse/brasse-pc.eu-v2). README:n täcker "få det att bygga"; här ligger arkitektur och designbeslut. -## Arkitektur +## Arkitektur (M2) ``` MainActivity (Compose, edge-to-edge) -└── FitnessDroidApp — växlar på AuthState (Restoring/LoggedOut/LoggedIn) - ├── LoginScreen — användarnamn/lösenord + API-url under "Avancerat" - └── HomeScreen — profil + kommande flikar (pass, logg, statistik, PB) +└── FitnessDroidApp — växlar på AuthState + ├── LoginScreen + └── AppRoot — bottenrad (Hem/Pass/Statistik/Profil) + start-FAB + minispelare + ├── HomeScreen — starta fritt pass, favoritpass/mallar, synk-chip + ├── SessionScreen — passläget: HorizontalPager per övning, steppers, + │ vilotimer (helskärm/banner), synk-badge per set + ├── ExercisePicker — sök + muskelgrupp/muskel-chips, favoriter först + ├── HistoryScreen — avslutade pass (direkt mot API:t) + ├── ProfileScreen — profil + Inställningar + logga ut + └── SettingsScreen — vilotimerstil, larmläge, standardvila -AppContainer (manuell DI i Application) -├── TokenStore — DataStore; username/sessionId/jwt/refreshToken/expiration/apiUrl -├── GraphQlClient — OkHttp POST {query,variables} + Bearer-header -├── AuthRepository — login/restore/refresh/logout, exponerar StateFlow -└── GymApi — autentiserade queries (myProfile, växer per milstolpe) +AppContainer (manuell DI) +├── TokenStore / SettingsStore (DataStore) +├── GraphQlClient (OkHttp; 5xx → IOException = nätfel, inte auth-fel) +├── AuthRepository — login/refresh/restore; loggar INTE ut vid nätfel +├── AppDatabase (Room) — LocalSession/-Exercise/-Set, PendingOp, +│ CachedExerciseType/MuscleGroup/Muscle/StartCard +├── SyncEngine — FIFO-op-kö, retry m. backoff, server-id-mappning +├── GymRepository — lokal-först-fasad som UI:t pratar med +└── RestTimerController — absolut sluttid, larm enligt inställning ``` +## Offline-modellen + +Passet lever i Room. Varje åtgärd (starta pass, lägg övning, logga set, +avsluta) blir en rad i `pending_op` som SyncEngine kör i strikt ordning mot +GraphQL-API:t; server-id:n skrivs tillbaka på raderna. Ej synkade set får en +moln-upp-ikon, synkade en moln-check; hemskärmen visar global status +(Synkat / N osynkat / Offline / Synkfel). Uppdateras ett set som ännu inte +synkats bär den väntande ADD-opsen automatiskt de nya värdena (den läser +raden när den körs). Referensdata (övningar, muskler, favoritkort) cachas +och överlever offline; senaste prestation per övning cachas för förifyllnad. + +## Vilotimern + +Startar automatiskt när ett set loggas. Längd: övningens +`defaultRestSeconds` från API:t, annars appens standard (inställning). +Presentation enligt inställning: helskärms-takeover eller banner längst ner. +Vid utgång: larmljud / vibration / bara visuellt / inget (inställning). +Räknar mot absolut sluttid så den överlever pausad app. + +## API-tillägg (2026-07-24) + +- `MuscleGroup.iconKey` — stabil ikonnyckel ("chest", "back", "legs", + "shoulders", "arms", "core", "cardio", "fullbody"); null → namnheuristik + i appen (`MuscleIcons.kt`) +- `ExerciseType.defaultRestSeconds` — förslagen vilotid; null → appens standard +- Migration `20260724000000_AddIconKeyAndDefaultRestSeconds` (körs automatiskt + vid API-uppstart) + ## Designbeslut - **Handskrivna GraphQL-strängar** (samma som webbens `gymTrackerApi.js`) - i stället för Apollo-codegen — API-ytan ägs av samma person, och parity - med webben är enklast att verifiera strängvis. Kan omprövas när fler - queries tillkommit. -- **Auth speglar webben:** JWT (2 h) + roterande refresh-token (7 d) + - sessions-id; förnyas automatiskt 2 min före utgång bakom en mutex - (`AuthRepository.bearerToken()`). Vid nätfel vid appstart släpps - användaren in om JWT:n ännu är giltig. -- **Manuell DI** — appen är för liten för Hilt; `AppContainer` i - `FitnessDroidApplication`. -- **Material You** — dynamiska färger kan användas villkorslöst eftersom - minSdk är 31. -- **versionCode = GITHUB_RUN_NUMBER** i CI så att varje release-APK räknas - som uppdatering på telefonen. +- **Auth speglar webben:** JWT 2h + roterande refresh-token 7d, auto-refresh + 2 min före utgång bakom mutex; 5xx/nätfel loggar aldrig ut +- **Manuell DI**, **Material You** (minSdk 31), **versionCode = CI-byggnummer** +- Favoritpass startas genom att kopiera övningslistan klient-side + (fungerar offline); mallar ger även auto-progression via API:t -## Signering +## Signering & CI -Nyckeln är inte incheckad (publikt repo). Lokalt: `signing/` (gitignorerad). -CI: secrets `KEYSTORE_B64` + `KEYSTORE_PASSWORD` skrivs till samma filer i -jobbet. Utan nyckel faller release-bygget tillbaka på debug-signering — -bygger, men kan inte uppdatera en installation från släppsidan. - -## CI på arm64 (Pi5-runnern) - -Googles aapt2 distribueras bara för linux x86_64. Bygget använder därför en -statiskt byggd arm64-aapt2 från -[lzhiyong/android-sdk-tools](https://github.com/lzhiyong/android-sdk-tools) -via `-Pandroid.aapt2FromMavenOverride=…`. Allt annat i Android-bygget -(kotlinc, d8, apksigner) är JVM-baserat och kör nativt på arm64. -cmdline-tools + `platforms;android-35` installeras i jobbet; gradle-cache -via `actions/cache`. +Se README + [infra-Doc](https://gitea.brasse-pc.eu/brasse/infra-Doc): +nyckeln aldrig incheckad (Actions-secrets), CI bygger på Pi5/arm64 med +statisk arm64-aapt2 via `android.aapt2FromMavenOverride`. ## Milstolpar -Se [doc/plan.md](https://gitea.brasse-pc.eu/brasse/FitnessDroid/src/branch/main/doc/plan.md) -— 1: grund+login (klar), 2: aktivt pass, 3: passhistorik, 4: lyftlogg+PB, -5: statistik, 6: mallar, 7: grunddata+profil. +Se [doc/plan.md](https://gitea.brasse-pc.eu/brasse/FitnessDroid/src/branch/main/doc/plan.md). +1 (grund+login) och 2 (aktivt pass, offline, vilotimer) är klara. +Näst: passhistorik-detaljer, lyftlogg+PB, statistik med kroppskarta, mallar.