import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'dart:async'; import 'dart:convert'; import 'package:get_storage/get_storage.dart'; import '../../../data/services/volcano_translation_service.dart'; import '../../../data/services/azure_asr_service.dart'; import '../../../data/services/azure_tts_service.dart'; import '../../../data/services/language_manager.dart'; import '../../../core/utils/logger.dart'; import '../../../data/services/voice_interaction_service.dart'; class TranslationController extends GetxController { // 服务 final AzureAsrService _asrService = Get.find(); final VolcanoTranslationService _translationService = Get.find(); final AzureTtsService _ttsService = Get.find(); final LanguageManager _languageManager = Get.find(); final GetStorage _storage = GetStorage(); final VoiceInteractionService _voiceInteractionService = Get.find(); // 存储相关 static const String _historyKey = 'translation_history'; static const String _sourceLanguageKey = 'translation_source_language'; static const String _targetLanguageKey = 'translation_target_language'; static const int _maxHistoryItems = 100; // 状态变量 final isRecognizing = false.obs; final isTranslating = false.obs; final isTtsEnabled = true.obs; // 语言相关 final sourceLanguage = '中文(简体)'.obs; final targetLanguage = '英语'.obs; final sourceLanguageCode = 'zh-CN'.obs; final targetLanguageCode = 'en-US'.obs; var detectedLanguageCode = ''; // 翻译历史和当前项 final translationHistory = [].obs; final Rx currentItem = Rx(null); String? currentSessionId; // UI相关 final ScrollController scrollController = ScrollController(); // 其他 StreamSubscription? _recognitionSubscription; StreamSubscription? _voiceInteractionSubscription; Timer? _translationDebounceTimer; // 获取支持的语言列表 Map get supportedLanguages => _languageManager.getChineseNameToAsrCodeMap(); @override void onInit() { super.onInit(); // 加载上次保存的语言设置 _loadSavedLanguages(); // 初始化语言代码 final sourceAsrCode = _languageManager.getAsrCodeByChineseName(sourceLanguage.value); if (sourceAsrCode != null) { sourceLanguageCode.value = sourceAsrCode; } final targetAsrCode = _languageManager.getAsrCodeByChineseName(targetLanguage.value); if (targetAsrCode != null) { targetLanguageCode.value = targetAsrCode; } // 订阅VoiceInteractionService事件 _subscribeToVoiceInteractionEvents(); // 加载历史记录 loadTranslationHistory(); // 初始化服务 _initServices(); // 使用微任务在布局完成后自动滚动到底部 WidgetsBinding.instance.addPostFrameCallback((_) { _scrollToBottom(); // 额外的延迟滚动,确保在复杂视图中也能正确滚动 Future.delayed(const Duration(milliseconds: 500), () { _scrollToBottom(); }); }); } /// 订阅VoiceInteractionService事件 void _subscribeToVoiceInteractionEvents() { if (_voiceInteractionService.eventStream != null) { _voiceInteractionSubscription = _voiceInteractionService.eventStream!.listen((event) { if (event is RecognitionStartedEvent) { // 收到recognitionStarted事件时,停止当前的AI交互、录音和TTS stopAll(); } }); } } // 加载保存的语言设置 void _loadSavedLanguages() { final savedSourceLanguage = _storage.read(_sourceLanguageKey); final savedTargetLanguage = _storage.read(_targetLanguageKey); if (savedSourceLanguage != null && supportedLanguages.containsKey(savedSourceLanguage)) { sourceLanguage.value = savedSourceLanguage; } if (savedTargetLanguage != null && supportedLanguages.containsKey(savedTargetLanguage)) { targetLanguage.value = savedTargetLanguage; } } // 保存当前语言设置 void _saveLanguageSettings() { _storage.write(_sourceLanguageKey, sourceLanguage.value); _storage.write(_targetLanguageKey, targetLanguage.value); } // 初始化服务 Future _initServices() async { try { await _translationService.initialize(); final List asrSupportedLanguages = [sourceLanguageCode.value, targetLanguageCode.value]; await _ttsService.initialize(); await _asrService.initialize( autoDetectLanguage: true, supportedLanguages: asrSupportedLanguages, ); } catch (e) { Logger.error('初始化服务失败: $e'); } } @override void onClose() { stopRecognition(); _recognitionSubscription?.cancel(); _voiceInteractionSubscription?.cancel(); _translationDebounceTimer?.cancel(); scrollController.dispose(); super.onClose(); } // 切换TTS功能 void toggleTts() => isTtsEnabled.toggle(); // 开始语音识别 Future startRecognition() async { if (isRecognizing.value) return; _voiceInteractionService.pauseVoiceInteraction(); try { // 更新ASR支持的语言 final List asrSupportedLanguages = [sourceLanguageCode.value, targetLanguageCode.value]; await _asrService.setSupportedLanguages(asrSupportedLanguages); currentSessionId = DateTime.now().millisecondsSinceEpoch.toString(); // 初始化新的翻译项 currentItem.value = TranslationItem( sourceText: '', translatedText: '', sourceLanguageCode: sourceLanguageCode.value, targetLanguageCode: targetLanguageCode.value, timestamp: DateTime.now(), sessionId: currentSessionId!, isFirstInSession: true, ); // 开始连续语音识别 final recognitionStream = await _asrService.startContinuousRecognition(); isRecognizing.value = true; // 监听识别结果 _recognitionSubscription?.cancel(); _recognitionSubscription = recognitionStream.listen(_handleRecognitionEvent); } catch (e) { isRecognizing.value = false; Logger.error('启动语音识别失败: $e'); } } // 处理识别事件 void _handleRecognitionEvent(RecognitionEvent event) { if (event.type == RecognitionEventType.finalResult && event.text.isNotEmpty) { detectedLanguageCode = event.detectedLanguage; handleFinalResult(event.text); } else if (event.type == RecognitionEventType.intermediateResult && event.text.isNotEmpty) { if (event.detectedLanguage.isNotEmpty) { detectedLanguageCode = event.detectedLanguage; } handleIntermediateResult(event.text); } else if (event.type == RecognitionEventType.error) { isRecognizing.value = false; Logger.error('语音识别错误: ${event.error}'); } else if (event.type == RecognitionEventType.canceled) { isRecognizing.value = false; Logger.error('语音识别取消: ${event.error}'); } } // 停止语音识别 Future stopRecognition() async { if (!isRecognizing.value) return; try { await _asrService.stopContinuousRecognition(); _recognitionSubscription?.cancel(); _recognitionSubscription = null; isRecognizing.value = false; // 如果当前有中间结果,将其作为最终结果处理 if (currentItem.value != null && currentItem.value!.sourceText.isNotEmpty) { // 标记为最终结果 currentItem.value!.isIntermediate = false; // 如果还没有翻译结果,尝试翻译 if (currentItem.value!.translatedText.isEmpty) { await translateText(currentItem.value!.sourceText); } translationHistory.add(currentItem.value!); saveTranslationHistory(); } currentSessionId = null; currentItem.value = null; } catch (e) { Logger.error('停止语音识别失败: $e'); } } // 处理中间识别结果 void handleIntermediateResult(String text) { if (text.isEmpty) return; if (currentItem.value == null) { currentSessionId ??= DateTime.now().millisecondsSinceEpoch.toString(); currentItem.value = TranslationItem( sourceText: text, translatedText: '', sourceLanguageCode: sourceLanguageCode.value, targetLanguageCode: targetLanguageCode.value, timestamp: DateTime.now(), sessionId: currentSessionId!, isFirstInSession: translationHistory.isEmpty || translationHistory.last.sessionId != currentSessionId, isIntermediate: true, ); // 添加新项后滚动到底部 _scrollToBottom(); } else { // 更新当前项 currentItem.value!.sourceText = text; currentItem.value!.isIntermediate = true; currentItem.refresh(); // 更新后滚动到底部 _scrollToBottom(); } // 防抖处理翻译请求 _translationDebounceTimer?.cancel(); _translationDebounceTimer = Timer(const Duration(milliseconds: 300), () { if (currentItem.value != null && currentItem.value!.sourceText.isNotEmpty) { translateText(currentItem.value!.sourceText); } }); } // 处理最终识别结果 Future handleFinalResult(String text) async { if (text.isEmpty || currentItem.value == null) return; // 更新当前项,但不立即添加到历史记录 currentItem.value!.sourceText = text; currentItem.value!.isIntermediate = false; currentItem.refresh(); // 更新后滚动到底部 _scrollToBottom(); // 先翻译,保持UI稳定 final translationResult = await translateText(text); // 翻译完成后再添加到历史记录 if (currentItem.value != null) { translationHistory.add(currentItem.value!); saveTranslationHistory(); // 添加到历史记录后滚动到底部 _scrollToBottom(); // 播放TTS if (translationResult != null && isTtsEnabled.value) { await playTranslatedText(translationResult); } // 重置当前项,准备下一次输入 currentItem.value = null; } } // 翻译文本 Future translateText(String sourceText) async { if (sourceText.isEmpty) return null; isTranslating.value = true; String? translationResult; try { // 根据检测到的语言确定源语言和目标语言 final bool shouldSwap = detectedLanguageCode == sourceLanguageCode.value; final detectedSourceLanguageCode = shouldSwap ? sourceLanguageCode.value : targetLanguageCode.value; final detectedTargetLanguageCode = shouldSwap ? targetLanguageCode.value : sourceLanguageCode.value; // 调用翻译服务 translationResult = await _translationService.translateText( text: sourceText, sourceLanguageCode: detectedSourceLanguageCode, targetLanguageCode: detectedTargetLanguageCode, ); if (translationResult != null) { // 更新翻译结果 if (currentItem.value != null && currentItem.value!.sourceText.contains(sourceText)) { // 更新当前项 currentItem.value!.translatedText = translationResult; currentItem.value!.sourceLanguageCode = detectedSourceLanguageCode; currentItem.value!.targetLanguageCode = detectedTargetLanguageCode; currentItem.refresh(); // 更新后滚动到底部 _scrollToBottom(); } else { // 更新历史记录 final index = translationHistory.indexWhere((item) => item.sourceText == sourceText); if (index != -1) { translationHistory[index].translatedText = translationResult; translationHistory[index].sourceLanguageCode = detectedSourceLanguageCode; translationHistory[index].targetLanguageCode = detectedTargetLanguageCode; translationHistory.refresh(); saveTranslationHistory(); // 更新后滚动到底部 _scrollToBottom(); } } } } catch (e) { Logger.error('翻译失败: $e'); } finally { isTranslating.value = false; } return translationResult; } // 播放翻译文本 Future playTranslatedText(String text) async { if (text.isEmpty) return; try { final bool shouldSwap = detectedLanguageCode == targetLanguageCode.value; final detectedTargetLanguageCode = shouldSwap ? sourceLanguageCode.value : targetLanguageCode.value; final voiceName = _languageManager.getTtsVoiceNameByAsrCode(detectedTargetLanguageCode) ?? 'en-US-AriaNeural'; await _ttsService.setVoice(voiceName); await _ttsService.speak(text); } catch (e) { Logger.error('播放翻译失败: $e'); } } // 停止所有操作 Future stopAll() async { try { if (isRecognizing.value) { await stopRecognition(); } _translationDebounceTimer?.cancel(); await _ttsService.stop(); } catch (e) { Logger.error('停止操作失败: $e'); } } // 播放特定翻译项 Future playTranslationItem(TranslationItem item) async { if (item.translatedText.isEmpty) return; try { final voiceName = _languageManager.getTtsVoiceNameByAsrCode(item.targetLanguageCode) ?? 'en-US-AriaNeural'; await _ttsService.setVoice(voiceName); await _ttsService.speak(item.translatedText); } catch (e) { Logger.error('播放翻译项失败: $e'); } } // 播放文本 Future playText(String text, {String? languageCode}) async { if (text.isEmpty) return; try { final voiceName = languageCode != null ? _languageManager.getTtsVoiceNameByAsrCode(languageCode) ?? 'en-US-AriaNeural' : _languageManager.getTtsVoiceNameByAsrCode(targetLanguageCode.value) ?? 'en-US-AriaNeural'; await _ttsService.setVoice(voiceName); await _ttsService.speak(text); } catch (e) { Logger.error('播放文本失败: $e'); } } // 滚动到底部 void _scrollToBottom() { if (scrollController.hasClients && (translationHistory.isNotEmpty || currentItem.value != null)) { try { scrollController.animateTo( scrollController.position.maxScrollExtent, duration: const Duration(milliseconds: 300), curve: Curves.easeOut, ); } catch (e) { Logger.error('滚动到底部失败: $e'); } } } // 设置滚动监听器(简化版,不再需要实际监听) void setupScrollListener() { // 简化后的空方法,保留接口兼容性 } // 底部控制栏高度 double _bottomBarHeight = 0.0; // 设置底部控制栏高度 void setBottomBarHeight(double height) { _bottomBarHeight = height; } // 获取底部控制栏高度 double get bottomBarHeight => _bottomBarHeight; // 切换源语言和目标语言 void swapLanguages() { stopAll(); final tempName = sourceLanguage.value; sourceLanguage.value = targetLanguage.value; targetLanguage.value = tempName; final tempCode = sourceLanguageCode.value; sourceLanguageCode.value = targetLanguageCode.value; targetLanguageCode.value = tempCode; // 保存语言设置 _saveLanguageSettings(); } // 设置源语言 void setSourceLanguage(String language) { if (sourceLanguage.value != language) { stopAll(); sourceLanguage.value = language; final asrCode = _languageManager.getAsrCodeByChineseName(language); if (asrCode != null) { sourceLanguageCode.value = asrCode; } // 保存语言设置 _saveLanguageSettings(); } } // 设置目标语言 void setTargetLanguage(String language) { if (targetLanguage.value != language) { stopAll(); targetLanguage.value = language; final asrCode = _languageManager.getAsrCodeByChineseName(language); if (asrCode != null) { targetLanguageCode.value = asrCode; } // 保存语言设置 _saveLanguageSettings(); } } // 加载翻译历史 void loadTranslationHistory() { final historyJson = _storage.read(_historyKey); if (historyJson != null) { try { final List historyList = jsonDecode(historyJson); translationHistory.value = historyList.map((item) => TranslationItem.fromJson(item)).toList(); // 使用微任务确保在视图完全渲染后滚动到底部 WidgetsBinding.instance.addPostFrameCallback((_) { _scrollToBottom(); }); } catch (e) { _storage.remove(_historyKey); Logger.error('加载翻译历史失败: $e'); } } } // 保存翻译历史 void saveTranslationHistory() { if (translationHistory.length > _maxHistoryItems) { translationHistory.removeRange(0, translationHistory.length - _maxHistoryItems); } try { final List> historyJson = translationHistory.map((item) => item.toJson()).toList(); _storage.write(_historyKey, jsonEncode(historyJson)); } catch (e) { Logger.error('保存翻译历史失败: $e'); } } // 清除翻译历史 void clearTranslationHistory() { translationHistory.clear(); _storage.remove(_historyKey); } // 滚动到底部 (公共方法,用于向后兼容) void scrollToBottom({bool animate = true}) { _scrollToBottom(); } } // 翻译项模型 class TranslationItem { String sourceText; String translatedText; String sourceLanguageCode; String targetLanguageCode; final DateTime timestamp; String sessionId; final bool isFirstInSession; bool isIntermediate; // 是否为中间结果 TranslationItem({ required this.sourceText, required this.translatedText, required this.sourceLanguageCode, required this.targetLanguageCode, required this.timestamp, required this.sessionId, this.isFirstInSession = false, this.isIntermediate = false, }); factory TranslationItem.fromJson(Map json) { DateTime timestamp; try { timestamp = DateTime.parse(json['timestamp']); } catch (e) { timestamp = DateTime.now(); } return TranslationItem( sourceText: json['sourceText'] ?? '', translatedText: json['translatedText'] ?? '', sourceLanguageCode: json['sourceLanguageCode'] ?? 'zh-CN', targetLanguageCode: json['targetLanguageCode'] ?? 'en-US', timestamp: timestamp, sessionId: json['sessionId'] ?? DateTime.now().millisecondsSinceEpoch.toString(), isFirstInSession: json['isFirstInSession'] ?? false, isIntermediate: false, // 从存储加载的都是最终结果 ); } Map toJson() { return { 'sourceText': sourceText, 'translatedText': translatedText, 'sourceLanguageCode': sourceLanguageCode, 'targetLanguageCode': targetLanguageCode, 'timestamp': timestamp.toIso8601String(), 'sessionId': sessionId, 'isFirstInSession': isFirstInSession, // 不保存isIntermediate,因为存储的都是最终结果 }; } // 获取源语言的中文名称 String get sourceLanguageName { final languageManager = Get.find(); return languageManager.getChineseNameByAsrCode(sourceLanguageCode) ?? '未知语言'; } // 获取目标语言的中文名称 String get targetLanguageName { final languageManager = Get.find(); return languageManager.getChineseNameByAsrCode(targetLanguageCode) ?? '未知语言'; } }