diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 8e8e64f..d686bc1 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 52450AF12A4C415B007B3E4B /* XSMessageMehtodChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52450AF02A4C415B007B3E4B /* XSMessageMehtodChannel.swift */; }; 525E171A2A4BD03900104CDF /* VoiceXSMessageChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 525E17192A4BD03900104CDF /* VoiceXSMessageChannel.swift */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -52,6 +53,7 @@ 3563EC8D55A646823FD26A83 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 48BCA0827DCB98991774F5AC /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 52450AF02A4C415B007B3E4B /* XSMessageMehtodChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSMessageMehtodChannel.swift; sourceTree = ""; }; 525E17192A4BD03900104CDF /* VoiceXSMessageChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceXSMessageChannel.swift; sourceTree = ""; }; 6DEBBC1D861BE053F3ECE0B9 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; @@ -152,6 +154,7 @@ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 52450AF02A4C415B007B3E4B /* XSMessageMehtodChannel.swift */, 525E17192A4BD03900104CDF /* VoiceXSMessageChannel.swift */, 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, ); @@ -397,6 +400,7 @@ buildActionMask = 2147483647; files = ( 525E171A2A4BD03900104CDF /* VoiceXSMessageChannel.swift in Sources */, + 52450AF12A4C415B007B3E4B /* XSMessageMehtodChannel.swift in Sources */, 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, ); diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 41fde88..d8f2483 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -10,6 +10,7 @@ import Flutter GeneratedPluginRegistrant.register(with: self) let controller : FlutterViewController = window?.rootViewController as! FlutterViewController _ = VoiceXSMessageChannel(messager: controller.binaryMessenger) + _ = XSMessageMehtodChannel(message: controller.binaryMessenger); return super.application(application, didFinishLaunchingWithOptions: launchOptions) } } diff --git a/ios/Runner/XSMessageMehtodChannel.swift b/ios/Runner/XSMessageMehtodChannel.swift new file mode 100644 index 0000000..948fc96 --- /dev/null +++ b/ios/Runner/XSMessageMehtodChannel.swift @@ -0,0 +1,133 @@ +// +// XSMessageMehtodChannel.swift +// Runner +// +// Created by MacBook Pro on 2023/6/28. +// + +import UIKit + +class XSMessageMehtodChannel: NSObject,SSOralEvaluatingManagerDelegate { + var resultData:Dictionary? + var messageChannel:FlutterMethodChannel? + init(message:FlutterBinaryMessenger) { + super.init() + resultData = Dictionary() + messageChannel = FlutterMethodChannel.init(name: "wow_english/sing_sound_method_channely", binaryMessenger: message) + messageChannel!.setMethodCallHandler { call, result in + self.handle(call, result) + } + } + + //配置评测信息 + func setEvaluateConfig(dict:Dictionary) { + let appKey = dict["appKey"] as? String + let secretKey = dict["secretKey"] as? String + let userId = dict.keys.contains("userId") ? dict["userId"] as! String:"" + let frontTime = dict["frontTime"] as? TimeInterval + let backTime = dict["frontTime"] as? TimeInterval + let config = SSOralEvaluatingManagerConfig.init() + config.appKey = appKey //"a418" + config.secretKey = secretKey //"1a16f31f2611bf32fb7b3fc38f5b2c81"' + config.frontTime = frontTime ?? 3 + config.backTime = backTime ?? 3 + SSOralEvaluatingManager.register(config) + SSOralEvaluatingManager.share().register(.line, userId: userId) + SSOralEvaluatingManager.share().delegate = self + } + + //开始评测 + func evaluateVioce(dict:Dictionary) { + let text = dict["word"] as! String + let type = dict["type"] as! Int + let userId = dict["userId"] as! String + let config = SSOralEvaluatingConfig() + config.oralContent = text + if (type == 0) { + config.oralType = .word + } else { + config.oralType = .sentence + } + config.userId = userId + SSOralEvaluatingManager.share().startEvaluateOral(with: config) + } + + func handle(_ call: FlutterMethodCall,_ result: @escaping FlutterResult) { + if (call.method == "initVoiceSdk") { + self.setEvaluateConfig(dict:call.arguments as! Dictionary) + return + } + if (call.method == "starVoice") { + self.evaluateVioce(dict: call.arguments as! Dictionary) + return + } + + if (call.method == "stopVoice") { + SSOralEvaluatingManager.share().stopEvaluate(); + return + } + } + + //评测结果回调 + func evaluateResult() { + messageChannel!.invokeMethod("voiceResult", arguments: resultData) + } + + //SSOralEvaluatingManagerDelegate + /** + 评测开始 + */ + func oralEvaluatingDidStart() { + print("评测开始") + messageChannel!.invokeMethod("voiceStart", arguments: nil) + } + + /** + 评测停止 + */ + func oralEvaluatingDidStop() { + print("评测结束") + messageChannel!.invokeMethod("voiceEnd",arguments: nil) + } + + /** + 评测完成后的结果 + */ + func oralEvaluatingDidEnd(withResult result: [AnyHashable : Any]?, requestId request_id: String?) { + print("评测完成结果") + let resultDict:Dictionary = result?["result"] as! Dictionary + resultData!["result"] = "1" + //分数 + resultData!["overall"] = resultDict["overall"] + self.evaluateResult() + } + + /** + 评测失败回调 + */ + func oralEvaluatingDidEndError(_ error: Error?, requestId request_id: String?) { + print("评测失败") + messageChannel!.invokeMethod("voiceFail", arguments: error?.localizedDescription) + } + + /** + VAD(前置时间)超时回调 + */ + func oralEvaluatingDidVADFrontTimeOut() { + print("前置超时--->取消") + SSOralEvaluatingManager.share().cancelEvaluate() + if(resultData?.keys.count == 0) { + resultData!["result"] = "0" + self.evaluateResult(); + } + } + + /** + VAD(后置时间)超时回调 + */ + func oralEvaluatingDidVADBackTimeOut() { + print("后置超时--->结束") + ///结束回调 + SSOralEvaluatingManager.share().stopEvaluate(); + } +} diff --git a/lib/pages/home/home_page.dart b/lib/pages/home/home_page.dart index 8edd735..34de2f6 100644 --- a/lib/pages/home/home_page.dart +++ b/lib/pages/home/home_page.dart @@ -40,11 +40,6 @@ class _HomePageView extends StatelessWidget { Navigator.of(AppRouter.context).pushNamed(AppRouteName.user); } else { // Navigator.of(AppRouter.context).pushNamed(AppRouteName.topicPic); - // Navigator.of(AppRouter.context).pushNamed(AppRouteName.topicWord); - // Navigator.of(AppRouter.context).pushNamed(AppRouteName.lookVideo); - // Navigator.of(AppRouter.context).pushNamed(AppRouteName.voicePic); - // Navigator.of(AppRouter.context).pushNamed(AppRouteName.voiceWord); - Navigator.of(AppRouter.context).pushNamed(AppRouteName.voiceAnswer); } } diff --git a/lib/pages/practice/bloc/topic_picture_bloc.dart b/lib/pages/practice/bloc/topic_picture_bloc.dart index 913d03c..465d58e 100644 --- a/lib/pages/practice/bloc/topic_picture_bloc.dart +++ b/lib/pages/practice/bloc/topic_picture_bloc.dart @@ -35,19 +35,34 @@ class TopicPictureBloc extends Bloc { bool get isVoicing => _isVoicing; - var messageChannel = const BasicMessageChannel('com.owEnglish.voiceXs.BasicMessageChannel', StandardMessageCodec()); + late MethodChannel methodChannel; - final audioPlayer = AudioPlayer(); + late AudioPlayer audioPlayer; TopicPictureBloc(this.pageController, this.modelCount) : super(TopicPictureInitial()) { on(_pageControllerChange); - on(_initMessageChannelCall); - on(_requestData); on(_selectItemLoad); - on(_voiceXsTest); - on(_voiceResult); + on(_requestData); + on(_voiceXsTest); + on(_voiceXsResult); + on(_initVoiceSdk); on((event, emit) { + //音频播放器 + audioPlayer = AudioPlayer(); + audioPlayer.onPlayerStateChanged.listen((event) { + if (event == PlayerState.stopped) { + + } + }); + + methodChannel = const MethodChannel('wow_english/sing_sound_method_channely'); + methodChannel.invokeMethod('initVoiceSdk',{}); + methodChannel.setMethodCallHandler((call) async { + if (call.method == 'voiceResult') {//评测结束 + add(XSVoiceResultEvent(call.arguments)); + } + }); }); } @@ -58,6 +73,7 @@ class TopicPictureBloc extends Bloc { return super.close(); } + ///请求数据 void _requestData(RequestDataEvent event,Emitter emitter) async { try { await loading(() async { @@ -71,6 +87,12 @@ class TopicPictureBloc extends Bloc { } } + ///初始化SDK + _initVoiceSdk(XSVoiceInitEvent event,Emitter emitter) async { + methodChannel.invokeMethod('initVoiceSdk',event.data); + } + + ///页面切换 void _pageControllerChange(CurrentPageIndexChangeEvent event,Emitter emitter) async { _currentPage = event.pageIndex; final topics = _entity?.topics?[_currentPage]; @@ -87,15 +109,25 @@ class TopicPictureBloc extends Bloc { emitter(CurrentPageIndexState()); } + ///选择 void _selectItemLoad(SelectItemEvent event,Emitter emitter) async { _selectItem = event.selectIndex; emitter(SelectItemChangeState()); } - ///messageChannel回调 - Future messageResultHandler(message,Emitter emitter) async { - EasyLoading.dismiss(); - final Map args = message as Map; + ///先声测试 + void _voiceXsTest(XSVoiceTestEvent event,Emitter emitter) async { + EasyLoading.show(status: '录音中....'); + methodChannel.invokeMethod( + 'startRecord', + {'word':event.testWord,'type':event.type,'userId':event.userId.toString()} + ); + _isVoicing = true; + emitter(XSVoiceTestState()); + } + + void _voiceXsResult(XSVoiceResultEvent event,Emitter emitter) async { + final Map args = event.message as Map; final result = args['result'] as String; if (result == '1') { final overall = args['overall'].toString(); @@ -103,35 +135,7 @@ class TopicPictureBloc extends Bloc { } else { EasyLoading.showToast('测评失败',duration: const Duration(seconds: 10)); } - if (kDebugMode) { - print('是否被移除>>>>>$emitter'); - } - _isVoicing = false; - } - - Future _initMessageChannelCall(InitMessageChannelEvent event,Emitter emitter) async { - messageChannel.setMessageHandler((message) => - messageResultHandler(message, emitter) - ); - - audioPlayer.onPlayerStateChanged.listen((event) { - if (event == PlayerState.stopped) { - if (kDebugMode) { - print('播放结束'); - } - } - }); - } - - void _voiceXsTest(VoiceXsTestEvent event,Emitter emitter) async { - EasyLoading.show(status: '录音中....'); - messageChannel.send({'word':event.testWord,'type':event.type,'userId':event.userId.toString()}); - _isVoicing = true; - emitter(VoiceXsTestState()); - } - - void _voiceResult(VoiceResultEvent event,Emitter emitter) async { _isVoicing = false; - emitter(VoiceXsTestState()); + emitter(XSVoiceTestState()); } } diff --git a/lib/pages/practice/bloc/topic_picture_event.dart b/lib/pages/practice/bloc/topic_picture_event.dart index 09cf5d7..0ab49b6 100644 --- a/lib/pages/practice/bloc/topic_picture_event.dart +++ b/lib/pages/practice/bloc/topic_picture_event.dart @@ -5,17 +5,24 @@ abstract class TopicPictureEvent {} class RequestDataEvent extends TopicPictureEvent {} -///初始化消息回调 -class InitMessageChannelEvent extends TopicPictureEvent {} +///初始化先声SDK +class XSVoiceInitEvent extends TopicPictureEvent { + final Map data; + XSVoiceInitEvent(this.data); +} -class VoiceResultEvent extends TopicPictureEvent {} +///评测结果 +class XSVoiceResultEvent extends TopicPictureEvent { + final dynamic message; + XSVoiceResultEvent(this.message); +} ///先声测试 -class VoiceXsTestEvent extends TopicPictureEvent { +class XSVoiceTestEvent extends TopicPictureEvent { final String testWord; final int type; final int userId; - VoiceXsTestEvent(this.testWord,this.type,this.userId); + XSVoiceTestEvent(this.testWord,this.type,this.userId); } class CurrentPageIndexChangeEvent extends TopicPictureEvent { diff --git a/lib/pages/practice/bloc/topic_picture_state.dart b/lib/pages/practice/bloc/topic_picture_state.dart index 876e24e..3d75788 100644 --- a/lib/pages/practice/bloc/topic_picture_state.dart +++ b/lib/pages/practice/bloc/topic_picture_state.dart @@ -5,7 +5,7 @@ abstract class TopicPictureState {} class RequestDataState extends TopicPictureState {} -class VoiceXsTestState extends TopicPictureState {} +class XSVoiceTestState extends TopicPictureState {} class TopicPictureInitial extends TopicPictureState {} diff --git a/lib/pages/practice/topic_picture_page.dart b/lib/pages/practice/topic_picture_page.dart index 28d6713..f5ad1ca 100644 --- a/lib/pages/practice/topic_picture_page.dart +++ b/lib/pages/practice/topic_picture_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:wow_english/common/blocs/cachebloc/cache_bloc.dart'; +import 'package:wow_english/common/core/user_util.dart'; import 'package:wow_english/common/extension/string_extension.dart'; import 'package:wow_english/common/widgets/ow_image_widget.dart'; import 'package:wow_english/models/course_process_entity.dart'; @@ -20,7 +20,13 @@ class TopicPicturePage extends StatelessWidget { 3 ) ..add(RequestDataEvent()) - ..add(InitMessageChannelEvent()), + ..add(XSVoiceInitEvent( + { + 'appKey':'a418', + 'secretKey':'1a16f31f2611bf32fb7b3fc38f5b2c81', + 'userId':UserUtil.getUser()!.id + } + )), child: _TopicPicturePage(), ); } @@ -32,7 +38,11 @@ class _TopicPicturePage extends StatelessWidget { return BlocListener( listener: (context, state){ if (state is RequestDataState) { - context.read().add(CurrentPageIndexChangeEvent(0)); + // context.read().add(CurrentPageIndexChangeEvent(0)); + } + + if (state is XSVoiceTestState) { + } }, child: _topicPictureView(), @@ -50,7 +60,10 @@ class _TopicPicturePage extends StatelessWidget { children: [ PracticeHeaderWidget( title: '${bloc.currentPage}/${bloc.entity?.topics?.length}', - onTap: (){Navigator.pop(context);}, + onTap: (){ + bloc.add(XSVoiceTestEvent('Hello', 0,UserUtil.getUser()!.id)); + // Navigator.pop(context); + }, ), Expanded( child: PageView.builder( @@ -332,27 +345,6 @@ class _TopicPicturePage extends StatelessWidget { return _decodeVoiceWordImageWidget(index, topics!.topicAnswerList![index]); }), ), - // Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // Offstage( - // offstage: topics.topicAnswerList!.length <= 1, - // child: _decodeVoiceWordImageWidget(1,topics.topicAnswerList![0]), - // ), - // Offstage( - // offstage: topics.topicAnswerList!.length <= 2, - // child: _decodeVoiceWordImageWidget(2,topics.topicAnswerList![1]), - // ), - // // Offstage( - // // offstage: topics.topicAnswerList!.length <= 3, - // // child: _decodeVoiceWordImageWidget(3,topics.topicAnswerList![2]), - // // ), - // // Offstage( - // // offstage: topics.topicAnswerList!.length <= 4, - // // child: _decodeVoiceWordImageWidget(4,topics.topicAnswerList![3]), - // // ) - // ], - // ) ], ), ); @@ -443,7 +435,7 @@ class _TopicPicturePage extends StatelessWidget { if (bloc.isVoicing) { return; } - bloc.add(VoiceXsTestEvent('Hello', 0,context.read().userEntity!.id)); + bloc.add(XSVoiceTestEvent('Hello', 0,UserUtil.getUser()!.id)); }, child: Image.asset( 'micro_phone'.assetPng,