feat: mise à jour v1.1.17 et ajout du tableau de bord des statistiques d'événements
- Mise à jour de la version de l'application à `1.1.17` dans `app_version.dart` et `version.json`. - Création d'un module complet de statistiques (`EventStatisticsPage`, `EventStatisticsService`, `EventStatisticsTab`) permettant de filtrer et visualiser les KPI d'événements (montants HT/TTC, panier moyen, répartition par type, top options). - Ajout d'une entrée "Statistiques événements" dans le menu latéral (`MainDrawer`) protégée par la permission `generate_reports`. - Migration exclusive vers Google Cloud TTS dans `SmartTextToSpeechService` et suppression de `TextToSpeechService` (Web Speech API native) pour garantir une compatibilité maximale sur tous les navigateurs. - Mise à jour des dépendances dans `pubspec.yaml` (`google_fonts`, `flutter_secure_storage`, `mobile_scanner`, `flutter_local_notifications`). - Migration du code d'export ICS vers `package:web` pour remplacer l'utilisation de `dart:html` obsolète. - Mise à jour du `CHANGELOG.md` documentant les statistiques et l'évolution du service de synthèse vocale.
This commit is contained in:
@@ -1,109 +1,50 @@
|
||||
import 'package:em2rp/services/text_to_speech_service.dart';
|
||||
import 'package:em2rp/services/cloud_text_to_speech_service.dart';
|
||||
import 'package:em2rp/utils/debug_log.dart';
|
||||
|
||||
/// Service hybride intelligent pour le Text-to-Speech
|
||||
/// Essaie d'abord Web Speech API (gratuit, rapide), puis fallback vers Cloud TTS
|
||||
/// Service de synthèse vocale utilisant exclusivement Google Cloud TTS
|
||||
/// Garantit une qualité et une compatibilité maximales sur tous les navigateurs
|
||||
class SmartTextToSpeechService {
|
||||
static bool _initialized = false;
|
||||
static bool _webSpeechWorks = true; // Optimiste par défaut
|
||||
static int _webSpeechFailures = 0;
|
||||
static const int _maxFailuresBeforeSwitch = 2;
|
||||
|
||||
/// Initialiser le service
|
||||
static Future<void> initialize() async {
|
||||
if (_initialized) return;
|
||||
|
||||
try {
|
||||
DebugLog.info('[SmartTTS] Initializing...');
|
||||
DebugLog.info('[SmartTTS] Initializing Cloud TTS only...');
|
||||
|
||||
// Initialiser Web Speech API
|
||||
await TextToSpeechService.initialize();
|
||||
|
||||
// Pré-charger les phrases courantes pour Cloud TTS en arrière-plan
|
||||
// (ne bloque pas l'initialisation)
|
||||
Future.delayed(const Duration(seconds: 2), () {
|
||||
// Pré-charger les phrases courantes pour Cloud TTS
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
CloudTextToSpeechService.preloadCommonPhrases();
|
||||
});
|
||||
|
||||
_initialized = true;
|
||||
DebugLog.info('[SmartTTS] ✓ Initialized (Web Speech preferred)');
|
||||
DebugLog.info('[SmartTTS] ✓ Initialized (Cloud TTS only)');
|
||||
} catch (e) {
|
||||
DebugLog.error('[SmartTTS] Initialization error', e);
|
||||
_initialized = true; // Continuer quand même
|
||||
}
|
||||
}
|
||||
|
||||
/// Lire un texte à haute voix avec stratégie intelligente
|
||||
/// Lire un texte à haute voix avec Google Cloud TTS
|
||||
static Future<void> speak(String text) async {
|
||||
if (!_initialized) {
|
||||
await initialize();
|
||||
}
|
||||
|
||||
// Si Web Speech a échoué plusieurs fois, utiliser directement Cloud TTS
|
||||
if (!_webSpeechWorks || _webSpeechFailures >= _maxFailuresBeforeSwitch) {
|
||||
return _speakWithCloudTTS(text);
|
||||
}
|
||||
|
||||
// Essayer Web Speech d'abord
|
||||
try {
|
||||
await _speakWithWebSpeech(text);
|
||||
// Si succès, réinitialiser le compteur d'échecs
|
||||
_webSpeechFailures = 0;
|
||||
} catch (e) {
|
||||
DebugLog.warning('[SmartTTS] Web Speech failed ($e), trying Cloud TTS...');
|
||||
_webSpeechFailures++;
|
||||
|
||||
// Si trop d'échecs, basculer vers Cloud TTS par défaut
|
||||
if (_webSpeechFailures >= _maxFailuresBeforeSwitch) {
|
||||
DebugLog.info('[SmartTTS] Switching to Cloud TTS as primary');
|
||||
_webSpeechWorks = false;
|
||||
}
|
||||
|
||||
// Fallback vers Cloud TTS
|
||||
await _speakWithCloudTTS(text);
|
||||
}
|
||||
}
|
||||
|
||||
/// Utiliser Web Speech API
|
||||
static Future<void> _speakWithWebSpeech(String text) async {
|
||||
DebugLog.info('[SmartTTS] → Trying Web Speech API');
|
||||
|
||||
// Timeout pour détecter si ça ne marche pas
|
||||
await Future.any([
|
||||
TextToSpeechService.speak(text),
|
||||
Future.delayed(const Duration(seconds: 3), () {
|
||||
throw Exception('Web Speech timeout');
|
||||
}),
|
||||
]);
|
||||
|
||||
DebugLog.info('[SmartTTS] ✓ Web Speech succeeded');
|
||||
}
|
||||
|
||||
/// Utiliser Cloud TTS
|
||||
static Future<void> _speakWithCloudTTS(String text) async {
|
||||
DebugLog.info('[SmartTTS] → Using Cloud TTS');
|
||||
|
||||
try {
|
||||
DebugLog.info('[SmartTTS] → Using Cloud TTS');
|
||||
await CloudTextToSpeechService.speak(text);
|
||||
DebugLog.info('[SmartTTS] ✓ Cloud TTS succeeded');
|
||||
} catch (e) {
|
||||
DebugLog.error('[SmartTTS] ✗ Cloud TTS failed', e);
|
||||
|
||||
// En dernier recours, réessayer Web Speech
|
||||
if (!_webSpeechWorks) {
|
||||
DebugLog.info('[SmartTTS] Last resort: trying Web Speech again');
|
||||
await TextToSpeechService.speak(text);
|
||||
} else {
|
||||
rethrow;
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Arrêter toute lecture en cours
|
||||
static Future<void> stop() async {
|
||||
try {
|
||||
await TextToSpeechService.stop();
|
||||
CloudTextToSpeechService.stopAll();
|
||||
} catch (e) {
|
||||
DebugLog.error('[SmartTTS] Error stopping', e);
|
||||
@@ -112,48 +53,22 @@ class SmartTextToSpeechService {
|
||||
|
||||
/// Vérifier si une lecture est en cours
|
||||
static Future<bool> isSpeaking() async {
|
||||
try {
|
||||
return await TextToSpeechService.isSpeaking();
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Forcer l'utilisation de Cloud TTS (pour tests ou préférence utilisateur)
|
||||
static void forceCloudTTS() {
|
||||
DebugLog.info('[SmartTTS] Forced to use Cloud TTS');
|
||||
_webSpeechWorks = false;
|
||||
_webSpeechFailures = _maxFailuresBeforeSwitch;
|
||||
}
|
||||
|
||||
/// Forcer l'utilisation de Web Speech (pour tests ou préférence utilisateur)
|
||||
static void forceWebSpeech() {
|
||||
DebugLog.info('[SmartTTS] Forced to use Web Speech');
|
||||
_webSpeechWorks = true;
|
||||
_webSpeechFailures = 0;
|
||||
}
|
||||
|
||||
/// Réinitialiser la stratégie (utile pour tests)
|
||||
static void resetStrategy() {
|
||||
DebugLog.info('[SmartTTS] Strategy reset');
|
||||
_webSpeechWorks = true;
|
||||
_webSpeechFailures = 0;
|
||||
// Cloud TTS n'a pas de méthode native pour vérifier le statut
|
||||
// Retourner false par défaut (peut être amélioré si nécessaire)
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Obtenir le statut actuel
|
||||
static Map<String, dynamic> getStatus() {
|
||||
return {
|
||||
'initialized': _initialized,
|
||||
'webSpeechWorks': _webSpeechWorks,
|
||||
'failures': _webSpeechFailures,
|
||||
'currentStrategy': _webSpeechWorks ? 'Web Speech (primary)' : 'Cloud TTS (primary)',
|
||||
'currentStrategy': 'Cloud TTS (exclusive)',
|
||||
};
|
||||
}
|
||||
|
||||
/// Nettoyer les ressources
|
||||
static Future<void> dispose() async {
|
||||
try {
|
||||
await TextToSpeechService.dispose();
|
||||
CloudTextToSpeechService.clearCache();
|
||||
} catch (e) {
|
||||
DebugLog.error('[SmartTTS] Error disposing', e);
|
||||
|
||||
Reference in New Issue
Block a user