diff --git a/lib/global.dart b/lib/global.dart index a1e8e1c..6a4ce83 100644 --- a/lib/global.dart +++ b/lib/global.dart @@ -271,7 +271,3 @@ const List orangeGradient = [ Color(0xFFFF9945), Color(0xFFFc6076), ]; - -trainModel( - step, -) {} diff --git a/lib/main.dart b/lib/main.dart index 2f1c9a3..90b2dfa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,4 @@ -import 'package:BHIM/screens/loginVerificationScreen.dart'; +import 'package:BHIM/screens/LoginScreens/loginVerificationScreen.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; diff --git a/lib/models/intentApiModel.dart b/lib/models/intentApiModel.dart index 49ef12c..f70429e 100644 --- a/lib/models/intentApiModel.dart +++ b/lib/models/intentApiModel.dart @@ -9,88 +9,29 @@ IntentApi intentApiFromJson(String str) => IntentApi.fromJson(json.decode(str)); String intentApiToJson(IntentApi data) => json.encode(data.toJson()); class IntentApi { - Intent intent; - List entities; - List intentRanking; - String text; + String amount; + double confidence; + String intent; + String name; IntentApi({ + this.amount, + this.confidence, this.intent, - this.entities, - this.intentRanking, - this.text, + this.name, }); factory IntentApi.fromJson(Map json) => IntentApi( - intent: Intent.fromJson(json["intent"]), - entities: - List.from(json["entities"].map((x) => Entity.fromJson(x))), - intentRanking: List.from( - json["intent_ranking"].map((x) => Intent.fromJson(x))), - text: json["text"], - ); - - Map toJson() => { - "intent": intent.toJson(), - "entities": List.from(entities.map((x) => x.toJson())), - "intent_ranking": - List.from(intentRanking.map((x) => x.toJson())), - "text": text, - }; -} - -class Entity { - int start; - int end; - String value; - String entity; - double confidence; - String extractor; - - Entity({ - this.start, - this.end, - this.value, - this.entity, - this.confidence, - this.extractor, - }); - - factory Entity.fromJson(Map json) => Entity( - start: json["start"], - end: json["end"], - value: json["value"], - entity: json["entity"], + amount: json["amount"], confidence: json["confidence"].toDouble(), - extractor: json["extractor"], - ); - - Map toJson() => { - "start": start, - "end": end, - "value": value, - "entity": entity, - "confidence": confidence, - "extractor": extractor, - }; -} - -class Intent { - String name; - double confidence; - - Intent({ - this.name, - this.confidence, - }); - - factory Intent.fromJson(Map json) => Intent( + intent: json["intent"], name: json["name"], - confidence: json["confidence"].toDouble(), ); Map toJson() => { - "name": name, + "amount": amount, "confidence": confidence, + "intent": intent, + "name": name, }; } diff --git a/lib/screens/LoginScreens/loginUi.dart b/lib/screens/LoginScreens/loginUi.dart index dd8f2f2..8238693 100644 --- a/lib/screens/LoginScreens/loginUi.dart +++ b/lib/screens/LoginScreens/loginUi.dart @@ -1,177 +1,230 @@ +import 'dart:convert'; +import 'dart:io' as io; +import 'dart:math'; + import 'package:BHIM/screens/DrawerScreens/navigationHomeScreen.dart'; +import 'package:async/async.dart'; +import 'package:audio_recorder/audio_recorder.dart'; +import 'package:file/file.dart'; +import 'package:file/local.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:recase/recase.dart'; +import 'package:http/http.dart' as http; +import 'package:path/path.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:random_string/random_string.dart'; import '../../global.dart'; class Login extends StatefulWidget { + final LocalFileSystem localFileSystem; + + Login({localFileSystem}) + : this.localFileSystem = localFileSystem ?? LocalFileSystem(); + @override _LoginState createState() => _LoginState(); } class _LoginState extends State { - bool allowLogin = true; - MaterialColor myColor = Colors.green; + List status = ['pending']; + + Recording _recording = Recording(); + bool _isRecording = false; + Random random = Random(); + TextEditingController _controller = TextEditingController(); + + _startRecording(i) async { + try { + setState(() { + status[i] = 'running'; + }); + _controller.text = + 'shivamgoyal@upi' + '__test' + '__' + randomNumeric(10).toString(); + if (await AudioRecorder.hasPermissions) { + if (_controller.text != null && _controller.text != "") { + String path = _controller.text; + if (!_controller.text.contains('/')) { + io.Directory appDocDirectory = + await getApplicationDocumentsDirectory(); + path = appDocDirectory.path + '/' + _controller.text; + } + print("Start recording: $path"); + await AudioRecorder.start( + path: path, audioOutputFormat: AudioOutputFormat.AAC); + } else { + await AudioRecorder.start(); + } + bool isRecording = await AudioRecorder.isRecording; + setState(() { + _recording = Recording(path: ""); + _isRecording = isRecording; + }); + } else {} + } catch (e) { + print(e); + } + } + + _stopRecording(i) async { + var recording = await AudioRecorder.stop(); + print("Stop recording: ${recording.path}"); + bool isRecording = await AudioRecorder.isRecording; + File file = widget.localFileSystem.file(recording.path); + print(" File length: ${await file.length()}"); + _uploadRecording(file, i); + setState(() { + _recording = recording; + _isRecording = isRecording; + }); + _controller.text = recording.path; + } + + _uploadRecording(File audioFile, i) async { + var stream = http.ByteStream(DelegatingStream.typed(audioFile.openRead())); + var length = await audioFile.length(); + + var uri = Uri.parse("https://radiant32.pythonanywhere.com/v1.0/audio"); + + var request = http.MultipartRequest("POST", uri); + + var multipartFile = http.MultipartFile('file', stream, length, + filename: basename(audioFile.path)); + + request.files.add(multipartFile); + + var response = await request.send(); + print(response.statusCode); + + response.stream.transform(utf8.decoder).listen((value) { + print(value); + if (value == 'OK') + setState(() { + status[i] = 'success'; + }); + else + setState(() { + status[i] = 'failure'; + }); + }); + } @override Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - GestureDetector( - onTap: () { - setState(() { - if (myColor == Colors.green) { - allowLogin = false; - myColor = Colors.red; - } else { - allowLogin = true; - myColor = Colors.green; - } - }); - }, - child: Text("Hi " + ReCase(myName).titleCase.split(' ')[0] + ' !', - style: TextStyle( - color: Color(0xFF999A9A), - fontSize: 32, - fontWeight: FontWeight.w700)), - ), - Text( - '-', - style: TextStyle( - color: myColor, fontSize: 22, fontWeight: FontWeight.w500), - textAlign: TextAlign.center, - ), - SizedBox(height: 5.0), - Text( - 'Hold the button and speak the following sentence to confirm your identity.', - style: TextStyle( - color: Color(0xFF999A9A), - fontSize: 22, - fontWeight: FontWeight.w500), - textAlign: TextAlign.center, - ), - SizedBox(height: 20.0), - Text( - '"Hey BHIM"', - style: TextStyle( - color: Color(0xFF999A9A), - fontSize: 25, - fontWeight: FontWeight.w500, - fontStyle: FontStyle.italic), - textAlign: TextAlign.center, - ), - SizedBox(height: 20.0), - Center( - child: GestureDetector( - child: Container( - alignment: Alignment.center, - width: 85, - decoration: ShapeDecoration( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50.0)), - gradient: LinearGradient( - colors: blueGradient, - begin: Alignment.topLeft, - end: Alignment.bottomRight), - ), - child: Icon(Icons.record_voice_over, - color: Colors.white, size: 50.0), - padding: EdgeInsets.only(top: 16, bottom: 16), + return Center( + child: Container( + width: 250, + height: 300, + alignment: Alignment.center, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Voice Login', + textAlign: TextAlign.center, + style: TextStyle(fontWeight: FontWeight.w700, fontSize: 25.0), ), - onLongPressUp: () async { - showDialog( - context: context, - builder: (context) => AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(25), - ), - ), - content: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Image.asset( - 'assets/images/loading.gif', - height: 50, - ), - Text( - ' Verifying Voice !', - textAlign: TextAlign.center, - style: TextStyle( - fontWeight: FontWeight.w500, fontSize: 25.0), - ), - ], + SizedBox(height: 20.0), + Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + GestureDetector( + onTap: () async { + _startRecording(0); + await Future.delayed(Duration(seconds: 2)); + _stopRecording(0); + }, + child: status[0] == 'pending' + ? Icon( + Icons.mic, + color: Colors.blue, + size: 50, + ) + : status[0] == 'running' + ? Container( + width: 50, + height: 50, + child: CircularProgressIndicator(), + ) + : status[0] == 'success' + ? Icon( + Icons.check_circle, + color: Colors.green, + size: 50, + ) + : Icon( + Icons.cancel, + color: Colors.red, + size: 50, + ), + ), + ], + ), + SizedBox(height: 30.0), + Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + Text( + 'Tap mic & Speak', + textAlign: TextAlign.center, + style: TextStyle(fontWeight: FontWeight.w400, fontSize: 18.0), + ), + ], + ), + SizedBox(height: 10.0), + Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '"Hey BHIM"', + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 24.0, + fontStyle: FontStyle.italic), + ), + ], + ), + SizedBox(height: 20.0), + Center( + child: GestureDetector( + child: Container( + alignment: Alignment.center, + width: MediaQuery.of(context).size.width / 2, + decoration: ShapeDecoration( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30.0)), + gradient: LinearGradient( + colors: blueGradient, + begin: Alignment.topLeft, + end: Alignment.bottomRight), ), + child: Text("Verify and Proceed", + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w500)), + padding: EdgeInsets.only(top: 16, bottom: 16), ), - ); - await Future.delayed(Duration(seconds: 3)); - Navigator.of(context).pop(); - allowLogin - ? showDialog( - context: context, - builder: (context) => AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(25), - ), - ), - content: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Image.asset( - 'assets/images/payment.gif', - height: 50, - ), - Text( - 'Verified !', - textAlign: TextAlign.center, - style: TextStyle( - fontWeight: FontWeight.w500, fontSize: 25.0), - ), - ], - ), - ), - ) - : showDialog( - context: context, - builder: (context) => AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(25), - ), - ), - content: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Login Failed !', - textAlign: TextAlign.center, - style: TextStyle( - fontWeight: FontWeight.w500, fontSize: 25.0), - ), - ], - ), - ), - ); - await Future.delayed(Duration(seconds: 2)); - Navigator.of(context).pop(); - allowLogin - ? Navigator.of(context).pushReplacement( - MaterialPageRoute(builder: (BuildContext context) { + onTap: () { + if (status[0] == 'success') { + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (BuildContext context) { return NavigationHomeScreen(); - })) - : null; - }, - ), + })); + } + }, + ), + ) + ], ), - ], + ), ); } } diff --git a/lib/screens/loginVerificationScreen.dart b/lib/screens/LoginScreens/loginVerificationScreen.dart similarity index 98% rename from lib/screens/loginVerificationScreen.dart rename to lib/screens/LoginScreens/loginVerificationScreen.dart index ebdfe38..61cf776 100644 --- a/lib/screens/loginVerificationScreen.dart +++ b/lib/screens/LoginScreens/loginVerificationScreen.dart @@ -3,7 +3,7 @@ import 'package:BHIM/screens/RegistrationScreens/registrationScreen.dart'; import 'package:BHIM/screens/background.dart'; import 'package:flutter/material.dart'; -import '../global.dart'; +import '../../global.dart'; class LoginVerificationScreen extends StatefulWidget { @override diff --git a/lib/screens/RegistrationScreens/registrationUi.dart b/lib/screens/RegistrationScreens/registrationUi.dart index caddc0e..980e902 100644 --- a/lib/screens/RegistrationScreens/registrationUi.dart +++ b/lib/screens/RegistrationScreens/registrationUi.dart @@ -1,11 +1,27 @@ +import 'dart:convert'; +import 'dart:io' as io; +import 'dart:math'; + import 'package:BHIM/components/inputField.dart'; import 'package:BHIM/screens/RegistrationScreens/voiceRegistrationDialog.dart'; -import 'package:BHIM/screens/loginVerificationScreen.dart'; +import 'package:async/async.dart'; +import 'package:audio_recorder/audio_recorder.dart'; +import 'package:file/file.dart'; +import 'package:file/local.dart'; import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:path/path.dart'; +import 'package:path_provider/path_provider.dart'; import '../../global.dart'; +import '../LoginScreens/loginVerificationScreen.dart'; class Register extends StatefulWidget { + final LocalFileSystem localFileSystem; + + Register({localFileSystem}) + : this.localFileSystem = localFileSystem ?? LocalFileSystem(); + @override _RegisterState createState() => _RegisterState(); } @@ -13,10 +29,77 @@ class Register extends StatefulWidget { class _RegisterState extends State { final nameController = TextEditingController(); final mobileNumberController = TextEditingController(); - final upiIDcontroller = TextEditingController(); FocusNode nameFocusNode; FocusNode mobileNumberFocusNode; - FocusNode upiIDFocusNode; + + var _index = 0; + + Recording _recording = new Recording(); + bool _isRecording = false; + Random random = new Random(); + TextEditingController _controller = new TextEditingController(); + + _startRecording() async { + try { + if (await AudioRecorder.hasPermissions) { + if (_controller.text != null && _controller.text != "") { + String path = _controller.text; + if (!_controller.text.contains('/')) { + io.Directory appDocDirectory = + await getApplicationDocumentsDirectory(); + path = appDocDirectory.path + '/' + _controller.text; + } + print("Start recording: $path"); + await AudioRecorder.start( + path: path, audioOutputFormat: AudioOutputFormat.AAC); + } else { + await AudioRecorder.start(); + } + bool isRecording = await AudioRecorder.isRecording; + setState(() { + _recording = new Recording(duration: new Duration(), path: ""); + _isRecording = isRecording; + }); + } else {} + } catch (e) { + print(e); + } + } + + _stopRecording() async { + var recording = await AudioRecorder.stop(); + print("Stop recording: ${recording.path}"); + bool isRecording = await AudioRecorder.isRecording; + File file = widget.localFileSystem.file(recording.path); + print(" File length: ${await file.length()}"); + _uploadRecording(file); + setState(() { + _recording = recording; + _isRecording = isRecording; + }); + _controller.text = recording.path; + } + + _uploadRecording(File imageFile) async { + var stream = http.ByteStream(DelegatingStream.typed(imageFile.openRead())); + var length = await imageFile.length(); + + var uri = Uri.parse("http://192.168.137.1:5000/v1.0/audio"); + + var request = http.MultipartRequest("POST", uri); + + var multipartFile = http.MultipartFile('file', stream, length, + filename: basename(imageFile.path)); + + request.files.add(multipartFile); + + var response = await request.send(); + print(response.statusCode); + + response.stream.transform(utf8.decoder).listen((value) { + print(value); + }); + } @override Widget build(BuildContext context) { @@ -24,8 +107,7 @@ class _RegisterState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: - EdgeInsets.only(top: MediaQuery.of(context).size.height / 3.5), + padding: EdgeInsets.only(top: MediaQuery.of(context).size.height / 3), ), InputField( hintText: 'Full Name', @@ -34,19 +116,88 @@ class _RegisterState extends State { controller: nameController, focusNode: nameFocusNode, ), - InputField( - hintText: 'Mobile Number', - keyboardType: TextInputType.number, - textCapitalization: TextCapitalization.none, - controller: mobileNumberController, - focusNode: mobileNumberFocusNode, + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + RaisedButton( + shape: RoundedRectangleBorder( + side: BorderSide(color: Colors.blue), + borderRadius: BorderRadius.circular(5), + ), + elevation: 8.0, + onPressed: () {}, + textColor: Colors.white, + color: Colors.white, + padding: EdgeInsets.symmetric(horizontal: 40.0, vertical: 12.0), + child: Column( + children: [ + Text( + "9012218994", + style: TextStyle(color: Colors.black, fontSize: 18.0), + ), + Text( + "SIM 1 | IDEA", + style: TextStyle(color: Colors.grey, fontSize: 14.0), + ) + ], + ), + ), + RaisedButton( + shape: RoundedRectangleBorder( + side: BorderSide(color: Colors.grey), + borderRadius: BorderRadius.circular(5), + ), + elevation: 8.0, + onPressed: () {}, + textColor: Colors.white, + color: Colors.white, + padding: EdgeInsets.symmetric(horizontal: 40.0, vertical: 15.0), + child: Column( + children: [ + Text( + "Not Available", + style: TextStyle(color: Colors.black, fontSize: 18.0), + ), + Text( + "SIM 2 | NA", + style: TextStyle(color: Colors.grey, fontSize: 14.0), + ) + ], + ), + ) + ], ), - InputField( - hintText: 'UPI ID', - keyboardType: TextInputType.emailAddress, - textCapitalization: TextCapitalization.none, - controller: upiIDcontroller, - focusNode: upiIDFocusNode, + SizedBox(height: 20.0), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + RaisedButton( + shape: RoundedRectangleBorder( + side: BorderSide(color: Colors.green), + borderRadius: BorderRadius.circular(5), + ), + elevation: 8.0, + onPressed: () {}, + textColor: Colors.white, + color: Colors.white, + padding: EdgeInsets.symmetric(horizontal: 70.0, vertical: 15.0), + child: Column( + children: [ + Text( + nameController.value.text + .toLowerCase() + .replaceAll(' ', '') + + "@upi", + style: TextStyle(color: Colors.black, fontSize: 18.0), + ), + Text( + "Auto-generated UPI ID", + style: TextStyle(color: Colors.grey, fontSize: 14.0), + ) + ], + ), + ), + ], ), SizedBox(height: 20.0), Center( @@ -62,7 +213,7 @@ class _RegisterState extends State { begin: Alignment.topLeft, end: Alignment.bottomRight), ), - child: Text("Enable Voice Payments", + child: Text("Set Up Voice Payments", style: TextStyle( color: Colors.white, fontSize: 18, @@ -101,14 +252,18 @@ class _RegisterState extends State { padding: EdgeInsets.only(top: 16, bottom: 16), ), onTap: () { - registerUser( + if (nameController.text != '') { + registerUser( nameController.value.text, - mobileNumberController.value.text, - upiIDcontroller.value.text); - Navigator.of(context).pushReplacement( - MaterialPageRoute(builder: (BuildContext context) { - return LoginVerificationScreen(); - })); + "9012218994", + nameController.value.text.toLowerCase().replaceAll(' ', '') + + "@upi", + ); + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (BuildContext context) { + return LoginVerificationScreen(); + })); + } }, ), ), diff --git a/lib/screens/RegistrationScreens/voiceRegistrationDialog.dart b/lib/screens/RegistrationScreens/voiceRegistrationDialog.dart index 2553b3c..93b2f9c 100644 --- a/lib/screens/RegistrationScreens/voiceRegistrationDialog.dart +++ b/lib/screens/RegistrationScreens/voiceRegistrationDialog.dart @@ -1,12 +1,25 @@ +import 'dart:convert'; +import 'dart:io' as io; +import 'dart:math'; + +import 'package:async/async.dart'; +import 'package:audio_recorder/audio_recorder.dart'; +import 'package:file/file.dart'; +import 'package:file/local.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:path/path.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:random_string/random_string.dart'; + +import '../../global.dart'; class VoiceRegistrationDialog extends StatefulWidget { - final String intent; - final String person; - final String amount; + final LocalFileSystem localFileSystem; - VoiceRegistrationDialog({this.intent, this.person, this.amount}); + VoiceRegistrationDialog({localFileSystem}) + : this.localFileSystem = localFileSystem ?? LocalFileSystem(); @override _VoiceRegistrationDialogState createState() => @@ -14,15 +27,96 @@ class VoiceRegistrationDialog extends StatefulWidget { } class _VoiceRegistrationDialogState extends State { - bool status1 = false; - bool status2 = false; - bool status3 = false; + List status = ['pending', 'pending', 'pending']; + + Recording _recording = Recording(); + bool _isRecording = false; + Random random = Random(); + TextEditingController _controller = TextEditingController(); + + _startRecording(i) async { + try { + setState(() { + status[i] = 'running'; + }); + _controller.text = 'shivamgoyal@upi' + + '__train-' + + i.toString() + + '__' + + randomNumeric(10).toString(); + if (await AudioRecorder.hasPermissions) { + if (_controller.text != null && _controller.text != "") { + String path = _controller.text; + if (!_controller.text.contains('/')) { + io.Directory appDocDirectory = + await getApplicationDocumentsDirectory(); + path = appDocDirectory.path + '/' + _controller.text; + } + print("Start recording: $path"); + await AudioRecorder.start( + path: path, audioOutputFormat: AudioOutputFormat.AAC); + } else { + await AudioRecorder.start(); + } + bool isRecording = await AudioRecorder.isRecording; + setState(() { + _recording = Recording(path: ""); + _isRecording = isRecording; + }); + } else {} + } catch (e) { + print(e); + } + } + + _stopRecording(i) async { + var recording = await AudioRecorder.stop(); + print("Stop recording: ${recording.path}"); + bool isRecording = await AudioRecorder.isRecording; + File file = widget.localFileSystem.file(recording.path); + print(" File length: ${await file.length()}"); + _uploadRecording(file, i); + setState(() { + _recording = recording; + _isRecording = isRecording; + }); + _controller.text = recording.path; + } + + _uploadRecording(File audioFile, i) async { + var stream = http.ByteStream(DelegatingStream.typed(audioFile.openRead())); + var length = await audioFile.length(); + + var uri = Uri.parse("https://radiant32.pythonanywhere.com/v1.0/audio"); + + var request = http.MultipartRequest("POST", uri); + + var multipartFile = http.MultipartFile('file', stream, length, + filename: basename(audioFile.path)); + + request.files.add(multipartFile); + + var response = await request.send(); + print(response.statusCode); + + response.stream.transform(utf8.decoder).listen((value) { + print(value); + if (value == 'OK') + setState(() { + status[i] = 'success'; + }); + else + setState(() { + status[i] = 'failure'; + }); + }); + } @override Widget build(BuildContext context) { return AlertDialog( title: Text( - 'Voice Registration', + 'Voice Payments', textAlign: TextAlign.center, style: TextStyle(fontWeight: FontWeight.w700, fontSize: 25.0), ), @@ -41,7 +135,7 @@ class _VoiceRegistrationDialogState extends State { mainAxisSize: MainAxisSize.max, children: [ Text( - 'Hold mic & Speak', + 'Tap mic & Speak', textAlign: TextAlign.center, style: TextStyle(fontWeight: FontWeight.w400, fontSize: 18.0), ), @@ -53,7 +147,7 @@ class _VoiceRegistrationDialogState extends State { mainAxisSize: MainAxisSize.max, children: [ Text( - '"Hello BHIM"', + '"Hey BHIM"', textAlign: TextAlign.center, style: TextStyle( fontWeight: FontWeight.w500, @@ -61,18 +155,25 @@ class _VoiceRegistrationDialogState extends State { fontStyle: FontStyle.italic), ), GestureDetector( - onLongPressUp: () { - setState(() { - status1 = true; - }); - }, child: IconButton( - icon: status1 - ? Icon(Icons.check_circle, color: Colors.green) - : Icon(Icons.mic_none), - onPressed: () {}, + icon: status[0] == 'pending' + ? Icon(Icons.mic, color: Colors.blue) + : status[0] == 'running' + ? Container( + width: 20, + height: 20, + child: CircularProgressIndicator(), + ) + : status[0] == 'success' + ? Icon(Icons.check_circle, color: Colors.green) + : Icon(Icons.cancel, color: Colors.red), + onPressed: () async { + _startRecording(0); + await Future.delayed(Duration(seconds: 2)); + _stopRecording(0); + }, ), - ) + ), ], ), SizedBox(height: 10.0), @@ -81,7 +182,7 @@ class _VoiceRegistrationDialogState extends State { mainAxisSize: MainAxisSize.max, children: [ Text( - '"Namaste BHIM"', + '"Hey BHIM"', textAlign: TextAlign.center, style: TextStyle( fontWeight: FontWeight.w500, @@ -89,16 +190,23 @@ class _VoiceRegistrationDialogState extends State { fontStyle: FontStyle.italic), ), GestureDetector( - onLongPressUp: () { - setState(() { - status2 = true; - }); - }, child: IconButton( - icon: status2 - ? Icon(Icons.check_circle, color: Colors.green) - : Icon(Icons.mic_none), - onPressed: () {}, + icon: status[1] == 'pending' + ? Icon(Icons.mic, color: Colors.blue) + : status[1] == 'running' + ? Container( + width: 20, + height: 20, + child: CircularProgressIndicator(), + ) + : status[1] == 'success' + ? Icon(Icons.check_circle, color: Colors.green) + : Icon(Icons.cancel, color: Colors.red), + onPressed: () async { + _startRecording(1); + await Future.delayed(Duration(seconds: 2)); + _stopRecording(1); + }, ), ) ], @@ -117,78 +225,53 @@ class _VoiceRegistrationDialogState extends State { fontStyle: FontStyle.italic), ), GestureDetector( - onLongPressUp: () async { - setState(() { - status3 = true; - }); - showDialog( - context: context, - builder: (context) => AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(25), - ), - ), - content: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Image.asset( - 'assets/images/loading.gif', - height: 50, - ), - Text( - ' Verifying Voice !', - textAlign: TextAlign.center, - style: TextStyle( - fontWeight: FontWeight.w500, fontSize: 25.0), - ), - ], - ), - ), - ); - await Future.delayed(Duration(seconds: 3)); - Navigator.of(context).pop(); - showDialog( - context: context, - builder: (context) => AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(25), - ), - ), - content: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Image.asset( - 'assets/images/payment.gif', - height: 50, - ), - Text( - 'Verified !', - textAlign: TextAlign.center, - style: TextStyle( - fontWeight: FontWeight.w500, fontSize: 25.0), - ), - ], - ), - ), - ); - await Future.delayed(Duration(seconds: 2)); - Navigator.of(context).pop(); - Navigator.of(context).pop(); - }, child: IconButton( - icon: status3 - ? Icon(Icons.check_circle, color: Colors.green) - : Icon(Icons.mic_none), - onPressed: () {}, + icon: status[2] == 'pending' + ? Icon(Icons.mic, color: Colors.blue) + : status[2] == 'running' + ? Container( + width: 20, + height: 20, + child: CircularProgressIndicator(), + ) + : status[2] == 'success' + ? Icon(Icons.check_circle, color: Colors.green) + : Icon(Icons.cancel, color: Colors.red), + onPressed: () async { + _startRecording(2); + await Future.delayed(Duration(seconds: 2)); + _stopRecording(2); + }, ), - ) + ), ], ), - SizedBox(height: 10.0), + SizedBox(height: 15.0), + Center( + child: GestureDetector( + child: Container( + alignment: Alignment.center, + width: MediaQuery.of(context).size.width / 3, + decoration: ShapeDecoration( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30.0)), + gradient: LinearGradient( + colors: blueGradient, + begin: Alignment.topLeft, + end: Alignment.bottomRight), + ), + child: Text("Done", + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w500)), + padding: EdgeInsets.only(top: 16, bottom: 16), + ), + onTap: () { + Navigator.of(context).pop(); + }, + ), + ) ], ), ); diff --git a/lib/screens/background.dart b/lib/screens/background.dart index 9c8ba32..d0203f0 100644 --- a/lib/screens/background.dart +++ b/lib/screens/background.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; class Background extends StatelessWidget { final isImage; + Background({Key key, this.title, this.isImage}) : super(key: key); final String title; diff --git a/lib/screens/voicePayDialog.dart b/lib/screens/voicePayDialog.dart index 3a7d941..8f2b6d2 100644 --- a/lib/screens/voicePayDialog.dart +++ b/lib/screens/voicePayDialog.dart @@ -2,12 +2,10 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; -import 'package:recase/recase.dart'; import 'package:speech_recognition/speech_recognition.dart'; import '../global.dart'; import '../models/intentApiModel.dart'; -import '../models/languageModel.dart'; import 'RegistrationScreens/registrationScreen.dart'; import 'languageScreen.dart'; import 'paymentDialog.dart'; @@ -20,6 +18,21 @@ import 'settingsScreen.dart'; import 'transactionHistoryScreen.dart'; import 'ussdServiceScreen.dart'; +const languages = const [ + const Language('English (India)', 'en'), + const Language('Hindi (हिंदी)', 'hi'), + const Language('Marathi (मराठी)', 'mr'), + const Language('Gujarati (ગુજરાતી)', 'gu'), + const Language('Kannada (ಕನ್ನಡ)', 'kn'), +]; + +class Language { + final String name; + final String code; + + const Language(this.name, this.code); +} + class VoicePay extends StatefulWidget { @override _VoicePayState createState() => _VoicePayState(); @@ -32,13 +45,13 @@ class _VoicePayState extends State { bool _speechRecognitionAvailable = false; bool _isListening = false; - LanguageModel selectedLang = languageData[0]; - String myIntent; var myIntentConfidence; String myAmountDetected = 'NULL'; String myPerson = 'NULL'; + Language selectedLang = languages[0]; + @override initState() { super.initState(); @@ -53,7 +66,6 @@ class _VoicePayState extends State { _speech.setRecognitionStartedHandler(onRecognitionStarted); _speech.setRecognitionResultHandler(onRecognitionResult); _speech.setRecognitionCompleteHandler(onRecognitionComplete); -// _speech.setErrorHandler(errorHandler); _speech .activate() .then((res) => setState(() => _speechRecognitionAvailable = res)); @@ -65,28 +77,21 @@ class _VoicePayState extends State { transcription); print( '----------------------------------------------------------------------------\n\n'); - String url = 'http://3.83.153.83:5005/model/parse'; + String url = 'http://3.83.153.83:5000/v1.0/nlp'; final headers = {'Content-Type': 'application/json'}; - Map body = {"text": transcription}; + Map body = { + "language_code": selectedLang.code, + "text": transcription + }; String jsonBody = json.encode(body); final encoding = Encoding.getByName('utf-8'); http.Response response = await http.post(url, headers: headers, body: jsonBody, encoding: encoding); IntentApi parsedResponse = IntentApi.fromJson(json.decode(response.body)); - myIntent = parsedResponse.intent.name; - myIntentConfidence = parsedResponse.intent.confidence.toString(); - if (parsedResponse.entities.length == 2) { - if (parsedResponse.entities[0].entity == 'amount') { - myAmountDetected = parsedResponse.entities[0].value; - } else if (parsedResponse.entities[1].entity == 'amount') { - myAmountDetected = parsedResponse.entities[1].value; - } - if (parsedResponse.entities[0].entity == 'name') { - myPerson = ReCase(parsedResponse.entities[0].value).titleCase; - } else if (parsedResponse.entities[1].entity == 'name') { - myPerson = ReCase(parsedResponse.entities[1].value).titleCase; - } - } + myIntent = parsedResponse.intent; + myIntentConfidence = parsedResponse.confidence.toString(); + myAmountDetected = parsedResponse.amount ?? 'NULL'; + myPerson = parsedResponse.name ?? 'NULL'; print( '---------------------------- Intent Detected -------------------------------\n' + myIntent); @@ -111,7 +116,7 @@ class _VoicePayState extends State { } navigate() { - if (double.parse(myIntentConfidence) >= 0.6) { + if (double.parse(myIntentConfidence) >= 0.7) { if (myIntent == 'check_balance') { Navigator.of(context) .pushReplacement(MaterialPageRoute(builder: (BuildContext context) { @@ -219,8 +224,114 @@ class _VoicePayState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ IconButton( - icon: Icon(Icons.keyboard), - onPressed: () {}, + icon: Icon( + Icons.help_outline, + size: 25, + ), + onPressed: () => showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25), + ), + ), + title: Text( + 'VoicePay Help', + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.w700, fontSize: 25.0), + ), + content: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + alignment: Alignment.center, + height: 80.0, + child: Text( + 'VoicePay allows the user to do financial transactions and navigate throurgh the BHIM App by making use of simple voice commands.', + style: TextStyle(fontSize: 16.0), + textAlign: TextAlign.center, + ), + ), + Divider(height: 20.0, color: Colors.grey), + Container( + alignment: Alignment.center, + height: 20.0, + child: Text( + 'Sample Commands', + style: TextStyle(fontSize: 16.0), + textAlign: TextAlign.center, + ), + ), + Divider(height: 20.0, color: Colors.grey), + Container( + alignment: Alignment.bottomCenter, + height: 20.0, + child: Text( + 'Send 500 Rupees to Mankaran Singh', + style: TextStyle( + fontStyle: FontStyle.italic, + fontSize: 14.0, + color: Colors.grey, + ), + ), + ), + Container( + alignment: Alignment.bottomCenter, + height: 20.0, + child: Text( + 'Show my Transaction History', + style: TextStyle( + fontStyle: FontStyle.italic, + fontSize: 14.0, + color: Colors.grey, + ), + ), + ), + Container( + alignment: Alignment.bottomCenter, + height: 20.0, + child: Text( + 'Request 250 Rupees from Utkarsh Mishra', + style: TextStyle( + fontStyle: FontStyle.italic, + fontSize: 14.0, + color: Colors.grey, + ), + ), + ), + Container( + alignment: Alignment.bottomCenter, + height: 20.0, + child: Text( + 'I want to change some Settings', + style: TextStyle( + fontStyle: FontStyle.italic, + fontSize: 14.0, + color: Colors.grey, + ), + ), + ), + Container( + alignment: Alignment.bottomCenter, + height: 20.0, + child: Text( + 'Show the Rewardz I have earned.', + style: TextStyle( + fontStyle: FontStyle.italic, + fontSize: 14.0, + color: Colors.grey, + ), + ), + ) + ]), + ); + }, + ), ), IconButton( icon: !_speechRecognitionAvailable || _isListening @@ -236,70 +347,69 @@ class _VoicePayState extends State { ? () => start() : null, ), - IconButton( + PopupMenuButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(25), + ), + ), icon: Icon(Icons.translate), - onPressed: () { - showModalBottomSheet( - context: context, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(25), - topRight: Radius.circular(25), - ), - ), - builder: (BuildContext context) { - return Container( - height: 350.0, - child: ListView.builder( - itemCount: languageData.length + 1, - itemBuilder: (context, index) => index == 0 - ? Column( - children: [ - ListTile( - title: Text( - 'Choose Language', - style: TextStyle( - fontWeight: FontWeight.w700, - ), - textAlign: TextAlign.center, - ), - ), - Divider( - height: 0.0, - color: Colors.black, - ), - ], - ) - : ListTile( - title: Text( - languageData[index - 1].languageName), - trailing: - languageData[index - 1].languageCode == - 'en_IN' - ? IconButton( - icon: Icon( - Icons.check, - color: Colors.black, - ), - onPressed: null, - ) - : null), - ), - ); - }, - ); + onSelected: (lang) { + setState(() => selectedLang = lang); }, + itemBuilder: (BuildContext context) => _buildLanguagesWidgets, + color: Colors.white, + elevation: 8.0, + initialValue: selectedLang, + offset: Offset.zero, + enabled: true, ), ], ), + Divider(height: 20.0, color: Colors.grey), + Container( + alignment: Alignment.bottomCenter, + height: 20.0, + child: Text( + 'Send 500 Rupees to Mankaran Singh', + style: TextStyle( + fontStyle: FontStyle.italic, + fontSize: 14.0, + color: Colors.grey, + ), + ), + ), + Container( + alignment: Alignment.bottomCenter, + height: 20.0, + child: Text( + 'Show my Transaction History', + style: TextStyle( + fontStyle: FontStyle.italic, + fontSize: 14.0, + color: Colors.grey, + ), + ), + ) ], ), ); } - void start() => _speech.listen(locale: selectedLang.languageCode); + List> get _buildLanguagesWidgets => languages + .map((l) => PopupMenuItem( + value: l, + child: Text(l.name), + )) + .toList(); + + void _selectLangHandler(lang) { + setState(() => selectedLang = lang); + } -// .then((result) => print('_MyAppState.start => result $result')); + void start() => _speech + .listen(locale: 'en_IN') + .then((result) => print('_MyAppState.start => result $result')); void cancel() => _speech.cancel().then((result) => setState(() => _isListening = result)); @@ -312,7 +422,7 @@ class _VoicePayState extends State { setState(() => _speechRecognitionAvailable = result); void onCurrentLocale(String locale) { -// print('_MyAppState.onCurrentLocale... $locale'); + print('_MyAppState.onCurrentLocale... $locale'); } void onRecognitionStarted() => setState(() => _isListening = true); diff --git a/pubspec.lock b/pubspec.lock index fea3514..77a816e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -8,6 +8,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.3.0" + audio_recorder: + dependency: "direct main" + description: + name: audio_recorder + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" barcode_scan: dependency: "direct main" description: @@ -36,6 +43,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.14.11" + file: + dependency: "direct main" + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0" + file_picker: + dependency: "direct main" + description: + name: file_picker + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.3+2" flutter: dependency: "direct main" description: flutter @@ -67,6 +88,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.3" + intl: + dependency: transitive + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.16.1" matcher: dependency: transitive description: @@ -255,7 +283,7 @@ packages: source: hosted version: "0.2.5" typed_data: - dependency: transitive + dependency: "direct main" description: name: typed_data url: "https://pub.dartlang.org" @@ -283,5 +311,5 @@ packages: source: hosted version: "2.0.8" sdks: - dart: ">=2.2.2 <3.0.0" + dart: ">=2.5.0 <3.0.0" flutter: ">=1.9.1+hotfix.5 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index f559ecc..86101a4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: BHIM description: Voice Based BHIM UPI Payments App -version: 0.9.9 +version: 1.0.0 repository: https://github.com/ShivamGoyal1899/BHIM authors: - Shivam Goyal @@ -28,6 +28,10 @@ dependencies: random_string: ^2.0.0 shared_preferences: ^0.5.4 recase: ^2.0.1 + file: ^5.0.4 + file_picker: ^1.3.6 + typed_data: ^1.1.6 + audio_recorder: any dev_dependencies: flutter_test: