diff --git a/lib/features/chat/services/voice_input_service.dart b/lib/features/chat/services/voice_input_service.dart index dbee34e..6e2f070 100644 --- a/lib/features/chat/services/voice_input_service.dart +++ b/lib/features/chat/services/voice_input_service.dart @@ -250,6 +250,20 @@ class VoiceInputService { return _textStreamController!.stream; } + /// Centralized entry point to begin voice recognition. + /// Ensures initialization and microphone permission before starting. + Future> beginListening() async { + // Ensure service is ready + await initialize(); + // Ensure microphone permission (triggers OS prompt if needed) + final hasMic = await checkPermissions(); + if (!hasMic) { + throw Exception('Microphone permission not granted'); + } + // Start listening and return the transcript stream + return startListening(); + } + Future stopListening() async { await _stopListening(); } diff --git a/lib/features/chat/views/chat_page.dart b/lib/features/chat/views/chat_page.dart index 12ef90e..c11fab0 100644 --- a/lib/features/chat/views/chat_page.dart +++ b/lib/features/chat/views/chat_page.dart @@ -1998,16 +1998,11 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> { ); try { - // Ensure service is initialized (local STT will request permissions itself) + // Ensure service is initialized final ok = await _voiceService.initialize(); if (!ok) { throw Exception('Voice service unavailable'); } - // Only check mic permission when falling back to recording - if (!_voiceService.hasLocalStt) { - final mic = await _voiceService.checkPermissions(); - if (!mic) throw Exception('Microphone permission not granted'); - } // Start elapsed timer for UX _elapsedTimer?.cancel(); @@ -2019,7 +2014,8 @@ class _VoiceInputSheetState extends ConsumerState<_VoiceInputSheet> { setState(() => _elapsedSeconds += 1); }); - final stream = _voiceService.startListening(); + // Centralized permission + start + final stream = await _voiceService.beginListening(); _intensitySub = _voiceService.intensityStream.listen((value) { if (!mounted) return; setState(() => _intensity = value); diff --git a/lib/features/chat/widgets/modern_chat_input.dart b/lib/features/chat/widgets/modern_chat_input.dart index 5c5ea40..b2ac0a9 100644 --- a/lib/features/chat/widgets/modern_chat_input.dart +++ b/lib/features/chat/widgets/modern_chat_input.dart @@ -1150,22 +1150,12 @@ class _ModernChatInputState extends ConsumerState ); return; } - if (!_voiceService.hasLocalStt) { - final mic = await _voiceService.checkPermissions(); - if (!mic) { - _showVoiceUnavailable( - AppLocalizations.of(context)?.errorMessage ?? - 'Microphone permission required', - ); - return; - } - } + // Centralized permission + start + final stream = await _voiceService.beginListening(); setState(() { _isRecording = true; _baseTextAtStart = _controller.text; }); - - final stream = _voiceService.startListening(); _intensitySub?.cancel(); _intensitySub = _voiceService.intensityStream.listen((value) { if (!mounted) return;