- Ajouter ADR-018 (librairies Go) dans TECHNICAL.md - Transformer Shared en menu dépliable dans mkdocs (cohérence avec autres domaines) - Corriger listes markdown (ajout lignes vides avant listes) - Corriger line breaks dans génération BDD (étapes "Et" sur nouvelles lignes) - Ajouter script fix-markdown-lists.sh pour corrections futures Impacte 86 fichiers de documentation et 164 fichiers BDD générés.
247 lines
11 KiB
Markdown
247 lines
11 KiB
Markdown
# ADR-012 : Frontend Mobile
|
|
|
|
**Statut** : Accepté
|
|
**Date** : 2025-01-20
|
|
|
|
## Contexte
|
|
|
|
RoadWave nécessite applications iOS et Android avec support CarPlay/Android Auto, lecture audio HLS avancée, géolocalisation temps réel. Le choix du framework impacte vélocité développement et performances.
|
|
|
|
## Décision
|
|
|
|
**Flutter** pour iOS et Android avec codebase unique.
|
|
|
|
## Alternatives considérées
|
|
|
|
| Framework | Codebase | Performance | Audio/CarPlay | Communauté |
|
|
|-----------|----------|-------------|---------------|------------|
|
|
| **Flutter** | Unique | Native | Excellente | Large |
|
|
| React Native | Unique | Bonne | Modules natifs requis | Très large |
|
|
| Native (Swift+Kotlin) | Double | Excellente | Native | Large |
|
|
| Ionic/Capacitor | Unique | Moyenne | Limitée | Moyenne |
|
|
|
|
## Justification
|
|
|
|
- **Codebase unique** : iOS + Android maintenus ensemble, vélocité développement x2
|
|
- **Performance** : Dart compilé en code natif (pas de bridge JS)
|
|
- **Audio HLS** : Package `just_audio` mature avec support HLS, buffering adaptatif
|
|
- **CarPlay/Android Auto** : Support via packages communautaires (`flutter_carplay`, `android_auto_flutter`)
|
|
- **Géolocalisation** : `geolocator` robuste avec gestion permissions
|
|
- **Écosystème** : Widgets riches (Material/Cupertino), state management mature (Bloc, Riverpod)
|
|
|
|
## Packages Flutter
|
|
|
|
> **Voir [ADR-020 - Librairies Flutter](020-librairies-flutter.md)** pour la liste complète des packages, licences, alternatives considérées et justifications détaillées.
|
|
|
|
**Packages clés pour RoadWave** :
|
|
|
|
- **State management** : `flutter_bloc` (pattern BLoC, testable, reactive)
|
|
- **Audio HLS** : `just_audio` (HLS natif, buffering adaptatif, background playback)
|
|
- **GPS temps réel** : `geolocator` (mode voiture haute précision)
|
|
- **Geofencing** : `geofence_service` (mode piéton, détection rayon 200m, économie batterie)
|
|
- **Notifications** : `flutter_local_notifications` (compteur dynamique, conformité CarPlay/Android Auto)
|
|
- **HTTP** : `dio` (client HTTP avec retry logic)
|
|
- **Stockage sécurisé** : `flutter_secure_storage` (JWT tokens, Keychain iOS, KeyStore Android)
|
|
- **Cache images** : `cached_network_image` (LRU cache)
|
|
|
|
**Points d'attention** :
|
|
|
|
- ⚠️ **Permissions progressives requises** pour `geofence_service` et `geolocator` (voir section "Stratégie de Permissions")
|
|
- ⚠️ **Licences** : 100% permissives (MIT, BSD-3) - voir ADR-020
|
|
|
|
## Stratégie de Permissions (iOS/Android)
|
|
|
|
### Contexte et Enjeux
|
|
|
|
**Problème** : La géolocalisation en arrière-plan (requise pour le mode piéton) est **très scrutée** par Apple et Google :
|
|
|
|
- **iOS App Store** : Taux de rejet ~70% si permission "Always Location" mal justifiée
|
|
- **Android Play Store** : `ACCESS_BACKGROUND_LOCATION` nécessite déclaration spéciale depuis Android 10
|
|
- **RGPD** : Permissions doivent être **optionnelles** et l'app **utilisable sans**
|
|
|
|
### Architecture de Permissions Progressive
|
|
|
|
**Principe** : Demander le **minimum** au départ, puis proposer upgrade **contextuel** uniquement si besoin.
|
|
|
|
#### Niveaux de Permissions
|
|
|
|
Trois niveaux de permissions doivent être gérés :
|
|
|
|
- **Denied** : Aucune permission → app limitée (contenu national)
|
|
- **When In Use** : "Quand l'app est ouverte" → mode voiture complet ✅
|
|
- **Always** : "Toujours" / Background → mode piéton ✅
|
|
|
|
#### Étape 1 : Permission de Base (Onboarding)
|
|
|
|
**Quand** : Premier lancement de l'app
|
|
|
|
**Demande** : `locationWhenInUse` uniquement
|
|
|
|
- iOS : "Allow While Using App"
|
|
- Android : `ACCESS_FINE_LOCATION`
|
|
|
|
**Justification affichée** :
|
|
```
|
|
📍 RoadWave a besoin de votre position
|
|
|
|
Pour vous proposer du contenu audio adapté
|
|
à votre localisation en temps réel.
|
|
|
|
[Autoriser] [Non merci]
|
|
```
|
|
|
|
**Si acceptée** : Mode voiture entièrement fonctionnel ✅
|
|
|
|
**Si refusée** : Mode dégradé (contenus nationaux/régionaux via GeoIP)
|
|
|
|
#### Étape 2 : Upgrade Optionnel (Contextuel)
|
|
|
|
**Quand** : Utilisateur **active explicitement** "Notifications audio-guides piéton" dans Settings
|
|
|
|
**Flow** :
|
|
1. **Écran d'éducation** (requis pour validation stores) :
|
|
```
|
|
┌────────────────────────────────────────┐
|
|
│ 📍 Notifications audio-guides piéton │
|
|
├────────────────────────────────────────┤
|
|
│ Pour vous alerter d'audio-guides à │
|
|
│ proximité même quand vous marchez avec │
|
|
│ l'app fermée, RoadWave a besoin de │
|
|
│ votre position en arrière-plan. │
|
|
│ │
|
|
│ Votre position sera utilisée pour : │
|
|
│ ✅ Détecter monuments à 200m │
|
|
│ ✅ Vous envoyer une notification │
|
|
│ │
|
|
│ Votre position ne sera jamais : │
|
|
│ ❌ Vendue à des tiers │
|
|
│ ❌ Utilisée pour de la publicité │
|
|
│ │
|
|
│ Cette fonctionnalité est optionnelle. │
|
|
│ Vous pouvez utiliser RoadWave sans │
|
|
│ cette permission. │
|
|
│ │
|
|
│ [Continuer] [Non merci] │
|
|
│ │
|
|
│ Plus d'infos : Politique confidentialité│
|
|
└────────────────────────────────────────┘
|
|
```
|
|
|
|
2. **Demande permission OS** : `locationAlways` / `ACCESS_BACKGROUND_LOCATION`
|
|
|
|
3. **Si refusée** :
|
|
- Toggle "Mode piéton" reste désactivé
|
|
- Message : "Mode piéton non disponible sans permission arrière-plan"
|
|
- **App reste pleinement fonctionnelle en mode voiture**
|
|
|
|
### Implémentation
|
|
|
|
Le service de gestion des permissions (`lib/core/services/location_permission_service.dart`) doit implémenter :
|
|
|
|
**Détection du niveau actuel** :
|
|
|
|
- Vérifier le statut de la permission `location` (when in use)
|
|
- Vérifier le statut de la permission `locationAlways` (background)
|
|
- Retourner le niveau le plus élevé accordé
|
|
|
|
**Demande de permission de base** (Étape 1) :
|
|
|
|
- Demander uniquement la permission `location` (when in use)
|
|
- Utilisée lors de l'onboarding
|
|
- Aucun écran d'éducation requis
|
|
|
|
**Demande de permission arrière-plan** (Étape 2) :
|
|
|
|
- **Toujours** afficher un écran d'éducation AVANT la demande OS
|
|
- Demander la permission `locationAlways` (iOS) ou `ACCESS_BACKGROUND_LOCATION` (Android)
|
|
- Si refusée de manière permanente, proposer l'ouverture des réglages système
|
|
|
|
**Gestion des refus** :
|
|
|
|
- Détecter si la permission est refusée de manière permanente
|
|
- Proposer l'ouverture des réglages de l'appareil avec un message clair
|
|
- Permettre à l'utilisateur d'annuler
|
|
|
|
### Configuration Platform-Specific
|
|
|
|
#### iOS (`ios/Runner/Info.plist`)
|
|
|
|
**Clés requises** :
|
|
|
|
- `NSLocationWhenInUseUsageDescription` : Décrire l'usage pour le mode voiture (contenu géolocalisé en temps réel)
|
|
- `NSLocationAlwaysAndWhenInUseUsageDescription` : Décrire l'usage optionnel pour le mode piéton (notifications audio-guides en arrière-plan), mentionner explicitement que c'est optionnel et désactivable
|
|
- `UIBackgroundModes` : Activer les modes `location` et `remote-notification`
|
|
|
|
**Exemple de texte pour `NSLocationAlwaysAndWhenInUseUsageDescription`** :
|
|
> "Si vous activez les notifications audio-guides piéton, RoadWave peut vous alerter lorsque vous passez près d'un monument ou musée, même quand l'app est en arrière-plan. Cette fonctionnalité est optionnelle et peut être désactivée à tout moment dans les réglages."
|
|
|
|
#### Android (`android/app/src/main/AndroidManifest.xml`)
|
|
|
|
**Permissions requises** :
|
|
|
|
- `ACCESS_FINE_LOCATION` et `ACCESS_COARSE_LOCATION` : Permission de base (when in use)
|
|
- `ACCESS_BACKGROUND_LOCATION` : Permission arrière-plan (Android 10+), nécessite justification Play Store
|
|
- `FOREGROUND_SERVICE` et `FOREGROUND_SERVICE_LOCATION` : Service persistant pour mode piéton (Android 12+)
|
|
|
|
**Android Play Store** : Déclaration requise dans Play Console lors de la soumission :
|
|
|
|
- Justification : "Notifications géolocalisées pour audio-guides touristiques en arrière-plan"
|
|
- Vidéo démo obligatoire montrant le flow de demande de permission
|
|
|
|
### Fallbacks et Dégradations Gracieuses
|
|
|
|
| Niveau Permission | Mode Voiture | Mode Piéton | Contenus Accessibles |
|
|
|-------------------|--------------|-------------|---------------------|
|
|
| **Always** | ✅ Complet | ✅ Complet | Tous (national + hyperlocal) |
|
|
| **When In Use** | ✅ Complet | ❌ Désactivé | Tous (si app ouverte) |
|
|
| **Denied** | ⚠️ Limité | ❌ Désactivé | Nationaux/régionaux (GeoIP) |
|
|
|
|
**Garantie RGPD** : App est **pleinement utilisable** même avec permission refusée (mode dégradé acceptable).
|
|
|
|
### Tests de Validation Stores
|
|
|
|
**Checklist App Store (iOS)** :
|
|
|
|
- [ ] Permission "Always" demandée **uniquement** si user active mode piéton
|
|
- [ ] Écran d'éducation **avant** demande OS (requis iOS 13+)
|
|
- [ ] App fonctionne sans permission "Always" (validation critique)
|
|
- [ ] Texte `Info.plist` clair et honnête (pas de tracking publicitaire)
|
|
|
|
**Checklist Play Store (Android)** :
|
|
|
|
- [ ] Déclaration `ACCESS_BACKGROUND_LOCATION` avec justification détaillée
|
|
- [ ] Vidéo démo flow de permissions (< 30s, requis Play Console)
|
|
- [ ] App fonctionne sans permission background (validation critique)
|
|
- [ ] Foreground service notification visible en mode piéton (requis Android 12+)
|
|
|
|
### Documentation Associée
|
|
|
|
- **Guide détaillé** : [/docs/mobile/permissions-strategy.md](../mobile/permissions-strategy.md)
|
|
- **Règles métier** : [Règle 05 - Mode Piéton](../domains/recommendation/rules/interactions-navigation.md#512-mode-piéton-audio-guides)
|
|
- **RGPD** : [Règle 02 - Conformité RGPD](../domains/_shared/rules/rgpd.md)
|
|
|
|
---
|
|
|
|
## Structure application
|
|
|
|
```
|
|
lib/
|
|
├── core/ # Config, DI, routes
|
|
│ └── services/ # LocationPermissionService, GeofencingService
|
|
├── data/ # Repositories, API clients
|
|
├── domain/ # Models, business logic
|
|
├── presentation/ # UI (screens, widgets, blocs)
|
|
│ └── dialogs/ # PedestrianModeEducationDialog
|
|
└── main.dart
|
|
```
|
|
|
|
## Conséquences
|
|
|
|
- Équipe doit apprendre Dart (syntaxe proche Java/TypeScript)
|
|
- Taille binaire : 8-15 MB (acceptable)
|
|
- Tests : `flutter_test` pour widgets, `integration_test` pour E2E
|
|
- CI/CD : Fastlane pour déploiement stores
|
|
- **Permissions** : Stratégie progressive critique pour validation stores (iOS/Android)
|
|
- **Validation stores** : Tests requis avec TestFlight beta (iOS) et Internal Testing (Android)
|
|
- **Documentation** : Justifications permissions détaillées requises pour soumission stores
|