diff --git a/mobile_app/.fvmrc b/mobile_app/.fvmrc new file mode 100644 index 0000000..a06c1db --- /dev/null +++ b/mobile_app/.fvmrc @@ -0,0 +1,3 @@ +{ + "flutter": "3.38.3" +} \ No newline at end of file diff --git a/mobile_app/.gitignore b/mobile_app/.gitignore index e569823..34eb249 100644 --- a/mobile_app/.gitignore +++ b/mobile_app/.gitignore @@ -190,3 +190,6 @@ Thumbs.db # Keep important files !**/lib/l10n/app_*.arb !**/lib/l10n/app_localizations.dart + +# FVM Version Cache +.fvm/ \ No newline at end of file diff --git a/mobile_app/lib/bloc/voice_bloc.dart b/mobile_app/lib/bloc/voice_bloc.dart index 8e5e8a4..a71f03f 100644 --- a/mobile_app/lib/bloc/voice_bloc.dart +++ b/mobile_app/lib/bloc/voice_bloc.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../models/models.dart' as models; import '../services/voice_repository.dart'; @@ -57,13 +58,21 @@ class ShowBeneficiariesDialog extends VoiceState { ShowBeneficiariesDialog(this.message, this.beneficiaries, this.sessionId); } +/// Emitted when the user recorded but said nothing (empty/silent audio). +class RecordingEmpty extends VoiceState {} + sealed class VoiceEvent {} -class StartListening extends VoiceEvent {} +class StartListening extends VoiceEvent { + final String locale; + final String sessionId; + StartListening({required this.locale, required this.sessionId}); +} class StopListening extends VoiceEvent { final String locale; - StopListening({required this.locale}); + final String sessionId; + StopListening({required this.locale, required this.sessionId}); } class GotTranscript extends VoiceEvent { @@ -74,6 +83,12 @@ class GotTranscript extends VoiceEvent { class Reset extends VoiceEvent {} +/// Fired when user said nothing for initial silence duration (e.g. first 5s). +class InitialSilence extends VoiceEvent { + final String locale; + InitialSilence({required this.locale}); +} + class VerifyOtp extends VoiceEvent { final String otp; final String sessionId; @@ -81,31 +96,97 @@ class VerifyOtp extends VoiceEvent { VerifyOtp({required this.otp, required this.sessionId, required this.locale}); } +/// User confirmed they want to stop voice banking; stop recording, TTS, timers and reset to Idle. +class CancelVoiceSession extends VoiceEvent {} + class VoiceBloc extends Bloc { final VoiceRepository repo; final BankingAPI bank = BankingAPI(); final TTSService tts = TTSService(); String _currentLocale = 'en'; // Track current locale + /// BuildContext of the currently shown dialog (excluding logout). Cleared when dialog is closed or when user speaks and we pop it. + BuildContext? currentDialogContext; + /// Set when user cancels; skip any further TTS and reset to Idle. + bool _voiceSessionCancelled = false; VoiceBloc(this.repo) : super(Idle()) { on((e, emit) async { + _voiceSessionCancelled = false; try { - await repo.start(); + await repo.start( + onIdle: () => add(StopListening(locale: e.locale, sessionId: e.sessionId)), + idleDuration: const Duration(seconds: 2), + onInitialSilence: () => add(InitialSilence(locale: e.locale)), + initialSilenceDuration: const Duration(seconds: 12), + onSilenceReminder: () async { + if (_voiceSessionCancelled) return; + final message = TranslationService.translateResponse( + 'empty_recording', e.locale, null); + if (_voiceSessionCancelled) return; + try { + await tts.speak(message, langCode: e.locale); + } catch (e) { + print("TTS Error: $e"); + } + if (_voiceSessionCancelled) return; + }, + silenceReminderDuration: const Duration(seconds: 5), + ); emit(Listening()); } catch (e) { print("Voice Bloc Error - Permission denied: $e"); - // Show a snackbar or dialog to inform user about permission emit(Idle()); } }); + on((e, emit) async { + try { + await repo.stopWithoutTranscribe(); + } catch (err) { + print("Voice Bloc Error - stopWithoutTranscribe: $err"); + } + emit(Idle()); + }); + + on((e, emit) async { + _voiceSessionCancelled = true; + try { + await repo.stopWithoutTranscribe(); + } catch (err) { + print("Voice Bloc Error - stopWithoutTranscribe on cancel: $err"); + } + try { + await tts.stop(); + } catch (err) { + print("TTS stop on cancel: $err"); + } + emit(Idle()); + }); + on((e, emit) async { emit(Transcribing()); _currentLocale = e.locale; // Store current locale print("Voice Bloc Debug - Processing normal voice input"); - final data = await repo.stopAndTranscribe(locale: e.locale); - add(GotTranscript(data, e.locale)); + try { + final data = await repo.stopAndTranscribe( + locale: e.locale, + sessionId: e.sessionId, + ifNotEmptyCallback: () { + if (currentDialogContext != null && currentDialogContext!.mounted) { + Navigator.of(currentDialogContext!).pop(); + currentDialogContext = null; + } + }, + ); + add(GotTranscript(data, e.locale)); + } on EmptyRecordingException catch (_) { + emit(RecordingEmpty()); + add(Reset()); + } catch (err) { + print("Voice Bloc Error - Stop/transcribe failed: $err"); + emit(Idle()); + } }); on((e, emit) { @@ -211,16 +292,18 @@ class VoiceBloc extends Bloc { originalMessage, _currentLocale, context); // Speak the translated message + if (_voiceSessionCancelled) return; try { await tts.speak(translatedMessage, langCode: ttsLanguage); } catch (e) { print("TTS Error: $e"); } + if (_voiceSessionCancelled) return; // Show transactions dialog emit( ShowTransactionsDialog(translatedMessage, transactions, sessionId)); - add(Reset()); + add(StartListening(locale: _currentLocale, sessionId: sessionId)); return; } @@ -267,17 +350,19 @@ class VoiceBloc extends Bloc { print("Voice Bloc Debug - Translated message: $translatedMessage"); // Speak the translated message + if (_voiceSessionCancelled) return; try { await tts.speak(translatedMessage, langCode: ttsLanguage); } catch (e) { print("TTS Error: $e"); } + if (_voiceSessionCancelled) return; // Show beneficiaries dialog print("Voice Bloc Debug - Emitting ShowBeneficiariesDialog"); emit(ShowBeneficiariesDialog( translatedMessage, beneficiaries, sessionId)); - add(Reset()); + add(StartListening(locale: _currentLocale, sessionId: sessionId)); return; } @@ -304,11 +389,13 @@ class VoiceBloc extends Bloc { {'amount': amount.toString(), 'recipient': recipient}); // Speak the translated message + if (_voiceSessionCancelled) return; try { await tts.speak(translatedMessage, langCode: ttsLanguage); } catch (e) { print("TTS Error: $e"); } + if (_voiceSessionCancelled) return; // Show OTP dialog emit(ShowOtpDialog(translatedMessage, sessionId, recipient, amount)); @@ -337,16 +424,18 @@ class VoiceBloc extends Bloc { message, _currentLocale, {}); // Speak the translated message + if (_voiceSessionCancelled) return; try { await tts.speak(translatedMessage, langCode: ttsLanguage); } catch (e) { print("TTS Error: $e"); } + if (_voiceSessionCancelled) return; // Show duplicate beneficiaries dialog emit(ShowDuplicateDialog(translatedMessage, sessionId, beneficiaries, originalAmount: originalAmount)); - add(Reset()); + add(StartListening(locale: _currentLocale, sessionId: sessionId)); return; } } @@ -386,16 +475,19 @@ class VoiceBloc extends Bloc { print("Voice Bloc Debug - Translated message: $translatedMessage"); // Speak the translated message + if (_voiceSessionCancelled) return; try { await tts.speak(translatedMessage, langCode: ttsLanguage); } catch (e) { print("TTS Error: $e"); } + if (_voiceSessionCancelled) return; // Show the error message emit(Executing( translatedMessage, VoiceIntent(VoiceIntentType.unknown))); - add(Reset()); + // After TTS completes, restart listening for continuous conversation + add(StartListening(locale: _currentLocale, sessionId: sessionId)); return; } else { print( @@ -501,11 +593,13 @@ class VoiceBloc extends Bloc { String translatedMessage = TranslationService.translateApiResponse( originalMessage, _currentLocale, context); + if (_voiceSessionCancelled) return; try { await tts.speak(translatedMessage, langCode: ttsLanguage); } catch (e) { print("TTS Error: $e"); } + if (_voiceSessionCancelled) return; // Determine intent type for execution state VoiceIntentType intentType = VoiceIntentType.unknown; @@ -524,7 +618,8 @@ class VoiceBloc extends Bloc { } emit(Executing(translatedMessage, VoiceIntent(intentType))); - add(Reset()); + // After TTS completes, restart listening for continuous conversation + add(StartListening(locale: _currentLocale, sessionId: sessionId)); return; } diff --git a/mobile_app/lib/l10n/app_bn.arb b/mobile_app/lib/l10n/app_bn.arb index 5495d93..556003d 100644 --- a/mobile_app/lib/l10n/app_bn.arb +++ b/mobile_app/lib/l10n/app_bn.arb @@ -3,6 +3,7 @@ "loginPrompt": "আপনার মোবাইল নম্বর লিখুন", "otpPrompt": "ওটিপি লিখুন", "micHint": "কথা বলার জন্য মাইক্রোফোনে ট্যাপ করুন", + "tapToSpeak": "বলতে ট্যাপ করুন", "listening": "শোনা হচ্ছে...", "transcribing": "লেখায় রূপান্তর হচ্ছে...", "executing": "আপনার অনুরোধ প্রক্রিয়া করা হচ্ছে...", @@ -22,6 +23,7 @@ "voice": "ভয়েস", "stop": "বন্ধ করুন", "cancel": "বাতিল", + "stopVoiceBankingConfirm": "ভয়েস ব্যাংকিং বন্ধ করবেন? সমস্ত বর্তমান ভয়েস কার্যকলাপ বাতিল করা হবে।", "welcomeTo": "স্বাগতম", "experienceBanking": "ভয়েসের শক্তিতে ব্যাংকিংয়ের অভিজ্ঞতা নিন", "enterMobileNumber": "মোবাইল নম্বর লিখুন", @@ -87,5 +89,13 @@ "tipNaturalLanguage": "প্রাকৃতিক ভাষা ব্যবহার করুন যেমন \"আমার ব্যালেন্স দেখান\"", "tipWaitForIndicator": "কথা বলার আগে শোনার নির্দেশকের জন্য অপেক্ষা করুন", "needMoreHelp": "আরো সাহায্য প্রয়োজন?", - "contactSupportDescription": "অতিরিক্ত সহায়তার জন্য আমাদের সহায়তা দলকে যোগাযোগ করুন" + "contactSupportDescription": "অতিরিক্ত সহায়তার জন্য আমাদের সহায়তা দলকে যোগাযোগ করুন", + "balanceSuccess": "আপনার বর্তমান জের {amount} টাকা।", + "transactionsFound": "এখানে আপনার {count}টি সাম্প্রতিক লেনদেন।", + "noTransactions": "কোন লেনদেন পাওয়া যায়নি।", + "transferSuccess": "{recipient}-এ {amount} টাকার ট্রান্সফার সফলভাবে শুরু হয়েছে।", + "transferFailed": "ট্রান্সফার ব্যর্থ। অনুগ্রহ করে আবার চেষ্টা করুন।", + "errorGeneric": "দুঃখিত, একটি ত্রুটি হয়েছে। অনুগ্রহ করে আবার চেষ্টা করুন।", + "errorInsufficientFunds": "অপর্যাপ্ত তহবিল। অনুগ্রহ করে আপনার ব্যালেন্স পরীক্ষা করুন।", + "pleaseSaySomething": "অনুগ্রহ করে কিছু বলুন" } \ No newline at end of file diff --git a/mobile_app/lib/l10n/app_en.arb b/mobile_app/lib/l10n/app_en.arb index 378495f..e60b28e 100644 --- a/mobile_app/lib/l10n/app_en.arb +++ b/mobile_app/lib/l10n/app_en.arb @@ -3,6 +3,7 @@ "loginPrompt": "Enter your mobile number", "otpPrompt": "Enter OTP", "micHint": "Tap the mic to speak", + "tapToSpeak": "Tap to speak", "listening": "Listening...", "transcribing": "Transcribing...", "executing": "Processing your request...", @@ -22,6 +23,7 @@ "voice": "Voice", "stop": "Stop", "cancel": "Cancel", + "stopVoiceBankingConfirm": "Stop voice banking? All current voice activity will be cancelled.", "welcomeTo": "Welcome to", "experienceBanking": "Experience banking with the power of voice", "enterMobileNumber": "Enter Mobile Number", @@ -94,6 +96,7 @@ "transferSuccess": "Transfer of {amount} rupees to {recipient} has been initiated successfully.", "transferFailed": "Transfer failed. Please try again.", "errorGeneric": "Sorry, I encountered an error. Please try again.", - "errorInsufficientFunds": "Insufficient funds. Please check your balance." + "errorInsufficientFunds": "Insufficient funds. Please check your balance.", + "pleaseSaySomething": "Please say something" } diff --git a/mobile_app/lib/l10n/app_gu.arb b/mobile_app/lib/l10n/app_gu.arb index afd926e..4850698 100644 --- a/mobile_app/lib/l10n/app_gu.arb +++ b/mobile_app/lib/l10n/app_gu.arb @@ -3,6 +3,7 @@ "loginPrompt": "તમારો મોબાઇલ નંબર દાખલ કરો", "otpPrompt": "OTP દાખલ કરો", "micHint": "બોલવા માટે માઇક પર ટેપ કરો", + "tapToSpeak": "બોલવા ટેપ કરો", "listening": "સાંભળી રહ્યા છીએ...", "transcribing": "લખાણમાં રૂપાંતર થઈ રહ્યું છે...", "executing": "તમારી વિનંતી પ્રક્રિયા કરવામાં આવી રહી છે...", @@ -22,6 +23,7 @@ "voice": "વૉઇસ", "stop": "બંધ કરો", "cancel": "રદ કરો", + "stopVoiceBankingConfirm": "વૉઇસ બેંકિંગ બંધ કરો? તમામ વર્તમાન વૉઇસ પ્રવૃત્તિ રદ કરવામાં આવશે.", "welcomeTo": "સ્વાગત છે", "experienceBanking": "વૉઇસની શક્તિથી બેંકિંગનો અનુભવ કરો", "enterMobileNumber": "મોબાઇલ નંબર દાખલ કરો", @@ -87,5 +89,13 @@ "tipNaturalLanguage": "કુદરતી ભાષા વાપરો જેમ કે \"મારું બેલન્સ બતાવો\"", "tipWaitForIndicator": "બોલતા પહેલા સાંભળવાના સૂચકની રાહ જુઓ", "needMoreHelp": "વધુ મદદ જોઈએ?", - "contactSupportDescription": "વધારાની સહાયતા માટે અમારી સહાયતા ટીમનો સંપર્ક કરો" + "contactSupportDescription": "વધારાની સહાયતા માટે અમારી સહાયતા ટીમનો સંપર્ક કરો", + "balanceSuccess": "તમારું વર્તમાન બેલન્સ {amount} રૂપિયા છે.", + "transactionsFound": "અહીં તમારા {count} તાજેતરના લેનદેન છે.", + "noTransactions": "કોઈ લેનદેન મળ્યા નથી.", + "transferSuccess": "{recipient}ને {amount} રૂપિયાનું ટ્રાન્સફર સફળતાપૂર્વક શરૂ થયું છે.", + "transferFailed": "ટ્રાન્સફર નિષ્ફળ. કૃપા કરીને ફરી પ્રયાસ કરો.", + "errorGeneric": "માફ કરો, ભૂલ આવી. કૃપા કરીને ફરી પ્રયાસ કરો.", + "errorInsufficientFunds": "અપૂર્ણ નિધિ. કૃપા કરીને તમારું બેલન્સ તપાસો.", + "pleaseSaySomething": "કૃપા કરીને કંઈક કહો" } \ No newline at end of file diff --git a/mobile_app/lib/l10n/app_hi.arb b/mobile_app/lib/l10n/app_hi.arb index b116b7b..86836fa 100644 --- a/mobile_app/lib/l10n/app_hi.arb +++ b/mobile_app/lib/l10n/app_hi.arb @@ -3,6 +3,7 @@ "loginPrompt": "अपना मोबाइल नंबर दर्ज करें", "otpPrompt": "ओटीपी दर्ज करें", "micHint": "बोलने के लिए माइक दबाएँ", + "tapToSpeak": "बोलने के लिए टैप करें", "listening": "सुन रहा हूँ...", "transcribing": "लिख रहा हूँ...", "executing": "आपका अनुरोध संसाधित हो रहा है...", @@ -22,6 +23,7 @@ "voice": "वॉइस", "stop": "रोकें", "cancel": "रद्द करें", + "stopVoiceBankingConfirm": "वॉइस बैंकिंग बंद करें? सभी वर्तमान वॉइस गतिविधि रद्द हो जाएगी।", "welcomeTo": "स्वागत है", "experienceBanking": "वॉइस की शक्ति से बैंकिंग का अनुभव करें", "enterMobileNumber": "मोबाइल नंबर दर्ज करें", @@ -87,5 +89,13 @@ "tipNaturalLanguage": "प्राकृतिक भाषा का उपयोग करें जैसे \"मेरा बैलेंस दिखाएं\"", "tipWaitForIndicator": "बोलने से पहले सुनने के संकेतक की प्रतीक्षा करें", "needMoreHelp": "और सहायता चाहिए?", - "contactSupportDescription": "अतिरिक्त सहायता के लिए हमारी सहायता टीम से संपर्क करें" + "contactSupportDescription": "अतिरिक्त सहायता के लिए हमारी सहायता टीम से संपर्क करें", + "balanceSuccess": "आपका वर्तमान बैलेंस {amount} रुपये है।", + "transactionsFound": "यहां आपके {count} सबसे हाल के लेनदेन हैं।", + "noTransactions": "कोई लेनदेन नहीं मिला।", + "transferSuccess": "{recipient} को {amount} रुपये का ट्रांसफर सफलतापूर्वक शुरू किया गया है।", + "transferFailed": "ट्रांसफर विफल। कृपया पुनः प्रयास करें।", + "errorGeneric": "क्षमा करें, एक त्रुटि हुई। कृपया पुनः प्रयास करें।", + "errorInsufficientFunds": "अपर्याप्त धन। कृपया अपना बैलेंस जांचें।", + "pleaseSaySomething": "कृपया कुछ बोलें" } diff --git a/mobile_app/lib/l10n/app_kn.arb b/mobile_app/lib/l10n/app_kn.arb index 1355e56..a67a959 100644 --- a/mobile_app/lib/l10n/app_kn.arb +++ b/mobile_app/lib/l10n/app_kn.arb @@ -3,6 +3,7 @@ "loginPrompt": "ನಿಮ್ಮ ಮೊಬೈಲ್ ಸಂಖ್ಯೆ ನಮೂದಿಸಿ", "otpPrompt": "OTP ನಮೂದಿಸಿ", "micHint": "ಮಾತನಾಡಲು ಮೈಕ್ ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ", + "tapToSpeak": "ಮಾತನಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ", "listening": "ಕೇಳುತ್ತಿದೆ...", "transcribing": "ಪಠ್ಯಕ್ಕೆ ಪರಿವರ್ತನೆ ಆಗುತ್ತಿದೆ...", "executing": "ನಿಮ್ಮ ವಿನಂತಿಯನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲಾಗುತ್ತಿದೆ...", @@ -22,6 +23,7 @@ "voice": "ವಾಯ್ಸ್", "stop": "ನಿಲ್ಲಿಸಿ", "cancel": "ರದ್ದುಗೊಳಿಸಿ", + "stopVoiceBankingConfirm": "ವಾಯ್ಸ್ ಬ್ಯಾಂಕಿಂಗ್ ನಿಲ್ಲಿಸುವುದೇ? ಎಲ್ಲಾ ಪ್ರಸ್ತುತ ವಾಯ್ಸ್ ಚಟುವಟಿಕೆ ರದ್ದುಗೊಳಿಸಲಾಗುತ್ತದೆ.", "welcomeTo": "ಸ್ವಾಗತ", "experienceBanking": "ವಾಯ್ಸ್‌ನ ಶಕ್ತಿಯಿಂದ ಬ್ಯಾಂಕಿಂಗ್ ಅನುಭವಿಸಿ", "enterMobileNumber": "ಮೊಬೈಲ್ ಸಂಖ್ಯೆ ನಮೂದಿಸಿ", @@ -87,5 +89,13 @@ "tipNaturalLanguage": "ನೈಸರ್ಗಿಕ ಭಾಷೆಯನ್ನು ಬಳಸಿ ಉದಾಹರಣೆಗೆ \"ನನ್ನ ಬ್ಯಾಲೆನ್ಸ್ ತೋರಿಸಿ\"", "tipWaitForIndicator": "ಮಾತನಾಡುವ ಮೊದಲು ಕೇಳುವ ಸೂಚಕಕ್ಕಾಗಿ ಕಾಯಿರಿ", "needMoreHelp": "ಹೆಚ್ಚು ಸಹಾಯ ಬೇಕೇ?", - "contactSupportDescription": "ಹೆಚ್ಚುವರಿ ಸಹಾಯಕ್ಕಾಗಿ ನಮ್ಮ ಬೆಂಬಲ ತಂಡವನ್ನು ಸಂಪರ್ಕಿಸಿ" + "contactSupportDescription": "ಹೆಚ್ಚುವರಿ ಸಹಾಯಕ್ಕಾಗಿ ನಮ್ಮ ಬೆಂಬಲ ತಂಡವನ್ನು ಸಂಪರ್ಕಿಸಿ", + "balanceSuccess": "ನಿಮ್ಮ ಪ್ರಸ್ತುತ ಬ್ಯಾಲೆನ್ಸ್ {amount} ರೂಪಾಯಿ.", + "transactionsFound": "ಇಲ್ಲಿ ನಿಮ್ಮ {count} ಇತ್ತೀಚಿನ ಲಾವಾದೇವಿಗಳು.", + "noTransactions": "ಯಾವುದೇ ಲಾವಾದೇವಿಗಳು ಕಂಡುಬಂದಿಲ್ಲ.", + "transferSuccess": "{recipient}ಗೆ {amount} ರೂಪಾಯಿ ವರ್ಗಾವಣೆ ಯಶಸ್ವಿಯಾಗಿ ಪ್ರಾರಂಭವಾಗಿದೆ.", + "transferFailed": "ವರ್ಗಾವಣೆ ವಿಫಲವಾಯಿತು. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.", + "errorGeneric": "ಕ್ಷಮಿಸಿ, ದೋಷ ಸಂಭವಿಸಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.", + "errorInsufficientFunds": "ಸಾಕಷ್ಟು ನಿಧಿ ಇಲ್ಲ. ದಯವಿಟ್ಟು ನಿಮ್ಮ ಬ್ಯಾಲೆನ್ಸ್ ಪರಿಶೀಲಿಸಿ.", + "pleaseSaySomething": "ದಯವಿಟ್ಟು ಏನಾದರೂ ಹೇಳಿ" } \ No newline at end of file diff --git a/mobile_app/lib/l10n/app_localizations.dart b/mobile_app/lib/l10n/app_localizations.dart index e4bc583..135327c 100644 --- a/mobile_app/lib/l10n/app_localizations.dart +++ b/mobile_app/lib/l10n/app_localizations.dart @@ -135,6 +135,12 @@ abstract class AppLocalizations { /// **'Tap the mic to speak'** String get micHint; + /// No description provided for @tapToSpeak. + /// + /// In en, this message translates to: + /// **'Tap to speak'** + String get tapToSpeak; + /// No description provided for @listening. /// /// In en, this message translates to: @@ -249,6 +255,12 @@ abstract class AppLocalizations { /// **'Cancel'** String get cancel; + /// No description provided for @stopVoiceBankingConfirm. + /// + /// In en, this message translates to: + /// **'Stop voice banking? All current voice activity will be cancelled.'** + String get stopVoiceBankingConfirm; + /// No description provided for @welcomeTo. /// /// In en, this message translates to: @@ -638,6 +650,54 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Contact our support team for additional assistance'** String get contactSupportDescription; + + /// No description provided for @balanceSuccess. + /// + /// In en, this message translates to: + /// **'Your current balance is {amount} rupees.'** + String balanceSuccess(Object amount); + + /// No description provided for @transactionsFound. + /// + /// In en, this message translates to: + /// **'Here are your {count} most recent transactions.'** + String transactionsFound(Object count); + + /// No description provided for @noTransactions. + /// + /// In en, this message translates to: + /// **'No transactions found.'** + String get noTransactions; + + /// No description provided for @transferSuccess. + /// + /// In en, this message translates to: + /// **'Transfer of {amount} rupees to {recipient} has been initiated successfully.'** + String transferSuccess(Object amount, Object recipient); + + /// No description provided for @transferFailed. + /// + /// In en, this message translates to: + /// **'Transfer failed. Please try again.'** + String get transferFailed; + + /// No description provided for @errorGeneric. + /// + /// In en, this message translates to: + /// **'Sorry, I encountered an error. Please try again.'** + String get errorGeneric; + + /// No description provided for @errorInsufficientFunds. + /// + /// In en, this message translates to: + /// **'Insufficient funds. Please check your balance.'** + String get errorInsufficientFunds; + + /// No description provided for @pleaseSaySomething. + /// + /// In en, this message translates to: + /// **'Please say something'** + String get pleaseSaySomething; } class _AppLocalizationsDelegate extends LocalizationsDelegate { diff --git a/mobile_app/lib/l10n/app_localizations_bn.dart b/mobile_app/lib/l10n/app_localizations_bn.dart index 93f210b..c4c6477 100644 --- a/mobile_app/lib/l10n/app_localizations_bn.dart +++ b/mobile_app/lib/l10n/app_localizations_bn.dart @@ -20,6 +20,9 @@ class AppLocalizationsBn extends AppLocalizations { @override String get micHint => 'কথা বলার জন্য মাইক্রোফোনে ট্যাপ করুন'; + @override + String get tapToSpeak => 'বলতে ট্যাপ করুন'; + @override String get listening => 'শোনা হচ্ছে...'; @@ -79,6 +82,9 @@ class AppLocalizationsBn extends AppLocalizations { @override String get cancel => 'বাতিল'; + @override + String get stopVoiceBankingConfirm => 'ভয়েস ব্যাংকিং বন্ধ করবেন? সমস্ত বর্তমান ভয়েস কার্যকলাপ বাতিল করা হবে।'; + @override String get welcomeTo => 'স্বাগতম'; @@ -277,4 +283,34 @@ class AppLocalizationsBn extends AppLocalizations { @override String get contactSupportDescription => 'অতিরিক্ত সহায়তার জন্য আমাদের সহায়তা দলকে যোগাযোগ করুন'; + + @override + String balanceSuccess(Object amount) { + return 'আপনার বর্তমান জের $amount টাকা।'; + } + + @override + String transactionsFound(Object count) { + return 'এখানে আপনার $countটি সাম্প্রতিক লেনদেন।'; + } + + @override + String get noTransactions => 'কোন লেনদেন পাওয়া যায়নি।'; + + @override + String transferSuccess(Object amount, Object recipient) { + return '$recipient-এ $amount টাকার ট্রান্সফার সফলভাবে শুরু হয়েছে।'; + } + + @override + String get transferFailed => 'ট্রান্সফার ব্যর্থ। অনুগ্রহ করে আবার চেষ্টা করুন।'; + + @override + String get errorGeneric => 'দুঃখিত, একটি ত্রুটি হয়েছে। অনুগ্রহ করে আবার চেষ্টা করুন।'; + + @override + String get errorInsufficientFunds => 'অপর্যাপ্ত তহবিল। অনুগ্রহ করে আপনার ব্যালেন্স পরীক্ষা করুন।'; + + @override + String get pleaseSaySomething => 'অনুগ্রহ করে কিছু বলুন'; } diff --git a/mobile_app/lib/l10n/app_localizations_en.dart b/mobile_app/lib/l10n/app_localizations_en.dart index 21cbe67..19362b5 100644 --- a/mobile_app/lib/l10n/app_localizations_en.dart +++ b/mobile_app/lib/l10n/app_localizations_en.dart @@ -20,6 +20,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get micHint => 'Tap the mic to speak'; + @override + String get tapToSpeak => 'Tap to speak'; + @override String get listening => 'Listening...'; @@ -79,6 +82,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get cancel => 'Cancel'; + @override + String get stopVoiceBankingConfirm => 'Stop voice banking? All current voice activity will be cancelled.'; + @override String get welcomeTo => 'Welcome to'; @@ -277,4 +283,34 @@ class AppLocalizationsEn extends AppLocalizations { @override String get contactSupportDescription => 'Contact our support team for additional assistance'; + + @override + String balanceSuccess(Object amount) { + return 'Your current balance is $amount rupees.'; + } + + @override + String transactionsFound(Object count) { + return 'Here are your $count most recent transactions.'; + } + + @override + String get noTransactions => 'No transactions found.'; + + @override + String transferSuccess(Object amount, Object recipient) { + return 'Transfer of $amount rupees to $recipient has been initiated successfully.'; + } + + @override + String get transferFailed => 'Transfer failed. Please try again.'; + + @override + String get errorGeneric => 'Sorry, I encountered an error. Please try again.'; + + @override + String get errorInsufficientFunds => 'Insufficient funds. Please check your balance.'; + + @override + String get pleaseSaySomething => 'Please say something'; } diff --git a/mobile_app/lib/l10n/app_localizations_gu.dart b/mobile_app/lib/l10n/app_localizations_gu.dart index 7049a55..66a1d75 100644 --- a/mobile_app/lib/l10n/app_localizations_gu.dart +++ b/mobile_app/lib/l10n/app_localizations_gu.dart @@ -20,6 +20,9 @@ class AppLocalizationsGu extends AppLocalizations { @override String get micHint => 'બોલવા માટે માઇક પર ટેપ કરો'; + @override + String get tapToSpeak => 'બોલવા ટેપ કરો'; + @override String get listening => 'સાંભળી રહ્યા છીએ...'; @@ -79,6 +82,9 @@ class AppLocalizationsGu extends AppLocalizations { @override String get cancel => 'રદ કરો'; + @override + String get stopVoiceBankingConfirm => 'વૉઇસ બેંકિંગ બંધ કરો? તમામ વર્તમાન વૉઇસ પ્રવૃત્તિ રદ કરવામાં આવશે.'; + @override String get welcomeTo => 'સ્વાગત છે'; @@ -277,4 +283,34 @@ class AppLocalizationsGu extends AppLocalizations { @override String get contactSupportDescription => 'વધારાની સહાયતા માટે અમારી સહાયતા ટીમનો સંપર્ક કરો'; + + @override + String balanceSuccess(Object amount) { + return 'તમારું વર્તમાન બેલન્સ $amount રૂપિયા છે.'; + } + + @override + String transactionsFound(Object count) { + return 'અહીં તમારા $count તાજેતરના લેનદેન છે.'; + } + + @override + String get noTransactions => 'કોઈ લેનદેન મળ્યા નથી.'; + + @override + String transferSuccess(Object amount, Object recipient) { + return '$recipientને $amount રૂપિયાનું ટ્રાન્સફર સફળતાપૂર્વક શરૂ થયું છે.'; + } + + @override + String get transferFailed => 'ટ્રાન્સફર નિષ્ફળ. કૃપા કરીને ફરી પ્રયાસ કરો.'; + + @override + String get errorGeneric => 'માફ કરો, ભૂલ આવી. કૃપા કરીને ફરી પ્રયાસ કરો.'; + + @override + String get errorInsufficientFunds => 'અપૂર્ણ નિધિ. કૃપા કરીને તમારું બેલન્સ તપાસો.'; + + @override + String get pleaseSaySomething => 'કૃપા કરીને કંઈક કહો'; } diff --git a/mobile_app/lib/l10n/app_localizations_hi.dart b/mobile_app/lib/l10n/app_localizations_hi.dart index 9fed865..402ea5b 100644 --- a/mobile_app/lib/l10n/app_localizations_hi.dart +++ b/mobile_app/lib/l10n/app_localizations_hi.dart @@ -20,6 +20,9 @@ class AppLocalizationsHi extends AppLocalizations { @override String get micHint => 'बोलने के लिए माइक दबाएँ'; + @override + String get tapToSpeak => 'बोलने के लिए टैप करें'; + @override String get listening => 'सुन रहा हूँ...'; @@ -79,6 +82,9 @@ class AppLocalizationsHi extends AppLocalizations { @override String get cancel => 'रद्द करें'; + @override + String get stopVoiceBankingConfirm => 'वॉइस बैंकिंग बंद करें? सभी वर्तमान वॉइस गतिविधि रद्द हो जाएगी।'; + @override String get welcomeTo => 'स्वागत है'; @@ -277,4 +283,34 @@ class AppLocalizationsHi extends AppLocalizations { @override String get contactSupportDescription => 'अतिरिक्त सहायता के लिए हमारी सहायता टीम से संपर्क करें'; + + @override + String balanceSuccess(Object amount) { + return 'आपका वर्तमान बैलेंस $amount रुपये है।'; + } + + @override + String transactionsFound(Object count) { + return 'यहां आपके $count सबसे हाल के लेनदेन हैं।'; + } + + @override + String get noTransactions => 'कोई लेनदेन नहीं मिला।'; + + @override + String transferSuccess(Object amount, Object recipient) { + return '$recipient को $amount रुपये का ट्रांसफर सफलतापूर्वक शुरू किया गया है।'; + } + + @override + String get transferFailed => 'ट्रांसफर विफल। कृपया पुनः प्रयास करें।'; + + @override + String get errorGeneric => 'क्षमा करें, एक त्रुटि हुई। कृपया पुनः प्रयास करें।'; + + @override + String get errorInsufficientFunds => 'अपर्याप्त धन। कृपया अपना बैलेंस जांचें।'; + + @override + String get pleaseSaySomething => 'कृपया कुछ बोलें'; } diff --git a/mobile_app/lib/l10n/app_localizations_kn.dart b/mobile_app/lib/l10n/app_localizations_kn.dart index feaada9..e72c669 100644 --- a/mobile_app/lib/l10n/app_localizations_kn.dart +++ b/mobile_app/lib/l10n/app_localizations_kn.dart @@ -20,6 +20,9 @@ class AppLocalizationsKn extends AppLocalizations { @override String get micHint => 'ಮಾತನಾಡಲು ಮೈಕ್ ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ'; + @override + String get tapToSpeak => 'ಮಾತನಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ'; + @override String get listening => 'ಕೇಳುತ್ತಿದೆ...'; @@ -79,6 +82,9 @@ class AppLocalizationsKn extends AppLocalizations { @override String get cancel => 'ರದ್ದುಗೊಳಿಸಿ'; + @override + String get stopVoiceBankingConfirm => 'ವಾಯ್ಸ್ ಬ್ಯಾಂಕಿಂಗ್ ನಿಲ್ಲಿಸುವುದೇ? ಎಲ್ಲಾ ಪ್ರಸ್ತುತ ವಾಯ್ಸ್ ಚಟುವಟಿಕೆ ರದ್ದುಗೊಳಿಸಲಾಗುತ್ತದೆ.'; + @override String get welcomeTo => 'ಸ್ವಾಗತ'; @@ -277,4 +283,34 @@ class AppLocalizationsKn extends AppLocalizations { @override String get contactSupportDescription => 'ಹೆಚ್ಚುವರಿ ಸಹಾಯಕ್ಕಾಗಿ ನಮ್ಮ ಬೆಂಬಲ ತಂಡವನ್ನು ಸಂಪರ್ಕಿಸಿ'; + + @override + String balanceSuccess(Object amount) { + return 'ನಿಮ್ಮ ಪ್ರಸ್ತುತ ಬ್ಯಾಲೆನ್ಸ್ $amount ರೂಪಾಯಿ.'; + } + + @override + String transactionsFound(Object count) { + return 'ಇಲ್ಲಿ ನಿಮ್ಮ $count ಇತ್ತೀಚಿನ ಲಾವಾದೇವಿಗಳು.'; + } + + @override + String get noTransactions => 'ಯಾವುದೇ ಲಾವಾದೇವಿಗಳು ಕಂಡುಬಂದಿಲ್ಲ.'; + + @override + String transferSuccess(Object amount, Object recipient) { + return '$recipientಗೆ $amount ರೂಪಾಯಿ ವರ್ಗಾವಣೆ ಯಶಸ್ವಿಯಾಗಿ ಪ್ರಾರಂಭವಾಗಿದೆ.'; + } + + @override + String get transferFailed => 'ವರ್ಗಾವಣೆ ವಿಫಲವಾಯಿತು. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.'; + + @override + String get errorGeneric => 'ಕ್ಷಮಿಸಿ, ದೋಷ ಸಂಭವಿಸಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.'; + + @override + String get errorInsufficientFunds => 'ಸಾಕಷ್ಟು ನಿಧಿ ಇಲ್ಲ. ದಯವಿಟ್ಟು ನಿಮ್ಮ ಬ್ಯಾಲೆನ್ಸ್ ಪರಿಶೀಲಿಸಿ.'; + + @override + String get pleaseSaySomething => 'ದಯವಿಟ್ಟು ಏನಾದರೂ ಹೇಳಿ'; } diff --git a/mobile_app/lib/l10n/app_localizations_ml.dart b/mobile_app/lib/l10n/app_localizations_ml.dart index fa9dfc8..5280ef0 100644 --- a/mobile_app/lib/l10n/app_localizations_ml.dart +++ b/mobile_app/lib/l10n/app_localizations_ml.dart @@ -20,6 +20,9 @@ class AppLocalizationsMl extends AppLocalizations { @override String get micHint => 'സംസാരിക്കാൻ മൈക്കിൽ ടാപ്പ് ചെയ്യുക'; + @override + String get tapToSpeak => 'സംസാരിക്കാൻ ടാപ്പ് ചെയ്യുക'; + @override String get listening => 'കേൾക്കുന്നു...'; @@ -79,6 +82,9 @@ class AppLocalizationsMl extends AppLocalizations { @override String get cancel => 'റദ്ദാക്കുക'; + @override + String get stopVoiceBankingConfirm => 'വോയ്സ് ബാങ്കിംഗ് നിർത്തണോ? എല്ലാ നിലവിലെ വോയ്സ് പ്രവർത്തനങ്ങളും റദ്ദാക്കപ്പെടും.'; + @override String get welcomeTo => 'സ്വാഗതം'; @@ -277,4 +283,34 @@ class AppLocalizationsMl extends AppLocalizations { @override String get contactSupportDescription => 'അധിക സഹായത്തിനായി ഞങ്ങളുടെ സപ്പോർട്ട് ടീമുമായി ബന്ധപ്പെടുക'; + + @override + String balanceSuccess(Object amount) { + return 'നിങ്ങളുടെ നിലവിലെ ബാലൻസ് $amount രൂപയാണ്.'; + } + + @override + String transactionsFound(Object count) { + return 'നിങ്ങളുടെ $count ഏറ്റവും പുതിയ ഇടപാടുകൾ ഇതാ.'; + } + + @override + String get noTransactions => 'ഇടപാടുകളൊന്നും കണ്ടെത്തിയില്ല.'; + + @override + String transferSuccess(Object amount, Object recipient) { + return '$recipient-ന് $amount രൂപ ട്രാൻസ്ഫർ വിജയകരമായി ആരംഭിച്ചു.'; + } + + @override + String get transferFailed => 'ട്രാൻസ്ഫർ പരാജയപ്പെട്ടു. ദയവായി വീണ്ടും ശ്രമിക്കുക.'; + + @override + String get errorGeneric => 'ക്ഷമിക്കണം, ഒരു പിശക് സംഭവിച്ചു. ദയവായി വീണ്ടും ശ്രമിക്കുക.'; + + @override + String get errorInsufficientFunds => 'ധനം പോരാ. ദയവായി നിങ്ങളുടെ ബാലൻസ് പരിശോധിക്കുക.'; + + @override + String get pleaseSaySomething => 'ദയവായി എന്തെങ്കിലും പറയുക'; } diff --git a/mobile_app/lib/l10n/app_localizations_mr.dart b/mobile_app/lib/l10n/app_localizations_mr.dart index 1007a09..3203a50 100644 --- a/mobile_app/lib/l10n/app_localizations_mr.dart +++ b/mobile_app/lib/l10n/app_localizations_mr.dart @@ -20,6 +20,9 @@ class AppLocalizationsMr extends AppLocalizations { @override String get micHint => 'बोलण्यासाठी माइक टॅप करा'; + @override + String get tapToSpeak => 'बोलण्यासाठी टॅप करा'; + @override String get listening => 'ऐकत आहे...'; @@ -79,6 +82,9 @@ class AppLocalizationsMr extends AppLocalizations { @override String get cancel => 'रद्द करा'; + @override + String get stopVoiceBankingConfirm => 'व्हॉइस बँकिंग थांबवायचे? सर्व सध्याची व्हॉइस क्रिया रद्द होईल.'; + @override String get welcomeTo => 'स्वागत'; @@ -277,4 +283,34 @@ class AppLocalizationsMr extends AppLocalizations { @override String get contactSupportDescription => 'अतिरिक्त सहाय्यासाठी आमच्या सहाय्य टीमशी संपर्क साधा'; + + @override + String balanceSuccess(Object amount) { + return 'तुमचे वर्तमान बॅलन्स $amount रुपये आहे.'; + } + + @override + String transactionsFound(Object count) { + return 'येथे तुमच्या $count अलीकडील व्यवहार आहेत.'; + } + + @override + String get noTransactions => 'कोणतेही व्यवहार आढळले नाहीत.'; + + @override + String transferSuccess(Object amount, Object recipient) { + return '$recipient ला $amount रुपये हस्तांतरण यशस्वीरित्या सुरू झाले.'; + } + + @override + String get transferFailed => 'हस्तांतरण अयशस्वी. कृपया पुन्हा प्रयत्न करा.'; + + @override + String get errorGeneric => 'क्षमा करा, त्रुटी आली. कृपया पुन्हा प्रयत्न करा.'; + + @override + String get errorInsufficientFunds => 'अपुरे निधी. कृपया तुमचे बॅलन्स तपासा.'; + + @override + String get pleaseSaySomething => 'कृपया काहीतरी बोला'; } diff --git a/mobile_app/lib/l10n/app_localizations_pa.dart b/mobile_app/lib/l10n/app_localizations_pa.dart index c702029..8429df8 100644 --- a/mobile_app/lib/l10n/app_localizations_pa.dart +++ b/mobile_app/lib/l10n/app_localizations_pa.dart @@ -20,6 +20,9 @@ class AppLocalizationsPa extends AppLocalizations { @override String get micHint => 'ਬੋਲਣ ਲਈ ਮਾਈਕ ਟੈਪ ਕਰੋ'; + @override + String get tapToSpeak => 'ਬੋਲਣ ਲਈ ਟੈਪ ਕਰੋ'; + @override String get listening => 'ਸੁਣ ਰਿਹਾ ਹੈ...'; @@ -79,6 +82,9 @@ class AppLocalizationsPa extends AppLocalizations { @override String get cancel => 'ਰੱਦ ਕਰੋ'; + @override + String get stopVoiceBankingConfirm => 'ਵੌਇਸ ਬੈਂਕਿੰਗ ਰੋਕੋ? ਸਾਰੀ ਮੌਜੂਦਾ ਵੌਇਸ ਗਤੀਵਿਧੀ ਰੱਦ ਹੋ ਜਾਵੇਗੀ।'; + @override String get welcomeTo => 'ਸਵਾਗਤ'; @@ -277,4 +283,34 @@ class AppLocalizationsPa extends AppLocalizations { @override String get contactSupportDescription => 'ਵਾਧੂ ਸਹਾਇਤਾ ਲਈ ਸਾਡੀ ਸਹਾਇਤਾ ਟੀਮ ਨਾਲ ਸੰਪਰਕ ਕਰੋ'; + + @override + String balanceSuccess(Object amount) { + return 'ਤੁਹਾਡਾ ਮੌਜੂਦਾ ਬੈਲੇਂਸ $amount ਰੁਪਏ ਹੈ।'; + } + + @override + String transactionsFound(Object count) { + return 'ਇੱਥੇ ਤੁਹਾਡੇ $count ਸਭ ਤੋਂ ਹਾਲ ਹੀ ਦੇ ਲੈਣ-ਦੇਣ ਹਨ।'; + } + + @override + String get noTransactions => 'ਕੋਈ ਲੈਣ-ਦੇਣ ਨਹੀਂ ਮਿਲੀ।'; + + @override + String transferSuccess(Object amount, Object recipient) { + return '$recipient ਨੂੰ $amount ਰੁਪਏ ਦਾ ਟ੍ਰਾਂਸਫਰ ਸਫਲਤਾਪੂਰਵਕ ਸ਼ੁਰੂ ਕੀਤਾ ਗਿਆ ਹੈ।'; + } + + @override + String get transferFailed => 'ਟ੍ਰਾਂਸਫਰ ਅਸਫਲ। ਕ੍ਰਿਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।'; + + @override + String get errorGeneric => 'ਮਾਫ਼ ਕਰੋ, ਗਲਤੀ ਆਈ। ਕ੍ਰਿਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।'; + + @override + String get errorInsufficientFunds => 'ਅਪੂਰਨ ਫੰਡ। ਕ੍ਰਿਪਾ ਕਰਕੇ ਆਪਣਾ ਬੈਲੇਂਸ ਜਾਂਚੋ।'; + + @override + String get pleaseSaySomething => 'ਕ੍ਰਿਪਾ ਕਰਕੇ ਕੁਝ ਕਹੋ'; } diff --git a/mobile_app/lib/l10n/app_localizations_ta.dart b/mobile_app/lib/l10n/app_localizations_ta.dart index 4e73be2..a37cec4 100644 --- a/mobile_app/lib/l10n/app_localizations_ta.dart +++ b/mobile_app/lib/l10n/app_localizations_ta.dart @@ -20,6 +20,9 @@ class AppLocalizationsTa extends AppLocalizations { @override String get micHint => 'பேச மைக் டேப் செய்யவும்'; + @override + String get tapToSpeak => 'பேச தட்டவும்'; + @override String get listening => 'கேட்டுக்கொண்டிருக்கிறது...'; @@ -79,6 +82,9 @@ class AppLocalizationsTa extends AppLocalizations { @override String get cancel => 'ரத்து செய்'; + @override + String get stopVoiceBankingConfirm => 'குரல் வங்கியை நிறுத்துவதா? அனைத்து தற்போதைய குரல் செயல்பாடுகளும் ரத்து செய்யப்படும்.'; + @override String get welcomeTo => 'வரவேற்கிறோம்'; @@ -277,4 +283,34 @@ class AppLocalizationsTa extends AppLocalizations { @override String get contactSupportDescription => 'கூடுதல் உதவிக்காக எங்கள் ஆதரவு குழுவைத் தொடர்பு கொள்ளுங்கள்'; + + @override + String balanceSuccess(Object amount) { + return 'உங்கள் தற்போதைய இருப்பு $amount ரூபாய்.'; + } + + @override + String transactionsFound(Object count) { + return 'இங்கே உங்கள் $count சமீபத்திய பரிவர்த்தனைகள்.'; + } + + @override + String get noTransactions => 'பரிவர்த்தனைகள் எதுவும் காணப்படவில்லை.'; + + @override + String transferSuccess(Object amount, Object recipient) { + return '$recipientக்கு $amount ரூபாய் பரிமாற்றம் வெற்றிகரமாக தொடங்கப்பட்டது.'; + } + + @override + String get transferFailed => 'பரிமாற்றம் தோல்வி. தயவுசெய்து மீண்டும் முயற்சிக்கவும்.'; + + @override + String get errorGeneric => 'மன்னிக்கவும், பிழை ஏற்பட்டது. தயவுசெய்து மீண்டும் முயற்சிக்கவும்.'; + + @override + String get errorInsufficientFunds => 'போதுமான நிதி இல்லை. தயவுசெய்து உங்கள் இருப்பை சரிபார்க்கவும்.'; + + @override + String get pleaseSaySomething => 'தயவுசெய்து ஏதாவது சொல்லுங்கள்'; } diff --git a/mobile_app/lib/l10n/app_localizations_te.dart b/mobile_app/lib/l10n/app_localizations_te.dart index 8cc569a..4ac3b7d 100644 --- a/mobile_app/lib/l10n/app_localizations_te.dart +++ b/mobile_app/lib/l10n/app_localizations_te.dart @@ -20,6 +20,9 @@ class AppLocalizationsTe extends AppLocalizations { @override String get micHint => 'మాట్లాడడానికి మైక్‌ను ట్యాప్ చేయండి'; + @override + String get tapToSpeak => 'మాట్లాడడానికి ట్యాప్ చేయండి'; + @override String get listening => 'వింటోంది...'; @@ -79,6 +82,9 @@ class AppLocalizationsTe extends AppLocalizations { @override String get cancel => 'రద్దు చేయి'; + @override + String get stopVoiceBankingConfirm => 'వాయిస్ బ్యాంకింగ్ ఆపాలా? అన్ని ప్రస్తుత వాయిస్ కార్యకలాపాలు రద్దు చేయబడతాయి.'; + @override String get welcomeTo => 'స్వాగతం'; @@ -277,4 +283,34 @@ class AppLocalizationsTe extends AppLocalizations { @override String get contactSupportDescription => 'అదనపు సహాయం కోసం మా మద్దతు బృందాన్ని సంప్రదించండి'; + + @override + String balanceSuccess(Object amount) { + return 'మీ ప్రస్తుత బ్యాలెన్స్ $amount రూపాయలు.'; + } + + @override + String transactionsFound(Object count) { + return 'ఇక్కడ మీ $count ఇటీవలి లావాదేవీలు.'; + } + + @override + String get noTransactions => 'లావాదేవీలు ఏవీ కనుగొనబడలేదు.'; + + @override + String transferSuccess(Object amount, Object recipient) { + return '$recipientకు $amount రూపాయల బదిలీ విజయవంతంగా ప్రారంభించబడింది.'; + } + + @override + String get transferFailed => 'బదిలీ విఫలమైంది. దయచేసి మళ్ళీ ప్రయత్నించండి.'; + + @override + String get errorGeneric => 'క్షమించండి, లోపం సంభవించింది. దయచేసి మళ్ళీ ప్రయత్నించండి.'; + + @override + String get errorInsufficientFunds => 'తగినంత నిధులు లేవు. దయచేసి మీ బ్యాలెన్స్ తనిఖీ చేయండి.'; + + @override + String get pleaseSaySomething => 'దయచేసి ఏదైనా చెప్పండి'; } diff --git a/mobile_app/lib/l10n/app_ml.arb b/mobile_app/lib/l10n/app_ml.arb index 6e369a7..7751bbc 100644 --- a/mobile_app/lib/l10n/app_ml.arb +++ b/mobile_app/lib/l10n/app_ml.arb @@ -3,6 +3,7 @@ "loginPrompt": "നിങ്ങളുടെ മൊബൈൽ നമ്പർ നൽകുക", "otpPrompt": "OTP നൽകുക", "micHint": "സംസാരിക്കാൻ മൈക്കിൽ ടാപ്പ് ചെയ്യുക", + "tapToSpeak": "സംസാരിക്കാൻ ടാപ്പ് ചെയ്യുക", "listening": "കേൾക്കുന്നു...", "transcribing": "എഴുത്താക്കുന്നു...", "executing": "നിങ്ങളുടെ അഭ്യർത്ഥന പ്രോസസ്സ് ചെയ്യുന്നു...", @@ -22,6 +23,7 @@ "voice": "വോയ്സ്", "stop": "നിർത്തുക", "cancel": "റദ്ദാക്കുക", + "stopVoiceBankingConfirm": "വോയ്സ് ബാങ്കിംഗ് നിർത്തണോ? എല്ലാ നിലവിലെ വോയ്സ് പ്രവർത്തനങ്ങളും റദ്ദാക്കപ്പെടും.", "welcomeTo": "സ്വാഗതം", "experienceBanking": "വോയ്സിന്റെ ശക്തിയിൽ ബാങ്കിംഗ് അനുഭവിക്കുക", "enterMobileNumber": "മൊബൈൽ നമ്പർ നൽകുക", @@ -87,5 +89,13 @@ "tipNaturalLanguage": "സ്വാഭാവിക ഭാഷ ഉപയോഗിക്കുക ഉദാഹരണത്തിന് \"എന്റെ ബാലൻസ് കാണിക്കുക\"", "tipWaitForIndicator": "സംസാരിക്കുന്നതിന് മുമ്പ് കേൾക്കുന്ന സൂചകത്തിനായി കാത്തിരിക്കുക", "needMoreHelp": "കൂടുതൽ സഹായം വേണോ?", - "contactSupportDescription": "അധിക സഹായത്തിനായി ഞങ്ങളുടെ സപ്പോർട്ട് ടീമുമായി ബന്ധപ്പെടുക" + "contactSupportDescription": "അധിക സഹായത്തിനായി ഞങ്ങളുടെ സപ്പോർട്ട് ടീമുമായി ബന്ധപ്പെടുക", + "balanceSuccess": "നിങ്ങളുടെ നിലവിലെ ബാലൻസ് {amount} രൂപയാണ്.", + "transactionsFound": "നിങ്ങളുടെ {count} ഏറ്റവും പുതിയ ഇടപാടുകൾ ഇതാ.", + "noTransactions": "ഇടപാടുകളൊന്നും കണ്ടെത്തിയില്ല.", + "transferSuccess": "{recipient}-ന് {amount} രൂപ ട്രാൻസ്ഫർ വിജയകരമായി ആരംഭിച്ചു.", + "transferFailed": "ട്രാൻസ്ഫർ പരാജയപ്പെട്ടു. ദയവായി വീണ്ടും ശ്രമിക്കുക.", + "errorGeneric": "ക്ഷമിക്കണം, ഒരു പിശക് സംഭവിച്ചു. ദയവായി വീണ്ടും ശ്രമിക്കുക.", + "errorInsufficientFunds": "ധനം പോരാ. ദയവായി നിങ്ങളുടെ ബാലൻസ് പരിശോധിക്കുക.", + "pleaseSaySomething": "ദയവായി എന്തെങ്കിലും പറയുക" } \ No newline at end of file diff --git a/mobile_app/lib/l10n/app_mr.arb b/mobile_app/lib/l10n/app_mr.arb index e668de3..208a3b3 100644 --- a/mobile_app/lib/l10n/app_mr.arb +++ b/mobile_app/lib/l10n/app_mr.arb @@ -3,6 +3,7 @@ "loginPrompt": "आपला मोबाइल नंबर प्रविष्ट करा", "otpPrompt": "OTP प्रविष्ट करा", "micHint": "बोलण्यासाठी माइक टॅप करा", + "tapToSpeak": "बोलण्यासाठी टॅप करा", "listening": "ऐकत आहे...", "transcribing": "लिहित आहे...", "executing": "आपली विनंती प्रक्रिया करत आहे...", @@ -22,6 +23,7 @@ "voice": "व्हॉइस", "stop": "थांबवा", "cancel": "रद्द करा", + "stopVoiceBankingConfirm": "व्हॉइस बँकिंग थांबवायचे? सर्व सध्याची व्हॉइस क्रिया रद्द होईल.", "welcomeTo": "स्वागत", "experienceBanking": "व्हॉइसच्या शक्तीने बँकिंगचा अनुभव घ्या", "enterMobileNumber": "मोबाइल नंबर प्रविष्ट करा", @@ -87,5 +89,13 @@ "tipNaturalLanguage": "नैसर्गिक भाषा वापरा जसे की \"माझा बॅलन्स दाखवा\"", "tipWaitForIndicator": "बोलण्यापूर्वी ऐकण्याच्या सूचकाची वाट पहा", "needMoreHelp": "अधिक मदत हवी?", - "contactSupportDescription": "अतिरिक्त सहाय्यासाठी आमच्या सहाय्य टीमशी संपर्क साधा" + "contactSupportDescription": "अतिरिक्त सहाय्यासाठी आमच्या सहाय्य टीमशी संपर्क साधा", + "balanceSuccess": "तुमचे वर्तमान बॅलन्स {amount} रुपये आहे.", + "transactionsFound": "येथे तुमच्या {count} अलीकडील व्यवहार आहेत.", + "noTransactions": "कोणतेही व्यवहार आढळले नाहीत.", + "transferSuccess": "{recipient} ला {amount} रुपये हस्तांतरण यशस्वीरित्या सुरू झाले.", + "transferFailed": "हस्तांतरण अयशस्वी. कृपया पुन्हा प्रयत्न करा.", + "errorGeneric": "क्षमा करा, त्रुटी आली. कृपया पुन्हा प्रयत्न करा.", + "errorInsufficientFunds": "अपुरे निधी. कृपया तुमचे बॅलन्स तपासा.", + "pleaseSaySomething": "कृपया काहीतरी बोला" } \ No newline at end of file diff --git a/mobile_app/lib/l10n/app_pa.arb b/mobile_app/lib/l10n/app_pa.arb index 8ea0595..4f903ea 100644 --- a/mobile_app/lib/l10n/app_pa.arb +++ b/mobile_app/lib/l10n/app_pa.arb @@ -3,6 +3,7 @@ "loginPrompt": "ਆਪਣਾ ਮੋਬਾਈਲ ਨੰਬਰ ਦਰਜ ਕਰੋ", "otpPrompt": "OTP ਦਰਜ ਕਰੋ", "micHint": "ਬੋਲਣ ਲਈ ਮਾਈਕ ਟੈਪ ਕਰੋ", + "tapToSpeak": "ਬੋਲਣ ਲਈ ਟੈਪ ਕਰੋ", "listening": "ਸੁਣ ਰਿਹਾ ਹੈ...", "transcribing": "ਲਿਖ ਰਿਹਾ ਹੈ...", "executing": "ਤੁਹਾਡੀ ਬੇਨਤੀ ਪ੍ਰਕਿਰਿਆ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ...", @@ -22,6 +23,7 @@ "voice": "ਵੌਇਸ", "stop": "ਰੋਕੋ", "cancel": "ਰੱਦ ਕਰੋ", + "stopVoiceBankingConfirm": "ਵੌਇਸ ਬੈਂਕਿੰਗ ਰੋਕੋ? ਸਾਰੀ ਮੌਜੂਦਾ ਵੌਇਸ ਗਤੀਵਿਧੀ ਰੱਦ ਹੋ ਜਾਵੇਗੀ।", "welcomeTo": "ਸਵਾਗਤ", "experienceBanking": "ਵੌਇਸ ਦੀ ਸ਼ਕਤੀ ਨਾਲ ਬੈਂਕਿੰਗ ਦਾ ਅਨੁਭਵ ਕਰੋ", "enterMobileNumber": "ਮੋਬਾਈਲ ਨੰਬਰ ਦਰਜ ਕਰੋ", @@ -87,5 +89,13 @@ "tipNaturalLanguage": "ਕੁਦਰਤੀ ਭਾਸ਼ਾ ਵਰਤੋ ਜਿਵੇਂ \"ਮੇਰਾ ਬੈਲੇਂਸ ਦਿਖਾਓ\"", "tipWaitForIndicator": "ਬੋਲਣ ਤੋਂ ਪਹਿਲਾਂ ਸੁਣਨ ਦੇ ਸੰਕੇਤਕ ਲਈ ਉਡੀਕ ਕਰੋ", "needMoreHelp": "ਹੋਰ ਮਦਦ ਚਾਹੀਦੀ ਹੈ?", - "contactSupportDescription": "ਵਾਧੂ ਸਹਾਇਤਾ ਲਈ ਸਾਡੀ ਸਹਾਇਤਾ ਟੀਮ ਨਾਲ ਸੰਪਰਕ ਕਰੋ" + "contactSupportDescription": "ਵਾਧੂ ਸਹਾਇਤਾ ਲਈ ਸਾਡੀ ਸਹਾਇਤਾ ਟੀਮ ਨਾਲ ਸੰਪਰਕ ਕਰੋ", + "balanceSuccess": "ਤੁਹਾਡਾ ਮੌਜੂਦਾ ਬੈਲੇਂਸ {amount} ਰੁਪਏ ਹੈ।", + "transactionsFound": "ਇੱਥੇ ਤੁਹਾਡੇ {count} ਸਭ ਤੋਂ ਹਾਲ ਹੀ ਦੇ ਲੈਣ-ਦੇਣ ਹਨ।", + "noTransactions": "ਕੋਈ ਲੈਣ-ਦੇਣ ਨਹੀਂ ਮਿਲੀ।", + "transferSuccess": "{recipient} ਨੂੰ {amount} ਰੁਪਏ ਦਾ ਟ੍ਰਾਂਸਫਰ ਸਫਲਤਾਪੂਰਵਕ ਸ਼ੁਰੂ ਕੀਤਾ ਗਿਆ ਹੈ।", + "transferFailed": "ਟ੍ਰਾਂਸਫਰ ਅਸਫਲ। ਕ੍ਰਿਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।", + "errorGeneric": "ਮਾਫ਼ ਕਰੋ, ਗਲਤੀ ਆਈ। ਕ੍ਰਿਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।", + "errorInsufficientFunds": "ਅਪੂਰਨ ਫੰਡ। ਕ੍ਰਿਪਾ ਕਰਕੇ ਆਪਣਾ ਬੈਲੇਂਸ ਜਾਂਚੋ।", + "pleaseSaySomething": "ਕ੍ਰਿਪਾ ਕਰਕੇ ਕੁਝ ਕਹੋ" } \ No newline at end of file diff --git a/mobile_app/lib/l10n/app_ta.arb b/mobile_app/lib/l10n/app_ta.arb index 5b767a6..2ae470d 100644 --- a/mobile_app/lib/l10n/app_ta.arb +++ b/mobile_app/lib/l10n/app_ta.arb @@ -3,6 +3,7 @@ "loginPrompt": "உங்கள் மொபைல் எண்ணை உள்ளிடவும்", "otpPrompt": "OTP உள்ளிடவும்", "micHint": "பேச மைக் டேப் செய்யவும்", + "tapToSpeak": "பேச தட்டவும்", "listening": "கேட்டுக்கொண்டிருக்கிறது...", "transcribing": "எழுதிக்கொண்டிருக்கிறது...", "executing": "உங்கள் கோரிக்கையை செயலாக்குகிறது...", @@ -22,6 +23,7 @@ "voice": "வாய்ஸ்", "stop": "நிறுத்து", "cancel": "ரத்து செய்", + "stopVoiceBankingConfirm": "குரல் வங்கியை நிறுத்துவதா? அனைத்து தற்போதைய குரல் செயல்பாடுகளும் ரத்து செய்யப்படும்.", "welcomeTo": "வரவேற்கிறோம்", "experienceBanking": "வாய்ஸின் சக்தியால் பேங்கிங் அனுபவிக்கவும்", "enterMobileNumber": "மொபைல் எண்ணை உள்ளிடவும்", @@ -87,5 +89,13 @@ "tipNaturalLanguage": "இயற்கையான மொழியைப் பயன்படுத்துங்கள் எடுத்துக்காட்டாக \"எனது இருப்பைக் காட்டுங்கள்\"", "tipWaitForIndicator": "பேசுவதற்கு முன் கேட்கும் குறிகாட்டிக்காக காத்திருக்கவும்", "needMoreHelp": "மேலும் உதவி தேவையா?", - "contactSupportDescription": "கூடுதல் உதவிக்காக எங்கள் ஆதரவு குழுவைத் தொடர்பு கொள்ளுங்கள்" + "contactSupportDescription": "கூடுதல் உதவிக்காக எங்கள் ஆதரவு குழுவைத் தொடர்பு கொள்ளுங்கள்", + "balanceSuccess": "உங்கள் தற்போதைய இருப்பு {amount} ரூபாய்.", + "transactionsFound": "இங்கே உங்கள் {count} சமீபத்திய பரிவர்த்தனைகள்.", + "noTransactions": "பரிவர்த்தனைகள் எதுவும் காணப்படவில்லை.", + "transferSuccess": "{recipient}க்கு {amount} ரூபாய் பரிமாற்றம் வெற்றிகரமாக தொடங்கப்பட்டது.", + "transferFailed": "பரிமாற்றம் தோல்வி. தயவுசெய்து மீண்டும் முயற்சிக்கவும்.", + "errorGeneric": "மன்னிக்கவும், பிழை ஏற்பட்டது. தயவுசெய்து மீண்டும் முயற்சிக்கவும்.", + "errorInsufficientFunds": "போதுமான நிதி இல்லை. தயவுசெய்து உங்கள் இருப்பை சரிபார்க்கவும்.", + "pleaseSaySomething": "தயவுசெய்து ஏதாவது சொல்லுங்கள்" } diff --git a/mobile_app/lib/l10n/app_te.arb b/mobile_app/lib/l10n/app_te.arb index 70b5558..e5c81b7 100644 --- a/mobile_app/lib/l10n/app_te.arb +++ b/mobile_app/lib/l10n/app_te.arb @@ -3,6 +3,7 @@ "loginPrompt": "మీ మొబైల్ నంబర్‌ను నమోదు చేయండి", "otpPrompt": "OTP నమోదు చేయండి", "micHint": "మాట్లాడడానికి మైక్‌ను ట్యాప్ చేయండి", + "tapToSpeak": "మాట్లాడడానికి ట్యాప్ చేయండి", "listening": "వింటోంది...", "transcribing": "రాస్తోంది...", "executing": "మీ అభ్యర్థనను ప్రాసెస్ చేస్తోంది...", @@ -22,6 +23,7 @@ "voice": "వాయిస్", "stop": "ఆపు", "cancel": "రద్దు చేయి", + "stopVoiceBankingConfirm": "వాయిస్ బ్యాంకింగ్ ఆపాలా? అన్ని ప్రస్తుత వాయిస్ కార్యకలాపాలు రద్దు చేయబడతాయి.", "welcomeTo": "స్వాగతం", "experienceBanking": "వాయిస్ శక్తితో బ్యాంకింగ్‌ను అనుభవించండి", "enterMobileNumber": "మొబైల్ నంబర్‌ను నమోదు చేయండి", @@ -87,5 +89,13 @@ "tipNaturalLanguage": "సహజ భాషను ఉపయోగించండి ఉదాహరణకు \"నా బ్యాలెన్స్ చూపించండి\"", "tipWaitForIndicator": "మాట్లాడే ముందు వినే సూచిక కోసం వేచి ఉండండి", "needMoreHelp": "మరింత సహాయం కావాలా?", - "contactSupportDescription": "అదనపు సహాయం కోసం మా మద్దతు బృందాన్ని సంప్రదించండి" + "contactSupportDescription": "అదనపు సహాయం కోసం మా మద్దతు బృందాన్ని సంప్రదించండి", + "balanceSuccess": "మీ ప్రస్తుత బ్యాలెన్స్ {amount} రూపాయలు.", + "transactionsFound": "ఇక్కడ మీ {count} ఇటీవలి లావాదేవీలు.", + "noTransactions": "లావాదేవీలు ఏవీ కనుగొనబడలేదు.", + "transferSuccess": "{recipient}కు {amount} రూపాయల బదిలీ విజయవంతంగా ప్రారంభించబడింది.", + "transferFailed": "బదిలీ విఫలమైంది. దయచేసి మళ్ళీ ప్రయత్నించండి.", + "errorGeneric": "క్షమించండి, లోపం సంభవించింది. దయచేసి మళ్ళీ ప్రయత్నించండి.", + "errorInsufficientFunds": "తగినంత నిధులు లేవు. దయచేసి మీ బ్యాలెన్స్ తనిఖీ చేయండి.", + "pleaseSaySomething": "దయచేసి ఏదైనా చెప్పండి" } \ No newline at end of file diff --git a/mobile_app/lib/screens/voice_bank_home.dart b/mobile_app/lib/screens/voice_bank_home.dart index 9a3a5ec..db38840 100644 --- a/mobile_app/lib/screens/voice_bank_home.dart +++ b/mobile_app/lib/screens/voice_bank_home.dart @@ -1,6 +1,6 @@ -import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:uuid/uuid.dart'; import '../l10n/app_localizations.dart'; import '../widgets/language_toggle_widget.dart'; import '../bloc/voice_bloc.dart'; @@ -27,10 +27,6 @@ class _VoiceBankHomeState extends State { bool _isLoadingTransactions = true; String? _balance; String? _customerName; - bool _isRecording = false; // Track recording state locally - bool _isHolding = false; // Track if user is holding the button - Timer? _holdTimer; // Timer for hold duration - double _holdProgress = 0.0; // Progress of hold (0.0 to 1.0) @override void initState() { @@ -38,12 +34,6 @@ class _VoiceBankHomeState extends State { _loadData(); } - @override - void dispose() { - _holdTimer?.cancel(); - super.dispose(); - } - Future _loadData() async { // Load from shared preferences and load transactions for home screen display _loadCustomerName(); @@ -173,6 +163,35 @@ class _VoiceBankHomeState extends State { } } + void _showStopVoiceBankingConfirmation(BuildContext context, VoiceBloc bloc) { + final loc = AppLocalizations.of(context)!; + showDialog( + context: context, + builder: (BuildContext dialogContext) { + return AlertDialog( + title: Text(loc.stop), + content: Text(loc.stopVoiceBankingConfirm), + actions: [ + TextButton( + onPressed: () => Navigator.of(dialogContext).pop(false), + child: Text(loc.cancel), + ), + TextButton( + onPressed: () { + Navigator.of(dialogContext).pop(true); + bloc.add(CancelVoiceSession()); + }, + child: Text(loc.stop), + style: TextButton.styleFrom( + foregroundColor: Colors.red, + ), + ), + ], + ); + }, + ); + } + void _logout() async { // Show confirmation dialog final bool? shouldLogout = await showDialog( @@ -573,17 +592,6 @@ class _VoiceBankHomeState extends State { _loadBalanceFromPrefs(); _loadCustomerName(); _loadRecentTransactionsFromPrefs(); - } else if (state is Listening) { - setState(() { - _isRecording = true; - _isHolding = false; // Stop holding when recording starts - }); - } else if (state is Transcribing || state is Idle) { - setState(() { - _isRecording = false; - _isHolding = false; - _holdProgress = 0.0; - }); } }, child: BlocBuilder( @@ -685,6 +693,8 @@ class _VoiceBankHomeState extends State { showDialog( context: context, builder: (BuildContext context) { + final bloc = context.read(); + bloc.currentDialogContext = context; return Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), @@ -950,17 +960,9 @@ class _VoiceBankHomeState extends State { Widget _buildButtonLabel(VoiceState state, BuildContext context) { final loc = AppLocalizations.of(context)!; - if (_isHolding && !_isRecording) { + if (state is Listening || state is ShowBeneficiariesDialog) { return Text( - "Hold... ${(0.1 - (_holdProgress * 0.1)).toStringAsFixed(1)}s", - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), - ); - } else if (state is Listening) { - return Text( - "Release to Stop", + loc.listening, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, @@ -984,7 +986,7 @@ class _VoiceBankHomeState extends State { ); } else { return Text( - "Hold to Speak", + loc.tapToSpeak, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, @@ -994,101 +996,41 @@ class _VoiceBankHomeState extends State { } Widget _buildAnimatedVoiceButton(VoiceState state, BuildContext context) { - return _buildHoldToSpeakButton(state, context); + return _buildClickToSpeakButton(state, context); } - Widget _buildHoldToSpeakButton(VoiceState state, BuildContext context) { - return GestureDetector( - onPanDown: (_) { - _startHold(); - }, - onPanEnd: (_) { - _endHold(); - }, - onPanCancel: () { - _endHold(); - }, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(28), - boxShadow: [ - BoxShadow( - color: _getButtonColor(state).withOpacity(0.3), - blurRadius: 15, - spreadRadius: 3, - ), - ], - ), - child: Stack( - children: [ - // Progress indicator - if (_isHolding && !_isRecording) - Positioned.fill( - child: CircularProgressIndicator( - value: _holdProgress, - strokeWidth: 4, - valueColor: AlwaysStoppedAnimation(Colors.white), - backgroundColor: Colors.white.withOpacity(0.3), - ), - ), - // Button - FloatingActionButton.extended( - onPressed: null, // Disable tap, only use gesture - backgroundColor: _getButtonColor(state), - icon: _buildButtonIcon(state), - label: _buildButtonLabel(state, context), - ), - ], - ), + Widget _buildClickToSpeakButton(VoiceState state, BuildContext context) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(28), + boxShadow: [ + BoxShadow( + color: _getButtonColor(state).withOpacity(0.3), + blurRadius: 15, + spreadRadius: 3, + ), + ], + ), + child: FloatingActionButton.extended( + onPressed: () { + final bloc = context.read(); + if (state is Idle) { + final sessionId = const Uuid().v4(); + bloc.add(StartListening( + locale: Localizations.localeOf(context).languageCode, + sessionId: sessionId, + )); + } else { + _showStopVoiceBankingConfirmation(context, bloc); + } + }, + backgroundColor: _getButtonColor(state), + icon: _buildButtonIcon(state), + label: _buildButtonLabel(state, context), ), ); } - void _startHold() { - if (_isHolding || _isRecording) return; - - setState(() { - _isHolding = true; - _holdProgress = 0.0; - }); - - // Start timer for 0.1 seconds - _holdTimer = Timer.periodic(const Duration(milliseconds: 50), (timer) { - setState(() { - _holdProgress = (timer.tick * 50) / 100.0; // 0.1 seconds = 100ms - }); - - if (_holdProgress >= 1.0) { - // 0.1 seconds completed, start recording - timer.cancel(); - _startRecording(); - } - }); - } - - void _endHold() { - _holdTimer?.cancel(); - - if (_isRecording) { - // Stop recording - final bloc = context.read(); - bloc.add( - StopListening(locale: Localizations.localeOf(context).languageCode)); - } - - setState(() { - _isHolding = false; - _holdProgress = 0.0; - }); - } - - void _startRecording() { - if (_isRecording) return; - - final bloc = context.read(); - bloc.add(StartListening()); - } - void _showOtpDialog(BuildContext context, String message, String sessionId, String recipient, double amount) { final TextEditingController otpController = TextEditingController(); @@ -1102,6 +1044,8 @@ class _VoiceBankHomeState extends State { context: context, barrierDismissible: false, builder: (BuildContext context) { + final bloc = context.read(); + bloc.currentDialogContext = context; return StatefulBuilder( builder: (context, setState) { return Dialog( @@ -1599,6 +1543,8 @@ class _VoiceBankHomeState extends State { context: context, barrierDismissible: false, builder: (BuildContext context) { + final bloc = context.read(); + bloc.currentDialogContext = context; return Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), @@ -1870,7 +1816,9 @@ class _VoiceBankHomeState extends State { showDialog( context: context, builder: (BuildContext context) { - return Dialog( + final bloc = context.read(); + bloc.currentDialogContext = context; + return Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), diff --git a/mobile_app/lib/services/banking_api.dart b/mobile_app/lib/services/banking_api.dart index bc326af..009b05b 100644 --- a/mobile_app/lib/services/banking_api.dart +++ b/mobile_app/lib/services/banking_api.dart @@ -5,7 +5,8 @@ import 'shared_preferences_service.dart'; class BankingAPI { //final Dio dio = Dio(BaseOptions(baseUrl: "http://192.168.1.6:8000/bank/me")); final Dio dio = - Dio(BaseOptions(baseUrl: "https://loglytics.joshsoftware.com/bank/me")); + Dio(BaseOptions(baseUrl: "https://loglytics.joshsoftware.com/bank/me")); + // Dio(BaseOptions(baseUrl: "http://192.168.1.237:8000/bank/me")); Future getBalance() async { final phone = SharedPreferencesService.getMobileNumber(); diff --git a/mobile_app/lib/services/translation_service.dart b/mobile_app/lib/services/translation_service.dart index 214b693..9a5ea64 100644 --- a/mobile_app/lib/services/translation_service.dart +++ b/mobile_app/lib/services/translation_service.dart @@ -110,6 +110,20 @@ class TranslationService { 'kn': 'ಸಾಕಷ್ಟು ನಿಧಿ ಇಲ್ಲ. ದಯವಿಟ್ಟು ನಿಮ್ಮ ಬ್ಯಾಲೆನ್ಸ್ ಪರಿಶೀಲಿಸಿ.', 'pa': 'ਅਪਰਿਪੂਰਨ ਫੰਡ। ਕਿਰਪਾ ਕਰਕੇ ਆਪਣਾ ਬੈਲੇਂਸ ਚੈੱਕ ਕਰੋ।', }, + + // Empty recording (no speech detected) + 'empty_recording': { + 'en': 'Please say something.', + 'hi': 'कृपया कुछ बोलें।', + 'ta': 'தயவுசெய்து ஏதாவது சொல்லுங்கள்.', + 'te': 'దయచేసి ఏదైనా చెప్పండి.', + 'bn': 'অনুগ্রহ করে কিছু বলুন।', + 'gu': 'કૃપા કરીને કંઈક બોલો.', + 'ml': 'ദയവായി എന്തെങ്കിലും പറയുക.', + 'mr': 'कृपया काहीतरी बोला.', + 'kn': 'ದಯವಿಟ್ಟು ಏನಾದರೂ ಹೇಳಿ.', + 'pa': 'ਕਿਰਪਾ ਕਰਕੇ ਕੁਝ ਕਹੋ।', + }, }; /// Translates a response message based on the current locale diff --git a/mobile_app/lib/services/tts_service.dart b/mobile_app/lib/services/tts_service.dart index a069521..3b534fa 100644 --- a/mobile_app/lib/services/tts_service.dart +++ b/mobile_app/lib/services/tts_service.dart @@ -34,10 +34,22 @@ class TTSService { } } + /// Stops any ongoing or queued speech. Call when user cancels voice session. + Future stop() async { + try { + await _tts.stop(); + } catch (e) { + print("TTS Stop Error: $e"); + } + } + Future speak(String text, {String langCode = "en"}) async { try { await _tts.stop(); + final availableLanguages = await _tts.getLanguages; + print("TTS Debug - Available languages: $availableLanguages"); + print("TTS Debug - Requested language code: $langCode"); print("TTS Debug - Text to speak: $text"); @@ -61,7 +73,7 @@ class TTSService { // Default fallback: English print("TTS Debug - Falling back to English"); - await _tts.setLanguage("en-IN"); + await _tts.setLanguage(langCode); await _tts.speak(text); } catch (e) { print("TTS Speak Error: $e"); diff --git a/mobile_app/lib/services/voice_repository.dart b/mobile_app/lib/services/voice_repository.dart index a294da3..7b0fe6e 100644 --- a/mobile_app/lib/services/voice_repository.dart +++ b/mobile_app/lib/services/voice_repository.dart @@ -1,13 +1,45 @@ +import 'dart:async'; +import 'dart:developer'; import 'dart:io'; import 'package:dio/dio.dart'; import 'package:record/record.dart'; -import 'package:uuid/uuid.dart'; import 'package:path_provider/path_provider.dart'; import 'shared_preferences_service.dart'; +/// Thrown when the recorded audio is empty/silent (no speech detected). +class EmptyRecordingException implements Exception { + EmptyRecordingException(); +} + class VoiceRepository { final _rec = AudioRecorder(); + /// Whether sustained speech (not just background noise) was detected. + bool _hasSpoken = false; + StreamSubscription? _amplitudeSubscription; + /// Number of consecutive amplitude samples above threshold (filters brief noise spikes). + int _consecutiveAboveThreshold = 0; + /// dB threshold: only amplitude above this is considered speech. Higher = ignore more background noise. + static const double _speechThreshold = -20; + /// Require this many consecutive samples above threshold to count as speech (avoids clicks/fan spikes). + static const int _minConsecutiveForSpeech = 2; + /// Last time amplitude was above threshold (speech detected). + DateTime? _lastSpeechTime; + /// Callback invoked when user is idle (silent) for [idleDuration] after having spoken. + void Function()? _onIdle; + Duration _idleDuration = const Duration(seconds: 2); + bool _idleTriggered = false; + + /// Callback invoked when user says nothing for [initialSilenceDuration] after starting (e.g. 12s). + void Function()? _onInitialSilence; + Duration _initialSilenceDuration = const Duration(seconds: 12); + Timer? _initialSilenceTimer; + + /// Callback invoked when user says nothing for [silenceReminderDuration] after starting (e.g. 5s) — remind to speak. + void Function()? _onSilenceReminder; + Duration _silenceReminderDuration = const Duration(seconds: 5); + Timer? _silenceReminderTimer; + //final Dio dio = Dio(BaseOptions(baseUrl: "http://192.168.1.6:8000")); late final Dio dio; @@ -15,9 +47,10 @@ class VoiceRepository { // Initialize Dio with proper configuration dio = Dio(BaseOptions( baseUrl: "https://loglytics.joshsoftware.com", - connectTimeout: const Duration(seconds: 30), - receiveTimeout: const Duration(seconds: 30), - sendTimeout: const Duration(seconds: 30), + // baseUrl: "http://192.168.1.237:8000", + connectTimeout: const Duration(seconds: 300), + receiveTimeout: const Duration(seconds: 300), + sendTimeout: const Duration(seconds: 300), headers: { 'Content-Type': 'multipart/form-data', }, @@ -51,9 +84,10 @@ class VoiceRepository { Dio _createFreshDio() { return Dio(BaseOptions( baseUrl: "https://loglytics.joshsoftware.com", - connectTimeout: const Duration(seconds: 30), - receiveTimeout: const Duration(seconds: 30), - sendTimeout: const Duration(seconds: 30), + // baseUrl: "http://192.168.1.237:8000", + connectTimeout: const Duration(seconds: 300), + receiveTimeout: const Duration(seconds: 300), + sendTimeout: const Duration(seconds: 300), headers: { 'Content-Type': 'multipart/form-data', }, @@ -67,7 +101,14 @@ class VoiceRepository { return '${dir.path}/recording.wav'; } - Future start() async { + Future start({ + void Function()? onIdle, + Duration idleDuration = const Duration(seconds: 2), + void Function()? onInitialSilence, + Duration initialSilenceDuration = const Duration(seconds: 12), + void Function()? onSilenceReminder, + Duration silenceReminderDuration = const Duration(seconds: 5), + }) async { try { // Check permission first bool hasPermission = await _rec.hasPermission(); @@ -87,22 +128,125 @@ class VoiceRepository { sampleRate: 16000, ), path: path); + + // Listen to amplitude during recording; ignore background noise via higher threshold + sustained level + _hasSpoken = false; + _consecutiveAboveThreshold = 0; + _lastSpeechTime = null; + _onIdle = onIdle; + _idleDuration = idleDuration; + _idleTriggered = false; + + + _onSilenceReminder = onSilenceReminder; + _silenceReminderDuration = silenceReminderDuration; + _silenceReminderTimer?.cancel(); + _silenceReminderTimer = Timer(_silenceReminderDuration, () { + if (!_hasSpoken) { + _silenceReminderTimer?.cancel(); + _silenceReminderTimer = null; + _onSilenceReminder?.call(); + } + }); + + _onInitialSilence = onInitialSilence; + _initialSilenceDuration = initialSilenceDuration; + _initialSilenceTimer?.cancel(); + _initialSilenceTimer = null; + _initialSilenceTimer = Timer(_initialSilenceDuration, () { + if (!_hasSpoken) { + _initialSilenceTimer?.cancel(); + _initialSilenceTimer = null; + _silenceReminderTimer?.cancel(); + _silenceReminderTimer = null; + _amplitudeSubscription?.cancel(); + _amplitudeSubscription = null; + _onInitialSilence?.call(); + } + }); + + _amplitudeSubscription?.cancel(); + _amplitudeSubscription = _rec + .onAmplitudeChanged(const Duration(milliseconds: 300)) + .listen((amp) { + if (amp.current > _speechThreshold) { + _consecutiveAboveThreshold++; + if (_consecutiveAboveThreshold >= _minConsecutiveForSpeech) { + _hasSpoken = true; + _lastSpeechTime = DateTime.now(); + _initialSilenceTimer?.cancel(); + _initialSilenceTimer = null; + _silenceReminderTimer?.cancel(); + _silenceReminderTimer = null; + } + } else { + _consecutiveAboveThreshold = 0; + // Check idle: user spoke before, now silent for idleDuration + if (_hasSpoken && + !_idleTriggered && + _lastSpeechTime != null && + DateTime.now().difference(_lastSpeechTime!) >= _idleDuration) { + _idleTriggered = true; + _onIdle?.call(); + _amplitudeSubscription?.cancel(); + _amplitudeSubscription = null; + } + } + }); } catch (e) { print("Voice Repository - Error starting recording: $e"); rethrow; } } - Future> stopAndTranscribe({locale = 'en'}) async { + /// Stops recording without sending to server. Use when initial silence timeout fires. + Future stopWithoutTranscribe() async { + _initialSilenceTimer?.cancel(); + _initialSilenceTimer = null; + _silenceReminderTimer?.cancel(); + _silenceReminderTimer = null; + await _amplitudeSubscription?.cancel(); + _amplitudeSubscription = null; + await _rec.stop(); + } + + Future> stopAndTranscribe( + {locale = 'en', + required String sessionId, + Function()? ifNotEmptyCallback}) async { try { + _initialSilenceTimer?.cancel(); + _initialSilenceTimer = null; + _silenceReminderTimer?.cancel(); + _silenceReminderTimer = null; final path = await _rec.stop(); + // Cancel amplitude listener + await _amplitudeSubscription?.cancel(); + _amplitudeSubscription = null; + if (path == null) { throw Exception("No recording file found"); } final file = File(path); + // First check: reject if file is missing or has zero size + if (!await file.exists()) { + throw EmptyRecordingException(); + } + final fileSize = await file.length(); + if (fileSize == 0) { + throw EmptyRecordingException(); + } + + // Reject if no speech was detected during recording (amplitude never above threshold) + if (!_hasSpoken) { + throw EmptyRecordingException(); + } + + ifNotEmptyCallback?.call(); + // Get phone number from shared preferences final phone = SharedPreferencesService.getMobileNumber(); if (phone == null) { @@ -112,13 +256,20 @@ class VoiceRepository { final form = FormData.fromMap({ 'audio': await MultipartFile.fromFile(file.path, filename: 'recording.wav'), - 'session_id': const Uuid().v4(), + 'session_id': sessionId, 'locale': locale, 'phone': phone, }); // Use a fresh HTTP client for each request to avoid connection issues final freshDio = _createFreshDio(); + freshDio.interceptors.add(LogInterceptor( + requestBody: true, + responseBody: true, + logPrint: (obj) => log('Dio: $obj'), + )); + log('Form data: ${form.fields.toString()}'); + log('Form data: ${form.files.toString()}'); final res = await freshDio.post('/voice/transcribe-intent', data: form); freshDio.close(); // Close the fresh client after use diff --git a/mobile_app/macos/Podfile.lock b/mobile_app/macos/Podfile.lock index 23881e5..c5ecd6c 100644 --- a/mobile_app/macos/Podfile.lock +++ b/mobile_app/macos/Podfile.lock @@ -7,12 +7,16 @@ PODS: - FlutterMacOS - record_macos (1.1.0): - FlutterMacOS + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS DEPENDENCIES: - flutter_tts (from `Flutter/ephemeral/.symlinks/plugins/flutter_tts/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - record_macos (from `Flutter/ephemeral/.symlinks/plugins/record_macos/macos`) + - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) EXTERNAL SOURCES: flutter_tts: @@ -23,12 +27,15 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin record_macos: :path: Flutter/ephemeral/.symlinks/plugins/record_macos/macos + shared_preferences_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin SPEC CHECKSUMS: flutter_tts: ae915565cc6948444b513acc8ee021993281e027 FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 record_macos: 43194b6c06ca6f8fa132e2acea72b202b92a0f5b + shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009 diff --git a/mobile_app/test_api.dart b/mobile_app/test_api.dart index fba3e75..9db154b 100644 --- a/mobile_app/test_api.dart +++ b/mobile_app/test_api.dart @@ -3,7 +3,8 @@ import 'package:dio/dio.dart'; void main() async { final dio = - Dio(BaseOptions(baseUrl: "https://loglytics.joshsoftware.com/bank/me")); + Dio(BaseOptions(baseUrl: "https://loglytics.joshsoftware.com/bank/me")); + // Dio(BaseOptions(baseUrl: "http://localhost:8000/bank/me")); try { print("Testing API call...");