# 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](../regles-metier/05-interactions-navigation.md#512-mode-piéton-audio-guides) - **RGPD** : [Règle 02 - Conformité RGPD](../regles-metier/02-conformite-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