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> |
||
|---|---|---|
| .. | ||
| android | ||
| src | ||
| .gitignore | ||
| BUILD-ANDROID.md | ||
| capacitor.config.json | ||
| CI-SETUP.md | ||
| docker-build.sh | ||
| Dockerfile | ||
| index.html | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| vite.config.js | ||
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 ci-dessous.
- Design détaillé :
docs/field-tech-app.md· build approfondi :BUILD-ANDROID.md· CI :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) oupairPaste()lit le lien tech (techFieldLink), extrait le token, le persiste. Plus jamais de lien à ouvrir sur place. - Géorepérage natif —
@transistorsoft/capacitor-background-geolocationenregistre un geofence par job du jour (identifier = token signé du job). Lesenter/exitsont POSTés nativement au hub/field/ts(déjà déployé) qui mappe l'identifier → checkpoint → dériveactual_start/actual_end. Survit à l'app fermée et au reboot — c'est tout l'intérêt par rapport à unwatchPositionJS. - UI hébergée — après appairage, l'app charge
/field?t=token(servi par le hublib/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
BarcodeDetectorweb 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'uncap sync). Ils ne sont à ré-appliquer que si on régénèreandroid/from scratch (rm -rf android && npx cap add android).
Prérequis (testé sur MacBook Pro M4, sans sudo)
# 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)
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.
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/endse 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. 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 publicpk.…(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