601 lines
20 KiB
Dart
601 lines
20 KiB
Dart
|
|
// lib/services/connectivity_service.dart - VERSION AVEC SUPPORT ENTREPRISE
|
||
|
|
import 'dart:async';
|
||
|
|
import 'dart:io';
|
||
|
|
import 'package:flutter/material.dart';
|
||
|
|
import 'package:flutter/services.dart';
|
||
|
|
import 'package:provider/provider.dart';
|
||
|
|
import '../main.dart'; // Pour accéder à AuthController
|
||
|
|
|
||
|
|
class ConnectivityService extends ChangeNotifier {
|
||
|
|
static final ConnectivityService _instance = ConnectivityService._internal();
|
||
|
|
factory ConnectivityService() => _instance;
|
||
|
|
ConnectivityService._internal();
|
||
|
|
|
||
|
|
bool _isConnected = true;
|
||
|
|
bool _isChecking = false;
|
||
|
|
Timer? _periodicTimer;
|
||
|
|
bool _hasNetworkInterface = true;
|
||
|
|
DateTime? _lastDisconnectionTime;
|
||
|
|
int _reconnectionAttempts = 0;
|
||
|
|
|
||
|
|
bool get isConnected => _isConnected;
|
||
|
|
bool get hasNetworkInterface => _hasNetworkInterface;
|
||
|
|
DateTime? get lastDisconnectionTime => _lastDisconnectionTime;
|
||
|
|
int get reconnectionAttempts => _reconnectionAttempts;
|
||
|
|
|
||
|
|
// Clé globale pour accéder au navigator
|
||
|
|
static GlobalKey<NavigatorState>? navigatorKey;
|
||
|
|
|
||
|
|
/// Initialiser le service avec la clé navigator
|
||
|
|
static void setNavigatorKey(GlobalKey<NavigatorState> key) {
|
||
|
|
navigatorKey = key;
|
||
|
|
print('✅ NavigatorKey configuré pour ConnectivityService');
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Démarrer la surveillance
|
||
|
|
void startMonitoring() {
|
||
|
|
print('🌐 Démarrage de la surveillance de connectivité');
|
||
|
|
_periodicTimer?.cancel();
|
||
|
|
_periodicTimer = Timer.periodic(Duration(seconds: 5), (timer) {
|
||
|
|
checkConnectivity();
|
||
|
|
});
|
||
|
|
|
||
|
|
// Vérification initiale après un délai
|
||
|
|
Future.delayed(Duration(seconds: 2), () {
|
||
|
|
checkConnectivity();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Vérifier la connectivité
|
||
|
|
Future<void> checkConnectivity() async {
|
||
|
|
if (_isChecking) return;
|
||
|
|
_isChecking = true;
|
||
|
|
|
||
|
|
try {
|
||
|
|
print('🔍 Vérification de la connectivité...');
|
||
|
|
|
||
|
|
// Étape 1: Vérifier les interfaces réseau
|
||
|
|
_hasNetworkInterface = await _checkNetworkInterfaces();
|
||
|
|
|
||
|
|
if (!_hasNetworkInterface) {
|
||
|
|
print('❌ Aucune interface réseau trouvée');
|
||
|
|
_updateConnectivityStatus(false);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Étape 2: Tester la connectivité internet
|
||
|
|
bool hasInternetAccess = await _testInternetConnectivity();
|
||
|
|
print('🌍 Accès internet: $hasInternetAccess');
|
||
|
|
|
||
|
|
_updateConnectivityStatus(hasInternetAccess);
|
||
|
|
} catch (e) {
|
||
|
|
print('❌ Erreur lors de la vérification: $e');
|
||
|
|
_updateConnectivityStatus(false);
|
||
|
|
} finally {
|
||
|
|
_isChecking = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Vérifier les interfaces réseau
|
||
|
|
Future<bool> _checkNetworkInterfaces() async {
|
||
|
|
try {
|
||
|
|
final interfaces = await NetworkInterface.list(
|
||
|
|
includeLinkLocal: false,
|
||
|
|
type: InternetAddressType.any,
|
||
|
|
);
|
||
|
|
|
||
|
|
bool hasActiveInterface = interfaces.any(
|
||
|
|
(interface) =>
|
||
|
|
!interface.name.toLowerCase().contains('lo') &&
|
||
|
|
interface.addresses.isNotEmpty,
|
||
|
|
);
|
||
|
|
|
||
|
|
print('📡 Interfaces réseau actives: $hasActiveInterface');
|
||
|
|
if (hasActiveInterface) {
|
||
|
|
print(
|
||
|
|
' Interfaces trouvées: ${interfaces.map((i) => i.name).join(", ")}',
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return hasActiveInterface;
|
||
|
|
} catch (e) {
|
||
|
|
print('❌ Erreur vérification interfaces: $e');
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Tester la connectivité internet avec plusieurs cibles
|
||
|
|
Future<bool> _testInternetConnectivity() async {
|
||
|
|
final testTargets = [
|
||
|
|
{'host': '1.1.1.1', 'port': 53, 'name': 'Cloudflare DNS'},
|
||
|
|
{'host': '8.8.8.8', 'port': 53, 'name': 'Google DNS'},
|
||
|
|
{'host': 'google.com', 'port': 80, 'name': 'Google'},
|
||
|
|
];
|
||
|
|
|
||
|
|
for (var target in testTargets) {
|
||
|
|
try {
|
||
|
|
final socket = await Socket.connect(
|
||
|
|
target['host'] as String,
|
||
|
|
target['port'] as int,
|
||
|
|
timeout: Duration(seconds: 3),
|
||
|
|
);
|
||
|
|
socket.destroy();
|
||
|
|
print('✅ Connexion réussie à ${target['name']}');
|
||
|
|
return true;
|
||
|
|
} catch (e) {
|
||
|
|
print('⚠️ Échec connexion à ${target['name']}: $e');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
print('❌ Toutes les tentatives de connexion ont échoué');
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Mettre à jour le statut de connectivité
|
||
|
|
void _updateConnectivityStatus(bool isConnected) {
|
||
|
|
if (isConnected != _isConnected) {
|
||
|
|
print('🔄 Changement connectivité: $_isConnected -> $isConnected');
|
||
|
|
|
||
|
|
final previousStatus = _isConnected;
|
||
|
|
_isConnected = isConnected;
|
||
|
|
|
||
|
|
if (!_isConnected) {
|
||
|
|
_lastDisconnectionTime = DateTime.now();
|
||
|
|
_reconnectionAttempts = 0;
|
||
|
|
_showNoConnectionDialog();
|
||
|
|
} else if (previousStatus == false && _isConnected) {
|
||
|
|
// Reconnexion réussie
|
||
|
|
print('✅ Reconnexion établie');
|
||
|
|
_reconnectionAttempts = 0;
|
||
|
|
_showReconnectionSuccess();
|
||
|
|
}
|
||
|
|
|
||
|
|
notifyListeners();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Afficher le dialog de perte de connexion
|
||
|
|
void _showNoConnectionDialog() {
|
||
|
|
if (navigatorKey?.currentState == null) {
|
||
|
|
print('⚠️ NavigatorState non disponible');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
print('🚨 Affichage du popup de perte de connexion');
|
||
|
|
|
||
|
|
navigatorKey!.currentState!.push(
|
||
|
|
PageRouteBuilder(
|
||
|
|
opaque: false,
|
||
|
|
barrierDismissible: false,
|
||
|
|
barrierColor: Colors.black54,
|
||
|
|
pageBuilder: (BuildContext context, _, __) {
|
||
|
|
return WillPopScope(
|
||
|
|
onWillPop: () async => false,
|
||
|
|
child: Dialog(
|
||
|
|
shape: RoundedRectangleBorder(
|
||
|
|
borderRadius: BorderRadius.circular(20),
|
||
|
|
),
|
||
|
|
child: _buildDialogContent(context),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
},
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Construire le contenu du dialog de déconnexion
|
||
|
|
Widget _buildDialogContent(BuildContext context) {
|
||
|
|
return Container(
|
||
|
|
padding: EdgeInsets.all(24),
|
||
|
|
child: Column(
|
||
|
|
mainAxisSize: MainAxisSize.min,
|
||
|
|
children: [
|
||
|
|
// Icône animée
|
||
|
|
TweenAnimationBuilder<double>(
|
||
|
|
duration: Duration(milliseconds: 800),
|
||
|
|
tween: Tween(begin: 0.0, end: 1.0),
|
||
|
|
builder: (context, value, child) {
|
||
|
|
return Transform.scale(
|
||
|
|
scale: value,
|
||
|
|
child: Container(
|
||
|
|
width: 80,
|
||
|
|
height: 80,
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
gradient: LinearGradient(
|
||
|
|
colors: [Colors.red[400]!, Colors.red[600]!],
|
||
|
|
begin: Alignment.topLeft,
|
||
|
|
end: Alignment.bottomRight,
|
||
|
|
),
|
||
|
|
shape: BoxShape.circle,
|
||
|
|
boxShadow: [
|
||
|
|
BoxShadow(
|
||
|
|
color: Colors.red.withOpacity(0.3),
|
||
|
|
blurRadius: 20,
|
||
|
|
offset: Offset(0, 10),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
child: Icon(
|
||
|
|
Icons.wifi_off_rounded,
|
||
|
|
color: Colors.white,
|
||
|
|
size: 40,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
},
|
||
|
|
),
|
||
|
|
|
||
|
|
SizedBox(height: 24),
|
||
|
|
|
||
|
|
// Titre
|
||
|
|
Text(
|
||
|
|
'Problème de connexion',
|
||
|
|
style: TextStyle(
|
||
|
|
fontSize: 24,
|
||
|
|
fontWeight: FontWeight.bold,
|
||
|
|
color: Color(0xFF006699),
|
||
|
|
),
|
||
|
|
textAlign: TextAlign.center,
|
||
|
|
),
|
||
|
|
|
||
|
|
SizedBox(height: 16),
|
||
|
|
|
||
|
|
// Message détaillé
|
||
|
|
Container(
|
||
|
|
padding: EdgeInsets.all(16),
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
color: Colors.red[50],
|
||
|
|
borderRadius: BorderRadius.circular(12),
|
||
|
|
border: Border.all(color: Colors.red[200]!, width: 1),
|
||
|
|
),
|
||
|
|
child: Row(
|
||
|
|
children: [
|
||
|
|
Icon(
|
||
|
|
_hasNetworkInterface
|
||
|
|
? Icons.cloud_off_rounded
|
||
|
|
: Icons.wifi_off_rounded,
|
||
|
|
color: Colors.red[700],
|
||
|
|
size: 24,
|
||
|
|
),
|
||
|
|
SizedBox(width: 12),
|
||
|
|
Expanded(
|
||
|
|
child: Text(
|
||
|
|
_getDetailedMessage(),
|
||
|
|
style: TextStyle(
|
||
|
|
fontSize: 14,
|
||
|
|
color: Colors.red[900],
|
||
|
|
height: 1.5,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
|
||
|
|
SizedBox(height: 24),
|
||
|
|
|
||
|
|
// Informations utilisateur (si connecté)
|
||
|
|
Consumer<AuthController>(
|
||
|
|
builder: (context, authController, child) {
|
||
|
|
if (authController.isLoggedIn) {
|
||
|
|
final isEnterprise = authController.isEnterpriseMember;
|
||
|
|
return Container(
|
||
|
|
padding: EdgeInsets.all(12),
|
||
|
|
margin: EdgeInsets.only(bottom: 16),
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
color:
|
||
|
|
isEnterprise
|
||
|
|
? Color(0xFF8B5CF6).withOpacity(0.1)
|
||
|
|
: Color(0xFF006699).withOpacity(0.1),
|
||
|
|
borderRadius: BorderRadius.circular(12),
|
||
|
|
border: Border.all(
|
||
|
|
color:
|
||
|
|
isEnterprise
|
||
|
|
? Color(0xFF8B5CF6).withOpacity(0.3)
|
||
|
|
: Color(0xFF006699).withOpacity(0.3),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
child: Row(
|
||
|
|
children: [
|
||
|
|
Icon(
|
||
|
|
isEnterprise ? Icons.business : Icons.person,
|
||
|
|
color:
|
||
|
|
isEnterprise
|
||
|
|
? Color(0xFF8B5CF6)
|
||
|
|
: Color(0xFF006699),
|
||
|
|
size: 20,
|
||
|
|
),
|
||
|
|
SizedBox(width: 8),
|
||
|
|
Expanded(
|
||
|
|
child: Column(
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
|
children: [
|
||
|
|
Text(
|
||
|
|
authController.agentName ?? 'Agent',
|
||
|
|
style: TextStyle(
|
||
|
|
fontSize: 14,
|
||
|
|
fontWeight: FontWeight.w600,
|
||
|
|
color: Colors.black87,
|
||
|
|
),
|
||
|
|
overflow: TextOverflow.ellipsis,
|
||
|
|
),
|
||
|
|
if (isEnterprise &&
|
||
|
|
authController.enterprise?.nomEntreprise !=
|
||
|
|
null) ...[
|
||
|
|
SizedBox(height: 2),
|
||
|
|
Text(
|
||
|
|
authController.enterprise!.nomEntreprise,
|
||
|
|
style: TextStyle(
|
||
|
|
fontSize: 12,
|
||
|
|
color: Colors.grey[600],
|
||
|
|
),
|
||
|
|
overflow: TextOverflow.ellipsis,
|
||
|
|
),
|
||
|
|
],
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
Container(
|
||
|
|
padding: EdgeInsets.symmetric(
|
||
|
|
horizontal: 8,
|
||
|
|
vertical: 4,
|
||
|
|
),
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
color:
|
||
|
|
isEnterprise
|
||
|
|
? Color(0xFF8B5CF6)
|
||
|
|
: Color(0xFF006699),
|
||
|
|
borderRadius: BorderRadius.circular(8),
|
||
|
|
),
|
||
|
|
child: Text(
|
||
|
|
isEnterprise ? 'Entreprise' : 'Agent',
|
||
|
|
style: TextStyle(
|
||
|
|
color: Colors.white,
|
||
|
|
fontSize: 10,
|
||
|
|
fontWeight: FontWeight.w600,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
return SizedBox.shrink();
|
||
|
|
},
|
||
|
|
),
|
||
|
|
|
||
|
|
// Boutons d'action
|
||
|
|
Row(
|
||
|
|
children: [
|
||
|
|
Expanded(
|
||
|
|
child: OutlinedButton.icon(
|
||
|
|
onPressed: () {
|
||
|
|
Navigator.of(context).pop();
|
||
|
|
_reconnectionAttempts++;
|
||
|
|
checkConnectivity();
|
||
|
|
},
|
||
|
|
icon: Icon(Icons.refresh_rounded, size: 20),
|
||
|
|
label: Text('Réessayer'),
|
||
|
|
style: OutlinedButton.styleFrom(
|
||
|
|
padding: EdgeInsets.symmetric(vertical: 16),
|
||
|
|
side: BorderSide(color: Color(0xFF006699), width: 2),
|
||
|
|
foregroundColor: Color(0xFF006699),
|
||
|
|
shape: RoundedRectangleBorder(
|
||
|
|
borderRadius: BorderRadius.circular(12),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
SizedBox(width: 12),
|
||
|
|
Expanded(
|
||
|
|
child: ElevatedButton.icon(
|
||
|
|
onPressed: () {
|
||
|
|
Navigator.of(context).pop();
|
||
|
|
_openWifiSettings();
|
||
|
|
},
|
||
|
|
icon: Icon(Icons.wifi_find_rounded, size: 20),
|
||
|
|
label: Text('WiFi'),
|
||
|
|
style: ElevatedButton.styleFrom(
|
||
|
|
backgroundColor: Color(0xFF006699),
|
||
|
|
foregroundColor: Colors.white,
|
||
|
|
padding: EdgeInsets.symmetric(vertical: 16),
|
||
|
|
elevation: 2,
|
||
|
|
shape: RoundedRectangleBorder(
|
||
|
|
borderRadius: BorderRadius.circular(12),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
|
||
|
|
// Compteur de tentatives
|
||
|
|
if (_reconnectionAttempts > 0) ...[
|
||
|
|
SizedBox(height: 12),
|
||
|
|
Text(
|
||
|
|
'Tentative ${_reconnectionAttempts + 1}',
|
||
|
|
style: TextStyle(
|
||
|
|
fontSize: 12,
|
||
|
|
color: Colors.grey[600],
|
||
|
|
fontStyle: FontStyle.italic,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
],
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Message détaillé selon le type de problème
|
||
|
|
String _getDetailedMessage() {
|
||
|
|
if (!_hasNetworkInterface) {
|
||
|
|
return 'Aucun réseau WiFi détecté. Veuillez vous connecter à un réseau WiFi pour utiliser WORTIS Agent.';
|
||
|
|
} else {
|
||
|
|
return 'Connexion internet indisponible. Vérifiez que votre réseau WiFi a accès à internet.';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Afficher un message de reconnexion réussie
|
||
|
|
void _showReconnectionSuccess() {
|
||
|
|
if (navigatorKey?.currentContext == null) return;
|
||
|
|
|
||
|
|
final context = navigatorKey!.currentContext!;
|
||
|
|
|
||
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
||
|
|
SnackBar(
|
||
|
|
content: Row(
|
||
|
|
children: [
|
||
|
|
Container(
|
||
|
|
padding: EdgeInsets.all(8),
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
color: Colors.white.withOpacity(0.2),
|
||
|
|
shape: BoxShape.circle,
|
||
|
|
),
|
||
|
|
child: Icon(Icons.wifi_rounded, color: Colors.white, size: 20),
|
||
|
|
),
|
||
|
|
SizedBox(width: 12),
|
||
|
|
Expanded(
|
||
|
|
child: Column(
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
|
mainAxisSize: MainAxisSize.min,
|
||
|
|
children: [
|
||
|
|
Text(
|
||
|
|
'Connexion rétablie',
|
||
|
|
style: TextStyle(
|
||
|
|
color: Colors.white,
|
||
|
|
fontSize: 16,
|
||
|
|
fontWeight: FontWeight.w600,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
SizedBox(height: 2),
|
||
|
|
Text(
|
||
|
|
'Vous pouvez continuer à utiliser l\'application',
|
||
|
|
style: TextStyle(
|
||
|
|
color: Colors.white.withOpacity(0.9),
|
||
|
|
fontSize: 12,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
backgroundColor: Colors.green[600],
|
||
|
|
behavior: SnackBarBehavior.floating,
|
||
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||
|
|
duration: Duration(seconds: 3),
|
||
|
|
margin: EdgeInsets.all(16),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Ouvrir les paramètres WiFi
|
||
|
|
void _openWifiSettings() async {
|
||
|
|
try {
|
||
|
|
print('📱 Ouverture des paramètres WiFi');
|
||
|
|
const platform = MethodChannel('com.wortis.agent/settings');
|
||
|
|
|
||
|
|
// Sortir du mode kiosque si activé
|
||
|
|
try {
|
||
|
|
await platform.invokeMethod('exitKioskMode');
|
||
|
|
} catch (e) {
|
||
|
|
print('⚠️ Mode kiosque non actif ou erreur: $e');
|
||
|
|
}
|
||
|
|
|
||
|
|
// Ouvrir les paramètres WiFi
|
||
|
|
await platform.invokeMethod('openWifiSettings');
|
||
|
|
|
||
|
|
// Fermer le dialog
|
||
|
|
if (navigatorKey?.currentContext != null) {
|
||
|
|
Navigator.of(navigatorKey!.currentContext!).pop();
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
print('❌ Erreur ouverture paramètres WiFi: $e');
|
||
|
|
|
||
|
|
if (navigatorKey?.currentContext != null) {
|
||
|
|
ScaffoldMessenger.of(navigatorKey!.currentContext!).showSnackBar(
|
||
|
|
SnackBar(
|
||
|
|
content: Text('Impossible d\'ouvrir les paramètres WiFi'),
|
||
|
|
backgroundColor: Colors.red,
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Forcer un test manuel (pour debug)
|
||
|
|
void forceCheck() {
|
||
|
|
print('🔧 Test forcé de connectivité');
|
||
|
|
checkConnectivity();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Méthode de test pour forcer l'affichage du popup
|
||
|
|
void showTestPopup() {
|
||
|
|
print('🧪 Test d\'affichage du popup de déconnexion');
|
||
|
|
_isConnected = false;
|
||
|
|
_lastDisconnectionTime = DateTime.now();
|
||
|
|
_reconnectionAttempts = 0;
|
||
|
|
_showNoConnectionDialog();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Simuler une reconnexion (pour test)
|
||
|
|
void simulateReconnection() {
|
||
|
|
print('🧪 Simulation de reconnexion');
|
||
|
|
_updateConnectivityStatus(true);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Obtenir les statistiques de connectivité
|
||
|
|
Map<String, dynamic> getConnectivityStats() {
|
||
|
|
return {
|
||
|
|
'is_connected': _isConnected,
|
||
|
|
'has_network_interface': _hasNetworkInterface,
|
||
|
|
'last_disconnection': _lastDisconnectionTime?.toIso8601String(),
|
||
|
|
'reconnection_attempts': _reconnectionAttempts,
|
||
|
|
'is_checking': _isChecking,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Réinitialiser les statistiques
|
||
|
|
void resetStats() {
|
||
|
|
_reconnectionAttempts = 0;
|
||
|
|
_lastDisconnectionTime = null;
|
||
|
|
print('📊 Statistiques de connectivité réinitialisées');
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
void dispose() {
|
||
|
|
print('🛑 Arrêt du service de connectivité');
|
||
|
|
_periodicTimer?.cancel();
|
||
|
|
super.dispose();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Extension pour des méthodes utilitaires
|
||
|
|
extension ConnectivityServiceExtension on ConnectivityService {
|
||
|
|
/// Vérifier si la connexion est stable
|
||
|
|
bool get isStable {
|
||
|
|
return _isConnected && _reconnectionAttempts == 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Obtenir le temps depuis la dernière déconnexion
|
||
|
|
Duration? get timeSinceLastDisconnection {
|
||
|
|
if (_lastDisconnectionTime == null) return null;
|
||
|
|
return DateTime.now().difference(_lastDisconnectionTime!);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Obtenir un message de statut lisible
|
||
|
|
String get statusMessage {
|
||
|
|
if (_isConnected) {
|
||
|
|
if (_reconnectionAttempts > 0) {
|
||
|
|
return 'Connexion rétablie après $_reconnectionAttempts tentatives';
|
||
|
|
}
|
||
|
|
return 'Connecté';
|
||
|
|
} else {
|
||
|
|
if (!_hasNetworkInterface) {
|
||
|
|
return 'Aucun réseau WiFi';
|
||
|
|
}
|
||
|
|
return 'Pas de connexion internet';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|