App technicien : appairage 1x (QR), géorepérage natif en arrière-plan (app fermée) -> checkpoints au hub /field/ts, scan série/MAC on-device MLKit. UI réutilisée depuis le hub (/field). APK Android buildé (debug, 19 Mo, arm64-v8a + armeabi-v7a). - apps/field-tech : Capacitor 6 + Vite ; src/main.js (appairage @capacitor/preferences + BackgroundGeolocation.addGeofences + redirection /field) ; projet android/ avec les 4 correctifs Gradle commités (force work-runtime 2.9.1, minSdk 24, repos maven xms.g + Huawei, googlePlayServicesLocationVersion 21.0.1) ; abiFilters arm (APK 32->19 Mo). - README : recette de build complète (toolchain M4, les 4 fixes, install/appairage) ; BUILD-ANDROID.md (Docker + Android Studio + release signée) ; CI-SETUP.md (runner Gitea). - CI Gitea Actions (.gitea/workflows) : android (runner Linux HORS prod, cache Gradle/npm -> artefact APK) + ios (runner macOS, workflow_dispatch, en attente compte Apple). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
107 lines
8.3 KiB
Markdown
107 lines
8.3 KiB
Markdown
# Targo Tech — app technicien (Capacitor)
|
||
|
||
App mobile native des techniciens TARGO/Gigafibre. **Capture passive** du temps par intervention : appairage **une seule fois** (QR), puis géorepérage natif en **arrière-plan** (app fermée) → arrivées/départs détectés automatiquement → checkpoints au hub. Le tech ne tape rien sur place. UI (liste/carte/Street View/photo/scan) **réutilisée depuis le hub** (`/field`). Scan série/MAC **on-device MLKit**.
|
||
|
||
- **Statut : APK Android buildé ✅** (debug, sideload) — voir [recette de build](#recette-de-build-android) ci-dessous.
|
||
- Design détaillé : [`docs/field-tech-app.md`](../../docs/field-tech-app.md) · build approfondi : [`BUILD-ANDROID.md`](BUILD-ANDROID.md) · CI : [`CI-SETUP.md`](CI-SETUP.md).
|
||
|
||
---
|
||
|
||
## Fonctionnement
|
||
|
||
```
|
||
┌─ Bureau (1×) ─────────┐ ┌─ Téléphone du tech ──────────────────────┐
|
||
│ Ops → bouton 📱 tech │ QR │ app Targo Tech │
|
||
│ techFieldLink (token) ├────▶│ src/main.js : token → @capacitor/ │
|
||
└───────────────────────┘ │ preferences (persistant) │
|
||
│ │ │
|
||
│ ▼ │
|
||
│ Transistorsoft BackgroundGeolocation: │
|
||
│ addGeofences(jobs du jour, r=200m) │
|
||
│ enter/exit ── autoSync ──▶ hub /field/ts │ ← app FERMÉE + reboot
|
||
│ │ │
|
||
│ ▼ (à l'ouverture) │
|
||
│ location.replace(hub /field?t=token) │
|
||
│ → liste/détail/carte/StreetView/photo │
|
||
│ → scan série/MAC = MLKit on-device │
|
||
└───────────────────────────────────────────┘
|
||
```
|
||
|
||
- **Appairage 1×** — `pairScan()` (MLKit) ou `pairPaste()` lit le lien tech (`techFieldLink`), extrait le token, le persiste. Plus jamais de lien à ouvrir sur place.
|
||
- **Géorepérage natif** — `@transistorsoft/capacitor-background-geolocation` enregistre **un geofence par job du jour** (identifier = token signé du job). Les `enter/exit` sont POSTés **nativement** au hub `/field/ts` (déjà déployé) qui mappe l'identifier → checkpoint → dérive `actual_start`/`actual_end`. Survit à l'app fermée et au reboot — c'est tout l'intérêt par rapport à un `watchPosition` JS.
|
||
- **UI hébergée** — après appairage, l'app charge `/field?t=token` (servi par le hub `lib/roster.js`) : liste des jobs, carte Mapbox, Google Street View, photo, scan. Aucune UI dupliquée dans l'app.
|
||
- **Scan série/MAC** — MLKit on-device en priorité (instantané, hors-ligne) ; replis `BarcodeDetector` web puis proxy IA Gemini (`/field/vision`) pour étiquettes texte seul.
|
||
|
||
> Backend (tout déjà déployé sur le hub) : `GET /field/tech?t=` (jobs du jour du tech), `GET /field/job?t=`, `POST /field/checkpoint`, `POST /field/ts` (webhook geofence Transistorsoft, auto-auth via identifier signé), `POST /field/photo`, `POST /field/device`, `POST /field/vision`. Tokens = **HMAC signés** (par job / par tech), sans PII, stateless.
|
||
|
||
---
|
||
|
||
## Recette de build Android
|
||
|
||
> Les 4 correctifs Gradle ci-dessous sont **déjà appliqués et commités** dans `android/` (Capacitor ne réécrit pas ces fichiers lors d'un `cap sync`). Ils ne sont à ré-appliquer **que** si on régénère `android/` from scratch (`rm -rf android && npx cap add android`).
|
||
|
||
### Prérequis (testé sur MacBook Pro M4, sans sudo)
|
||
```bash
|
||
# JDK 17
|
||
brew install openjdk@17
|
||
export JAVA_HOME=/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home
|
||
|
||
# Android SDK (cmdline-tools) → ~/Library/Android/sdk
|
||
export ANDROID_HOME="$HOME/Library/Android/sdk"
|
||
sdkmanager "platform-tools" "platforms;android-34" "build-tools;34.0.0"
|
||
```
|
||
|
||
### Build (APK debug)
|
||
```bash
|
||
cd apps/field-tech
|
||
npm install
|
||
npm run build # Vite → www/
|
||
npx cap sync android # copie www + plugins (NE touche pas aux fixes Gradle)
|
||
cd android && ./gradlew assembleDebug
|
||
# → android/app/build/outputs/apk/debug/app-debug.apk
|
||
```
|
||
|
||
### Les 4 correctifs Gradle (pourquoi)
|
||
| # | Symptôme | Fichier | Correctif |
|
||
|---|----------|---------|-----------|
|
||
| 1 | `androidx.work:work-runtime:2.10.0 requires compileSdk 35` | `android/app/build.gradle` | `configurations.all { resolutionStrategy { force 'androidx.work:work-runtime:2.9.1'; force '…-ktx:2.9.1' } }` (on reste en compileSdk 34) |
|
||
| 2 | `Manifest merger failed: minSdkVersion 22 < 24 (tslocationmanager)` | `android/variables.gradle` | `minSdkVersion = 24` |
|
||
| 3 | `package com.transistorsoft.xms.g.common does not exist` | `android/build.gradle` | dans `allprojects.repositories` : les 2 repos maven locaux des plugins (`…background-geolocation/libs`, `…background-fetch/libs`) **+** `maven { url 'https://developer.huawei.com/repo/' }`. Les AAR `tslocationmanager*` (classes `xms.g`) vivent dans `node_modules/.../libs` ; le plugin n'ajoute pas ce repo → étape manuelle (= INSTALL-ANDROID officiel Transistorsoft). |
|
||
| 4 | `cannot find symbol EVENT_PROVIDERCHANGE/EVENT_AUTHORIZATION/…` (67 err) | `android/variables.gradle` | `googlePlayServicesLocationVersion = '21.0.1'` → le plugin sélectionne l'AAR `tslocationmanager-v21` (API moderne attendue par Capacitor v6.1.5 ; major <21 prendrait l'ancien 3.6.4 sans ces constantes). |
|
||
|
||
### Optimisation taille
|
||
`android/app/build.gradle` filtre les ABI sur `arm64-v8a` + `armeabi-v7a` (téléphones réels ; l'émulateur Apple Silicon est arm64 → test local OK). Pour un émulateur Intel x86_64, ajouter `'x86_64'` au bloc `ndk { abiFilters … }`.
|
||
|
||
### Release / Play Store (plus tard)
|
||
Préférer **compileSdk/targetSdk 35 + AGP 8.7+** (et retirer le force work-runtime) plutôt que rester en 34 ; + clé licence Transistorsoft en `meta-data` du manifest + keystore. Détails : [`BUILD-ANDROID.md`](BUILD-ANDROID.md).
|
||
|
||
---
|
||
|
||
## Installer & appairer
|
||
- **USB** (débogage USB activé) : `~/Library/Android/sdk/platform-tools/adb install -r app-debug.apk`
|
||
- **Sans fil** : envoyer l'APK (Drive/courriel) → ouvrir sur le tél → autoriser « Installer applis inconnues ».
|
||
- **1er lancement** → écran d'appairage → scanner le QR (ou coller le lien) du bouton **📱** dans Ops → l'app mémorise le token, arme le geofence, et charge la liste des jobs.
|
||
- **Tester le geofence** : se déplacer vers/depuis une adresse de job (rayon 200 m), **app fermée** → vérifier dans Ops que `actual_start/end` se posent.
|
||
|
||
## CI (git.targo.ca)
|
||
Push sur `apps/field-tech/**` → `.gitea/workflows/field-tech-android.yml` build l'APK sur un runner Linux auto-hébergé (**jamais sur erp/prod**) → artefact téléchargeable. Voir [`CI-SETUP.md`](CI-SETUP.md). iOS : `field-tech-ios.yml` (runner macOS + compte Apple, `workflow_dispatch`).
|
||
|
||
## iOS
|
||
Même `src/main.js`. Scaffolder quand le compte Apple Developer est validé : `npx cap add ios` + cocoapods + Xcode + licence Transistorsoft iOS + `NSLocationAlwaysAndWhenInUseUsageDescription` + mode arrière-plan `location`.
|
||
|
||
## Sécurité
|
||
- **Aucun secret dans le repo** : seul figure l'URL publique `https://msg.gigafibre.ca` (`src/main.js`) et le token Mapbox **public** `pk.…` (côté hub `/field`). Les secrets serveur restent dans le hub (`ops_secret.php` / env), jamais ici.
|
||
- Tokens d'appairage = **HMAC signés**, sans PII. Keystores / certificats : gitignorés (jamais commités).
|
||
|
||
## Arborescence
|
||
```
|
||
apps/field-tech/
|
||
├── index.html # écran d'appairage (coquille)
|
||
├── src/main.js # appairage + init géorepérage + redirection /field
|
||
├── capacitor.config.json # appId ca.targo.field
|
||
├── vite.config.js # build → www/
|
||
├── android/ # projet natif (4 fixes Gradle commités) — artefacts gitignorés
|
||
├── Dockerfile # build headless (alternative au build local) — voir BUILD-ANDROID.md
|
||
├── README.md BUILD-ANDROID.md CI-SETUP.md
|
||
```
|