Files
roadwave/docs/mobile/permissions-strategy.md
jpgiannetti be9fc998cc fix(docs): corriger les liens internes cassés après refactorisation DDD
Corrections:
- Liens vers ADR: docs/adr/ → adr/ dans index.md et technical.md
- Liens internes entre règles métier (anciens noms numérotés)
- Chemins relatifs ADR depuis les domaines: ../adr/ → ../../../adr/
- Lien ADR-010 → ADR-012 (frontend-mobile)
- Suppression référence vers sequences/scoring-recommandation.md (non créé)

Script: scripts/fix-remaining-links.sh
2026-02-07 17:25:47 +01:00

30 KiB

Stratégie de Permissions Géolocalisation

Date : 2026-01-31 Auteur : Architecture Mobile RoadWave Statut : Approuvé Version : 1.0


Contexte

La géolocalisation est critique pour RoadWave, mais les permissions arrière-plan sont le #1 motif de rejet sur iOS App Store et Android Play Store.

Problématiques Identifiées

iOS App Store

  • Taux de rejet ~70% si permission "Always Location" mal justifiée
  • Apple exige que l'app soit pleinement utilisable sans "Always Location"
  • Textes Info.plist scrutés manuellement par reviewers humains
  • Rejection si suspicion de tracking publicitaire ou vente de données

Android Play Store

  • Depuis Android 10 : ACCESS_BACKGROUND_LOCATION nécessite déclaration justifiée
  • Vidéo démo obligatoire montrant le flow de demande (< 30s)
  • Google vérifie que la permission est réellement optionnelle
  • Foreground service notification obligatoire en arrière-plan (Android 12+)

RGPD (Règle 02)

  • Permissions doivent être optionnelles
  • Utilisateur doit pouvoir refuser sans pénalité
  • App doit fonctionner en mode dégradé acceptable

Stratégie Progressive (2 Étapes)

Vue d'Ensemble

┌─────────────────────────────────────────────────────────┐
│ ONBOARDING                                              │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Étape 1: Permission "When In Use"                   │ │
│ │ → Mode voiture complet ✅                            │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
                         │
                         │ User utilise l'app normalement
                         │
                         ▼
┌─────────────────────────────────────────────────────────┐
│ SETTINGS (Plus tard, si besoin)                         │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Étape 2: Permission "Always" (optionnelle)          │ │
│ │ → Mode piéton avec notifications push ✅             │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

Étape 1 : Permission de Base (Onboarding)

Quand

  • Premier lancement de l'app
  • Avant de pouvoir utiliser les fonctionnalités principales

Permission Demandée

Platform Permission Nom Utilisateur
iOS NSLocationWhenInUseUsageDescription "Allow While Using App"
Android ACCESS_FINE_LOCATION "Autorisez uniquement lorsque l'application est en cours d'utilisation"

Flow UI

