Commit e3c2820cfc9977f7aa833d1d7930557fb8a37347

Authored by liangchengyou
1 parent aa4e28be

feat:先声SDK逻辑调整

ios/Runner.xcodeproj/project.pbxproj
... ... @@ -10,6 +10,7 @@
10 10 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 11 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
12 12 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
  13 + 52450AF12A4C415B007B3E4B /* XSMessageMehtodChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52450AF02A4C415B007B3E4B /* XSMessageMehtodChannel.swift */; };
13 14 525E171A2A4BD03900104CDF /* VoiceXSMessageChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 525E17192A4BD03900104CDF /* VoiceXSMessageChannel.swift */; };
14 15 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
15 16 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
... ... @@ -52,6 +53,7 @@
52 53 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 = "<group>"; };
53 54 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
54 55 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 = "<group>"; };
  56 + 52450AF02A4C415B007B3E4B /* XSMessageMehtodChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSMessageMehtodChannel.swift; sourceTree = "<group>"; };
55 57 525E17192A4BD03900104CDF /* VoiceXSMessageChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceXSMessageChannel.swift; sourceTree = "<group>"; };
56 58 6DEBBC1D861BE053F3ECE0B9 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
57 59 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
... ... @@ -152,6 +154,7 @@
152 154 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
153 155 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
154 156 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
  157 + 52450AF02A4C415B007B3E4B /* XSMessageMehtodChannel.swift */,
155 158 525E17192A4BD03900104CDF /* VoiceXSMessageChannel.swift */,
156 159 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
157 160 );
... ... @@ -397,6 +400,7 @@
397 400 buildActionMask = 2147483647;
398 401 files = (
399 402 525E171A2A4BD03900104CDF /* VoiceXSMessageChannel.swift in Sources */,
  403 + 52450AF12A4C415B007B3E4B /* XSMessageMehtodChannel.swift in Sources */,
400 404 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
401 405 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
402 406 );
... ...
ios/Runner/AppDelegate.swift
... ... @@ -10,6 +10,7 @@ import Flutter
10 10 GeneratedPluginRegistrant.register(with: self)
11 11 let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
12 12 _ = VoiceXSMessageChannel(messager: controller.binaryMessenger)
  13 + _ = XSMessageMehtodChannel(message: controller.binaryMessenger);
