import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:permission_handler/permission_handler.dart'; import '../models/membre_model.dart'; import '../../shared/theme/app_theme.dart'; /// Service de gestion des communications (appels, SMS, emails) /// Gère les permissions et l'intégration avec les applications natives class CommunicationService { static final CommunicationService _instance = CommunicationService._internal(); factory CommunicationService() => _instance; CommunicationService._internal(); /// Effectue un appel téléphonique vers un membre Future callMember(BuildContext context, MembreModel membre) async { try { // Vérifier si le numéro de téléphone est valide if (membre.telephone.isEmpty) { _showErrorSnackBar(context, 'Numéro de téléphone non disponible pour ${membre.nomComplet}'); return false; } // Nettoyer le numéro de téléphone final cleanPhone = _cleanPhoneNumber(membre.telephone); if (cleanPhone.isEmpty) { _showErrorSnackBar(context, 'Numéro de téléphone invalide pour ${membre.nomComplet}'); return false; } // Vérifier les permissions sur Android if (Platform.isAndroid) { final phonePermission = await Permission.phone.status; if (phonePermission.isDenied) { final result = await Permission.phone.request(); if (result.isDenied) { _showPermissionDeniedDialog(context, 'Téléphone', 'effectuer des appels'); return false; } } } // Construire l'URL d'appel final phoneUrl = Uri.parse('tel:$cleanPhone'); // Vérifier si l'application peut gérer les appels if (await canLaunchUrl(phoneUrl)) { // Feedback haptique HapticFeedback.mediumImpact(); // Lancer l'appel final success = await launchUrl(phoneUrl); if (success) { _showSuccessSnackBar(context, 'Appel lancé vers ${membre.nomComplet}'); // Log de l'action pour audit debugPrint('📞 Appel effectué vers ${membre.nomComplet} (${membre.telephone})'); return true; } else { _showErrorSnackBar(context, 'Impossible de lancer l\'appel vers ${membre.nomComplet}'); return false; } } else { _showErrorSnackBar(context, 'Application d\'appel non disponible sur cet appareil'); return false; } } catch (e) { debugPrint('❌ Erreur lors de l\'appel vers ${membre.nomComplet}: $e'); _showErrorSnackBar(context, 'Erreur lors de l\'appel vers ${membre.nomComplet}'); return false; } } /// Envoie un SMS à un membre Future sendSMS(BuildContext context, MembreModel membre, {String? message}) async { try { // Vérifier si le numéro de téléphone est valide if (membre.telephone.isEmpty) { _showErrorSnackBar(context, 'Numéro de téléphone non disponible pour ${membre.nomComplet}'); return false; } // Nettoyer le numéro de téléphone final cleanPhone = _cleanPhoneNumber(membre.telephone); if (cleanPhone.isEmpty) { _showErrorSnackBar(context, 'Numéro de téléphone invalide pour ${membre.nomComplet}'); return false; } // Construire l'URL SMS String smsUrl = 'sms:$cleanPhone'; if (message != null && message.isNotEmpty) { final encodedMessage = Uri.encodeComponent(message); smsUrl += '?body=$encodedMessage'; } final smsUri = Uri.parse(smsUrl); // Vérifier si l'application peut gérer les SMS if (await canLaunchUrl(smsUri)) { // Feedback haptique HapticFeedback.lightImpact(); // Lancer l'application SMS final success = await launchUrl(smsUri); if (success) { _showSuccessSnackBar(context, 'SMS ouvert pour ${membre.nomComplet}'); // Log de l'action pour audit debugPrint('💬 SMS ouvert pour ${membre.nomComplet} (${membre.telephone})'); return true; } else { _showErrorSnackBar(context, 'Impossible d\'ouvrir l\'application SMS'); return false; } } else { _showErrorSnackBar(context, 'Application SMS non disponible sur cet appareil'); return false; } } catch (e) { debugPrint('❌ Erreur lors de l\'envoi SMS vers ${membre.nomComplet}: $e'); _showErrorSnackBar(context, 'Erreur lors de l\'envoi SMS vers ${membre.nomComplet}'); return false; } } /// Envoie un email à un membre Future sendEmail(BuildContext context, MembreModel membre, {String? subject, String? body}) async { try { // Vérifier si l'email est valide if (membre.email.isEmpty) { _showErrorSnackBar(context, 'Adresse email non disponible pour ${membre.nomComplet}'); return false; } // Construire l'URL email String emailUrl = 'mailto:${membre.email}'; final params = []; if (subject != null && subject.isNotEmpty) { params.add('subject=${Uri.encodeComponent(subject)}'); } if (body != null && body.isNotEmpty) { params.add('body=${Uri.encodeComponent(body)}'); } if (params.isNotEmpty) { emailUrl += '?${params.join('&')}'; } final emailUri = Uri.parse(emailUrl); // Vérifier si l'application peut gérer les emails if (await canLaunchUrl(emailUri)) { // Feedback haptique HapticFeedback.lightImpact(); // Lancer l'application email final success = await launchUrl(emailUri); if (success) { _showSuccessSnackBar(context, 'Email ouvert pour ${membre.nomComplet}'); // Log de l'action pour audit debugPrint('📧 Email ouvert pour ${membre.nomComplet} (${membre.email})'); return true; } else { _showErrorSnackBar(context, 'Impossible d\'ouvrir l\'application email'); return false; } } else { _showErrorSnackBar(context, 'Application email non disponible sur cet appareil'); return false; } } catch (e) { debugPrint('❌ Erreur lors de l\'envoi email vers ${membre.nomComplet}: $e'); _showErrorSnackBar(context, 'Erreur lors de l\'envoi email vers ${membre.nomComplet}'); return false; } } /// Nettoie un numéro de téléphone en supprimant les caractères non numériques String _cleanPhoneNumber(String phone) { // Garder seulement les chiffres et le signe + final cleaned = phone.replaceAll(RegExp(r'[^\d+]'), ''); // Vérifier que le numéro n'est pas vide après nettoyage if (cleaned.isEmpty) return ''; // Si le numéro commence par +, le garder tel quel if (cleaned.startsWith('+')) return cleaned; // Si le numéro commence par 00, le remplacer par + if (cleaned.startsWith('00')) { return '+${cleaned.substring(2)}'; } return cleaned; } /// Affiche un SnackBar de succès void _showSuccessSnackBar(BuildContext context, String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: AppTheme.successColor, duration: const Duration(seconds: 2), behavior: SnackBarBehavior.floating, ), ); } /// Affiche un SnackBar d'erreur void _showErrorSnackBar(BuildContext context, String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: AppTheme.errorColor, duration: const Duration(seconds: 3), behavior: SnackBarBehavior.floating, ), ); } /// Affiche une dialog pour les permissions refusées void _showPermissionDeniedDialog(BuildContext context, String permission, String action) { showDialog( context: context, builder: (context) => AlertDialog( title: Text('Permission $permission requise'), content: Text( 'L\'application a besoin de la permission $permission pour $action. ' 'Veuillez autoriser cette permission dans les paramètres de l\'application.', ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Annuler'), ), ElevatedButton( onPressed: () { Navigator.of(context).pop(); openAppSettings(); }, child: const Text('Paramètres'), ), ], ), ); } }