Écran pré-permission (recommandé pour taux d'acceptation) :

┌────────────────────────────────────────┐
│ 🗺️ Bienvenue sur RoadWave              │
├────────────────────────────────────────┤
│                                        │
│ RoadWave vous propose du contenu audio│
│ adapté à votre position en temps réel.│
│                                        │
│ Nous avons besoin de votre localisation│
│ pour :                                 │
│                                        │
│ ✅ Recommander du contenu proche       │
│ ✅ Détecter votre mode (voiture/piéton)│
│ ✅ Synchroniser avec vos trajets       │
│                                        │
│ [Continuer]                            │
│                                        │
│ Votre vie privée est protégée         │
└────────────────────────────────────────┘

Puis demande système iOS/Android

Si Permission Acceptée

  • Mode voiture complet
  • Détection POI quand app ouverte
  • Recommandations géolocalisées temps réel
  • Pas de demande supplémentaire sauf si user veut mode piéton

Si Permission Refusée

Mode dégradé (IP2Location) :

  • Détection pays/ville via adresse IP (IP2Location Lite, voir ADR-019)
  • Contenus nationaux et régionaux disponibles
  • Pas de contenus hyperlocaux (< 10km)

UI :

┌────────────────────────────────────────┐
│ ⚠️ Géolocalisation désactivée          │
├────────────────────────────────────────┤
│ Vous écoutez des contenus de votre    │
│ région (détection approximative).      │
│                                        │
│ Pour débloquer les contenus proches : │
│ [Activer la géolocalisation]          │
└────────────────────────────────────────┘

Tap "Activer"openAppSettings() (réglages système)

Code d'Implémentation

// lib/presentation/onboarding/location_onboarding_screen.dart

class LocationOnboardingScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: EdgeInsets.all(24),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(Icons.map, size: 80, color: Colors.blue),
              SizedBox(height: 32),
              Text(
                'Bienvenue sur RoadWave',
                style: Theme.of(context).textTheme.headlineMedium,
              ),
              SizedBox(height: 16),
              Text(
                'RoadWave vous propose du contenu audio '
                'adapté à votre position en temps réel.',
                textAlign: TextAlign.center,
              ),
              SizedBox(height: 32),
              _buildFeatureList(),
              SizedBox(height: 48),
              ElevatedButton(
                onPressed: () => _requestLocationPermission(context),
                child: Text('Continuer'),
              ),
              SizedBox(height: 16),
              Text(
                'Votre vie privée est protégée',
                style: Theme.of(context).textTheme.bodySmall,
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildFeatureList() {
    return Column(
      children: [
        _buildFeature('Recommander du contenu proche'),
        _buildFeature('Détecter votre mode (voiture/piéton)'),
        _buildFeature('Synchroniser avec vos trajets'),
      ],
    );
  }

  Widget _buildFeature(String text) {
    return Padding(
      padding: EdgeInsets.symmetric(vertical: 8),
      child: Row(
        children: [
          Icon(Icons.check_circle, color: Colors.green),
          SizedBox(width: 16),
          Expanded(child: Text(text)),
        ],
      ),
    );
  }

  Future<void> _requestLocationPermission(BuildContext context) async {
    final service = context.read<LocationPermissionService>();
    final granted = await service.requestBasicPermission();

    if (granted) {
      // Navigation vers écran principal
      Navigator.pushReplacementNamed(context, '/home');
    } else {
      // Afficher mode dégradé disponible
      _showDegradedModeDialog(context);
    }
  }

  void _showDegradedModeDialog(BuildContext context) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text('Géolocalisation désactivée'),
        content: Text(
          'Vous pouvez toujours utiliser RoadWave avec des contenus '
          'de votre région (détection approximative).',
        ),
        actions: [
          TextButton(
            onPressed: () {
              Navigator.pop(context);
              Navigator.pushReplacementNamed(context, '/home');
            },
            child: Text('Continuer sans GPS'),
          ),
          TextButton(
            onPressed: () async {
              Navigator.pop(context);
              await openAppSettings();
            },
            child: Text('Ouvrir réglages'),
          ),
        ],
      ),
    );
  }
}

Étape 2 : Permission Arrière-Plan (Optionnelle)

Quand

  • User active explicitement "Notifications audio-guides piéton" dans Settings
  • Jamais au premier lancement

Permission Demandée

Platform Permission Nom Utilisateur
iOS NSLocationAlwaysAndWhenInUseUsageDescription "Allow Always"
Android ACCESS_BACKGROUND_LOCATION "Toujours autoriser"

Flow UI (Critique pour Validation Stores)

1. Toggle dans Settings

Settings > Notifications
┌────────────────────────────────────────┐
│ 🔔 Notifications                       │
├────────────────────────────────────────┤
│ Recommendations de contenu             │
│ ├─ En conduite             [ON]       │
│ └─ Au volant               [ON]       │
│                                        │
│ Audio-guides piéton        [OFF]      │
│ ⓘ Nécessite localisation arrière-plan │
│                                        │
│ Live de créateurs suivis   [ON]       │
└────────────────────────────────────────┘

