Blame view

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