Blame view

lib/pages/reading/bloc/reading_bloc.dart 15.4 KB
a506beff   吴启风   feat:先声sdk方法找不到问题...
1
  import 'package:audioplayers/audioplayers.dart';
1dc46cf7   吴启风   feat:绘本ui
2
  import 'package:flutter/cupertino.dart';
a506beff   吴启风   feat:先声sdk方法找不到问题...
3
4
  import 'package:flutter/foundation.dart';
  import 'package:flutter/services.dart';
1dc46cf7   吴启风   feat:绘本ui
5
  import 'package:flutter_bloc/flutter_bloc.dart';
a506beff   吴启风   feat:先声sdk方法找不到问题...
6
  import 'package:flutter_easyloading/flutter_easyloading.dart';
fd737d6a   吴启风   feat:绘本作答结果以单词为单位...
7
  import 'package:flutter_screenutil/flutter_screenutil.dart';
6051f850   吴启风   feat:带隐私合规权限申请描述的...
8
  import 'package:permission_handler/permission_handler.dart';
d0623cfd   吴启风   feat:绘本作答结果动效
9
  import 'package:wow_english/common/extension/string_extension.dart';
2a427e12   吴启风   feat:绘本静态ui基本完成
10
  import 'package:wow_english/pages/reading/widgets/ReadingModeType.dart';
820997e6   吴启风   feat:过渡页-绘本环节
11
12
13
  import 'package:wow_english/pages/section/subsection/base_section/bloc.dart';
  import 'package:wow_english/pages/section/subsection/base_section/event.dart';
  import 'package:wow_english/pages/section/subsection/base_section/state.dart';
1dc46cf7   吴启风   feat:绘本ui
14
  
dae7f2fd   吴启风   feat:增加android端先声...
15
  import '../../../common/core/user_util.dart';
e811f164   吴启风   feat:权限申请页面增加隐私合规...
16
  import '../../../common/permission/permissionRequester.dart';
a506beff   吴启风   feat:先声sdk方法找不到问题...
17
18
  import '../../../common/request/dao/listen_dao.dart';
  import '../../../common/request/exception.dart';
d0623cfd   吴启风   feat:绘本作答结果动效
19
20
  import '../../../common/utils/click_with_music_controller.dart';
  import '../../../common/utils/show_star_reward_dialog.dart';
a506beff   吴启风   feat:先声sdk方法找不到问题...
21
  import '../../../models/course_process_entity.dart';
fd737d6a   吴启风   feat:绘本作答结果以单词为单位...
22
  import '../../../models/singsound_result_detail_entity.dart';
d0623cfd   吴启风   feat:绘本作答结果动效
23
  import '../../../models/voice_result_type.dart';
820997e6   吴启风   feat:过渡页-绘本环节
24
  import '../../../route/route.dart';
a506beff   吴启风   feat:先声sdk方法找不到问题...
25
  import '../../../utils/loading.dart';
a506beff   吴启风   feat:先声sdk方法找不到问题...
26
  
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
27
  import '../../../utils/log_util.dart';
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
28
  
1dc46cf7   吴启风   feat:绘本ui
29
  part 'reading_event.dart';
d0623cfd   吴启风   feat:绘本作答结果动效
30
  
1dc46cf7   吴启风   feat:绘本ui
31
32
  part 'reading_state.dart';
  
53e9e6db   吴启风   feat:绘本语音评测逻辑
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  enum VoicePlayState {
    ///未知
    unKnow,
  
    ///播放中
    playing,
  
    ///播放完成
    completed,
  
    ///播放终止
    stop
  }
  
