From 6eb2da3770bedc39bb525219afcf10ea01fe26dc Mon Sep 17 00:00:00 2001 From: Sultan Khan Date: Tue, 4 Aug 2020 15:29:04 +0300 Subject: [PATCH] voice search --- lib/config/config.dart | 8 +- .../pharmacies_List_request_model.dart | 11 +- .../pharmacies_items_request_model.dart | 7 +- lib/providers/medicine_provider.dart | 45 +++--- .../medicine/medicine_search_screen.dart | 148 ++++++++++++++++-- lib/widgets/auth/login_form.dart | 2 +- .../medicine/medicine_item_widget.dart | 4 +- lib/widgets/shared/app_text_form_field.dart | 116 +++++++------- speech_to_text/android/build.gradle | 2 +- .../example/android/app/build.gradle | 2 +- .../lib/speech_to_text_provider.dart | 33 ++-- 11 files changed, 250 insertions(+), 128 deletions(-) diff --git a/lib/config/config.dart b/lib/config/config.dart index 7b75da2a..9409d112 100644 --- a/lib/config/config.dart +++ b/lib/config/config.dart @@ -2,9 +2,9 @@ const MAX_SMALL_SCREEN = 660; const ONLY_NUMBERS = "[0-9]"; const ONLY_LETTERS = "[a-zA-Z &'\"]"; const ONLY_DATE = "[0-9/]"; -//const BASE_URL = 'https://hmgwebservices.com/Services/'; -const BASE_URL = 'https://uat.hmgwebservices.com/'; -const PHARMACY_ITEMS_URL = "Services/Lists.svc/REST/GetPharmcyItems_Region"; +const BASE_URL = 'https://hmgwebservices.com/Services/'; +//const BASE_URL = 'https://uat.hmgwebservices.com/'; +const PHARMACY_ITEMS_URL = "Services/Lists.svc/REST/GetPharmcyItems_Region_enh"; const PHARMACY_LIST_URL = "Services/Patients.svc/REST/GetPharmcyList"; const PATIENT_PROGRESS_NOTE_URL = "Services/DoctorApplication.svc/REST/GetProgressNoteForInPatient"; @@ -126,7 +126,7 @@ const TRANSACTION_NO = 0; const LANGUAGE_ID = 2; const STAMP = '2020-04-27T12:17:17.721Z'; const IP_ADDRESS = '11.11.11.11'; -const VERSION_ID = 1.2; +const VERSION_ID = 5.3; const CHANNEL = 9; const SESSION_ID = 'BlUSkYymTt'; const IS_LOGIN_FOR_DOCTOR_APP = true; diff --git a/lib/models/pharmacies/pharmacies_List_request_model.dart b/lib/models/pharmacies/pharmacies_List_request_model.dart index eea446dd..4d55651b 100644 --- a/lib/models/pharmacies/pharmacies_List_request_model.dart +++ b/lib/models/pharmacies/pharmacies_List_request_model.dart @@ -1,4 +1,3 @@ - /* *@author: Ibrahim Albitar *@Date:27/4/2020 @@ -8,8 +7,6 @@ */ class PharmaciesListRequestModel { - - int itemID; int languageID; String stamp; @@ -23,13 +20,13 @@ class PharmaciesListRequestModel { int channel; PharmaciesListRequestModel( - {this.itemID , + {this.itemID, this.patientTypeID = 1, this.languageID = 2, this.stamp = '2020-04-23T21:01:21.492Z', this.ipAdress = '11.11.11.11', - this.versionID = 1.2, - this.tokenID , + this.versionID = 5.3, + this.tokenID, this.sessionID = 'e29zoooEJ4', this.isLoginForDoctorApp = true, this.channel = 9, @@ -64,4 +61,4 @@ class PharmaciesListRequestModel { data['Channel'] = this.channel; return data; } -} \ No newline at end of file +} diff --git a/lib/models/pharmacies/pharmacies_items_request_model.dart b/lib/models/pharmacies/pharmacies_items_request_model.dart index 96df6da8..9602a803 100644 --- a/lib/models/pharmacies/pharmacies_items_request_model.dart +++ b/lib/models/pharmacies/pharmacies_items_request_model.dart @@ -6,12 +6,11 @@ *@desc: */ - class PharmaciesItemsRequestModel { String pHRItemName; int pageIndex = 0; int pageSize = 20; - double versionID = 5.2; + double versionID = 5.3; int channel = 3; int languageID = 2; String iPAdress = "10.20.10.20"; @@ -25,7 +24,7 @@ class PharmaciesItemsRequestModel { {this.pHRItemName, this.pageIndex = 0, this.pageSize = 20, - this.versionID = 5.2, + this.versionID = 5.3, this.channel = 3, this.languageID = 2, this.iPAdress = "10.20.10.20", @@ -66,4 +65,4 @@ class PharmaciesItemsRequestModel { data['DeviceTypeID'] = this.deviceTypeID; return data; } -} \ No newline at end of file +} diff --git a/lib/providers/medicine_provider.dart b/lib/providers/medicine_provider.dart index 873d28a7..8a0bef74 100644 --- a/lib/providers/medicine_provider.dart +++ b/lib/providers/medicine_provider.dart @@ -15,16 +15,15 @@ class MedicineProvider with ChangeNotifier { String errorMsg = ''; PharmaciesItemsRequestModel _itemsRequestModel = - PharmaciesItemsRequestModel(); + PharmaciesItemsRequestModel(); PharmaciesListRequestModel _listRequestModel = PharmaciesListRequestModel(); - - clearPharmacyItemsList(){ + clearPharmacyItemsList() { pharmacyItemsList.clear(); notifyListeners(); } - getMedicineItem(String itemName) async { + getMedicineItem(String itemName) async { _itemsRequestModel.pHRItemName = itemName; resetDefaultValues(); pharmacyItemsList.clear(); @@ -32,37 +31,37 @@ class MedicineProvider with ChangeNotifier { try { await BaseAppClient.post(PHARMACY_ITEMS_URL, onSuccess: (dynamic response, int statusCode) { - pharmacyItemsList = response['ListPharmcy_Region']; - hasError = false; - isFinished = true; - errorMsg = "Done"; - }, onFailure: (String error, int statusCode) { - isFinished = true; - hasError = true; - errorMsg = error; - }, body: _itemsRequestModel.toJson()); + pharmacyItemsList = response['ListPharmcy_Region_enh']; + hasError = false; + isFinished = true; + errorMsg = "Done"; + }, onFailure: (String error, int statusCode) { + isFinished = true; + hasError = true; + errorMsg = error; + }, body: _itemsRequestModel.toJson()); notifyListeners(); } catch (error) { throw error; } } - getPharmaciesList(int itemId) async { + getPharmaciesList(int itemId) async { resetDefaultValues(); try { _listRequestModel.itemID = itemId; isFinished = true; await BaseAppClient.post(PHARMACY_LIST_URL, onSuccess: (dynamic response, int statusCode) { - pharmaciesList = response['PharmList']; - hasError = false; - isFinished = true; - errorMsg = "Done"; - }, onFailure: (String error, int statusCode) { - isFinished = true; - hasError = true; - errorMsg = error; - }, body: _listRequestModel.toJson()); + pharmaciesList = response['PharmList']; + hasError = false; + isFinished = true; + errorMsg = "Done"; + }, onFailure: (String error, int statusCode) { + isFinished = true; + hasError = true; + errorMsg = error; + }, body: _listRequestModel.toJson()); notifyListeners(); } catch (error) { throw error; diff --git a/lib/screens/medicine/medicine_search_screen.dart b/lib/screens/medicine/medicine_search_screen.dart index 67b4a651..48a53af3 100644 --- a/lib/screens/medicine/medicine_search_screen.dart +++ b/lib/screens/medicine/medicine_search_screen.dart @@ -13,8 +13,14 @@ import 'package:doctor_app_flutter/widgets/shared/app_texts_widget.dart'; import 'package:doctor_app_flutter/widgets/shared/dr_app_circular_progress_Indeicator.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; + import '../../util/extenstions.dart'; import 'package:doctor_app_flutter/util/translations_delegate_base.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:speech_to_text/speech_recognition_error.dart'; +import 'package:speech_to_text/speech_recognition_result.dart'; +import 'package:speech_to_text/speech_to_text.dart'; +import 'dart:math'; DrAppSharedPreferances sharedPref = DrAppSharedPreferances(); @@ -30,20 +36,55 @@ class _MedicineSearchState extends State { var data; final myController = TextEditingController(); Helpers helpers = new Helpers(); - + bool _hasSpeech = false; MedicineProvider _medicineProvider; - + String _currentLocaleId = ""; bool _isInit = true; - + final SpeechToText speech = SpeechToText(); + String lastStatus = ''; + // String lastWords; + List _localeNames = []; + String lastError; + double level = 0.0; + double minSoundLevel = 50000; + double maxSoundLevel = -50000; + String reconizedWord; @override void didChangeDependencies() { super.didChangeDependencies(); if (_isInit) { _medicineProvider = Provider.of(context); + requestPermissions(); + initSpeechState(); } _isInit = false; } + void requestPermissions() async { + Map statuses = await [ + Permission.microphone, + ].request(); + } + + Future initSpeechState() async { + bool hasSpeech = await speech.initialize( + onError: errorListener, onStatus: statusListener); + // if (hasSpeech) { + // _localeNames = await speech.locales(); + + // var systemLocale = await speech.systemLocale(); + _currentLocaleId = TranslationBase.of(context).locale.languageCode == 'en' + ? 'en-GB' + : 'ar-SA'; // systemLocale.localeId; + // } + + if (!mounted) return; + + setState(() { + _hasSpeech = hasSpeech; + }); + } + @override Widget build(BuildContext context) { return AppScaffold( @@ -60,10 +101,22 @@ class _MedicineSearchState extends State { hintText: TranslationBase.of(context).searchMedicineNameHere, controller: myController, onSaved: (value) {}, - onFieldSubmitted: (value){ - searchMedicine(context); + onFieldSubmitted: (value) { + searchMedicine(context); }, textInputAction: TextInputAction.search, + prefix: IconButton( + icon: Icon(Icons.mic), + color: + lastStatus == 'listening' ? Colors.red : Colors.grey, + onPressed: () { + myController.text = ''; + setState(() { + lastStatus = 'listening'; + }); + + startVoiceSearch(); + }), inputFormatter: ONLY_LETTERS), ), Container( @@ -92,7 +145,10 @@ class _MedicineSearchState extends State { children: [ AppText( TranslationBase.of(context).youCanFind + - (_medicineProvider.pharmacyItemsList == null ? "0" : _medicineProvider.pharmacyItemsList.length.toString()) + + (_medicineProvider.pharmacyItemsList == null + ? "0" + : _medicineProvider.pharmacyItemsList.length + .toString()) + TranslationBase.of(context).itemsInSearch, fontWeight: FontWeight.bold, margin: 5, @@ -116,12 +172,19 @@ class _MedicineSearchState extends State { : ListView.builder( scrollDirection: Axis.vertical, shrinkWrap: true, - itemCount: _medicineProvider.pharmacyItemsList == null ? 0 : _medicineProvider.pharmacyItemsList.length, + itemCount: _medicineProvider.pharmacyItemsList == + null + ? 0 + : _medicineProvider.pharmacyItemsList.length, itemBuilder: (BuildContext context, int index) { return InkWell( child: MedicineItemWidget( - label: _medicineProvider.pharmacyItemsList[index]["ItemDescription"], - url: _medicineProvider.pharmacyItemsList[index]["ProductImageBase64"], + label: + _medicineProvider.pharmacyItemsList[index] + ["ItemDescription"], + url: + _medicineProvider.pharmacyItemsList[index] + ["ImageThumbUrl"], ), onTap: () { Navigator.push( @@ -129,9 +192,12 @@ class _MedicineSearchState extends State { MaterialPageRoute( builder: (context) => PharmaciesListScreen( - itemID: _medicineProvider.pharmacyItemsList[index]["ItemID"], - url: _medicineProvider.pharmacyItemsList[index] - ["ProductImageBase64"]), + itemID: _medicineProvider + .pharmacyItemsList[index] + ["ItemID"], + url: _medicineProvider + .pharmacyItemsList[index] + ["ProductImageBase64"]), ), ); }, @@ -153,4 +219,62 @@ class _MedicineSearchState extends State { } _medicineProvider.getMedicineItem(myController.text); } + + startVoiceSearch() { + // lastWords = ""; + lastError = ""; + speech.listen( + onResult: resultListener, + listenFor: Duration(seconds: 10), + localeId: _currentLocaleId, + onSoundLevelChange: soundLevelListener, + cancelOnError: true, + partialResults: true, + onDevice: true, + listenMode: ListenMode.confirmation); + setState(() {}); + } + + void resultListener(SpeechRecognitionResult result) { + setState(() { + // lastWords = "${result.recognizedWords} - ${result.finalResult}"; + reconizedWord = result.recognizedWords; + lastStatus = ''; + myController.text = reconizedWord; + Future.delayed(const Duration(seconds: 2), () { + searchMedicine(context); + }); + }); + } + + void errorListener(SpeechRecognitionError error) { + // print("Received error status: $error, listening: ${speech.isListening}"); + setState(() { + lastError = "${error.errorMsg} - ${error.permanent}"; + }); + } + + void statusListener(String status) { + // print( + // "Received listener status: $status, listening: ${speech.isListening}"); + setState(() { + lastStatus = status; + }); + } + + // _switchLang(selectedVal) { + // setState(() { + // _currentLocaleId = selectedVal; + // }); + // print(selectedVal); + // } + + void soundLevelListener(double level) { + minSoundLevel = min(minSoundLevel, level); + maxSoundLevel = max(maxSoundLevel, level); + // print("sound level $level: $minSoundLevel - $maxSoundLevel "); + setState(() { + this.level = level; + }); + } } diff --git a/lib/widgets/auth/login_form.dart b/lib/widgets/auth/login_form.dart index a4b60be7..b87cc800 100644 --- a/lib/widgets/auth/login_form.dart +++ b/lib/widgets/auth/login_form.dart @@ -273,7 +273,7 @@ class _LoginFormState extends State { "ProjectName": "", "DoctorImageURL": "UNKNOWN", "LogInTokenID": preRes['LogInTokenID'], - "VersionID": 1.2 + "VersionID": 5.3 }; authProv.insertDeviceImei(imeiInfo).then((res) { if (res['MessageStatus'] == 1) { diff --git a/lib/widgets/medicine/medicine_item_widget.dart b/lib/widgets/medicine/medicine_item_widget.dart index ad15ddab..d3a3830c 100644 --- a/lib/widgets/medicine/medicine_item_widget.dart +++ b/lib/widgets/medicine/medicine_item_widget.dart @@ -47,8 +47,8 @@ class _MedicineItemWidgetState extends State { width: 39, child: ClipRRect( borderRadius: BorderRadius.all(Radius.circular(7)), - child: Image.memory( - dataFromBase64String(widget.url), + child: Image.network( + widget.url, height: SizeConfig.imageSizeMultiplier * 15, width: SizeConfig.imageSizeMultiplier * 15, fit: BoxFit.cover, diff --git a/lib/widgets/shared/app_text_form_field.dart b/lib/widgets/shared/app_text_form_field.dart index b0b9a8ec..efe8d389 100644 --- a/lib/widgets/shared/app_text_form_field.dart +++ b/lib/widgets/shared/app_text_form_field.dart @@ -8,61 +8,63 @@ import 'package:flutter/services.dart'; // DESCRIPTION : Custom Text Form Field for app. class AppTextFormField extends FormField { - - AppTextFormField({ - FormFieldSetter onSaved, - String inputFormatter, - FormFieldValidator validator, - ValueChanged onChanged, - GestureTapCallback onTap, - TextEditingController controller, - bool autovalidate = true, - TextInputType textInputType, - String hintText, - FocusNode focusNode, - TextInputAction textInputAction, - ValueChanged onFieldSubmitted, - }) : super( - onSaved: onSaved, - validator: validator, - autovalidate: autovalidate, - builder: (FormFieldState state) { - return Column( - children: [ - TextFormField( - focusNode: focusNode, - keyboardType: textInputType, - inputFormatters: [WhitelistingTextInputFormatter(RegExp(inputFormatter)),], - onChanged: onChanged?? (value){ - state.didChange(value); - }, - textInputAction: textInputAction, - onFieldSubmitted: onFieldSubmitted, - decoration: InputDecoration( - hintText: hintText, - hintStyle: TextStyle(fontSize: SizeConfig.textMultiplier * 2), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - borderSide: BorderSide(color: Color(0xff707070)), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ) - //BorderRadius.all(Radius.circular(20)); - ), - onTap: onTap, - controller: controller, - ), - state.hasError? - Text( - state.errorText, - style: TextStyle( - color: Colors.red - ), - ) : - Container() - ], - ); - } - ); + AppTextFormField( + {FormFieldSetter onSaved, + String inputFormatter, + FormFieldValidator validator, + ValueChanged onChanged, + GestureTapCallback onTap, + TextEditingController controller, + bool autovalidate = true, + TextInputType textInputType, + String hintText, + FocusNode focusNode, + TextInputAction textInputAction, + ValueChanged onFieldSubmitted, + IconButton prefix}) + : super( + onSaved: onSaved, + validator: validator, + autovalidate: autovalidate, + builder: (FormFieldState state) { + return Column( + children: [ + TextFormField( + focusNode: focusNode, + keyboardType: textInputType, + inputFormatters: [ + WhitelistingTextInputFormatter(RegExp(inputFormatter)), + ], + onChanged: onChanged ?? + (value) { + state.didChange(value); + }, + textInputAction: textInputAction, + onFieldSubmitted: onFieldSubmitted, + decoration: InputDecoration( + hintText: hintText, + suffixIcon: prefix, + hintStyle: + TextStyle(fontSize: SizeConfig.textMultiplier * 2), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(10)), + borderSide: BorderSide(color: Color(0xff707070)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(10)), + ) + //BorderRadius.all(Radius.circular(20)); + ), + onTap: onTap, + controller: controller, + ), + state.hasError + ? Text( + state.errorText, + style: TextStyle(color: Colors.red), + ) + : Container() + ], + ); + }); } diff --git a/speech_to_text/android/build.gradle b/speech_to_text/android/build.gradle index 6b23b300..cc06ea57 100644 --- a/speech_to_text/android/build.gradle +++ b/speech_to_text/android/build.gradle @@ -31,7 +31,7 @@ android { main.java.srcDirs += 'src/main/kotlin' } defaultConfig { - minSdkVersion 21 + minSdkVersion 18 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { diff --git a/speech_to_text/example/android/app/build.gradle b/speech_to_text/example/android/app/build.gradle index 8b616f29..104069d3 100644 --- a/speech_to_text/example/android/app/build.gradle +++ b/speech_to_text/example/android/app/build.gradle @@ -39,7 +39,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.csdcorp.speech_to_text_example" - minSdkVersion 21 + minSdkVersion 18 targetSdkVersion 28 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/speech_to_text/lib/speech_to_text_provider.dart b/speech_to_text/lib/speech_to_text_provider.dart index 2093db44..91adf3b4 100644 --- a/speech_to_text/lib/speech_to_text_provider.dart +++ b/speech_to_text/lib/speech_to_text_provider.dart @@ -120,12 +120,13 @@ class SpeechToTextProvider extends ChangeNotifier { _lastResult = null; if (soundLevel) { _speechToText.listen( - partialResults: partialResults, - listenFor: listenFor, - pauseFor: pauseFor, - cancelOnError: true, - onResult: _onListenResult, - onSoundLevelChange: _onSoundLevelChange); + partialResults: partialResults, + listenFor: listenFor, + pauseFor: pauseFor, + cancelOnError: true, + onResult: _onListenResult, + // onSoundLevelChange: _onSoundLevelChange); + ); } else { _speechToText.listen( partialResults: partialResults, @@ -187,14 +188,14 @@ class SpeechToTextProvider extends ChangeNotifier { notifyListeners(); } - void _onSoundLevelChange(double level) { - _lastLevel = level; - _recognitionController.add(SpeechRecognitionEvent( - SpeechRecognitionEventType.soundLevelChangeEvent, - null, - null, - null, - level)); - notifyListeners(); - } + // void _onSoundLevelChange(double level) { + // _lastLevel = level; + // _recognitionController.add(SpeechRecognitionEvent( + // SpeechRecognitionEventType.soundLevelChangeEvent, + // null, + // null, + // null, + // level)); + // notifyListeners(); + // } }