2. Écran d'éducation (OBLIGATOIRE avant demande OS)

┌────────────────────────────────────────┐
│ 📍 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é       │
│ ❌ Partagée sans votre consentement    │
│                                        │
│ Cette fonctionnalité est optionnelle.  │
│ Vous pouvez utiliser RoadWave sans     │
│ cette permission.                      │
│                                        │
│ [Continuer] [Non merci]                │
│                                        │
│ Plus d'infos : Politique confidentialité│
└────────────────────────────────────────┘

3. Demande système iOS/Android

4. Si permission accordée

✅ Mode piéton activé !

Vous recevrez une notification lorsque
vous passez près d'un audio-guide.

5. Si permission refusée

⚠️ Mode piéton non disponible

Sans permission "Toujours autoriser",
nous ne pouvons pas détecter les
audio-guides en arrière-plan.

Vous pouvez toujours :
✅ Utiliser le mode voiture
✅ Lancer manuellement les audio-guides

[Ouvrir réglages] [Fermer]

Code d'Implémentation

// lib/presentation/settings/notifications_settings_screen.dart

class NotificationsSettingsScreen extends StatefulWidget {
  @override
  _NotificationsSettingsScreenState createState() => _NotificationsSettingsScreenState();
}

class _NotificationsSettingsScreenState extends State<NotificationsSettingsScreen> {
  bool _pedestrianModeEnabled = false;

  @override
  void initState() {
    super.initState();
    _loadPermissionStatus();
  }

  Future<void> _loadPermissionStatus() async {
    final service = context.read<LocationPermissionService>();
    final level = await service.getCurrentLevel();
    setState(() {
      _pedestrianModeEnabled = (level == LocationPermissionLevel.always);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Notifications')),
      body: ListView(
        children: [
          SwitchListTile(
            title: Text('Recommendations de contenu'),
            subtitle: Text('En conduite'),
            value: true,
            onChanged: (value) { /* ... */ },
          ),
          SwitchListTile(
            title: Text('Audio-guides piéton'),
            subtitle: Text('Nécessite localisation arrière-plan'),
            value: _pedestrianModeEnabled,
            onChanged: _handlePedestrianModeToggle,
          ),
        ],
      ),
    );
  }

  Future<void> _handlePedestrianModeToggle(bool enabled) async {
    if (enabled) {
      // User veut activer → demander permission
      final granted = await _requestBackgroundPermission();
      setState(() {
        _pedestrianModeEnabled = granted;
      });
    } else {
      // User veut désactiver → juste disable service
      setState(() {
        _pedestrianModeEnabled = false;
      });
      // Arrêter geofencing service
      context.read<GeofencingService>().stop();
    }
  }

  Future<bool> _requestBackgroundPermission() async {
    // Étape 1: Afficher écran d'éducation
    final userWantsToContinue = await _showEducationDialog();
    if (!userWantsToContinue) return false;

    // Étape 2: Demander permission OS
    final service = context.read<LocationPermissionService>();
    final granted = await service.requestBackgroundPermission(context: context);

    if (granted) {
      _showSuccessDialog();
      // Démarrer geofencing service
      context.read<GeofencingService>().start();
    } else {
      _showDeniedDialog();
    }

    return granted;
  }

  Future<bool> _showEducationDialog() async {
    return await showDialog<bool>(
      context: context,
      barrierDismissible: false,
      builder: (context) => AlertDialog(
        title: Text('📍 Notifications audio-guides piéton'),
        content: SingleChildScrollView(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                '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.',
              ),
              SizedBox(height: 16),
              Text('🔍 Votre position sera utilisée pour :',
                style: TextStyle(fontWeight: FontWeight.bold)),
              _buildListItem('Détecter monuments à 200m'),
              _buildListItem('Vous envoyer une notification'),
              SizedBox(height: 16),
              Text('🔒 Votre position ne sera jamais :',
                style: TextStyle(fontWeight: FontWeight.bold)),
              _buildListItem('Vendue à des tiers', isNegative: true),
              _buildListItem('Utilisée pour de la publicité', isNegative: true),
              _buildListItem('Partagée sans votre consentement', isNegative: true),
              SizedBox(height: 16),
              Text(
                'Cette fonctionnalité est optionnelle. '
                'Vous pouvez utiliser RoadWave sans cette permission.',
                style: TextStyle(fontStyle: FontStyle.italic, fontSize: 12),
              ),
            ],
          ),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context, false),
            child: Text('Non merci'),
          ),
          ElevatedButton(
            onPressed: () => Navigator.pop(context, true),
            child: Text('Continuer'),
          ),
        ],
      ),
    ) ?? false;
  }

  Widget _buildListItem(String text, {bool isNegative = false}) {
    return Padding(
      padding: EdgeInsets.symmetric(vertical: 4),
      child: Row(
        children: [
          Icon(
            isNegative ? Icons.cancel : Icons.check_circle,
            color: isNegative ? Colors.red : Colors.green,
            size: 20,
          ),
          SizedBox(width: 8),
          Expanded(child: Text(text)),
        ],
      ),
    );
  }

  void _showSuccessDialog() {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text('✅ Mode piéton activé !'),
        content: Text(
          'Vous recevrez une notification lorsque vous passez près d\'un audio-guide.',
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: Text('OK'),
          ),
        ],
      ),
    );
  }

  void _showDeniedDialog() {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text('⚠️ Mode piéton non disponible'),
        content: Text(
          'Sans permission "Toujours autoriser", nous ne pouvons pas '
          'détecter les audio-guides en arrière-plan.\n\n'
          'Vous pouvez toujours :\n'
          '✅ Utiliser le mode voiture\n'
          '✅ Lancer manuellement les audio-guides',
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: Text('Fermer'),
          ),
          TextButton(
            onPressed: () {
              Navigator.pop(context);
              openAppSettings();
            },
            child: Text('Ouvrir réglages'),
          ),
        ],
      ),
    );
  }
}