13 14 return super.application(application, didFinishLaunchingWithOptions: launchOptions)
14 15 }
15 16 }
... ...
ios/Runner/XSMessageMehtodChannel.swift 0 → 100644
  1 +//
  2 +// XSMessageMehtodChannel.swift
  3 +// Runner
  4 +//
  5 +// Created by MacBook Pro on 2023/6/28.
  6 +//
  7 +
  8 +import UIKit
  9 +
  10 +class XSMessageMehtodChannel: NSObject,SSOralEvaluatingManagerDelegate {
  11 + var resultData:Dictionary<String, Any>?
  12 + var messageChannel:FlutterMethodChannel?
  13 + init(message:FlutterBinaryMessenger) {
  14 + super.init()
  15 + resultData = Dictionary()
  16 + messageChannel = FlutterMethodChannel.init(name: "wow_english/sing_sound_method_channely", binaryMessenger: message)
  17 + messageChannel!.setMethodCallHandler { call, result in
  18 + self.handle(call, result)
  19 + }
  20 + }
  21 +
  22 + //配置评测信息
  23 + func setEvaluateConfig(dict:Dictionary<String, Any>) {
  24 + let appKey = dict["appKey"] as? String
  25 + let secretKey = dict["secretKey"] as? String
  26 + let userId = dict.keys.contains("userId") ? dict["userId"] as! String:""
  27 + let frontTime = dict["frontTime"] as? TimeInterval
  28 + let backTime = dict["frontTime"] as? TimeInterval
  29 + let config = SSOralEvaluatingManagerConfig.init()
  30 + config.appKey = appKey //"a418"
  31 + config.secretKey = secretKey //"1a16f31f2611bf32fb7b3fc38f5b2c81"'
  32 + config.frontTime = frontTime ?? 3
  33 + config.backTime = backTime ?? 3
  34 + SSOralEvaluatingManager.register(config)
  35 + SSOralEvaluatingManager.share().register(.line, userId: userId)
  36 + SSOralEvaluatingManager.share().delegate = self
  37 + }
  38 +
  39 + //开始评测
  40 + func evaluateVioce(dict:Dictionary<String, Any>) {
  41 + let text = dict["word"] as! String
  42 + let type = dict["type"] as! Int
  43 + let userId = dict["userId"] as! String
  44 + let config = SSOralEvaluatingConfig()
  45 + config.oralContent = text
  46 + if (type == 0) {
  47 + config.oralType = .word
  48 + } else {
  49 + config.oralType = .sentence
  50 + }
  51 + config.userId = userId
  52 + SSOralEvaluatingManager.share().startEvaluateOral(with: config)
  53 + }
  54 +
  55 + func handle(_ call: FlutterMethodCall,_ result: @escaping FlutterResult) {
  56 + if (call.method == "initVoiceSdk") {
  57 + self.setEvaluateConfig(dict:call.arguments as! Dictionary<String, Any>)
  58 + return
  59 + }
  60 + if (call.method == "starVoice") {
  61 + self.evaluateVioce(dict: call.arguments as! Dictionary<String, Any>)
  62 + return
  63 + }
  64 +
  65 + if (call.method == "stopVoice") {
  66 + SSOralEvaluatingManager.share().stopEvaluate();
  67 + return
  68 + }
  69 + }
  70 +
  71 + //评测结果回调
  72 + func evaluateResult() {
  73 + messageChannel!.invokeMethod("voiceResult", arguments: resultData)
  74 + }
  75 +
  76 + //SSOralEvaluatingManagerDelegate
  77 + /**
  78 + 评测开始
  79 + */
  80 + func oralEvaluatingDidStart() {
  81 + print("评测开始")
  82 + messageChannel!.invokeMethod("voiceStart", arguments: nil)
  83 + }
  84 +
  85 + /**
  86 + 评测停止
  87 + */
  88 + func oralEvaluatingDidStop() {
  89 + print("评测结束")
  90 + messageChannel!.invokeMethod("voiceEnd",arguments: nil)
  91 + }
  92 +
  93 + /**
  94 + 评测完成后的结果
  95 + */
  96 + func oralEvaluatingDidEnd(withResult result: [AnyHashable : Any]?, requestId request_id: String?) {
  97 + print("评测完成结果")
  98 + let resultDict:Dictionary<String, Any> = result?["result"] as! Dictionary
  99 + resultData!["result"] = "1"
  100 + //分数
  101 + resultData!["overall"] = resultDict["overall"]
  102 + self.evaluateResult()
  103 + }
  104 +
  105 + /**
  106 + 评测失败回调
  107 + */
  108 + func oralEvaluatingDidEndError(_ error: Error?, requestId request_id: String?) {
  109 + print("评测失败")
  110 + messageChannel!.invokeMethod("voiceFail", arguments: error?.localizedDescription)
  111 + }
  112 +
  113 + /**
  114 + VAD(前置时间)超时回调
  115 + */
  116 + func oralEvaluatingDidVADFrontTimeOut() {
  117 + print("前置超时--->取消")
  118 + SSOralEvaluatingManager.share().cancelEvaluate()
  119 + if(resultData?.keys.count == 0) {
  120 + resultData!["result"] = "0"
  121 + self.evaluateResult();
  122 + }
  123 + }
  124 +
  125 + /**
  126 + VAD(后置时间)超时回调
  127 + */
  128 + func oralEvaluatingDidVADBackTimeOut() {
  129 + print("后置超时--->结束")
  130 + ///结束回调
  131 + SSOralEvaluatingManager.share().stopEvaluate();
  132 + }
  133 +}
