eric / piclead-client
client mobile pour Piclead, permet les capture de cartes de visites ainsi que de séparer son carnet d'adresse pro et perso.
| android | ||
| docs | ||
| ios | ||
| .gitignore | 930 B | |
| CONFIDENTIALITE.md | 7 kB | |
| LICENSE | 33 kB | |
| README.md | 19 kB | |
| design.md | 23 kB | |
| icon.png | 373 kB |
README.md
PicLead
Documentation build/technique de l'app Android. Vue d'ensemble du produit : README racine · mode d'emploi écran par écran : guide utilisateur.
Application Android hors-ligne : scan d’une carte de visite → OCR local (Tesseract) → brouillon éditable → carnet local (SQLite), contact Android et export .vcf.
Aucune dépendance Google Play Services / ML Kit, aucun LLM. OCR et carnet restent 100 % hors-ligne ; la permission Internet sert uniquement à la sync optionnelle avec un serveur PicLead (voir ci-dessous).
Mini-CRM (carnet local)
L’écran d’accueil est un carnet offline stocké en SQLite (Room) sur l’appareil :
- Recherche FTS sur nom, entreprise, téléphones, e-mails et notes
- Tri (nom, prénom, entreprise, date) + abcédaire
- Fiche contact (swipe), notes, édition, photo de profil, rescan de la carte (les notes sont conservées)
- Import VCF : lecture vCard 3.0 et 4.0, anti-doublons (skip si déjà présent)
- Export VCF : écriture vCard 3.0 uniquement
- Gestion des doublons : badge, revue, fusion ou marquage « contacts différents »
- Création : scan, saisie manuelle ou import
Sans configuration sync : aucun compte, aucune donnée CRM envoyée hors appareil (export VCF manuel possible).
Sync PicLead (optionnelle)
Sync offline-first avec un serveur PicLead (piclead-server) : carnet, projets, tâches kanban et comptes-rendus. L’OCR et le scan ne nécessitent pas Internet.
Prérequis serveur : variable PICLEAD_API_CLES (clés API par utilisateur), endpoints /api/auth/cle, /api/sync/status, /api/sync/pull. Le contrat de ces endpoints est figé par les fixtures Server/contrats/*.json, rejouées par les tests des trois plateformes.
Configuration dans l’app : Paramètres (engrenage) → URL HTTPS du serveur → identifiant + mot de passe → « Obtenir la clé » → retaper le mot de passe pour déchiffrer la clé API (stockée chiffrée sur l’appareil ; le mot de passe n’est jamais conservé).
Pour le développement, un réseau interne ou une démo sans TLS : cocher « Autoriser HTTP (clair) » avant de saisir une URL http://…. Sans cette case, l’app refuse le HTTP (HTTPS obligatoire). Ne pas activer cette option en production sur Internet.
Utilisation : à l’ouverture, un bandeau signale les changements distants sans pull automatique. Le bouton Sync pousse les modifications locales (contacts créés/édités dans le carnet y compris l’image de carte scannée et la photo de profil si présentes, tâches, CR…) puis tire le delta serveur. Sans sync configurée, le carnet reste local uniquement.
Upgrade schéma v2 : la migration Room peut réinitialiser les données locales (documenté dans le plan sync).
Tâches et planification (dépend de la sync)
Les projets et leurs tâches arrivent par la sync ; l’écran projet affiche un kanban par colonnes de workflow. Il n’y a pas de diagramme de Gantt sur téléphone : l’écran retenu est la carte kanban enrichie.
Quand le projet a la planification activée (réglage web), chaque carte porte :
- une pastille de couleur d’étiquette — même teinte que sur le web, le hachage étant reproduit à l’identique sur les trois plateformes ;
- la période « 22/07 → 24/07 », calculée en jours ouvrés selon le rythme du projet (5 = lun-ven, 6 = lun-sam, 7 = tous les jours) ;
- l’avancement des sous-tâches (« 2/5 fait ») ;
- un repère de retard si la fin dépasse l’échéance du projet ;
- la mention « ↳ regroupée » si la tâche a une tâche parente.
Si le projet n’active pas la planification, aucun de ces repères ne s’affiche — comme sur le web.
Éditable depuis le téléphone : titre, assigné, date de début (saisie en texte ISO AAAA-MM-JJ ; une date malformée bloque la validation plutôt que de partir au serveur), durée en jours ouvrés, étiquette (avec suggestions tirées des tâches du projet) et cases des sous-tâches.
Réservé au web : le diagramme de Gantt, l’édition des dépendances et du regroupement (transportés et conservés, mais non modifiables ici), ainsi que l’échéance et le rythme du projet.
duree_joursse compte en jours ouvrés, pas en jours calendaires. Les projets antérieurs sont neutralisés parjours_ouvres = 7; basculer un projet en rythme 5 allonge visiblement ses tâches en durée calendaire.
Agenda / calendriers (optionnel, dépend de la sync)
PicLead ne contient aucun écran agenda : les rendez-vous et réservations de ressources se consultent et se modifient dans l’app Agenda système (calendriers locaux, hors-GMS — aucun compte Google requis), via des calendriers dédiés que l’app crée et synchronise avec PicLead.
Permissions : READ_CALENDAR / WRITE_CALENDAR, demandées à l’exécution depuis Paramètres. Sans autorisation, le pont Agenda reste désactivé ; le carnet et la sync CRM (contacts/projets/tâches) continuent de fonctionner normalement.
Configuration dans l’app : Paramètres → section Calendriers (visible une fois connecté) :
- Toggle « Mes RDV » → crée/lie le calendrier local
PicLead — Mes RDV; les événements qui y sont créés/modifiés deviennent des RDV PicLead à la sync. - Si des identifiants sont configurés, la liste des ressources (salles, matériel, véhicules) du serveur s’affiche avec un toggle par ressource active ; les ressources inactives sont grisées (non sélectionnables). Activer une ressource crée le calendrier
PicLead — {Salle|Matériel|Véhicule} {nom}; un événement qui y est créé devient une réservation Active poussée au serveur. - Désactiver un toggle demande confirmation avant retrait. Le retrait supprime réellement le calendrier local et ses événements de l’appareil (
CalendarBridge.deleteCalendar, suppression des événements en cascade par Android), puis retire la liaison : le calendrier disparaît de l’app Agenda système. Les données serveur ne sont pas supprimées — vos RDV et réservations restent dans PicLead. Si l’autorisation Agenda a été révoquée entre-temps, la suppression échoue proprement : la liaison est retirée malgré tout et un message le signale dans la section Calendriers.
Sync et conflits : le bouton Sync pousse aussi les événements Agenda liés (RDV + réservations) puis tire le delta serveur (RDV, réservations actives, indisponibilités en lecture). Un bandeau signale un calendrier local en avance (événements pas encore poussés). Si une réservation entre en conflit avec le serveur (HTTP 409, chevauchement), PicLead conserve l’événement local, retire le blocage serveur pour affichage, puis propose un dialog « Annuler ma réservation ? » :
- Oui → la réservation locale et son événement Agenda sont supprimés.
- Non → rien n’est modifié ; l’arbitrage se fait sur le serveur web (demandes / réclamations restent hors périmètre mobile).
Plan d’implémentation : docs/plans/sync_agenda_rdv_ressources_v2.md. La spec de conception correspondante n’est pas versionnée (voir Documentation locale).
Room v3 (agenda) : l’ajout des tables RDV/réservations/indisponibilités passe la base Room en version 3 avec
fallbackToDestructiveMigration— comme pour l’upgrade v2 ci-dessus, la mise à jour réinitialise les données locales (carnet inclus) ; ré-exportez en VCF ou synchronisez avant de mettre à jour l’app si nécessaire.
Fonctionnement (scan / OCR)
- Cadrez la carte et capturez (CameraX).
- Correction géométrique (OpenCV) + orientation EXIF.
- Prétraitement contraste (gris + CLAHE + binarisation adaptative, repli sur l’image brute) puis OCR Tesseract (7 langues) + post-traitement.
- Structuration heuristique (pas de modèle de langage).
- Vérifiez le brouillon, puis :
- Enregistrer → carnet local (SQLite)
- Créer le contact →
ACTION_INSERTContacts - Exporter VCF → partage d’un fichier
.vcf(vCard 3.0)
Langues OCR
| Code | Langue |
|---|---|
fra | Français |
deu | Allemand |
eng | Anglais |
spa | Espagnol |
por | Portugais |
ita | Italien |
pol | Polonais |
Sélection des langues dans l’app
Les langues effectivement chargées par le moteur sont celles que l’utilisateur coche dans
Paramètres → Modèles hors-ligne, parmi celles réellement installées. Par défaut : fra et eng.
Une langue cochée mais non installée est simplement ignorée ; si aucune langue n’est
disponible, le scan n’échoue pas en erreur : il propose le téléchargement, ou la saisie
manuelle sur la photo de carte (voir « Saveurs » ci-dessous).
Saveurs de distribution : fdroid et direct
| Saveur | Modèles dans le APK | Taille release | Usage |
|---|---|---|---|
fdroid | aucun — téléchargés depuis l’app | ~94 Mo | catalogue F-Droid |
direct | tessdata + Vosk embarqués | ~182 Mo (full) | bêta-testeurs, distribution directe |
cd android ./gradlew :app:assembleFdroidDebug # sans modèle, aucun téléchargement au build ./gradlew :app:assembleDirectDebug # modèles embarqués, variante fast ./gradlew :app:assembleDirectDebug -Ptessdata=full # modèles embarqués, variante full
Les propriétés -Ptessdata=fast|full et -Pvosk=standard|none ne concernent que la saveur
direct : la saveur fdroid n’embarque rien et ne déclenche donc aucun téléchargement au build.
Variante direct | Propriété Gradle | Source | Taille approx. |
|---|---|---|---|
| fast (défaut) | -Ptessdata=fast | tessdata_fast | ~18 Mo |
| full | -Ptessdata=full | tessdata | ~80 Mo+ |
Premier build d’une variante direct : téléchargement dans android/tessdata-cache/{fast,full}/ (gitignored), puis injection dans les assets générés. Réseau requis uniquement si le cache est vide.
Suivi des modèles (le modèle a-t-il changé ?)
Les modèles sont figés au build : jamais re-téléchargés tant que le cache existe, jamais mis à jour au runtime. Les dépôts upstream tessdata_fast/tessdata sont d’ailleurs stables depuis des années ; les gains de précision viennent de la variante full, du prétraitement d’image (OcrPreprocessor) et des montées de version de Tesseract4Android — pas de nouveaux traineddata.
cd android ./gradlew tessdataStatus # variante fast ./gradlew tessdataStatus -Ptessdata=full # variante full
Pour chaque langue : état vs upstream GitHub (à jour / DIFFÉRENT / cache absent), taille et SHA-256. Le build embarque aussi model-info.txt (taille + SHA-256 par langue) dans les assets, à côté de variant.txt. Pour forcer un re-téléchargement : supprimer android/tessdata-cache/ puis relancer un build.
Chargement ultérieur (sans rebuild)
Au runtime, Tesseract lit filesDir/tesseract/tessdata/ :
- Saveur
direct, première exécution (ou changement fast↔full du APK) → copie depuis les assets embarqués. - Saveur
fdroid→ rien dans les assets : les modèles arrivent par Paramètres → Modèles hors-ligne, téléchargés depuistessdata_fast(ou un miroir interne configurable) et vérifiés par SHA-256 épinglé. - Ensuite → réutilisation des fichiers locaux.
- Sideload : déposer/remplacer les
*.traineddatadans ce dossier (ex.adb push), puis éventuellement écrire le stampexternalpour empêcher l’écrasement au prochain changement de variante APK :
/data/data/fr.ebii.piclead/files/tesseract/tessdata/ fra.traineddata … .card2vcf-tessdata-variant # "fast" | "full" | "external"
Le téléchargement des modèles est explicitement déclenché par l’utilisateur (bouton dédié) :
rien n’est récupéré en arrière-plan, conformément à la politique d’inclusion F-Droid. Les modèles
tessdata_fast et vosk-model-small-fr sont sous licence Apache 2.0.
APK debug filtré arm64-v8a. Pour émulateur x86, retirez temporairement ndk.abiFilters dans app/build.gradle.kts.
Release signée (bêta-testeurs)
La signature release est lue depuis android/keystore.properties (gitignoré), qui pointe vers le keystore android/keystore/piclead-release.jks (gitignoré aussi). Sans ce fichier, assembleRelease produit un APK non signé, ininstallable par les testeurs.
⚠️ Sauvegardez
keystore/piclead-release.jks+keystore.propertieshors git (coffre, sauvegarde chiffrée). Toute mise à jour doit être signée avec la même clé : une clé perdue ou changée force les testeurs à désinstaller/réinstaller (perte des données locales).
Mise en place sur une nouvelle machine (à partir du keystore sauvegardé) — format de android/keystore.properties :
storeFile=keystore/piclead-release.jks storePassword=… keyAlias=piclead keyPassword=…
À chaque bêta :
cd android # 1. Incrémenter versionCode (obligatoire pour qu'Android accepte la mise à jour) # et versionName dans app/build.gradle.kts # 2. Builder (mêmes variantes tessdata que le debug) ./gradlew :app:assembleDirectRelease # bêta-testeurs, fast (défaut) ./gradlew :app:assembleDirectRelease -Ptessdata=full # bêta-testeurs, full ./gradlew :app:assembleFdroidRelease # sans modèle # 3. Récupérer l'APK nommé d'après l'application et sa saveur ls app/build/outputs/apk/*/release/PicLead-*-release.apk
Vérification optionnelle de la signature avant envoi :
"$HOME"/Android/Sdk/build-tools/*/apksigner verify --print-certs \ app/build/outputs/apk/direct/release/PicLead-*-release.apk
Distribution : envoyez le fichier PicLead-<version>-direct-release.apk tel quel (mail, lien) ; à l'installation, Android demandera d'autoriser l'installation depuis une source inconnue.
Publication en magasin :
docs/publication-fdroid.md(F-Droid) etdocs/publication-google-play.md(Google Play, App Bundle signé avec la même clé d’upload). La politique de confidentialité déclarée à Play estCONFIDENTIALITE.md, versionnée à la racine de ce dossier.
Build
Quatre variantes : deux saveurs (fdroid, direct) × deux types (debug, release).
Toutes les commandes se lancent depuis android/.
| Variante | Commande | APK produit |
|---|---|---|
fdroid debug | ./gradlew :app:assembleFdroidDebug | app/build/outputs/apk/fdroid/debug/PicLead-<version>-fdroid-debug.apk |
fdroid release | ./gradlew :app:assembleFdroidRelease | app/build/outputs/apk/fdroid/release/PicLead-<version>-fdroid-release.apk |
direct debug | ./gradlew :app:assembleDirectDebug | app/build/outputs/apk/direct/debug/PicLead-<version>-direct-debug.apk |
direct release | ./gradlew :app:assembleDirectRelease | app/build/outputs/apk/direct/release/PicLead-<version>-direct-release.apk |
Ajouter -Ptessdata=full aux variantes direct pour embarquer les modèles full
au lieu des fast (voir « Saveurs de distribution » ci-dessus). La saveur fdroid
ignore ces propriétés : elle n’embarque rien et n’accède pas au réseau au build.
cd android ./gradlew clean :app:assembleFdroidRelease # build F-Droid reproductible, hors ligne ./gradlew :app:testFdroidDebugUnitTest ./gradlew :app:testDirectDebugUnitTest
Les tests d’absence de modèle vivent dans la source set app/src/testFdroid/ : la saveur
direct embarque ses traineddata, l’absence n’y est pas reproductible.
Vérifier qu’un APK fdroid est bien vide de modèles (doit afficher 0) :
unzip -l app/build/outputs/apk/fdroid/release/PicLead-*-fdroid-release.apk \ | grep -cE 'traineddata|vosk-model'
Install
adb install -r android/app/build/outputs/apk/fdroid/debug/PicLead-<version>-fdroid-debug.apk adb install -r android/app/build/outputs/apk/direct/release/PicLead-<version>-direct-release.apk
Prérequis : JDK 21, Android SDK (API 35).
Limites (assumées)
- Pas de LLM : la qualité du brouillon dépend des heuristiques + du texte OCR. Cartes très décoratives ou manuscrites peuvent échouer.
- Caméra obligatoire (
android.hardware.camerarequired) pour le scan ; le carnet et l’import VCF restent utilisables sans. - Pas de sync obligatoire : sans configuration PicLead, le carnet est local uniquement ; perte/désinstallation = perte des données sauf export VCF ou sync préalable.
- Structuration sémantique avancée (homonymes, mises en page atypiques) hors périmètre.
Design
UI N&B éditoriale (design.md) : coins carrés, hairlines, polices TTF embarquées (Playfair Display, Lora, Manrope — sous licence OFL). Icône : icon.png.
L’OFL impose que sa copie accompagne les polices redistribuées : le texte et les trois notices de copyright sont embarqués dans l’app (
app/src/main/res/raw/licence_polices_ofl.txt) et accessibles par À propos → Texte de la licence des polices. C’est la source unique ;docs/licenses/OFL-fonts.txtn’est qu’une archive locale.
Documentation locale (non versionnée)
Le .gitignore racine exclut docs/plans, Server/docs/plans, Server/docs/superpowers, Client/docs/plans/ et Client/docs/superpowers/ ; Client/.gitignore exclut en plus tout docs/*. Une partie de la documentation de travail n’est donc pas dans le dépôt, et les liens qui y pointaient ont été retirés de ce README.
Présent en local seulement, côté client :
| Fichier | Contenu |
|---|---|
docs/publication-fdroid.md | Procédure de publication F-Droid (recette YAML, checklist) |
docs/publication-google-play.md | Procédure de publication Google Play (signature, AAB, fiche, Sécurité des données) |
docs/superpowers/specs/2026-07-22-sync-agenda-rdv-ressources-v2-design.md | Conception de la sync agenda / ressources v2 |
docs/audit-securite-2026-07-27.md | Audit de sécurité |
docs/licenses/OFL-fonts.txt | Archive locale de la licence des polices (la version embarquée est dans res/raw) |
docs/bandeau-play.sh | Génère le bandeau 1024 × 500 de Play depuis l’icône et la charte |
docs/captures-fastlane.sh | Captures fastlane (téléphone et tablettes) sur une copie isolée de l’app |
Les plans du monorepo (docs/plans/ à la racine, dont les chantiers modèles OCR téléchargeables et planification des tâches) sont dans le même cas.
Seuls les six plans historiques de Client/docs/plans/ restent suivis : ils avaient été committés avant l’ajout des règles d’exclusion.
Un point à trancher, hors périmètre de ce README : l’exclusion de
docs/planscontredit son propre commentaire dans le.gitignore(« les plans versionnés restent dansdocs/plans/») et la consigne duCLAUDE.md(« les plans vivent dans le dépôt, avec le code »).
Origine du code
Pipeline scan/OCR/contact adapté depuis le projet open source luciole-mobile (AGPL), sans le module LLM (cerveau).
GitRust