Tableau de Dégradation Gracieuse

Niveau Permission Mode Voiture Mode Piéton Contenus Hyperlocaux Notifications
Always Complet Complet Tous Push en arrière-plan
When In Use Complet Désactivé Si app ouverte Sonores (app ouverte)
Denied ⚠️ IP2Location (ville) Désactivé Aucun Aucune

Garanties :

  • App utilisable à tous niveaux de permission
  • Pas de fonctionnalité bloquante sans permission
  • Mode dégradé acceptable (contenus régionaux)

Configuration Plateformes

iOS (ios/Runner/Info.plist)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <!-- ÉTAPE 1: Permission "When In Use" -->
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>RoadWave utilise votre position pour vous proposer des contenus audio géolocalisés adaptés à votre trajet en temps réel.</string>

    <!-- ÉTAPE 2: Permission "Always" (optionnelle) -->
    <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
    <string>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.</string>

    <!-- Background modes -->
    <key>UIBackgroundModes</key>
    <array>
        <string>location</string>
        <string>remote-notification</string>
    </array>

    <!-- Privacy - Location Always Usage Description (fallback iOS < 11) -->
    <key>NSLocationAlwaysUsageDescription</key>
    <string>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.</string>
</dict>
</plist>

Android (android/app/src/main/AndroidManifest.xml)

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.roadwave.app">

    <!-- ÉTAPE 1: Permission "When In Use" -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <!-- ÉTAPE 2: Permission "Always" (Android 10+, optionnelle) -->
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

    <!-- Foreground service (requis Android 12+ pour background location) -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />

    <!-- Notifications -->
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

    <application
        android:label="RoadWave"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">

        <!-- Foreground service declaration -->
        <service
            android:name="com.pravera.flutter_foreground_task.service.ForegroundService"
            android:foregroundServiceType="location"
            android:exported="false" />

        <!-- ... rest of manifest ... -->
    </application>
