From 0b59086cdddd40689e57969aa7914ba38f1ec2dd Mon Sep 17 00:00:00 2001 From: Anuj-S62 Date: Thu, 11 Jul 2024 15:18:31 +0530 Subject: Update Voice Agent Flutter App - update voice-agent flutter app to use whisper AI for speech-to-text functionality. - Integrated SharedPreferences to store the application state. Bug-AGL: SPEC-5200 Change-Id: I9a05b1d135c1fa07949333391ff828f166b7fe8e Signed-off-by: Anuj-S62 --- lib/grpc/generated/voice_agent.pb.dart | 70 ++++++ lib/grpc/generated/voice_agent.pbenum.dart | 30 +++ lib/grpc/generated/voice_agent.pbgrpc.dart | 176 +++++-------- lib/grpc/generated/voice_agent.pbjson.dart | 74 +++++- lib/main.dart | 3 +- lib/models/app_state.dart | 2 + lib/protos/voice_agent.proto | 14 +- lib/screens/home_screen.dart | 380 +++++++++++++++++++++-------- lib/screens/loading_screen.dart | 81 ++++++ lib/widgets/assistant_mode_choice.dart | 8 +- lib/widgets/nlu_engine_choice.dart | 6 +- lib/widgets/online_mode_choice.dart | 148 +++++++++++ lib/widgets/stt_model_choice.dart | 148 +++++++++++ 13 files changed, 926 insertions(+), 214 deletions(-) create mode 100644 lib/screens/loading_screen.dart create mode 100644 lib/widgets/online_mode_choice.dart create mode 100644 lib/widgets/stt_model_choice.dart (limited to 'lib') diff --git a/lib/grpc/generated/voice_agent.pb.dart b/lib/grpc/generated/voice_agent.pb.dart index 2c0f9fa..892b95e 100644 --- a/lib/grpc/generated/voice_agent.pb.dart +++ b/lib/grpc/generated/voice_agent.pb.dart @@ -9,6 +9,7 @@ // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import +import 'dart:async' as $async; import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; @@ -274,6 +275,7 @@ class S_RecognizeVoiceControl extends $pb.GeneratedMessage { VoiceAudio? audioStream, NLUModel? nluModel, $core.String? streamId, + STTFramework? sttFramework, }) { final $result = create(); if (audioStream != null) { @@ -285,6 +287,9 @@ class S_RecognizeVoiceControl extends $pb.GeneratedMessage { if (streamId != null) { $result.streamId = streamId; } + if (sttFramework != null) { + $result.sttFramework = sttFramework; + } return $result; } S_RecognizeVoiceControl._() : super(); @@ -295,6 +300,7 @@ class S_RecognizeVoiceControl extends $pb.GeneratedMessage { ..aOM(1, _omitFieldNames ? '' : 'audioStream', subBuilder: VoiceAudio.create) ..e(2, _omitFieldNames ? '' : 'nluModel', $pb.PbFieldType.OE, defaultOrMaker: NLUModel.SNIPS, valueOf: NLUModel.valueOf, enumValues: NLUModel.values) ..aOS(3, _omitFieldNames ? '' : 'streamId') + ..e(4, _omitFieldNames ? '' : 'sttFramework', $pb.PbFieldType.OE, defaultOrMaker: STTFramework.VOSK, valueOf: STTFramework.valueOf, enumValues: STTFramework.values) ..hasRequiredFields = false ; @@ -347,6 +353,15 @@ class S_RecognizeVoiceControl extends $pb.GeneratedMessage { $core.bool hasStreamId() => $_has(2); @$pb.TagNumber(3) void clearStreamId() => clearField(3); + + @$pb.TagNumber(4) + STTFramework get sttFramework => $_getN(3); + @$pb.TagNumber(4) + set sttFramework(STTFramework v) { setField(4, v); } + @$pb.TagNumber(4) + $core.bool hasSttFramework() => $_has(3); + @$pb.TagNumber(4) + void clearSttFramework() => clearField(4); } class RecognizeVoiceControl extends $pb.GeneratedMessage { @@ -355,6 +370,8 @@ class RecognizeVoiceControl extends $pb.GeneratedMessage { NLUModel? nluModel, RecordMode? recordMode, $core.String? streamId, + STTFramework? sttFramework, + OnlineMode? onlineMode, }) { final $result = create(); if (action != null) { @@ -369,6 +386,12 @@ class RecognizeVoiceControl extends $pb.GeneratedMessage { if (streamId != null) { $result.streamId = streamId; } + if (sttFramework != null) { + $result.sttFramework = sttFramework; + } + if (onlineMode != null) { + $result.onlineMode = onlineMode; + } return $result; } RecognizeVoiceControl._() : super(); @@ -380,6 +403,8 @@ class RecognizeVoiceControl extends $pb.GeneratedMessage { ..e(2, _omitFieldNames ? '' : 'nluModel', $pb.PbFieldType.OE, defaultOrMaker: NLUModel.SNIPS, valueOf: NLUModel.valueOf, enumValues: NLUModel.values) ..e(3, _omitFieldNames ? '' : 'recordMode', $pb.PbFieldType.OE, defaultOrMaker: RecordMode.MANUAL, valueOf: RecordMode.valueOf, enumValues: RecordMode.values) ..aOS(4, _omitFieldNames ? '' : 'streamId') + ..e(5, _omitFieldNames ? '' : 'sttFramework', $pb.PbFieldType.OE, defaultOrMaker: STTFramework.VOSK, valueOf: STTFramework.valueOf, enumValues: STTFramework.values) + ..e(6, _omitFieldNames ? '' : 'onlineMode', $pb.PbFieldType.OE, defaultOrMaker: OnlineMode.ONLINE, valueOf: OnlineMode.valueOf, enumValues: OnlineMode.values) ..hasRequiredFields = false ; @@ -439,6 +464,24 @@ class RecognizeVoiceControl extends $pb.GeneratedMessage { $core.bool hasStreamId() => $_has(3); @$pb.TagNumber(4) void clearStreamId() => clearField(4); + + @$pb.TagNumber(5) + STTFramework get sttFramework => $_getN(4); + @$pb.TagNumber(5) + set sttFramework(STTFramework v) { setField(5, v); } + @$pb.TagNumber(5) + $core.bool hasSttFramework() => $_has(4); + @$pb.TagNumber(5) + void clearSttFramework() => clearField(5); + + @$pb.TagNumber(6) + OnlineMode get onlineMode => $_getN(5); + @$pb.TagNumber(6) + set onlineMode(OnlineMode v) { setField(6, v); } + @$pb.TagNumber(6) + $core.bool hasOnlineMode() => $_has(5); + @$pb.TagNumber(6) + void clearOnlineMode() => clearField(6); } class RecognizeTextControl extends $pb.GeneratedMessage { @@ -791,6 +834,33 @@ class ExecuteResult extends $pb.GeneratedMessage { void clearStatus() => clearField(2); } +class VoiceAgentServiceApi { + $pb.RpcClient _client; + VoiceAgentServiceApi(this._client); + + $async.Future checkServiceStatus($pb.ClientContext? ctx, Empty request) => + _client.invoke(ctx, 'VoiceAgentService', 'CheckServiceStatus', request, ServiceStatus()) + ; + $async.Future s_DetectWakeWord($pb.ClientContext? ctx, VoiceAudio request) => + _client.invoke(ctx, 'VoiceAgentService', 'S_DetectWakeWord', request, WakeWordStatus()) + ; + $async.Future detectWakeWord($pb.ClientContext? ctx, Empty request) => + _client.invoke(ctx, 'VoiceAgentService', 'DetectWakeWord', request, WakeWordStatus()) + ; + $async.Future s_RecognizeVoiceCommand($pb.ClientContext? ctx, S_RecognizeVoiceControl request) => + _client.invoke(ctx, 'VoiceAgentService', 'S_RecognizeVoiceCommand', request, RecognizeResult()) + ; + $async.Future recognizeVoiceCommand($pb.ClientContext? ctx, RecognizeVoiceControl request) => + _client.invoke(ctx, 'VoiceAgentService', 'RecognizeVoiceCommand', request, RecognizeResult()) + ; + $async.Future recognizeTextCommand($pb.ClientContext? ctx, RecognizeTextControl request) => + _client.invoke(ctx, 'VoiceAgentService', 'RecognizeTextCommand', request, RecognizeResult()) + ; + $async.Future executeCommand($pb.ClientContext? ctx, ExecuteInput request) => + _client.invoke(ctx, 'VoiceAgentService', 'ExecuteCommand', request, ExecuteResult()) + ; +} + const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/lib/grpc/generated/voice_agent.pbenum.dart b/lib/grpc/generated/voice_agent.pbenum.dart index 78da13f..51e7427 100644 --- a/lib/grpc/generated/voice_agent.pbenum.dart +++ b/lib/grpc/generated/voice_agent.pbenum.dart @@ -13,6 +13,36 @@ import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; +class STTFramework extends $pb.ProtobufEnum { + static const STTFramework VOSK = STTFramework._(0, _omitEnumNames ? '' : 'VOSK'); + static const STTFramework WHISPER = STTFramework._(1, _omitEnumNames ? '' : 'WHISPER'); + + static const $core.List values = [ + VOSK, + WHISPER, + ]; + + static final $core.Map<$core.int, STTFramework> _byValue = $pb.ProtobufEnum.initByValue(values); + static STTFramework? valueOf($core.int value) => _byValue[value]; + + const STTFramework._($core.int v, $core.String n) : super(v, n); +} + +class OnlineMode extends $pb.ProtobufEnum { + static const OnlineMode ONLINE = OnlineMode._(0, _omitEnumNames ? '' : 'ONLINE'); + static const OnlineMode OFFLINE = OnlineMode._(1, _omitEnumNames ? '' : 'OFFLINE'); + + static const $core.List values = [ + ONLINE, + OFFLINE, + ]; + + static final $core.Map<$core.int, OnlineMode> _byValue = $pb.ProtobufEnum.initByValue(values); + static OnlineMode? valueOf($core.int value) => _byValue[value]; + + const OnlineMode._($core.int v, $core.String n) : super(v, n); +} + class RecordAction extends $pb.ProtobufEnum { static const RecordAction START = RecordAction._(0, _omitEnumNames ? '' : 'START'); static const RecordAction STOP = RecordAction._(1, _omitEnumNames ? '' : 'STOP'); diff --git a/lib/grpc/generated/voice_agent.pbgrpc.dart b/lib/grpc/generated/voice_agent.pbgrpc.dart index 502c797..5b31b44 100644 --- a/lib/grpc/generated/voice_agent.pbgrpc.dart +++ b/lib/grpc/generated/voice_agent.pbgrpc.dart @@ -21,92 +21,66 @@ export 'voice_agent.pb.dart'; // @$pb.GrpcServiceName('VoiceAgentService') class VoiceAgentServiceClient extends $grpc.Client { - static final _$checkServiceStatus = - $grpc.ClientMethod<$0.Empty, $0.ServiceStatus>( - '/VoiceAgentService/CheckServiceStatus', - ($0.Empty value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $0.ServiceStatus.fromBuffer(value)); - static final _$s_DetectWakeWord = - $grpc.ClientMethod<$0.VoiceAudio, $0.WakeWordStatus>( - '/VoiceAgentService/S_DetectWakeWord', - ($0.VoiceAudio value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $0.WakeWordStatus.fromBuffer(value)); - static final _$detectWakeWord = - $grpc.ClientMethod<$0.Empty, $0.WakeWordStatus>( - '/VoiceAgentService/DetectWakeWord', - ($0.Empty value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $0.WakeWordStatus.fromBuffer(value)); - static final _$s_RecognizeVoiceCommand = - $grpc.ClientMethod<$0.S_RecognizeVoiceControl, $0.RecognizeResult>( - '/VoiceAgentService/S_RecognizeVoiceCommand', - ($0.S_RecognizeVoiceControl value) => value.writeToBuffer(), - ($core.List<$core.int> value) => - $0.RecognizeResult.fromBuffer(value)); - static final _$recognizeVoiceCommand = - $grpc.ClientMethod<$0.RecognizeVoiceControl, $0.RecognizeResult>( - '/VoiceAgentService/RecognizeVoiceCommand', - ($0.RecognizeVoiceControl value) => value.writeToBuffer(), - ($core.List<$core.int> value) => - $0.RecognizeResult.fromBuffer(value)); - static final _$recognizeTextCommand = - $grpc.ClientMethod<$0.RecognizeTextControl, $0.RecognizeResult>( - '/VoiceAgentService/RecognizeTextCommand', - ($0.RecognizeTextControl value) => value.writeToBuffer(), - ($core.List<$core.int> value) => - $0.RecognizeResult.fromBuffer(value)); - static final _$executeCommand = - $grpc.ClientMethod<$0.ExecuteInput, $0.ExecuteResult>( - '/VoiceAgentService/ExecuteCommand', - ($0.ExecuteInput value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $0.ExecuteResult.fromBuffer(value)); + static final _$checkServiceStatus = $grpc.ClientMethod<$0.Empty, $0.ServiceStatus>( + '/VoiceAgentService/CheckServiceStatus', + ($0.Empty value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.ServiceStatus.fromBuffer(value)); + static final _$s_DetectWakeWord = $grpc.ClientMethod<$0.VoiceAudio, $0.WakeWordStatus>( + '/VoiceAgentService/S_DetectWakeWord', + ($0.VoiceAudio value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.WakeWordStatus.fromBuffer(value)); + static final _$detectWakeWord = $grpc.ClientMethod<$0.Empty, $0.WakeWordStatus>( + '/VoiceAgentService/DetectWakeWord', + ($0.Empty value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.WakeWordStatus.fromBuffer(value)); + static final _$s_RecognizeVoiceCommand = $grpc.ClientMethod<$0.S_RecognizeVoiceControl, $0.RecognizeResult>( + '/VoiceAgentService/S_RecognizeVoiceCommand', + ($0.S_RecognizeVoiceControl value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.RecognizeResult.fromBuffer(value)); + static final _$recognizeVoiceCommand = $grpc.ClientMethod<$0.RecognizeVoiceControl, $0.RecognizeResult>( + '/VoiceAgentService/RecognizeVoiceCommand', + ($0.RecognizeVoiceControl value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.RecognizeResult.fromBuffer(value)); + static final _$recognizeTextCommand = $grpc.ClientMethod<$0.RecognizeTextControl, $0.RecognizeResult>( + '/VoiceAgentService/RecognizeTextCommand', + ($0.RecognizeTextControl value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.RecognizeResult.fromBuffer(value)); + static final _$executeCommand = $grpc.ClientMethod<$0.ExecuteInput, $0.ExecuteResult>( + '/VoiceAgentService/ExecuteCommand', + ($0.ExecuteInput value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.ExecuteResult.fromBuffer(value)); VoiceAgentServiceClient($grpc.ClientChannel channel, {$grpc.CallOptions? options, $core.Iterable<$grpc.ClientInterceptor>? interceptors}) - : super(channel, options: options, interceptors: interceptors); + : super(channel, options: options, + interceptors: interceptors); - $grpc.ResponseFuture<$0.ServiceStatus> checkServiceStatus($0.Empty request, - {$grpc.CallOptions? options}) { + $grpc.ResponseFuture<$0.ServiceStatus> checkServiceStatus($0.Empty request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$checkServiceStatus, request, options: options); } - $grpc.ResponseStream<$0.WakeWordStatus> s_DetectWakeWord( - $async.Stream<$0.VoiceAudio> request, - {$grpc.CallOptions? options}) { + $grpc.ResponseStream<$0.WakeWordStatus> s_DetectWakeWord($async.Stream<$0.VoiceAudio> request, {$grpc.CallOptions? options}) { return $createStreamingCall(_$s_DetectWakeWord, request, options: options); } - $grpc.ResponseStream<$0.WakeWordStatus> detectWakeWord($0.Empty request, - {$grpc.CallOptions? options}) { - return $createStreamingCall( - _$detectWakeWord, $async.Stream.fromIterable([request]), - options: options); + $grpc.ResponseStream<$0.WakeWordStatus> detectWakeWord($0.Empty request, {$grpc.CallOptions? options}) { + return $createStreamingCall(_$detectWakeWord, $async.Stream.fromIterable([request]), options: options); } - $grpc.ResponseFuture<$0.RecognizeResult> s_RecognizeVoiceCommand( - $async.Stream<$0.S_RecognizeVoiceControl> request, - {$grpc.CallOptions? options}) { - return $createStreamingCall(_$s_RecognizeVoiceCommand, request, - options: options) - .single; + $grpc.ResponseFuture<$0.RecognizeResult> s_RecognizeVoiceCommand($async.Stream<$0.S_RecognizeVoiceControl> request, {$grpc.CallOptions? options}) { + return $createStreamingCall(_$s_RecognizeVoiceCommand, request, options: options).single; } - $grpc.ResponseFuture<$0.RecognizeResult> recognizeVoiceCommand( - $async.Stream<$0.RecognizeVoiceControl> request, - {$grpc.CallOptions? options}) { - return $createStreamingCall(_$recognizeVoiceCommand, request, - options: options) - .single; + $grpc.ResponseFuture<$0.RecognizeResult> recognizeVoiceCommand($async.Stream<$0.RecognizeVoiceControl> request, {$grpc.CallOptions? options}) { + return $createStreamingCall(_$recognizeVoiceCommand, request, options: options).single; } - $grpc.ResponseFuture<$0.RecognizeResult> recognizeTextCommand( - $0.RecognizeTextControl request, - {$grpc.CallOptions? options}) { + $grpc.ResponseFuture<$0.RecognizeResult> recognizeTextCommand($0.RecognizeTextControl request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$recognizeTextCommand, request, options: options); } - $grpc.ResponseFuture<$0.ExecuteResult> executeCommand($0.ExecuteInput request, - {$grpc.CallOptions? options}) { + $grpc.ResponseFuture<$0.ExecuteResult> executeCommand($0.ExecuteInput request, {$grpc.CallOptions? options}) { return $createUnaryCall(_$executeCommand, request, options: options); } } @@ -137,31 +111,26 @@ abstract class VoiceAgentServiceBase extends $grpc.Service { true, ($core.List<$core.int> value) => $0.Empty.fromBuffer(value), ($0.WakeWordStatus value) => value.writeToBuffer())); - $addMethod( - $grpc.ServiceMethod<$0.S_RecognizeVoiceControl, $0.RecognizeResult>( - 'S_RecognizeVoiceCommand', - s_RecognizeVoiceCommand, - true, - false, - ($core.List<$core.int> value) => - $0.S_RecognizeVoiceControl.fromBuffer(value), - ($0.RecognizeResult value) => value.writeToBuffer())); - $addMethod( - $grpc.ServiceMethod<$0.RecognizeVoiceControl, $0.RecognizeResult>( - 'RecognizeVoiceCommand', - recognizeVoiceCommand, - true, - false, - ($core.List<$core.int> value) => - $0.RecognizeVoiceControl.fromBuffer(value), - ($0.RecognizeResult value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.S_RecognizeVoiceControl, $0.RecognizeResult>( + 'S_RecognizeVoiceCommand', + s_RecognizeVoiceCommand, + true, + false, + ($core.List<$core.int> value) => $0.S_RecognizeVoiceControl.fromBuffer(value), + ($0.RecognizeResult value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.RecognizeVoiceControl, $0.RecognizeResult>( + 'RecognizeVoiceCommand', + recognizeVoiceCommand, + true, + false, + ($core.List<$core.int> value) => $0.RecognizeVoiceControl.fromBuffer(value), + ($0.RecognizeResult value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$0.RecognizeTextControl, $0.RecognizeResult>( 'RecognizeTextCommand', recognizeTextCommand_Pre, false, false, - ($core.List<$core.int> value) => - $0.RecognizeTextControl.fromBuffer(value), + ($core.List<$core.int> value) => $0.RecognizeTextControl.fromBuffer(value), ($0.RecognizeResult value) => value.writeToBuffer())); $addMethod($grpc.ServiceMethod<$0.ExecuteInput, $0.ExecuteResult>( 'ExecuteCommand', @@ -172,40 +141,27 @@ abstract class VoiceAgentServiceBase extends $grpc.Service { ($0.ExecuteResult value) => value.writeToBuffer())); } - $async.Future<$0.ServiceStatus> checkServiceStatus_Pre( - $grpc.ServiceCall call, $async.Future<$0.Empty> request) async { + $async.Future<$0.ServiceStatus> checkServiceStatus_Pre($grpc.ServiceCall call, $async.Future<$0.Empty> request) async { return checkServiceStatus(call, await request); } - $async.Stream<$0.WakeWordStatus> detectWakeWord_Pre( - $grpc.ServiceCall call, $async.Future<$0.Empty> request) async* { + $async.Stream<$0.WakeWordStatus> detectWakeWord_Pre($grpc.ServiceCall call, $async.Future<$0.Empty> request) async* { yield* detectWakeWord(call, await request); } - $async.Future<$0.RecognizeResult> recognizeTextCommand_Pre( - $grpc.ServiceCall call, - $async.Future<$0.RecognizeTextControl> request) async { + $async.Future<$0.RecognizeResult> recognizeTextCommand_Pre($grpc.ServiceCall call, $async.Future<$0.RecognizeTextControl> request) async { return recognizeTextCommand(call, await request); } - $async.Future<$0.ExecuteResult> executeCommand_Pre( - $grpc.ServiceCall call, $async.Future<$0.ExecuteInput> request) async { + $async.Future<$0.ExecuteResult> executeCommand_Pre($grpc.ServiceCall call, $async.Future<$0.ExecuteInput> request) async { return executeCommand(call, await request); } - $async.Future<$0.ServiceStatus> checkServiceStatus( - $grpc.ServiceCall call, $0.Empty request); - $async.Stream<$0.WakeWordStatus> s_DetectWakeWord( - $grpc.ServiceCall call, $async.Stream<$0.VoiceAudio> request); - $async.Stream<$0.WakeWordStatus> detectWakeWord( - $grpc.ServiceCall call, $0.Empty request); - $async.Future<$0.RecognizeResult> s_RecognizeVoiceCommand( - $grpc.ServiceCall call, - $async.Stream<$0.S_RecognizeVoiceControl> request); - $async.Future<$0.RecognizeResult> recognizeVoiceCommand( - $grpc.ServiceCall call, $async.Stream<$0.RecognizeVoiceControl> request); - $async.Future<$0.RecognizeResult> recognizeTextCommand( - $grpc.ServiceCall call, $0.RecognizeTextControl request); - $async.Future<$0.ExecuteResult> executeCommand( - $grpc.ServiceCall call, $0.ExecuteInput request); + $async.Future<$0.ServiceStatus> checkServiceStatus($grpc.ServiceCall call, $0.Empty request); + $async.Stream<$0.WakeWordStatus> s_DetectWakeWord($grpc.ServiceCall call, $async.Stream<$0.VoiceAudio> request); + $async.Stream<$0.WakeWordStatus> detectWakeWord($grpc.ServiceCall call, $0.Empty request); + $async.Future<$0.RecognizeResult> s_RecognizeVoiceCommand($grpc.ServiceCall call, $async.Stream<$0.S_RecognizeVoiceControl> request); + $async.Future<$0.RecognizeResult> recognizeVoiceCommand($grpc.ServiceCall call, $async.Stream<$0.RecognizeVoiceControl> request); + $async.Future<$0.RecognizeResult> recognizeTextCommand($grpc.ServiceCall call, $0.RecognizeTextControl request); + $async.Future<$0.ExecuteResult> executeCommand($grpc.ServiceCall call, $0.ExecuteInput request); } diff --git a/lib/grpc/generated/voice_agent.pbjson.dart b/lib/grpc/generated/voice_agent.pbjson.dart index 6fa9502..1b8365d 100644 --- a/lib/grpc/generated/voice_agent.pbjson.dart +++ b/lib/grpc/generated/voice_agent.pbjson.dart @@ -13,6 +13,32 @@ import 'dart:convert' as $convert; import 'dart:core' as $core; import 'dart:typed_data' as $typed_data; +@$core.Deprecated('Use sTTFrameworkDescriptor instead') +const STTFramework$json = { + '1': 'STTFramework', + '2': [ + {'1': 'VOSK', '2': 0}, + {'1': 'WHISPER', '2': 1}, + ], +}; + +/// Descriptor for `STTFramework`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List sTTFrameworkDescriptor = $convert.base64Decode( + 'CgxTVFRGcmFtZXdvcmsSCAoEVk9TSxAAEgsKB1dISVNQRVIQAQ=='); + +@$core.Deprecated('Use onlineModeDescriptor instead') +const OnlineMode$json = { + '1': 'OnlineMode', + '2': [ + {'1': 'ONLINE', '2': 0}, + {'1': 'OFFLINE', '2': 1}, + ], +}; + +/// Descriptor for `OnlineMode`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List onlineModeDescriptor = $convert.base64Decode( + 'CgpPbmxpbmVNb2RlEgoKBk9OTElORRAAEgsKB09GRkxJTkUQAQ=='); + @$core.Deprecated('Use recordActionDescriptor instead') const RecordAction$json = { '1': 'RecordAction', @@ -151,6 +177,7 @@ const S_RecognizeVoiceControl$json = { {'1': 'audio_stream', '3': 1, '4': 1, '5': 11, '6': '.VoiceAudio', '10': 'audioStream'}, {'1': 'nlu_model', '3': 2, '4': 1, '5': 14, '6': '.NLUModel', '10': 'nluModel'}, {'1': 'stream_id', '3': 3, '4': 1, '5': 9, '10': 'streamId'}, + {'1': 'stt_framework', '3': 4, '4': 1, '5': 14, '6': '.STTFramework', '10': 'sttFramework'}, ], }; @@ -158,7 +185,8 @@ const S_RecognizeVoiceControl$json = { final $typed_data.Uint8List s_RecognizeVoiceControlDescriptor = $convert.base64Decode( 'ChdTX1JlY29nbml6ZVZvaWNlQ29udHJvbBIuCgxhdWRpb19zdHJlYW0YASABKAsyCy5Wb2ljZU' 'F1ZGlvUgthdWRpb1N0cmVhbRImCglubHVfbW9kZWwYAiABKA4yCS5OTFVNb2RlbFIIbmx1TW9k' - 'ZWwSGwoJc3RyZWFtX2lkGAMgASgJUghzdHJlYW1JZA=='); + 'ZWwSGwoJc3RyZWFtX2lkGAMgASgJUghzdHJlYW1JZBIyCg1zdHRfZnJhbWV3b3JrGAQgASgOMg' + '0uU1RURnJhbWV3b3JrUgxzdHRGcmFtZXdvcms='); @$core.Deprecated('Use recognizeVoiceControlDescriptor instead') const RecognizeVoiceControl$json = { @@ -168,6 +196,8 @@ const RecognizeVoiceControl$json = { {'1': 'nlu_model', '3': 2, '4': 1, '5': 14, '6': '.NLUModel', '10': 'nluModel'}, {'1': 'record_mode', '3': 3, '4': 1, '5': 14, '6': '.RecordMode', '10': 'recordMode'}, {'1': 'stream_id', '3': 4, '4': 1, '5': 9, '10': 'streamId'}, + {'1': 'stt_framework', '3': 5, '4': 1, '5': 14, '6': '.STTFramework', '10': 'sttFramework'}, + {'1': 'online_mode', '3': 6, '4': 1, '5': 14, '6': '.OnlineMode', '10': 'onlineMode'}, ], }; @@ -176,7 +206,8 @@ final $typed_data.Uint8List recognizeVoiceControlDescriptor = $convert.base64Dec 'ChVSZWNvZ25pemVWb2ljZUNvbnRyb2wSJQoGYWN0aW9uGAEgASgOMg0uUmVjb3JkQWN0aW9uUg' 'ZhY3Rpb24SJgoJbmx1X21vZGVsGAIgASgOMgkuTkxVTW9kZWxSCG5sdU1vZGVsEiwKC3JlY29y' 'ZF9tb2RlGAMgASgOMgsuUmVjb3JkTW9kZVIKcmVjb3JkTW9kZRIbCglzdHJlYW1faWQYBCABKA' - 'lSCHN0cmVhbUlk'); + 'lSCHN0cmVhbUlkEjIKDXN0dF9mcmFtZXdvcmsYBSABKA4yDS5TVFRGcmFtZXdvcmtSDHN0dEZy' + 'YW1ld29yaxIsCgtvbmxpbmVfbW9kZRgGIAEoDjILLk9ubGluZU1vZGVSCm9ubGluZU1vZGU='); @$core.Deprecated('Use recognizeTextControlDescriptor instead') const RecognizeTextControl$json = { @@ -252,3 +283,42 @@ final $typed_data.Uint8List executeResultDescriptor = $convert.base64Decode( 'Cg1FeGVjdXRlUmVzdWx0EhoKCHJlc3BvbnNlGAEgASgJUghyZXNwb25zZRIqCgZzdGF0dXMYAi' 'ABKA4yEi5FeGVjdXRlU3RhdHVzVHlwZVIGc3RhdHVz'); +const $core.Map<$core.String, $core.dynamic> VoiceAgentServiceBase$json = { + '1': 'VoiceAgentService', + '2': [ + {'1': 'CheckServiceStatus', '2': '.Empty', '3': '.ServiceStatus'}, + {'1': 'S_DetectWakeWord', '2': '.VoiceAudio', '3': '.WakeWordStatus', '5': true, '6': true}, + {'1': 'DetectWakeWord', '2': '.Empty', '3': '.WakeWordStatus', '6': true}, + {'1': 'S_RecognizeVoiceCommand', '2': '.S_RecognizeVoiceControl', '3': '.RecognizeResult', '5': true}, + {'1': 'RecognizeVoiceCommand', '2': '.RecognizeVoiceControl', '3': '.RecognizeResult', '5': true}, + {'1': 'RecognizeTextCommand', '2': '.RecognizeTextControl', '3': '.RecognizeResult'}, + {'1': 'ExecuteCommand', '2': '.ExecuteInput', '3': '.ExecuteResult'}, + ], +}; + +@$core.Deprecated('Use voiceAgentServiceDescriptor instead') +const $core.Map<$core.String, $core.Map<$core.String, $core.dynamic>> VoiceAgentServiceBase$messageJson = { + '.Empty': Empty$json, + '.ServiceStatus': ServiceStatus$json, + '.VoiceAudio': VoiceAudio$json, + '.WakeWordStatus': WakeWordStatus$json, + '.S_RecognizeVoiceControl': S_RecognizeVoiceControl$json, + '.RecognizeResult': RecognizeResult$json, + '.IntentSlot': IntentSlot$json, + '.RecognizeVoiceControl': RecognizeVoiceControl$json, + '.RecognizeTextControl': RecognizeTextControl$json, + '.ExecuteInput': ExecuteInput$json, + '.ExecuteResult': ExecuteResult$json, +}; + +/// Descriptor for `VoiceAgentService`. Decode as a `google.protobuf.ServiceDescriptorProto`. +final $typed_data.Uint8List voiceAgentServiceDescriptor = $convert.base64Decode( + 'ChFWb2ljZUFnZW50U2VydmljZRIsChJDaGVja1NlcnZpY2VTdGF0dXMSBi5FbXB0eRoOLlNlcn' + 'ZpY2VTdGF0dXMSNAoQU19EZXRlY3RXYWtlV29yZBILLlZvaWNlQXVkaW8aDy5XYWtlV29yZFN0' + 'YXR1cygBMAESKwoORGV0ZWN0V2FrZVdvcmQSBi5FbXB0eRoPLldha2VXb3JkU3RhdHVzMAESRw' + 'oXU19SZWNvZ25pemVWb2ljZUNvbW1hbmQSGC5TX1JlY29nbml6ZVZvaWNlQ29udHJvbBoQLlJl' + 'Y29nbml6ZVJlc3VsdCgBEkMKFVJlY29nbml6ZVZvaWNlQ29tbWFuZBIWLlJlY29nbml6ZVZvaW' + 'NlQ29udHJvbBoQLlJlY29nbml6ZVJlc3VsdCgBEj8KFFJlY29nbml6ZVRleHRDb21tYW5kEhUu' + 'UmVjb2duaXplVGV4dENvbnRyb2waEC5SZWNvZ25pemVSZXN1bHQSLwoORXhlY3V0ZUNvbW1hbm' + 'QSDS5FeGVjdXRlSW5wdXQaDi5FeGVjdXRlUmVzdWx0'); + diff --git a/lib/main.dart b/lib/main.dart index 9dfbd54..6349d8d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_voiceassistant/screens/loading_screen.dart'; import 'package:provider/provider.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'models/app_state.dart'; @@ -103,7 +104,7 @@ class App extends StatelessWidget { Consumer( builder: (context, provider, child) { return provider.isServiceOnline - ? HomePage(config: config, wakeWord: provider.wakeWord) + ? Loading(config: config, wakeWord: provider.wakeWord) : ErrorScreen( onRetry: onRetry, ); diff --git a/lib/models/app_state.dart b/lib/models/app_state.dart index 709b445..8325b50 100644 --- a/lib/models/app_state.dart +++ b/lib/models/app_state.dart @@ -7,4 +7,6 @@ class AppState extends ChangeNotifier { String streamId = ""; bool isCommandProcessing = false; String commandProcessingText = "Processing..."; + String sttFramework = "vosk"; + bool onlineMode = false; } diff --git a/lib/protos/voice_agent.proto b/lib/protos/voice_agent.proto index 40dfe6a..09b9461 100644 --- a/lib/protos/voice_agent.proto +++ b/lib/protos/voice_agent.proto @@ -11,6 +11,15 @@ service VoiceAgentService { rpc ExecuteCommand(ExecuteInput) returns (ExecuteResult); } +enum STTFramework { + VOSK = 0; + WHISPER = 1; +} + +enum OnlineMode { + ONLINE = 0; + OFFLINE = 1; +} enum RecordAction { START = 0; @@ -50,7 +59,7 @@ message Empty {} message ServiceStatus { string version = 1; - bool status = 2; + bool status = 2; string wake_word = 3; } @@ -69,6 +78,7 @@ message S_RecognizeVoiceControl { VoiceAudio audio_stream = 1; NLUModel nlu_model = 2; string stream_id = 3; + STTFramework stt_framework = 4; } message RecognizeVoiceControl { @@ -76,6 +86,8 @@ message RecognizeVoiceControl { NLUModel nlu_model = 2; RecordMode record_mode = 3; string stream_id = 4; + STTFramework stt_framework = 5; + OnlineMode online_mode = 6; } message RecognizeTextControl { diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index a9d567b..4c2407f 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -1,8 +1,10 @@ import 'dart:ui'; import 'package:flutter/material.dart'; +import 'package:flutter_voiceassistant/widgets/online_mode_choice.dart'; import 'package:flutter_voiceassistant/widgets/try_commands.dart'; import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'dart:async'; import '../models/app_state.dart'; import '../widgets/nlu_engine_choice.dart'; @@ -13,6 +15,7 @@ import '../widgets/chat_section.dart'; import '../grpc/generated/voice_agent.pbgrpc.dart'; import '../grpc/voice_agent_client.dart'; import '../utils/app_config.dart'; +import '../widgets/stt_model_choice.dart'; class HomePage extends StatefulWidget { final AppConfig config; @@ -36,18 +39,30 @@ class HomePageState extends State { super.initState(); _config = widget.config; // Initialize _config in the initState _wakeWord = widget.wakeWord; // Initialize _wakeWord in the initState - addChatMessage( - "Assistant in Manual mode. You can send commands directly by pressing the record button."); + final appState = context.read(); + if(appState.isWakeWordMode){ + addChatMessage( + 'Switched to Wake Word mode. I\'ll listen for the wake word "$_wakeWord" before responding.'); + _startWakeWordDetection(context); + } + else{ + addChatMessage( + "Assistant in Manual mode. You can send commands directly by pressing the record button."); + } + } - void changeAssistantMode(BuildContext context, AssistantMode newMode) { + Future changeAssistantMode(BuildContext context, AssistantMode newMode) async { final appState = context.read(); clearChatMessages(); appState.streamId = ""; appState.isWakeWordDetected = false; appState.isCommandProcessing = false; + SharedPreferences prefs = await SharedPreferences.getInstance(); + if (newMode == AssistantMode.wakeWord) { + await prefs.setBool('isWakeWordMode', true); addChatMessage( 'Switched to Wake Word mode. I\'ll listen for the wake word "$_wakeWord" before responding.'); @@ -62,6 +77,7 @@ class HomePageState extends State { toggleWakeWordDetection(context, true); } } else if (newMode == AssistantMode.manual) { + prefs.setBool('isWakeWordMode', false); addChatMessage( 'Switched to Manual mode. You can send commands directly by pressing record button.'); @@ -74,15 +90,17 @@ class HomePageState extends State { setState(() {}); // Trigger a rebuild } - void changeIntentEngine(BuildContext context, NLUEngine newEngine) { + Future changeIntentEngine(BuildContext context, NLUEngine newEngine) async { final appState = context.read(); - + SharedPreferences prefs = await SharedPreferences.getInstance(); if (newEngine == NLUEngine.snips) { appState.intentEngine = "snips"; + await prefs.setString("intentEngine","snips"); addChatMessage( 'Switched to 🚀 Snips engine. Lets be precise and accurate.'); } else if (newEngine == NLUEngine.rasa) { appState.intentEngine = "rasa"; + await prefs.setString("intentEngine","rasa"); addChatMessage( 'Switched to 🤖 RASA engine. Conversations just got smarter!'); } @@ -90,6 +108,42 @@ class HomePageState extends State { setState(() {}); // Trigger a rebuild } + Future changeSTTFramework(BuildContext context, STTModel newModel) async { + final appState = context.read(); + SharedPreferences prefs = await SharedPreferences.getInstance(); + if (newModel == STTModel.vosk) { + appState.sttFramework = "vosk"; + await prefs.setString("sttFramework", "vosk"); + // vosk is fast and efficient + addChatMessage( + 'Switched to 🚀 Vosk framework. Lets be quick and efficient.'); + } else if (newModel == STTModel.whisper) { + appState.sttFramework = "whisper"; + await prefs.setString("sttFramework", "whisper"); + addChatMessage( + 'Switched to 🤖 Whisper framework. Conversations just got smarter!'); + } + print(appState.sttFramework); + setState(() {}); // Trigger a rebuild + } + + Future toggleOnlineMode(BuildContext context, OnlineModeEnum mode) async { + final appState = context.read(); + SharedPreferences prefs = await SharedPreferences.getInstance(); + if (mode == OnlineModeEnum.enabled) { + appState.onlineMode = true; + await prefs.setBool('onlineMode', true); + addChatMessage( + 'Switched to Online mode. I\'ll be connected to the internet for better results.'); + } else { + appState.onlineMode = false; + await prefs.setBool('onlineMode', false); + addChatMessage( + 'Switched to Offline mode. I\'ll be disconnected from the internet.'); + } + setState(() {}); // Trigger a rebuild + } + void addChatMessage(String text, {bool isUserMessage = false}) { final newMessage = ChatMessage(text: text, isUserMessage: isUserMessage); setState(() { @@ -123,7 +177,7 @@ class HomePageState extends State { setState( () {}); // Trigger a rebuild to ensure the loading indicator is shown, tis a bad practice though but deosn't heavily affect the performance final response = - await stopRecording(appState.streamId, appState.intentEngine); + await stopRecording(appState.streamId, appState.intentEngine,appState.sttFramework,appState.onlineMode); // Process and store the result if (response.status == RecognizeStatusType.REC_SUCCESS) { appState.commandProcessingText = "Executing command..."; @@ -204,19 +258,31 @@ class HomePageState extends State { } Future stopRecording( - String streamId, String nluModel) async { + String streamId, String nluModel, String stt,bool isOnlineMode) async { + try { NLUModel model = NLUModel.RASA; if (nluModel == "snips") { model = NLUModel.SNIPS; } + STTFramework sttFramework = STTFramework.VOSK; + if (stt == "whisper") { + sttFramework = STTFramework.WHISPER; + } + OnlineMode onlineMode = OnlineMode.OFFLINE; + if (isOnlineMode) { + onlineMode = OnlineMode.ONLINE; + } // Create a RecognizeControl message to stop recording final controlMessage = RecognizeVoiceControl() ..action = RecordAction.STOP ..nluModel = model ..streamId = streamId // Use the same stream ID as when starting recording - ..recordMode = RecordMode.MANUAL; + ..recordMode = RecordMode.MANUAL + ..sttFramework = sttFramework + ..onlineMode = onlineMode; + // Create a Stream with the control message final controlStream = Stream.fromIterable([controlMessage]); @@ -375,116 +441,234 @@ class HomePageState extends State { style: TextStyle(fontSize: 26, fontWeight: FontWeight.bold), ), SizedBox(height: 15), - Row( - mainAxisAlignment: MainAxisAlignment.center, + Column( children: [ - Flexible( - flex: 1, - child: ClipRect( - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), - child: Card( - color: _config.theme == "textured-dark" || - _config.theme == "textured-light" - ? Colors.transparent - : null, - elevation: 4, // Add elevation for shadow - shadowColor: _config.theme == "textured-dark" || - _config.theme == "textured-light" - ? Colors.transparent - : null, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: Padding( - padding: EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Assistant Mode', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + flex: 1, + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + child: Card( + color: _config.theme == "textured-dark" || + _config.theme == "textured-light" + ? Colors.transparent + : null, + elevation: 4, // Add elevation for shadow + shadowColor: _config.theme == "textured-dark" || + _config.theme == "textured-light" + ? Colors.transparent + : null, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Assistant Mode', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 16), // Add spacing if needed + Center( + child: Consumer( + builder: (context, appState, _) { + return AssistantModeChoice( + onModeChanged: (newMode) { + changeAssistantMode( + context, newMode); + print(newMode); + }, + theme: _config.theme, + ); + }, + ), + ), + ], ), - SizedBox(height: 16), // Add spacing if needed - Center( - child: Consumer( - builder: (context, appState, _) { - return AssistantModeChoice( - onModeChanged: (newMode) { - changeAssistantMode( - context, newMode); - print(newMode); + ), + ), + ), + ), + ), + + SizedBox(width: 20), // Add spacing between buttons + + Flexible( + flex: 1, + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + child: Card( + color: _config.theme == "textured-dark" || + _config.theme == "textured-light" + ? Colors.transparent + : null, + elevation: 4, // Add elevation for shadow + shadowColor: _config.theme == "textured-dark" || + _config.theme == "textured-light" + ? Colors.transparent + : null, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Intent Engine', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 16), // Add spacing if needed + Center( + child: Consumer( + builder: (context, appState, _) { + return NLUEngineChoice( + onEngineChanged: (newEngine) { + changeIntentEngine( + context, newEngine); + print(newEngine); + }, + theme: _config.theme, + ); }, - theme: _config.theme, - ); - }, - ), + ), + ), + ], ), - ], + ), ), ), ), ), - ), + ], ), - SizedBox(width: 20), // Add spacing between buttons - - Flexible( - flex: 1, - child: ClipRect( - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), - child: Card( - color: _config.theme == "textured-dark" || + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + flex: 1, + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + child: Card( + color: _config.theme == "textured-dark" || _config.theme == "textured-light" - ? Colors.transparent - : null, - elevation: 4, // Add elevation for shadow - shadowColor: _config.theme == "textured-dark" || + ? Colors.transparent + : null, + elevation: 4, // Add elevation for shadow + shadowColor: _config.theme == "textured-dark" || _config.theme == "textured-light" - ? Colors.transparent - : null, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: Padding( - padding: EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Intent Engine', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), + ? Colors.transparent + : null, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Speech-to-Text Model', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 16), // Add spacing if needed + Center( + child: Consumer( + builder: (context, appState, _) { + return STTModelChoice( + onModelChanged: (newModel) { + changeSTTFramework( + context, newModel); + print(newModel); + }, + theme: _config.theme, + ); + }, + ), + ), + ], ), - SizedBox(height: 16), // Add spacing if needed - Center( - child: Consumer( - builder: (context, appState, _) { - return NLUEngineChoice( - onEngineChanged: (newEngine) { - changeIntentEngine( - context, newEngine); - print(newEngine); + ), + ), + ), + ), + ), + + SizedBox(width: 20), // Add spacing between buttons + + Flexible( + flex: 1, + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + child: Card( + color: _config.theme == "textured-dark" || + _config.theme == "textured-light" + ? Colors.transparent + : null, + elevation: 4, // Add elevation for shadow + shadowColor: _config.theme == "textured-dark" || + _config.theme == "textured-light" + ? Colors.transparent + : null, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Online Mode', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 16), // Add spacing if needed + Center( + child: Consumer( + builder: (context, appState, _) { + return OnlineModeChoice( + onModeChanged: (mode) { + toggleOnlineMode(context, mode); + print(mode); + }, + theme: _config.theme, + ); }, - theme: _config.theme, - ); - }, - ), + ), + ), + ], ), - ], + ), ), ), ), ), - ), + ], ), + + ], ), SizedBox(height: 15), diff --git a/lib/screens/loading_screen.dart b/lib/screens/loading_screen.dart new file mode 100644 index 0000000..f4d6c6d --- /dev/null +++ b/lib/screens/loading_screen.dart @@ -0,0 +1,81 @@ +import 'dart:async'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter_voiceassistant/screens/home_screen.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../models/app_state.dart'; +import '../utils/app_config.dart'; + +class Loading extends StatelessWidget { + final AppConfig config; + final String wakeWord; + Loading({required this.config, required this.wakeWord}); + + Future initialize(BuildContext context) async { + // Initialize the app + final appState = context.read(); + bool isWakeWordMode = false; + String intentEngine = "snips"; + String streamId = ""; + bool isCommandProcessing = false; + String commandProcessingText = "Processing..."; + String sttFramework = "vosk"; + bool onlineMode = false; + + SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.reload(); + if(prefs.containsKey('isWakeWordMode')) { + isWakeWordMode = await prefs.getBool('isWakeWordMode')!; + } + if(prefs.containsKey('intentEngine')) { + intentEngine = await prefs.getString('intentEngine')!; + } + if(prefs.containsKey('streamId')) { + streamId = await prefs.getString('streamId')!; + } + if(prefs.containsKey('isCommandProcessing')) { + isCommandProcessing = await prefs.getBool('isCommandProcessing')!; + } + if(prefs.containsKey('commandProcessingText')) { + commandProcessingText = await prefs.getString('commandProcessingText')!; + } + if(prefs.containsKey('sttFramework')) { + sttFramework = await prefs.getString('sttFramework')!; + } + if(prefs.containsKey('onlineMode')) { + onlineMode = await prefs.getBool('onlineMode')!; + } + + appState.isWakeWordMode = isWakeWordMode; + appState.intentEngine = intentEngine; + appState.streamId = streamId; + appState.isCommandProcessing = isCommandProcessing; + appState.commandProcessingText = commandProcessingText; + appState.sttFramework = sttFramework; + appState.onlineMode = onlineMode; + + print('isWakeWordMode: $isWakeWordMode'); + print('intentEngine: $intentEngine'); + print('streamId: $streamId'); + print('isCommandProcessing: $isCommandProcessing'); + print('commandProcessingText: $commandProcessingText'); + print('sttFramework: $sttFramework'); + print('onlineMode: $onlineMode'); + } + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: initialize(context), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return HomePage(config: config, wakeWord: wakeWord); + } else { + return CupertinoActivityIndicator(); + } + }, + ); + } +} diff --git a/lib/widgets/assistant_mode_choice.dart b/lib/widgets/assistant_mode_choice.dart index c2afe5b..2c776df 100644 --- a/lib/widgets/assistant_mode_choice.dart +++ b/lib/widgets/assistant_mode_choice.dart @@ -70,6 +70,9 @@ // } import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../models/app_state.dart'; enum AssistantMode { wakeWord, manual } @@ -92,7 +95,10 @@ class AssistantModeChoiceState extends State { @override void initState() { super.initState(); - _selectedMode = AssistantMode.manual; // Initialize the selection + final appState = context.read(); + _selectedMode = appState.isWakeWordMode + ? AssistantMode.wakeWord + : AssistantMode.manual; // Initialize the selection _theme = widget.theme; print(widget.theme); } diff --git a/lib/widgets/nlu_engine_choice.dart b/lib/widgets/nlu_engine_choice.dart index 32db7dd..53a4aaa 100644 --- a/lib/widgets/nlu_engine_choice.dart +++ b/lib/widgets/nlu_engine_choice.dart @@ -68,6 +68,9 @@ // } import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../models/app_state.dart'; enum NLUEngine { snips, rasa } @@ -90,7 +93,8 @@ class _NLUEngineChoiceState extends State { @override void initState() { super.initState(); - _selectedEngine = NLUEngine.snips; // Initialize the selection + final appState = context.read(); + _selectedEngine = appState.intentEngine == "snips" ? NLUEngine.snips : NLUEngine.rasa; _theme = widget.theme; } diff --git a/lib/widgets/online_mode_choice.dart b/lib/widgets/online_mode_choice.dart new file mode 100644 index 0000000..3146d15 --- /dev/null +++ b/lib/widgets/online_mode_choice.dart @@ -0,0 +1,148 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../models/app_state.dart'; + +enum OnlineModeEnum{ + enabled, + disabled +} + +class OnlineModeChoice extends StatefulWidget { + final Function(OnlineModeEnum) onModeChanged; + final String theme; + + const OnlineModeChoice({ + Key? key, + required this.onModeChanged, + required this.theme + }) : super(key: key); + + @override + State createState() => _OnlineModeChoiceState(); +} + +class _OnlineModeChoiceState extends State { + late OnlineModeEnum _selectedMode; + late String _theme; + + @override + void initState() { + super.initState(); + final appState = context.read(); + _selectedMode = appState.onlineMode ? OnlineModeEnum.enabled : OnlineModeEnum.disabled; + _theme = widget.theme; + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + InkWell( + onTap: () => _onModelChanged(OnlineModeEnum.disabled), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20.0), + bottomLeft: Radius.circular(20.0), + ), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 17.5, vertical: 5.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20.0), + bottomLeft: Radius.circular(20.0), + ), + color: _selectedMode == OnlineModeEnum.disabled + ? Colors.green + : _theme == "dark" || _theme == "textured-dark" + ? Colors.black + : Colors.white, + border: Border.all( + color: Colors.transparent, + ), + ), + child: Row( + children: [ + Icon( + _selectedMode == OnlineModeEnum.disabled + ? Icons.check + : Icons.cloud_off_outlined, + color: _selectedMode == OnlineModeEnum.disabled + ? Colors.white + : Colors.green, + ), + SizedBox(width: 8), + Text( + 'Disabled', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 17, + color: _selectedMode == OnlineModeEnum.disabled + ? Colors.white + : Colors.green, + ), + ), + ], + ), + ), + ), + InkWell( + onTap: () => _onModelChanged(OnlineModeEnum.enabled), + borderRadius: BorderRadius.only( + topRight: Radius.circular(20.0), + bottomRight: Radius.circular(20.0), + ), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 17.5, vertical: 5.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topRight: Radius.circular(20.0), + bottomRight: Radius.circular(20.0), + ), + color: _selectedMode == OnlineModeEnum.enabled + ? Colors.green + : _theme == "dark" || _theme == "textured-dark" + ? Colors.black + : Colors.white, + border: Border.all( + color: Colors.transparent, + ), + ), + child: Row( + children: [ + Icon( + _selectedMode == OnlineModeEnum.enabled + ? Icons.check + : Icons.cloud_done_outlined, + color: _selectedMode == OnlineModeEnum.enabled + ? Colors.white + : Colors.green, + ), + SizedBox(width: 8), + Text( + 'Enabled', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 17, + color: _selectedMode == OnlineModeEnum.enabled + ? Colors.white + : Colors.green, + ), + ), + ], + ), + ), + ), + ], + ); + } + + void _onModelChanged(OnlineModeEnum newMode) { + setState(() { + _selectedMode = newMode; + }); + + // Call the callback function to notify the engine change + widget.onModeChanged(newMode); + } +} \ No newline at end of file diff --git a/lib/widgets/stt_model_choice.dart b/lib/widgets/stt_model_choice.dart new file mode 100644 index 0000000..fc0e099 --- /dev/null +++ b/lib/widgets/stt_model_choice.dart @@ -0,0 +1,148 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../models/app_state.dart'; + +enum STTModel{ + vosk, + whisper +} + +class STTModelChoice extends StatefulWidget { + final Function(STTModel) onModelChanged; + final String theme; + + const STTModelChoice({ + Key? key, + required this.onModelChanged, + required this.theme + }) : super(key: key); + + @override + State createState() => _STTModelChoiceState(); +} + +class _STTModelChoiceState extends State { + late STTModel _selectedModel; + late String _theme; + + @override + void initState() { + super.initState(); + final appState = context.read(); + _selectedModel = appState.sttFramework == "vosk" ? STTModel.vosk : STTModel.whisper; + _theme = widget.theme; + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + InkWell( + onTap: () => _onModelChanged(STTModel.vosk), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20.0), + bottomLeft: Radius.circular(20.0), + ), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 17.5, vertical: 5.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20.0), + bottomLeft: Radius.circular(20.0), + ), + color: _selectedModel == STTModel.vosk + ? Colors.green + : _theme == "dark" || _theme == "textured-dark" + ? Colors.black + : Colors.white, + border: Border.all( + color: Colors.transparent, + ), + ), + child: Row( + children: [ + Icon( + _selectedModel == STTModel.vosk + ? Icons.check + : Icons.transcribe_sharp, + color: _selectedModel == STTModel.vosk + ? Colors.white + : Colors.green, + ), + SizedBox(width: 8), + Text( + 'Vosk', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 17, + color: _selectedModel == STTModel.vosk + ? Colors.white + : Colors.green, + ), + ), + ], + ), + ), + ), + InkWell( + onTap: () => _onModelChanged(STTModel.whisper), + borderRadius: BorderRadius.only( + topRight: Radius.circular(20.0), + bottomRight: Radius.circular(20.0), + ), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 17.5, vertical: 5.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topRight: Radius.circular(20.0), + bottomRight: Radius.circular(20.0), + ), + color: _selectedModel == STTModel.whisper + ? Colors.green + : _theme == "dark" || _theme == "textured-dark" + ? Colors.black + : Colors.white, + border: Border.all( + color: Colors.transparent, + ), + ), + child: Row( + children: [ + Icon( + _selectedModel == STTModel.whisper + ? Icons.check + : Icons.transcribe_sharp, + color: _selectedModel == STTModel.whisper + ? Colors.white + : Colors.green, + ), + SizedBox(width: 8), + Text( + 'Whisper', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 17, + color: _selectedModel == STTModel.whisper + ? Colors.white + : Colors.green, + ), + ), + ], + ), + ), + ), + ], + ); + } + + void _onModelChanged(STTModel newModel) { + setState(() { + _selectedModel = newModel; + }); + + // Call the callback function to notify the engine change + widget.onModelChanged(newModel); + } +} \ No newline at end of file -- cgit 1.2.3-korg