// voice_recognition_service.dart import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter/foundation.dart'; import 'volcano_asr_service.dart'; import 'package:get/get.dart'; class VoiceRecognitionService extends GetxService { VolcanoAsrService? _asrService; bool _isInitialized = false; bool _isStoppingRecognition = false; final isRecognizing = false.obs; Function(String)? onRecognitionResult; Function(String)? onError; Function()? onRecognitionComplete; Function(String)? onSentenceComplete; Function(String)? onConnectionStatusChanged; VoiceRecognitionService(); void _logError(String message, [dynamic error, StackTrace? stackTrace]) { debugPrint('🔴 VoiceRecognitionService Error: $message'); if (error != null) debugPrint('Error details: $error'); if (stackTrace != null) debugPrint('Stack trace: $stackTrace'); } @override Future onInit() async { super.onInit(); if (!_isInitialized) { try { await _initializeServices(); _isInitialized = true; debugPrint('✅ VoiceRecognitionService initialized successfully'); } catch (e, stackTrace) { _logError('Failed to initialize services', e, stackTrace); rethrow; } } } Future _initializeServices() async { try { final appKey = dotenv.env['VOLCANO_APP_KEY']; final accessKey = dotenv.env['VOLCANO_ACCESS_KEY']; if (appKey == null || accessKey == null) { throw Exception('Volcano ASR service configuration is missing. Please check your .env file.'); } _asrService = VolcanoAsrService( appKey: appKey, accessKey: accessKey, onResult: _handleRecognitionResult, onError: _handleError, onConnectionStatusChanged: _handleConnectionStatusChanged, ); await _setupServices(); } catch (e, stackTrace) { _logError('Error during service initialization', e, stackTrace); rethrow; } } Future _setupServices() async { try { _asrService?.onResult = (result) { debugPrint('ASR Result: $result'); onRecognitionResult?.call(result); onSentenceComplete?.call(result); }; _asrService?.onError = (error) { _logError('ASR service error', error); onError?.call('ASR error: $error'); }; _asrService?.onConnectionStatusChanged = (status) { final isConnected = status == 'connected'; debugPrint('ASR Connection status: $status'); if (!isConnected && isRecognizing.value && !_isStoppingRecognition) { stopRecognition(); onRecognitionComplete?.call(); } onConnectionStatusChanged?.call(status); }; } catch (e, stackTrace) { _logError('Error during services setup', e, stackTrace); rethrow; } } Future startRecognition() async { if (isRecognizing.value || _asrService == null) { debugPrint('⚠️ Cannot start recognition: ${isRecognizing.value ? 'Already recognizing' : 'ASR service not initialized'}'); return; } try { _isStoppingRecognition = false; debugPrint('Starting voice recognition...'); await _asrService?.startRecognition(); isRecognizing.value = true; } catch (e, stackTrace) { _logError('Failed to start recognition', e, stackTrace); onError?.call('Failed to start recognition: $e'); await stopRecognition(); } } Future stopRecognition() async { if (!isRecognizing.value || _asrService == null || _isStoppingRecognition) return; try { _isStoppingRecognition = true; debugPrint('Stopping voice recognition...'); isRecognizing.value = false; await _asrService?.stopRecognition().timeout( const Duration(seconds: 3), onTimeout: () { debugPrint('ASR service stop timeout, forcing cleanup'); _cleanupResources(); }, ); final complete = onRecognitionComplete; if (complete != null) { Future.microtask(complete); } } catch (e, stackTrace) { _logError('Failed to stop recognition', e, stackTrace); onError?.call('Failed to stop recognition: $e'); _cleanupResources(); } finally { _isStoppingRecognition = false; } } void _cleanupResources() { if (_asrService != null) { _asrService!.onResult = null; _asrService!.onError = null; _asrService!.onConnectionStatusChanged = null; } isRecognizing.value = false; _isStoppingRecognition = false; } void _handleRecognitionResult(String result) { onRecognitionResult?.call(result); } void _handleError(String error) { onError?.call(error); } void _handleConnectionStatusChanged(String status) { onConnectionStatusChanged?.call(status); } @override void onClose() { debugPrint('Cleaning up VoiceRecognitionService...'); stopRecognition().then((_) { debugPrint('VoiceRecognitionService cleanup completed'); super.onClose(); }).catchError((e) { debugPrint('Error during VoiceRecognitionService cleanup: $e'); super.onClose(); }); } }