</manifest>

Checklist Validation Stores

iOS App Store

  • Permission "Always" demandée uniquement après activation explicite mode piéton
  • Écran d'éducation avant demande OS (avec raisons claires)
  • Texte NSLocationAlwaysAndWhenInUseUsageDescription mentionne :
    • Fonctionnalité précise ("audio-guides piéton")
    • Optionnalité ("Cette fonctionnalité est optionnelle")
    • Pas de mention tracking/publicité
  • App fonctionne complètement avec permission "When In Use" uniquement
  • App fonctionne en mode dégradé sans aucune permission (IP2Location)
  • Screenshots montrant app fonctionnelle sans permission "Always"
  • Video demo flow de permissions (< 1 min, optionnel mais recommandé)

Android Play Store

  • Déclaration ACCESS_BACKGROUND_LOCATION avec justification dans Play Console :
    • "Notifications géolocalisées pour audio-guides touristiques en arrière-plan"
    • "Permet aux utilisateurs de recevoir des alertes lorsqu'ils passent près de monuments"
  • Vidéo démo obligatoire (< 30s) montrant :
    • Activation toggle "Mode piéton" dans Settings
    • Écran d'éducation pré-permission
    • Demande permission système Android
    • App fonctionnelle si permission refusée
  • Foreground service notification visible en mode piéton (Android 12+)
  • App fonctionne complètement avec ACCESS_FINE_LOCATION uniquement
  • App fonctionne en mode dégradé sans permissions
  • Screenshots montrant app fonctionnelle sans permission background

Tests Requis

Tests Unitaires

// test/core/services/location_permission_service_test.dart

void main() {
  group('LocationPermissionService', () {
    test('getCurrentLevel returns denied when no permission', () async {
      // ...
    });

    test('getCurrentLevel returns whenInUse with basic permission', () async {
      // ...
    });

    test('getCurrentLevel returns always with background permission', () async {
      // ...
    });

    test('requestBasicPermission shows system dialog', () async {
      // ...
    });

    test('requestBackgroundPermission requires education dialog first', () async {
      // ...
    });
  });
}

Tests d'Intégration

// integration_test/permissions_flow_test.dart

void main() {
  testWidgets('Onboarding flow with permission acceptance', (tester) async {
    app.main();
    await tester.pumpAndSettle();

    // Voir écran onboarding
    expect(find.text('Bienvenue sur RoadWave'), findsOneWidget);

    // Tap continuer
    await tester.tap(find.text('Continuer'));
    await tester.pumpAndSettle();

    // Permission acceptée (mock) → navigation home
    expect(find.byType(HomeScreen), findsOneWidget);
  });

  testWidgets('Settings pedestrian mode activation flow', (tester) async {
    // ...
    await tester.tap(find.byType(SwitchListTile).last);
    await tester.pumpAndSettle();

    // Voir écran d'éducation
    expect(find.text('Notifications audio-guides piéton'), findsOneWidget);
    expect(find.text('Votre position sera utilisée pour'), findsOneWidget);

    // Tap continuer
    await tester.tap(find.text('Continuer'));
    await tester.pumpAndSettle();

    // Vérifier demande système (mock)
    // ...
  });
}

Tests Manuels (Devices Réels)

iOS :

  • iPhone avec iOS 14, 15, 16, 17, 18
  • Tester flow onboarding permission "When In Use"
  • Tester activation mode piéton avec permission "Always"
  • Tester refus permission "Always" → app reste fonctionnelle
  • Tester changement permission dans Settings iOS → app réagit correctement

Android :

  • Android 10, 11, 12, 13, 14, 15
  • Tester flow onboarding permission FINE_LOCATION
  • Tester activation mode piéton avec BACKGROUND_LOCATION
  • Tester refus permission background → app reste fonctionnelle
  • Vérifier foreground notification visible en arrière-plan (Android 12+)

Validation TestFlight / Internal Testing

Phase 1 : TestFlight Beta (iOS)