820997e6   吴启风   feat:过渡页-绘本环节
47
48
  class ReadingPageBloc
      extends BaseSectionBloc<ReadingPageEvent, ReadingPageState> {
2a427e12   吴启风   feat:绘本静态ui基本完成
49
50
    final PageController pageController;
  
53e9e6db   吴启风   feat:绘本语音评测逻辑
51
52
    final String courseLessonId;
  
2a427e12   吴启风   feat:绘本静态ui基本完成
53
54
55
56
    ///当前页索引
    int _currentPage = 0;
  
    ///当前播放模式
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
57
    ReadingModeType _currentMode = ReadingModeType.manual;
2a427e12   吴启风   feat:绘本静态ui基本完成
58
59
60
61
62
  
    int get currentPage => _currentPage + 1;
  
    ReadingModeType get currentMode => _currentMode;
  
a506beff   吴启风   feat:先声sdk方法找不到问题...
63
64
65
66
67
68
69
70
71
    CourseProcessEntity? _entity;
  
    CourseProcessEntity? get entity => _entity;
  
    ///正在评测
    bool _isRecording = false;
  
    bool get isRecording => _isRecording;
  
53e9e6db   吴启风   feat:绘本语音评测逻辑
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
    ///原始音频是否正在播放
    bool _isOriginAudioPlaying = false;
  
    bool get isOriginAudioPlaying => _isOriginAudioPlaying;
  
    ///录音音频是否正在播放
    bool _isRecordAudioPlaying = false;
  
    bool get isRecordAudioPlaying => _isRecordAudioPlaying;
  
    ///正在播放音频状态
    VoicePlayState _voicePlayState = VoicePlayState.unKnow;
  
    VoicePlayState get voicePlayState => _voicePlayState;
  
a506beff   吴启风   feat:先声sdk方法找不到问题...
87
88
89
90
    late MethodChannel methodChannel;
  
    late AudioPlayer audioPlayer;
  
6051f850   吴启风   feat:带隐私合规权限申请描述的...
91
92
93
    final BuildContext context;
  
    ReadingPageBloc(this.context, this.pageController, this.courseLessonId)
53e9e6db   吴启风   feat:绘本语音评测逻辑
94
        : super(ReadingPageInitial()) {
2a427e12   吴启风   feat:绘本静态ui基本完成
95
      on<CurrentPageIndexChangeEvent>(_pageControllerChange);
53e9e6db   吴启风   feat:绘本语音评测逻辑
96
      on<CurrentModeChangeEvent>(_playModeChange);
2a427e12   吴启风   feat:绘本静态ui基本完成
97
98
99
      // pageController.addListener(() {
      //   _currentPage = pageController.page!.round();
      // });
a506beff   吴启风   feat:先声sdk方法找不到问题...
100
      on<RequestDataEvent>(_requestData);
53e9e6db   吴启风   feat:绘本语音评测逻辑
101
      on<InitBlocEvent>((event, emit) {
a506beff   吴启风   feat:先声sdk方法找不到问题...
102
103
        //音频播放器
        audioPlayer = AudioPlayer();
53e9e6db   吴启风   feat:绘本语音评测逻辑
104
        audioPlayer.onPlayerStateChanged.listen((event) async {
2ee6209a   吴启风   feat:绘本播放状态优化
105
          Log.d("播放状态变化 event=$event");
a506beff   吴启风   feat:先声sdk方法找不到问题...
106
          if (event == PlayerState.completed) {
53e9e6db   吴启风   feat:绘本语音评测逻辑
107
108
            debugPrint('播放完成');
            _voicePlayState = VoicePlayState.completed;
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
109
            _onAudioPlayComplete();
53e9e6db   吴启风   feat:绘本语音评测逻辑
110
111
112
113
          }
          if (event == PlayerState.stopped) {
            debugPrint('播放结束');
            _voicePlayState = VoicePlayState.stop;
53e9e6db   吴启风   feat:绘本语音评测逻辑
114
          }
a506beff   吴启风   feat:先声sdk方法找不到问题...
115
  
53e9e6db   吴启风   feat:绘本语音评测逻辑
116
117
118
119
          if (event == PlayerState.playing) {
            debugPrint('正在播放中');
            _voicePlayState = VoicePlayState.playing;
          }
2ee6209a   吴启风   feat:绘本播放状态优化
120
121
122
123
124
125
  
          if (event == PlayerState.disposed) {
            debugPrint('播放器释放');
            _voicePlayState = VoicePlayState.unKnow;
          }
  
53e9e6db   吴启风   feat:绘本语音评测逻辑
126
127
          if (isClosed) {
            return;
a506beff   吴启风   feat:先声sdk方法找不到问题...
128
          }
53e9e6db   吴启风   feat:绘本语音评测逻辑
129
          add(VoicePlayStateChangeEvent());
a506beff   吴启风   feat:先声sdk方法找不到问题...
130
131
        });
  
53e9e6db   吴启风   feat:绘本语音评测逻辑
132
133
134
        methodChannel =
            const MethodChannel('wow_english/sing_sound_method_channel');
        methodChannel.invokeMethod('initVoiceSdk', {}); //初始化评测
a506beff   吴启风   feat:先声sdk方法找不到问题...
135
        methodChannel.setMethodCallHandler((call) async {
6051f850   吴启风   feat:带隐私合规权限申请描述的...
136
137
          Log.d(
              "setMethodCallHandler method=${call.method} arguments=${call.arguments}");
53e9e6db   吴启风   feat:绘本语音评测逻辑
138
139
          if (call.method == 'voiceResult') {
            //评测结果
1f5969b8   biao   修复 练习和绘本播放音频问题
140
141
142
            await audioPlayer.setAudioContext(AudioContext());
            await audioPlayer.setBalance(0.0);
  
53e9e6db   吴启风   feat:绘本语音评测逻辑
143
144
145
146
147
148
149
150
151
            add(XSVoiceResultEvent(call.arguments));
            return;
          }
  
          if (call.method == 'voiceStart') {
            //评测开始
            if (kDebugMode) {
              print('评测开始');
            }
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
152
153
            _isRecording = true;
            add(OnXSVoiceStateChangeEvent());
53e9e6db   吴启风   feat:绘本语音评测逻辑
154
155
156
            return;
          }
  
46c68b45   吴启风   feat:翻页取消先声评测&增加先...
157
158
          if (call.method == 'voiceEnd' || call.method == 'voiceCancel') {
            //评测结束or评测取消
53e9e6db   吴启风   feat:绘本语音评测逻辑
159
            if (kDebugMode) {
46c68b45   吴启风   feat:翻页取消先声评测&增加先...
160
              print(call.method == 'voiceEnd' ? '评测结束' : '评测取消');
53e9e6db   吴启风   feat:绘本语音评测逻辑
161
            }
1f5969b8   biao   修复 练习和绘本播放音频问题
162
163
164
            await audioPlayer.setAudioContext(AudioContext());
            await audioPlayer.setBalance(0.0);
  
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
165
            _isRecording = false;
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
166
            add(OnXSVoiceStateChangeEvent());
53e9e6db   吴启风   feat:绘本语音评测逻辑
167
168
169
170
171
            return;
          }
  
          if (call.method == 'voiceFail') {
            //评测失败
46c68b45   吴启风   feat:翻页取消先声评测&增加先...
172
            _isRecording = false;
1f5969b8   biao   修复 练习和绘本播放音频问题
173
174
175
            await audioPlayer.setAudioContext(AudioContext());
            await audioPlayer.setBalance(0.0);
  
53e9e6db   吴启风   feat:绘本语音评测逻辑
176
177
            EasyLoading.showToast('评测失败');
            return;
a506beff   吴启风   feat:先声sdk方法找不到问题...
178
179
180
          }
        });
      });
53e9e6db   吴启风   feat:绘本语音评测逻辑
181
      on<VoicePlayStateChangeEvent>(_voicePlayStateChange);
a506beff   吴启风   feat:先声sdk方法找不到问题...
182
      on<PlayOriginalAudioEvent>(_playOriginalAudio);
53e9e6db   吴启风   feat:绘本语音评测逻辑
183
184
185
      on<XSVoiceInitEvent>(_initVoiceSdk);
      on<XSVoiceStartEvent>(_voiceXsStart);
      on<XSVoiceStopEvent>(_voiceXsStop);
a506beff   吴启风   feat:先声sdk方法找不到问题...
186
      on<XSVoiceResultEvent>(_voiceXsResult);
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
187
      on<OnXSVoiceStateChangeEvent>(_onVoiceXsStateChange);
53e9e6db   吴启风   feat:绘本语音评测逻辑
188
189
  
      on<PlayRecordAudioEvent>(_playRecordAudio);
2a427e12   吴启风   feat:绘本静态ui基本完成
190
191
192
193
194
    }
  
    @override
    Future<void> close() {
      pageController.dispose();
a506beff   吴启风   feat:先声sdk方法找不到问题...
195
196
      audioPlayer.release();
      audioPlayer.dispose();
081fbff7   吴启风   feat:性能优化-录音内存泄漏
197
      _voiceXsCancel(force: true);
2a427e12   吴启风   feat:绘本静态ui基本完成
198
199
200
      return super.close();
    }
  
a506beff   吴启风   feat:先声sdk方法找不到问题...
201
202
    void _pageControllerChange(CurrentPageIndexChangeEvent event,
        Emitter<ReadingPageState> emitter) async {
2a427e12   吴启风   feat:绘本静态ui基本完成
203
      _currentPage = event.pageIndex;
46c68b45   吴启风   feat:翻页取消先声评测&增加先...
204
      _voiceXsCancel();
2ee6209a   吴启风   feat:绘本播放状态优化
205
      _playOriginalAudioInner(null, forcePlay: true);
2a427e12   吴启风   feat:绘本静态ui基本完成
206
207
208
      emitter(CurrentPageIndexState());
    }
  
53e9e6db   吴启风   feat:绘本语音评测逻辑
209
    void _playModeChange(
a506beff   吴启风   feat:先声sdk方法找不到问题...
210
        CurrentModeChangeEvent event, Emitter<ReadingPageState> emitter) async {
2a427e12   吴启风   feat:绘本静态ui基本完成
211
212
213
214
215
216
      if (_currentMode == ReadingModeType.auto) {
        _currentMode = ReadingModeType.manual;
      } else {
        _currentMode = ReadingModeType.auto;
      }
      emitter(CurrentModeState());
1dc46cf7   吴启风   feat:绘本ui
217
    }
a506beff   吴启风   feat:先声sdk方法找不到问题...
218
219
220
221
222
223
  
    ///请求数据
    void _requestData(
        RequestDataEvent event, Emitter<ReadingPageState> emitter) async {
      try {
        await loading(() async {
53e9e6db   吴启风   feat:绘本语音评测逻辑
224
          _entity = await ListenDao.process(courseLessonId);
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
225
          Log.d("reading page entity: ${_entity!.toJson()}");
a506beff   吴启风   feat:先声sdk方法找不到问题...
226
227
228
229
230
231
232
233
234
          emitter(RequestDataState());
        });
      } catch (e) {
        if (e is ApiException) {
          EasyLoading.showToast(e.message ?? '请求失败,请检查网络连接');
        }
      }
    }
  
53e9e6db   吴启风   feat:绘本语音评测逻辑
235
236
237
238
239
240
    /// 播放绘本原音
    void _playOriginalAudio(
        PlayOriginalAudioEvent event, Emitter<ReadingPageState> emitter) async {
      _playOriginalAudioInner(event.url);
    }
  
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
241
    ///播放原音音频
2ee6209a   吴启风   feat:绘本播放状态优化
242
243
    /// - [force]: 是否强制播放(true:不管当前状态如何,都会播放目标原音音频,比如翻页场景)
    /// (false:如果正在播放,暂停播放,比如点击播放按钮场景)
93efa69f   biao   删除评测代码
244
245
    void _playOriginalAudioInner(String? audioUrl,
        {bool forcePlay = false}) async {
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
246
      if (_isRecordAudioPlaying) {
2ee6209a   吴启风   feat:绘本播放状态优化
247
        await audioPlayer.stop();
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
248
249
        _isRecordAudioPlaying = false;
      }
6051f850   吴启风   feat:带隐私合规权限申请描述的...
250
251
      Log.d(
          "_playOriginalAudio _isRecordAudioPlaying=$_isRecordAudioPlaying _isOriginAudioPlaying=$_isOriginAudioPlaying url=$audioUrl");
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
252
      if (_isOriginAudioPlaying) {
75e0dcd2   吴启风   feat:修复绘本音频ios听筒出声
253
        await audioPlayer.stop();
2ee6209a   吴启风   feat:绘本播放状态优化
254
255
256
257
258
259
        _isOriginAudioPlaying = false;
        if (forcePlay) {
          audioUrl ??= currentPageData()?.audioUrl ?? '';
          await _playAudio(audioUrl);
          _isOriginAudioPlaying = true;
        }
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
260
      } else {
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
261
        audioUrl ??= currentPageData()?.audioUrl ?? '';
2ee6209a   吴启风   feat:绘本播放状态优化
262
263
        await _playAudio(audioUrl);
        _isOriginAudioPlaying = true;
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
264
      }
a506beff   吴启风   feat:先声sdk方法找不到问题...
265
266
    }
  
53e9e6db   吴启风   feat:绘本语音评测逻辑
267
268
269
270
271
272
    /// 播放录音
    void _playRecordAudio(
        PlayRecordAudioEvent event, Emitter<ReadingPageState> emitter) async {
      _playRecordAudioInner();
    }
  
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
273
    Future<void> _playRecordAudioInner() async {
2ee6209a   吴启风   feat:绘本播放状态优化
274
      Log.d(
93efa69f   biao   删除评测代码
275
          "_playRecordAudioInner _isOriginAudioPlaying=$_isOriginAudioPlaying _isRecordAudioPlaying=$_isRecordAudioPlaying url=${currentPageData()?.recordUrl}");
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
276
      if (_isOriginAudioPlaying) {
2ee6209a   吴启风   feat:绘本播放状态优化
277
278
        ///如果正在播放原音,暂停
        await audioPlayer.stop();
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
279
280
        _isOriginAudioPlaying = false;
      }
2ee6209a   吴启风   feat:绘本播放状态优化
281
  
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
282
      if (_isRecordAudioPlaying) {
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
283
        await audioPlayer.stop();
2ee6209a   吴启风   feat:绘本播放状态优化
284
        _isRecordAudioPlaying = false;
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
285
      } else {
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
286
        final recordAudioUrl = currentPageData()?.recordUrl;
2ee6209a   吴启风   feat:绘本播放状态优化
287
288
        await _playAudio(recordAudioUrl);
        _isRecordAudioPlaying = true;
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
289
      }
ad37b653   吴启风   feat:1、完成微信支付;2、商...
290
      // emitter(VoicePlayStateChange());
53e9e6db   吴启风   feat:绘本语音评测逻辑
291
292
    }
  
2ee6209a   吴启风   feat:绘本播放状态优化
293
    Future<void> _playAudio(String? audioUrl) async {
53e9e6db   吴启风   feat:绘本语音评测逻辑
294
      if (audioUrl!.isNotEmpty) {
1f5969b8   biao   修复 练习和绘本播放音频问题
295
296
        await audioPlayer.play(UrlSource(audioUrl),
            balance: 0.0, ctx: AudioContext());
a506beff   吴启风   feat:先声sdk方法找不到问题...
297
298
299
300
      }
    }
  
    int dataCount() {
a506beff   吴启风   feat:先声sdk方法找不到问题...
301
302
303
304
305
306
307
      return _entity?.readings?.length ?? 0;
    }
  
    CourseProcessReadings? currentPageData() {
      return _entity?.readings?[_currentPage];
    }
  
4d8b0da4   吴启风   feat:绘本跟读底部ui优化
308
309
310
311
312
    ///当前页绘本跟读内容
    String readingContent() {
      return currentPageData()?.word?.trim() ?? '';
    }
  
fd737d6a   吴启风   feat:绘本作答结果以单词为单位...
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
    /// 结合单词分数返回带颜色的TextSpan数组
    List<TextSpan>? displayContent() {
      // return _textController.text.isNotEmpty ? _textController.text : readingContent();
      // 创建用于展示每个单词的 TextSpan 列表
      if (currentPageData() == null) {
        return null;
      }
      List<SingsoundResultDetailEntity> resultDetails;
      if (currentPageData()?.resultDetails != null) {
        resultDetails = currentPageData()!.resultDetails!;
      } else {
        List<String>? wordList = currentPageData()?.word?.split(RegExp(r'\s+'));
        resultDetails = wordList!
            .map(
                (word) => SingsoundResultDetailEntity.withCharAndScore(word, 0))
            .toList();
      }
      List<TextSpan> textSpans = resultDetails.asMap().entries.map((entry) {
        int index = entry.key;
        SingsoundResultDetailEntity detail = entry.value;
        // Check if this is the last word in the list
        bool isLastWord = index == resultDetails.length - 1;
        return TextSpan(
          text: '${detail.char}${isLastWord ? '' : ' '}',
          style: TextStyle(
            color: detail.score > 80
                ? const Color(0XFF35C137)
                : const Color(0xFF333333),
            fontSize: 20.sp,
          ),
        );
      }).toList();
      return textSpans;
    }
  
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
348
    void nextPage() {
820997e6   吴启风   feat:过渡页-绘本环节
349
350
351
352
353
354
355
356
357
358
359
      if (currentPage >= dataCount()) {
        sectionComplete(() {
          popPage(data: {
            'currentStep': currentPage,
            'courseLessonId': courseLessonId,
            'isCompleted': true,
            'nextSection': true
          });
        }, againSectionTap: () {
          pageController.jumpToPage(0);
        });
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
360
361
362
      } else {
        _currentPage += 1;
        pageController.nextPage(
8df5fbf9   吴启风   feat:单元列表页由于ios嵌套...
363
          duration: const Duration(milliseconds: 250),
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
364
365
366
367
368
          curve: Curves.ease,
        );
      }
    }
  
53e9e6db   吴启风   feat:绘本语音评测逻辑
369
370
371
372
373
374
    ///初始化SDK
    _initVoiceSdk(
        XSVoiceInitEvent event, Emitter<ReadingPageState> emitter) async {
      methodChannel.invokeMethod('initVoiceSdk', event.data);
    }
  
a506beff   吴启风   feat:先声sdk方法找不到问题...
375
    ///先声测试
53e9e6db   吴启风   feat:绘本语音评测逻辑
376
377
    void _voiceXsStart(
        XSVoiceStartEvent event, Emitter<ReadingPageState> emitter) async {
2ee6209a   吴启风   feat:绘本播放状态优化
378
      await _stopAudio();
a506beff   吴启风   feat:先声sdk方法找不到问题...
379
      startRecord(event.content);
a506beff   吴启风   feat:先声sdk方法找不到问题...
380
381
    }
  
2ee6209a   吴启风   feat:绘本播放状态优化
382
    ///开始录音
a506beff   吴启风   feat:先声sdk方法找不到问题...
383
    void startRecord(String content) async {
6051f850   吴启风   feat:带隐私合规权限申请描述的...
384
      // 调用封装好的权限检查和请求方法
d0623cfd   吴启风   feat:绘本作答结果动效
385
386
      bool result = await requestPermission(
          context, Permission.microphone, "录音", "用于开启录音,识别您的开口作答并给出反馈");
6051f850   吴启风   feat:带隐私合规权限申请描述的...
387
      if (result) {
820997e6   吴启风   feat:过渡页-绘本环节
388
389
390
391
392
        methodChannel.invokeMethod('startVoice', {
          'word': content,
          'type': '0',
          'userId': UserUtil.getUser()?.id.toString()
        });
a506beff   吴启风   feat:先声sdk方法找不到问题...
393
      }
a506beff   吴启风   feat:先声sdk方法找不到问题...
394
395
    }
  
53e9e6db   吴启风   feat:绘本语音评测逻辑
396
397
    void _voiceXsResult(
        XSVoiceResultEvent event, Emitter<ReadingPageState> emitter) async {
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
398
399
400
401
      final Map args = event.message as Map;
      final result = args['result'] as Map;
      Log.d("_voiceXsResult result=$result");
      final overall = result['overall'].toString();
fd737d6a   吴启风   feat:绘本作答结果以单词为单位...
402
403
404
405
406
407
408
409
410
      List<dynamic> resultDetailsJsons = result['details'];
      // 提取 score 和 char 字段
      List<SingsoundResultDetailEntity> detailEntities = [];
      for (var detail in resultDetailsJsons) {
        int score = detail['score'] as int;
        String char = detail['char'] as String;
        detailEntities
            .add(SingsoundResultDetailEntity.withCharAndScore(char, score));
      }
d0623cfd   吴启风   feat:绘本作答结果动效
411
412
413
414
  
      ///todo 后面可以考虑要不要传自己的服务器
      final recordFileUrl = args['audioUrl'].toString();
      int score = int.parse(overall);
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
415
      currentPageData()?.recordScore = overall;
d0623cfd   吴启风   feat:绘本作答结果动效
416
      currentPageData()?.recordUrl = recordFileUrl.assetMp3;
fd737d6a   吴启风   feat:绘本作答结果以单词为单位...
417
418
      currentPageData()?.resultDetails = detailEntities;
      add(OnXSVoiceStateChangeEvent());
93efa69f   biao   删除评测代码
419
  
d0623cfd   吴启风   feat:绘本作答结果动效
420
421
422
      final voiceResult = VoiceResultType.fromScore(score);
      if (voiceResult.lottieFilePath != null) {
        showCheerRewardDialog(context, lottieFile: voiceResult.lottieFilePath!);
820997e6   吴启风   feat:过渡页-绘本环节
423
      }
d0623cfd   吴启风   feat:绘本作答结果动效
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
      await ClickWithMusicController.instance
          .playMusicAndPerformAction(context, voiceResult.audioType, () async {
        ///完成录音后紧接着播放录音
        await _playRecordAudioInner();
        if (isLastPage()) {
          sectionComplete(() {
            popPage(data: {
              'currentStep': currentPage,
              'courseLessonId': courseLessonId,
              'isCompleted': true,
              'nextSection': true
            });
          }, againSectionTap: () {
            _resetLocalResult();
            pageController.jumpToPage(0);
          });
        }
      });
a506beff   吴启风   feat:先声sdk方法找不到问题...
442
    }
53e9e6db   吴启风   feat:绘本语音评测逻辑
443
444
445
446
447
448
449
  
    ///终止评测
    void _voiceXsStop(
        XSVoiceStopEvent event, Emitter<ReadingPageState> emitter) async {
      methodChannel.invokeMethod('stopVoice');
    }
  
6b0947ca   吴启风   feat:绘本增加initVoic...
450
    ///取消评测(用于处理退出页面后录音未停止等异常情况的保护操作)
081fbff7   吴启风   feat:性能优化-录音内存泄漏
451
    void _voiceXsCancel({bool force = false}) {
46c68b45   吴启风   feat:翻页取消先声评测&增加先...
452
      Log.d("取消评测 _voiceXsCancel _isRecording=$_isRecording");
081fbff7   吴启风   feat:性能优化-录音内存泄漏
453
      if (_isRecording || force) {
46c68b45   吴启风   feat:翻页取消先声评测&增加先...
454
455
        methodChannel.invokeMethod('cancelVoice');
      }
6b0947ca   吴启风   feat:绘本增加initVoic...
456
457
    }
  
53e9e6db   吴启风   feat:绘本语音评测逻辑
458
459
460
461
462
    void _voicePlayStateChange(VoicePlayStateChangeEvent event,
        Emitter<ReadingPageState> emitter) async {
      emitter(VoicePlayStateChange());
    }
  
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
463
464
465
466
467
    void _onAudioPlayComplete() {
      if (_isRecordAudioPlaying && _currentMode == ReadingModeType.auto) {
        nextPage();
      }
  
820997e6   吴启风   feat:过渡页-绘本环节
468
469
470
      Log.d(
          "_onAudioPlayComplete _isOriginAudioPlaying=$_isOriginAudioPlaying _voicePlayState=$_voicePlayState recordUrl=${currentPageData()?.recordUrl?.isNotEmpty}");
      if (_isOriginAudioPlaying &&
820997e6   吴启风   feat:过渡页-绘本环节
471
          currentPageData()?.recordUrl?.isNotEmpty != true) {
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
472
473
474
475
476
477
478
479
        ///如果刚刚完成原音播放&&录音为空,则开始录音
        startRecord(currentPageData()?.word ?? '');
      }
  
      _isOriginAudioPlaying = false;
      _isRecordAudioPlaying = false;
    }
  
2ee6209a   吴启风   feat:绘本播放状态优化
480
    Future<void> _stopAudio() async {
53e9e6db   吴启风   feat:绘本语音评测逻辑
481
482
483
484
      await audioPlayer.stop();
      _isOriginAudioPlaying = false;
      _isRecordAudioPlaying = false;
    }
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
485
  
820997e6   吴启风   feat:过渡页-绘本环节
486
487
    void _onVoiceXsStateChange(OnXSVoiceStateChangeEvent event,
        Emitter<ReadingPageState> emitter) async {
ad37b653   吴启风   feat:1、完成微信支付;2、商...
488
      emitter(XSVoiceTestState());
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
489
    }
065022b7   吴启风   feat:绘本评测反馈弹窗+原音播...
490
  
820997e6   吴启风   feat:过渡页-绘本环节
491
492
493
494
495
496
497
498
499
500
501
502
503
    ///是否是最后一页
    bool isLastPage() {
      return currentPage == dataCount();
    }
  
    ///重置数据
    void _resetLocalResult() {
      _entity?.readings?.forEach((element) {
        element.recordScore = null;
        element.recordUrl = null;
      });
    }
  }