# 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 ```