... ...
lib/pages/home/home_page.dart
... ... @@ -40,11 +40,6 @@ class _HomePageView extends StatelessWidget {
40 40 Navigator.of(AppRouter.context).pushNamed(AppRouteName.user);
41 41 } else {
42 42 // Navigator.of(AppRouter.context).pushNamed(AppRouteName.topicPic);
43   - // Navigator.of(AppRouter.context).pushNamed(AppRouteName.topicWord);
44   - // Navigator.of(AppRouter.context).pushNamed(AppRouteName.lookVideo);
45   - // Navigator.of(AppRouter.context).pushNamed(AppRouteName.voicePic);
46   - // Navigator.of(AppRouter.context).pushNamed(AppRouteName.voiceWord);
47   - Navigator.of(AppRouter.context).pushNamed(AppRouteName.voiceAnswer);
48 43 }
49 44 }
50 45  
... ...
lib/pages/practice/bloc/topic_picture_bloc.dart
... ... @@ -35,19 +35,34 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
35 35  
36 36 bool get isVoicing => _isVoicing;
37 37  
38   - var messageChannel = const BasicMessageChannel('com.owEnglish.voiceXs.BasicMessageChannel', StandardMessageCodec());
  38 + late MethodChannel methodChannel;
39 39  
40   - final audioPlayer = AudioPlayer();
  40 + late AudioPlayer audioPlayer;
41 41  
42 42 TopicPictureBloc(this.pageController, this.modelCount) : super(TopicPictureInitial()) {
43 43 on<CurrentPageIndexChangeEvent>(_pageControllerChange);
44   - on<InitMessageChannelEvent>(_initMessageChannelCall);
45   - on<RequestDataEvent>(_requestData);
46 44 on<SelectItemEvent>(_selectItemLoad);
47   - on<VoiceXsTestEvent>(_voiceXsTest);
48   - on<VoiceResultEvent>(_voiceResult);
  45 + on<RequestDataEvent>(_requestData);
  46 + on<XSVoiceTestEvent>(_voiceXsTest);
  47 + on<XSVoiceResultEvent>(_voiceXsResult);
  48 + on<XSVoiceInitEvent>(_initVoiceSdk);
49 49 on<TopicPictureEvent>((event, emit) {
  50 + //音频播放器
  51 + audioPlayer = AudioPlayer();
  52 + audioPlayer.onPlayerStateChanged.listen((event) {
  53 + if (event == PlayerState.stopped) {
  54 +
  55 + }
  56 + });
  57 +
50 58  
  59 + methodChannel = const MethodChannel('wow_english/sing_sound_method_channely');
  60 + methodChannel.invokeMethod('initVoiceSdk',{});
  61 + methodChannel.setMethodCallHandler((call) async {
  62 + if (call.method == 'voiceResult') {//评测结束
  63 + add(XSVoiceResultEvent(call.arguments));
  64 + }
  65 + });
51 66 });
52 67 }
53 68  
... ... @@ -58,6 +73,7 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
58 73 return super.close();
59 74 }
60 75  
  76 + ///请求数据