Objectif : Valider que Apple accepte notre stratégie de permissions

Participants : 10-20 beta testers externes

Durée : 2 semaines

Checklist :

  • Upload build vers TestFlight
  • Compléter questionnaire App Store Connect :
    • "Why does your app use background location?" → "To send push notifications when users walk near tourist audio-guides, even when app is closed. This feature is optional and can be disabled in settings."
    • Screenshots montrant app fonctionnelle sans permission "Always"
  • Attendre review Apple (24-48h)
  • Si rejet : analyser feedback, ajuster textes/flow, re-soumettre
  • Si accepté : lancer beta test avec testeurs

Scénarios de test beta :

  1. Installation fresh → onboarding → accepter "When In Use"
  2. Utiliser mode voiture pendant 1 semaine
  3. Activer mode piéton dans settings → accepter "Always"
  4. Vérifier réception notifications push en arrière-plan
  5. Désactiver mode piéton → vérifier app toujours fonctionnelle

Métriques collectées :

  • Taux acceptation permission "When In Use" : cible >85%
  • Taux acceptation permission "Always" : cible >40%
  • Taux rejet App Review : cible 0%

Phase 2 : Internal Testing (Android)

Objectif : Valider conformité Play Store + foreground service

Participants : 5-10 beta testers internes

Durée : 1 semaine

Checklist :

  • Upload build vers Play Console (Internal Testing)
  • Compléter déclaration permissions :
    • ACCESS_BACKGROUND_LOCATION justification
    • Upload vidéo démo (< 30s)
  • Tester sur Android 10, 11, 12, 13, 14, 15
  • Vérifier foreground notification visible (Android 12+)

Scénarios de test :

  1. Installation → onboarding → accepter FINE_LOCATION
  2. Utiliser app mode voiture
  3. Activer mode piéton → voir écran éducation → accepter BACKGROUND_LOCATION
  4. App en arrière-plan → marcher près d'un POI → vérifier notification push
  5. Vérifier notification foreground service visible dans panneau notifications

Métriques collectées :

  • Consommation batterie mode piéton : cible <5% par heure
  • Taux crash background service : cible <0.1%

Vidéo Démo Play Store (Script)

Durée : 25 secondes Format : MP4 1080p, portrait Voix off : Optionnel

Storyboard :

Seconde Écran Action Texte Overlay
0-5 Settings > Notifications Scroll vers "Audio-guides piéton" "Utilisateur active mode piéton"
5-8 Toggle OFF → ON Tap toggle
8-15 Écran d'éducation Scroll, lire texte "Écran explicatif affiché"
15-18 Tap "Continuer" Demande permission Android "Permission arrière-plan demandée"
18-22 Dialog Android Tap "Toujours autoriser" "Utilisateur accepte (optionnel)"
22-25 Retour Settings Toggle ON "Mode piéton activé"

Fichier : android/play-store-assets/background-location-demo.mp4


FAQ

Q1 : Pourquoi ne pas demander "Always" dès le début ?

R : Taux d'acceptation ~15% vs ~85% pour "When In Use". Strategy progressive maximise utilisateurs avec permissions.

Q2 : Que se passe-t-il si user change permission dans Settings OS ?

R : App détecte changement via AppLifecycleState et permission_handler. Si downgrade "Always" → "When In Use", mode piéton désactivé automatiquement avec notification in-app.

Q3 : Est-ce que IP2Location suffit pour le MVP ?

R : Non. Mode voiture nécessite GPS précis pour ETA et notifications géolocalisées (règle métier 05). IP2Location = fallback uniquement.

Q4 : Combien de temps pour validation TestFlight/Play Store ?

R :

  • TestFlight : 24-48h (review Apple)
  • Play Console Internal Testing : Immédiat (pas de review)
  • Play Console Production : 3-7 jours (review Google)

Références


Dernière mise à jour : 2026-01-31 Prochaine revue : Après validation TestFlight (Sprint 3)