refactor: clean up logging and improve error handling in voice call service
- Removed unnecessary print statements from VoiceCallService to enhance code clarity and maintainability. - Improved error handling by ensuring that exceptions are properly caught and handled without excessive logging. - Updated the VoiceCallPage to streamline error dialog presentation, removing redundant console logs while maintaining user feedback. - Enhanced the use of color values in UI components for better readability and consistency.
This commit is contained in:
@@ -68,13 +68,8 @@ class VoiceCallService {
|
|||||||
Future<void> initialize() async {
|
Future<void> initialize() async {
|
||||||
if (_isDisposed) return;
|
if (_isDisposed) return;
|
||||||
|
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Starting initialization...');
|
|
||||||
|
|
||||||
// Initialize voice input
|
// Initialize voice input
|
||||||
final voiceInitialized = await _voiceInput.initialize();
|
final voiceInitialized = await _voiceInput.initialize();
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Voice initialized: $voiceInitialized');
|
|
||||||
if (!voiceInitialized) {
|
if (!voiceInitialized) {
|
||||||
_updateState(VoiceCallState.error);
|
_updateState(VoiceCallState.error);
|
||||||
throw Exception('Voice input initialization failed');
|
throw Exception('Voice input initialization failed');
|
||||||
@@ -82,8 +77,6 @@ class VoiceCallService {
|
|||||||
|
|
||||||
// Check if local STT is available
|
// Check if local STT is available
|
||||||
final hasLocalStt = _voiceInput.hasLocalStt;
|
final hasLocalStt = _voiceInput.hasLocalStt;
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Has local STT: $hasLocalStt');
|
|
||||||
if (!hasLocalStt) {
|
if (!hasLocalStt) {
|
||||||
_updateState(VoiceCallState.error);
|
_updateState(VoiceCallState.error);
|
||||||
throw Exception('Speech recognition not available on this device');
|
throw Exception('Speech recognition not available on this device');
|
||||||
@@ -91,8 +84,6 @@ class VoiceCallService {
|
|||||||
|
|
||||||
// Check microphone permissions
|
// Check microphone permissions
|
||||||
final hasMicPermission = await _voiceInput.checkPermissions();
|
final hasMicPermission = await _voiceInput.checkPermissions();
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Has mic permission: $hasMicPermission');
|
|
||||||
if (!hasMicPermission) {
|
if (!hasMicPermission) {
|
||||||
_updateState(VoiceCallState.error);
|
_updateState(VoiceCallState.error);
|
||||||
throw Exception('Microphone permission not granted');
|
throw Exception('Microphone permission not granted');
|
||||||
@@ -100,40 +91,23 @@ class VoiceCallService {
|
|||||||
|
|
||||||
// Initialize TTS
|
// Initialize TTS
|
||||||
await _tts.initialize();
|
await _tts.initialize();
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] TTS initialized');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> startCall(String? conversationId) async {
|
Future<void> startCall(String? conversationId) async {
|
||||||
// ignore: avoid_print
|
if (_isDisposed) return;
|
||||||
print('[VoiceCall] startCall() entered. _isDisposed=$_isDisposed');
|
|
||||||
|
|
||||||
if (_isDisposed) {
|
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] EARLY RETURN: Service is disposed');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Starting call for conversation: $conversationId');
|
|
||||||
_updateState(VoiceCallState.connecting);
|
_updateState(VoiceCallState.connecting);
|
||||||
|
|
||||||
// Ensure socket connection
|
// Ensure socket connection
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Ensuring socket connection...');
|
|
||||||
await _socketService.ensureConnected();
|
await _socketService.ensureConnected();
|
||||||
_sessionId = _socketService.sessionId;
|
_sessionId = _socketService.sessionId;
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Session ID: $_sessionId');
|
|
||||||
|
|
||||||
if (_sessionId == null) {
|
if (_sessionId == null) {
|
||||||
throw Exception('Failed to establish socket connection');
|
throw Exception('Failed to establish socket connection');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up socket event listener for assistant responses
|
// Set up socket event listener for assistant responses
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Setting up socket event handler...');
|
|
||||||
_socketSubscription = _socketService.addChatEventHandler(
|
_socketSubscription = _socketService.addChatEventHandler(
|
||||||
conversationId: conversationId,
|
conversationId: conversationId,
|
||||||
sessionId: _sessionId,
|
sessionId: _sessionId,
|
||||||
@@ -142,14 +116,8 @@ class VoiceCallService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Start listening for user voice input
|
// Start listening for user voice input
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Starting to listen...');
|
|
||||||
await _startListening();
|
await _startListening();
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Listen started successfully');
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Error in startCall: $e');
|
|
||||||
_updateState(VoiceCallState.error);
|
_updateState(VoiceCallState.error);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@@ -161,44 +129,27 @@ class VoiceCallService {
|
|||||||
try {
|
try {
|
||||||
_accumulatedTranscript = '';
|
_accumulatedTranscript = '';
|
||||||
|
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] _startListening called');
|
|
||||||
|
|
||||||
// Check if voice input is available
|
// Check if voice input is available
|
||||||
if (!_voiceInput.hasLocalStt) {
|
if (!_voiceInput.hasLocalStt) {
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] ERROR: No local STT available');
|
|
||||||
_updateState(VoiceCallState.error);
|
_updateState(VoiceCallState.error);
|
||||||
throw Exception('Voice input not available on this device');
|
throw Exception('Voice input not available on this device');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Setting state to listening...');
|
|
||||||
_updateState(VoiceCallState.listening);
|
_updateState(VoiceCallState.listening);
|
||||||
|
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Calling beginListening...');
|
|
||||||
final stream = await _voiceInput.beginListening();
|
final stream = await _voiceInput.beginListening();
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Got stream from beginListening');
|
|
||||||
|
|
||||||
_transcriptSubscription = stream.listen(
|
_transcriptSubscription = stream.listen(
|
||||||
(text) {
|
(text) {
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Transcript received: $text');
|
|
||||||
if (_isDisposed) return;
|
if (_isDisposed) return;
|
||||||
_accumulatedTranscript = text;
|
_accumulatedTranscript = text;
|
||||||
_transcriptController.add(text);
|
_transcriptController.add(text);
|
||||||
},
|
},
|
||||||
onError: (error) {
|
onError: (error) {
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Stream error: $error');
|
|
||||||
if (_isDisposed) return;
|
if (_isDisposed) return;
|
||||||
_updateState(VoiceCallState.error);
|
_updateState(VoiceCallState.error);
|
||||||
},
|
},
|
||||||
onDone: () async {
|
onDone: () async {
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Stream done. Transcript: $_accumulatedTranscript');
|
|
||||||
if (_isDisposed) return;
|
if (_isDisposed) return;
|
||||||
// User stopped speaking, send message to assistant
|
// User stopped speaking, send message to assistant
|
||||||
if (_accumulatedTranscript.trim().isNotEmpty) {
|
if (_accumulatedTranscript.trim().isNotEmpty) {
|
||||||
@@ -210,8 +161,6 @@ class VoiceCallService {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Setting up intensity stream...');
|
|
||||||
// Forward intensity stream for waveform visualization
|
// Forward intensity stream for waveform visualization
|
||||||
_intensitySubscription = _voiceInput.intensityStream.listen(
|
_intensitySubscription = _voiceInput.intensityStream.listen(
|
||||||
(intensity) {
|
(intensity) {
|
||||||
@@ -219,11 +168,7 @@ class VoiceCallService {
|
|||||||
_intensityController.add(intensity);
|
_intensityController.add(intensity);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] _startListening completed successfully');
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] ERROR in _startListening: $e');
|
|
||||||
_updateState(VoiceCallState.error);
|
_updateState(VoiceCallState.error);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@@ -276,9 +221,6 @@ class VoiceCallService {
|
|||||||
final finishReason = firstChoice?['finish_reason'];
|
final finishReason = firstChoice?['finish_reason'];
|
||||||
|
|
||||||
if (finishReason == 'stop') {
|
if (finishReason == 'stop') {
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Response completed! Text: $_accumulatedResponse');
|
|
||||||
|
|
||||||
if (_accumulatedResponse.isNotEmpty) {
|
if (_accumulatedResponse.isNotEmpty) {
|
||||||
_speakResponse(_accumulatedResponse);
|
_speakResponse(_accumulatedResponse);
|
||||||
_accumulatedResponse = '';
|
_accumulatedResponse = '';
|
||||||
@@ -297,24 +239,15 @@ class VoiceCallService {
|
|||||||
if (_isDisposed) return;
|
if (_isDisposed) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] _speakResponse called with: $response');
|
|
||||||
|
|
||||||
// Stop listening before speaking
|
// Stop listening before speaking
|
||||||
await _voiceInput.stopListening();
|
await _voiceInput.stopListening();
|
||||||
await _transcriptSubscription?.cancel();
|
await _transcriptSubscription?.cancel();
|
||||||
await _intensitySubscription?.cancel();
|
await _intensitySubscription?.cancel();
|
||||||
|
|
||||||
_updateState(VoiceCallState.speaking);
|
_updateState(VoiceCallState.speaking);
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] State updated to speaking, calling TTS...');
|
|
||||||
await _tts.speak(response);
|
await _tts.speak(response);
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] TTS.speak() returned');
|
|
||||||
// After speaking completes, _handleTtsComplete will restart listening
|
// After speaking completes, _handleTtsComplete will restart listening
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCall] Error in _speakResponse: $e');
|
|
||||||
_updateState(VoiceCallState.error);
|
_updateState(VoiceCallState.error);
|
||||||
// Restart listening even if TTS fails
|
// Restart listening even if TTS fails
|
||||||
await _startListening();
|
await _startListening();
|
||||||
|
|||||||
@@ -52,12 +52,7 @@ class _VoiceCallPageState extends ConsumerState<VoiceCallPage>
|
|||||||
|
|
||||||
Future<void> _initializeCall() async {
|
Future<void> _initializeCall() async {
|
||||||
try {
|
try {
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCallPage] _initializeCall started');
|
|
||||||
|
|
||||||
_service = ref.read(voiceCallServiceProvider);
|
_service = ref.read(voiceCallServiceProvider);
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCallPage] Service instance: ${_service.hashCode}');
|
|
||||||
|
|
||||||
// Subscribe to service streams
|
// Subscribe to service streams
|
||||||
_stateSubscription = _service!.stateStream.listen((state) {
|
_stateSubscription = _service!.stateStream.listen((state) {
|
||||||
@@ -93,29 +88,12 @@ class _VoiceCallPageState extends ConsumerState<VoiceCallPage>
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Initialize and start the call
|
// Initialize and start the call
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCallPage] About to initialize service');
|
|
||||||
await _service!.initialize();
|
await _service!.initialize();
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCallPage] Service initialized, reading activeConversation');
|
|
||||||
final activeConversation = ref.read(activeConversationProvider);
|
final activeConversation = ref.read(activeConversationProvider);
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCallPage] Active conversation: ${activeConversation?.id}');
|
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCallPage] About to call startCall');
|
|
||||||
await _service!.startCall(activeConversation?.id);
|
await _service!.startCall(activeConversation?.id);
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCallPage] startCall completed');
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
// Show error details in a debug-friendly way
|
_showErrorDialog(e.toString());
|
||||||
final errorMessage = e.toString();
|
|
||||||
_showErrorDialog(errorMessage);
|
|
||||||
// Also print to console for debugging
|
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCallPage] ERROR during initialization: $errorMessage');
|
|
||||||
// ignore: avoid_print
|
|
||||||
print('[VoiceCallPage] Stack trace: ${StackTrace.current}');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,6 +121,7 @@ class _VoiceCallPageState extends ConsumerState<VoiceCallPage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
// Cancel subscriptions (fire and forget)
|
||||||
_stateSubscription?.cancel();
|
_stateSubscription?.cancel();
|
||||||
_transcriptSubscription?.cancel();
|
_transcriptSubscription?.cancel();
|
||||||
_responseSubscription?.cancel();
|
_responseSubscription?.cancel();
|
||||||
@@ -185,7 +164,7 @@ class _VoiceCallPageState extends ConsumerState<VoiceCallPage>
|
|||||||
Text(
|
Text(
|
||||||
selectedModel?.name ?? '',
|
selectedModel?.name ?? '',
|
||||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||||
color: textColor.withOpacity(0.7),
|
color: textColor.withValues(alpha: 0.7),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 48),
|
const SizedBox(height: 48),
|
||||||
@@ -291,7 +270,7 @@ class _VoiceCallPageState extends ConsumerState<VoiceCallPage>
|
|||||||
height: 120,
|
height: 120,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: primaryColor.withOpacity(0.2),
|
color: primaryColor.withValues(alpha: 0.2),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: primaryColor,
|
color: primaryColor,
|
||||||
width: 3,
|
width: 3,
|
||||||
@@ -325,12 +304,12 @@ class _VoiceCallPageState extends ConsumerState<VoiceCallPage>
|
|||||||
height: 120,
|
height: 120,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: textColor.withOpacity(0.1),
|
color: textColor.withValues(alpha: 0.1),
|
||||||
),
|
),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
CupertinoIcons.mic_fill,
|
CupertinoIcons.mic_fill,
|
||||||
size: 48,
|
size: 48,
|
||||||
color: textColor.withOpacity(0.5),
|
color: textColor.withValues(alpha: 0.5),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -359,7 +338,7 @@ class _VoiceCallPageState extends ConsumerState<VoiceCallPage>
|
|||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: textColor.withOpacity(0.8),
|
color: textColor.withValues(alpha: 0.8),
|
||||||
height: 1.5,
|
height: 1.5,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user