61 77 void _requestData(RequestDataEvent event,Emitter<TopicPictureState> emitter) async {
62 78 try {
63 79 await loading(() async {
... ... @@ -71,6 +87,12 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
71 87 }
72 88 }
73 89  
  90 + ///初始化SDK
  91 + _initVoiceSdk(XSVoiceInitEvent event,Emitter<TopicPictureState> emitter) async {
  92 + methodChannel.invokeMethod('initVoiceSdk',event.data);
  93 + }
  94 +
  95 + ///页面切换
74 96 void _pageControllerChange(CurrentPageIndexChangeEvent event,Emitter<TopicPictureState> emitter) async {
75 97 _currentPage = event.pageIndex;
76 98 final topics = _entity?.topics?[_currentPage];
... ... @@ -87,15 +109,25 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
87 109 emitter(CurrentPageIndexState());
88 110 }
89 111  
  112 + ///选择
90 113 void _selectItemLoad(SelectItemEvent event,Emitter<TopicPictureState> emitter) async {
91 114 _selectItem = event.selectIndex;
92 115 emitter(SelectItemChangeState());
93 116 }
94 117  
95   - ///messageChannel回调
96   - Future messageResultHandler(message,Emitter<TopicPictureState> emitter) async {
97   - EasyLoading.dismiss();
98   - final Map args = message as Map;
  118 + ///先声测试
  119 + void _voiceXsTest(XSVoiceTestEvent event,Emitter<TopicPictureState> emitter) async {
  120 + EasyLoading.show(status: '录音中....');
  121 + methodChannel.invokeMethod(
  122 + 'startRecord',
  123 + {'word':event.testWord,'type':event.type,'userId':event.userId.toString()}
  124 + );
  125 + _isVoicing = true;
  126 + emitter(XSVoiceTestState());
  127 + }
  128 +
  129 + void _voiceXsResult(XSVoiceResultEvent event,Emitter<TopicPictureState> emitter) async {
  130 + final Map args = event.message as Map;
99 131 final result = args['result'] as String;
100 132 if (result == '1') {
101 133 final overall = args['overall'].toString();
... ... @@ -103,35 +135,7 @@ class TopicPictureBloc extends Bloc&lt;TopicPictureEvent, TopicPictureState&gt; {
103 135 } else {
104 136 EasyLoading.showToast('测评失败',duration: const Duration(seconds: 10));
105 137 }
106   - if (kDebugMode) {
107   - print('是否被移除>>>>>$emitter');
108   - }
109   - _isVoicing = false;
110   - }
111   -
112   - Future _initMessageChannelCall(InitMessageChannelEvent event,Emitter<TopicPictureState> emitter) async {
113   - messageChannel.setMessageHandler((message) =>
114   - messageResultHandler(message, emitter)
115   - );
116   -
117   - audioPlayer.onPlayerStateChanged.listen((event) {
118   - if (event == PlayerState.stopped) {
119   - if (kDebugMode) {
120   - print('播放结束');
121   - }
122   - }
123   - });
124   - }
125   -
126   - void _voiceXsTest(VoiceXsTestEvent event,Emitter<TopicPictureState> emitter) async {
127   - EasyLoading.show(status: '录音中....');
128   - messageChannel.send({'word':event.testWord,'type':event.type,'userId':event.userId.toString()});
129   - _isVoicing = true;
130   - emitter(VoiceXsTestState());
131   - }
132   -
133   - void _voiceResult(VoiceResultEvent event,Emitter<TopicPictureState> emitter) async {
134 138 _isVoicing = false;
135   - emitter(VoiceXsTestState());
  139 + emitter(XSVoiceTestState());
136 140 }
137 141 }
... ...
lib/pages/practice/bloc/topic_picture_event.dart
... ... @@ -5,17 +5,24 @@ abstract class TopicPictureEvent {}
5 5  
6 6 class RequestDataEvent extends TopicPictureEvent {}
7 7  
8   -///初始化消息回调
9   -class InitMessageChannelEvent extends TopicPictureEvent {}
  8 +///初始化先声SDK
  9 +class XSVoiceInitEvent extends TopicPictureEvent {
  10 + final Map data;
  11 + XSVoiceInitEvent(this.data);
  12 +}
10 13  
11   -class VoiceResultEvent extends TopicPictureEvent {}
  14 +///评测结果
  15 +class XSVoiceResultEvent extends TopicPictureEvent {
  16 + final dynamic message;
  17 + XSVoiceResultEvent(this.message);
  18 +}
12 19  
13 20 ///先声测试
14   -class VoiceXsTestEvent extends TopicPictureEvent {
  21 +class XSVoiceTestEvent extends TopicPictureEvent {
15 22 final String testWord;
16 23 final int type;
17 24 final int userId;
18   - VoiceXsTestEvent(this.testWord,this.type,this.userId);
  25 + XSVoiceTestEvent(this.testWord,this.type,this.userId);
19 26 }
20 27  
21 28 class CurrentPageIndexChangeEvent extends TopicPictureEvent {
... ...
lib/pages/practice/bloc/topic_picture_state.dart
... ... @@ -5,7 +5,7 @@ abstract class TopicPictureState {}
5 5  
6 6 class RequestDataState extends TopicPictureState {}
7 7  
8   -class VoiceXsTestState extends TopicPictureState {}
  8 +class XSVoiceTestState extends TopicPictureState {}
9 9  
10 10 class TopicPictureInitial extends TopicPictureState {}
11 11  
... ...
lib/pages/practice/topic_picture_page.dart
1 1 import 'package:flutter/material.dart';
2 2 import 'package:flutter_bloc/flutter_bloc.dart';
3 3 import 'package:flutter_screenutil/flutter_screenutil.dart';
4   -import 'package:wow_english/common/blocs/cachebloc/cache_bloc.dart';
  4 +import 'package:wow_english/common/core/user_util.dart';
5 5 import 'package:wow_english/common/extension/string_extension.dart';
6 6 import 'package:wow_english/common/widgets/ow_image_widget.dart';
7 7 import 'package:wow_english/models/course_process_entity.dart';
... ... @@ -20,7 +20,13 @@ class TopicPicturePage extends StatelessWidget {
20 20 3
21 21 )
22 22 ..add(RequestDataEvent())
23   - ..add(InitMessageChannelEvent()),
  23 + ..add(XSVoiceInitEvent(
  24 + {
  25 + 'appKey':'a418',
  26 + 'secretKey':'1a16f31f2611bf32fb7b3fc38f5b2c81',
  27 + 'userId':UserUtil.getUser()!.id
  28 + }
  29 + )),
24 30 child: _TopicPicturePage(),
25 31 );
26 32 }
... ... @@ -32,7 +38,11 @@ class _TopicPicturePage extends StatelessWidget {
32 38 return BlocListener<TopicPictureBloc,TopicPictureState>(
33 39 listener: (context, state){
34 40 if (state is RequestDataState) {
35   - context.read<TopicPictureBloc>().add(CurrentPageIndexChangeEvent(0));
  41 + // context.read<TopicPictureBloc>().add(CurrentPageIndexChangeEvent(0));
  42 + }
  43 +
  44 + if (state is XSVoiceTestState) {
  45 +
36 46 }
37 47 },
38 48 child: _topicPictureView(),
... ... @@ -50,7 +60,10 @@ class _TopicPicturePage extends StatelessWidget {
50 60 children: [
51 61 PracticeHeaderWidget(
52 62 title: '${bloc.currentPage}/${bloc.entity?.topics?.length}',
53   - onTap: (){Navigator.pop(context);},
  63 + onTap: (){
  64 + bloc.add(XSVoiceTestEvent('Hello', 0,UserUtil.getUser()!.id));
  65 + // Navigator.pop(context);
  66 + },
54 67 ),
55 68 Expanded(
56 69 child: PageView.builder(
... ... @@ -332,27 +345,6 @@ class _TopicPicturePage extends StatelessWidget {
332 345 return _decodeVoiceWordImageWidget(index, topics!.topicAnswerList![index]);
333 346 }),
334 347 ),
335   - // Row(
336   - // mainAxisAlignment: MainAxisAlignment.spaceBetween,
337   - // children: [
338   - // Offstage(
339   - // offstage: topics.topicAnswerList!.length <= 1,
340   - // child: _decodeVoiceWordImageWidget(1,topics.topicAnswerList![0]),
341   - // ),
342   - // Offstage(
343   - // offstage: topics.topicAnswerList!.length <= 2,
344   - // child: _decodeVoiceWordImageWidget(2,topics.topicAnswerList![1]),
345   - // ),
346   - // // Offstage(
347   - // // offstage: topics.topicAnswerList!.length <= 3,
348   - // // child: _decodeVoiceWordImageWidget(3,topics.topicAnswerList![2]),
349   - // // ),
350   - // // Offstage(
351   - // // offstage: topics.topicAnswerList!.length <= 4,
352   - // // child: _decodeVoiceWordImageWidget(4,topics.topicAnswerList![3]),
353   - // // )
354   - // ],
355   - // )
356 348 ],
357 349 ),
358 350 );
... ... @@ -443,7 +435,7 @@ class _TopicPicturePage extends StatelessWidget {
443 435 if (bloc.isVoicing) {
444 436 return;
445 437 }
446   - bloc.add(VoiceXsTestEvent('Hello', 0,context.read<CacheBloc>().userEntity!.id));
  438 + bloc.add(XSVoiceTestEvent('Hello', 0,UserUtil.getUser()!.id));
447 439 },
448 440 child: Image.asset(
449 441 'micro_phone'.assetPng,
... ...