Blame view

lib/pages/repeataftercontent/bloc/repeat_after_content_bloc.dart 10.9 KB
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
1
2
3
4
  import 'dart:io';
  import 'dart:async';
  
  import 'package:audio_session/audio_session.dart';
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
5
  import 'package:flutter/cupertino.dart';
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
6
  import 'package:flutter_bloc/flutter_bloc.dart';
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
7
8
9
  import 'package:flutter_sound/flutter_sound.dart';
  import 'package:path_provider/path_provider.dart';
  import 'package:permission_handler/permission_handler.dart';
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
10
  import 'package:wow_english/common/request/dao/listen_dao.dart';
934e2b47   liangchengyou   feat:权限调整+课程进度接口对接
11
12
  import 'package:wow_english/route/route.dart';
  import '../../../common/dialogs/show_dialog.dart';
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
13
  import '../../../common/request/exception.dart';
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
14
  import '../../../common/speech/chivox_evaluation_channel.dart';
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
15
16
17
18
  import '../../../models/read_content_entity.dart';
  import '../../../utils/loading.dart';
  import '../../../utils/toast_util.dart';
  
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
19
20
21
22
23
24
  part 'repeat_after_content_event.dart';
  part 'repeat_after_content_state.dart';
  
  enum VoiceRecordState {
    ///未知
    voiceRecordUnkonw,
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
25
  
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
26
27
    ///开始录音
    voiceRecordStat,
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
28
  
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
29
30
    ///正在录音
    voiceRecording,
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
31
  
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
32
33
34
35
    ///录音结束
    voiceRecordEnd
  }
  
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
36
  ///驰声测评状态
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
37
  enum XSVoiceCheckState {
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
38
39
    ///未知
    unKnow,
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
40
  
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
41
42
    ///测评开始
    start,
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
43
  
31d42f06   liangchengyou   feat:视频跟读录音修改
44
45
    ///评测结果
    result,
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
46
  
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
47
48
    ///测评结束
    stop,
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
49
50
  }
  
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
51
52
  class RepeatAfterContentBloc
      extends Bloc<RepeatAfterContentEvent, RepeatAfterContentState> {
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
53
54
    final String courseLessonId;
  
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
55
    /// 是否正在播放视频
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
56
    bool _videoPlaying = true;
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
57
    bool get videoPlaying => _videoPlaying;
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
58
  
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
59
    /// 是否正在录音
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
60
    bool _isRecord = false;
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
61
    bool get isRecord => _isRecord;
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
62
63
  
    /// 驰声评测状态
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
64
65
    XSVoiceCheckState _xSCheckState = XSVoiceCheckState.unKnow;
    XSVoiceCheckState get xSCheckState => _xSCheckState;
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
66
  
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
67
    /// 评测结果
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
68
    Map? _voiceTestResult;
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
69
    Map? get voiceTestResult => _voiceTestResult;
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
70
  
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
71
    /// 录音的次数
cc3b183a   liangchengyou   feat:ios启动图/logo
72
    int _recordNumber = 0;
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
73
  
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
74
75
76
    /// 录音文件地址
    String _path = '';
    String get path => _path;
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
77
  
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
78
79
80
81
82
    /// 当前播放的视频位置
    int _currentPlayIndex = 0;
    int get currentPlayIndex => _currentPlayIndex;
  
    /// 录音状态
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
83
    VoiceRecordState _voiceRecordState = VoiceRecordState.voiceRecordUnkonw;
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
84
85
    VoiceRecordState get voiceRecordState => _voiceRecordState;
  
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
86
    /// 跟读内容数字
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
87
    List<ReadContentEntity?>? _entityList;
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
88
    List<ReadContentEntity?>? get entityList => _entityList;
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
89
  
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
90
    /// 方法
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
91
    final ChivoxEvaluationChannel methodChannel = ChivoxEvaluationChannel();
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
92
  
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
93
94
95
    ///录音
    late FlutterSoundRecorder _soundRecorder;
    late FlutterSoundPlayer _soundPlayer;
934e2b47   liangchengyou   feat:权限调整+课程进度接口对接
96
    // StreamSubscription? _soundPlayerListen;
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
97
  
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
98
99
    RepeatAfterContentBloc(this.courseLessonId)
        : super(RepeatAfterContentInitial()) {
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
100
      on<VoiceRecordStateChangeEvent>(_voiceRecordStateChange);
cc3b183a   liangchengyou   feat:ios启动图/logo
101
      on<PostFollowReadContentEvent>(_postFollowReadContent);
7d6a6ddc   liangchengyou   feat:1.0.1优化
102
      on<ChangeVideoPlayIndexEvent>(_changeVideoPlayIndex);
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
103
104
      on<VideoPlayChangeEvent>(_videoPlayStateChange);
      on<RecordeVoicePlayEvent>(_recordeVoicePlay);
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
105
106
      on<StarRecordVoiceEvent>(_starRecordVoice);
      on<StopRecordVoiceEvent>(_stopRecordVoice);
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
107
      on<XSVoiceResultEvent>(_voiceXsResult);
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
108
109
110
111
      on<RequestDataEvent>(_requestData);
      on<XSVoiceTestEvent>(_voiceXsTest);
      on<XSVoiceStopEvent>(_voiceXsStop);
      on<VoiceRecordEvent>(_voiceRecord);
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
112
113
      on<InitBlocEvent>(_initBlocData);
    }
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
114
  
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
115
    @override
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
116
117
118
119
    Future<void> close() async {
      await _releaseFlauto();
      await _voiceXsCancel();
      await methodChannel.dispose();
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
120
121
      return super.close();
    }
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
122
  
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
123
    ///初始化功能
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
124
125
    void _initBlocData(
        InitBlocEvent event, Emitter<RepeatAfterContentState> emitter) async {
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
126
      methodChannel.setMethodCallHandler((call) async {
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
127
128
        if (call.method == 'voiceResult') {
          //评测结果
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
129
130
          add(XSVoiceResultEvent(call.arguments));
          add(PostFollowReadContentEvent());
31d42f06   liangchengyou   feat:视频跟读录音修改
131
132
133
134
135
          return;
        }
  
        if (call.method == 'voiceEnd') {
          return;
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
136
        }
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
137
138
139
140
141
142
  
        if (call.method == 'voiceFail') {
          _xSCheckState = XSVoiceCheckState.unKnow;
          add(VoiceRecordStateChangeEvent(_voiceRecordState));
          showToast('评测失败');
        }
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
143
      });
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
144
  
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
145
146
      //录音
      _soundRecorder = FlutterSoundRecorder();
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
147
148
      //音屏
      _soundPlayer = FlutterSoundPlayer();
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
149
150
      await _init();
      await methodChannel.invokeMethod('initVoiceSdk', {});
934e2b47   liangchengyou   feat:权限调整+课程进度接口对接
151
152
    }
  
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
153
    Future<void> _init() async {
934e2b47   liangchengyou   feat:权限调整+课程进度接口对接
154
      await _soundRecorder.openRecorder();
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
155
156
      await _soundRecorder
          .setSubscriptionDuration(const Duration(milliseconds: 10));
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
157
158
159
160
161
      //设置音频
      final session = await AudioSession.instance;
      await session.configure(AudioSessionConfiguration(
        avAudioSessionCategory: AVAudioSessionCategory.playAndRecord,
        avAudioSessionCategoryOptions:
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
162
163
            AVAudioSessionCategoryOptions.allowBluetooth |
                AVAudioSessionCategoryOptions.defaultToSpeaker,
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
164
165
        avAudioSessionMode: AVAudioSessionMode.spokenAudio,
        avAudioSessionRouteSharingPolicy:
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
166
            AVAudioSessionRouteSharingPolicy.defaultPolicy,
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
167
168
169
170
171
172
173
174
175
176
177
        avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none,
        androidAudioAttributes: const AndroidAudioAttributes(
          contentType: AndroidAudioContentType.speech,
          flags: AndroidAudioFlags.none,
          usage: AndroidAudioUsage.voiceCommunication,
        ),
        androidAudioFocusGainType: AndroidAudioFocusGainType.gain,
        androidWillPauseWhenDucked: true,
      ));
      await _soundPlayer.closePlayer();
      await _soundPlayer.openPlayer();
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
178
179
      await _soundPlayer
          .setSubscriptionDuration(const Duration(milliseconds: 10));
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
180
181
182
    }
  
    ///请求数据
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
183
184
    void _requestData(
        RequestDataEvent event, Emitter<RepeatAfterContentState> emitter) async {
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
185
186
187
188
189
190
191
      try {
        await loading(() async {
          _entityList = await ListenDao.readContent(courseLessonId);
          emitter(RequestDataState());
        });
      } catch (e) {
        if (e is ApiException) {
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
192
          showToast(e.message ?? '请求失败,请检查网络连接');
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
193
194
195
196
        }
      }
    }
  
68dd7ba8   liangchengyou   feat:首页主题颜色+已知问题修改
197
    ///提交跟读结果
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
198
199
    void _postFollowReadContent(PostFollowReadContentEvent event,
        Emitter<RepeatAfterContentState> emitter) async {
cc3b183a   liangchengyou   feat:ios启动图/logo
200
      try {
68dd7ba8   liangchengyou   feat:首页主题颜色+已知问题修改
201
        ReadContentEntity entity = _entityList![_currentPlayIndex]!;
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
202
        await ListenDao.followResult(_recordNumber.toString(), entity.id);
cc3b183a   liangchengyou   feat:ios启动图/logo
203
      } catch (e) {
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
204
        if (e is ApiException) {}
cc3b183a   liangchengyou   feat:ios启动图/logo
205
206
207
      }
    }
  
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
208
209
    void _videoPlayStateChange(VideoPlayChangeEvent event,
        Emitter<RepeatAfterContentState> emitter) async {
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
210
211
212
213
      _videoPlaying = !_videoPlaying;
      emitter(VideoPlayChangeState());
    }
  
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
214
215
    void _voiceRecord(
        VoiceRecordEvent event, Emitter<RepeatAfterContentState> emitter) async {
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
216
217
218
219
      _isRecord = !_isRecord;
      emitter(VoiceRecordChangeState());
    }
  
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
220
221
    void _voiceRecordStateChange(VoiceRecordStateChangeEvent event,
        Emitter<RepeatAfterContentState> emitter) async {
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
222
223
224
225
      _voiceRecordState = event.voiceRecordState;
      emitter(VoiceRecordStateChange());
    }
  
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
226
227
228
    ///驰声测试
    void _voiceXsTest(
        XSVoiceTestEvent event, Emitter<RepeatAfterContentState> emitter) async {
934e2b47   liangchengyou   feat:权限调整+课程进度接口对接
229
230
231
      _recordNumber += 1;
      _xSCheckState = XSVoiceCheckState.start;
      emitter(XSVoiceTestState());
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
232
233
234
235
236
237
      await methodChannel.invokeMethod('startLocalVoice', {
        'type': event.type,
        'word': event.testWord,
        'voicePath': _path,
        'userId': event.userId.toString()
      });
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
238
239
240
    }
  
    ///终止评测
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
241
242
243
    void _voiceXsStop(
        XSVoiceStopEvent event, Emitter<RepeatAfterContentState> emitter) async {
      await methodChannel.invokeMethod('stopVoice');
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
244
245
    }
  
6b0947ca   吴启风   feat:绘本增加initVoic...
246
    ///取消评测(用于处理退出页面后录音未停止等异常情况的保护操作)
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
247
248
    Future<void> _voiceXsCancel() async {
      await methodChannel.invokeMethod('cancelVoice');
6b0947ca   吴启风   feat:绘本增加initVoic...
249
250
    }
  
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
251
252
253
    ///驰声评测结果
    void _voiceXsResult(XSVoiceResultEvent event,
        Emitter<RepeatAfterContentState> emitter) async {
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
254
255
      final Map args = event.message as Map;
      final result = args['result'] as Map;
37b78a15   liangchengyou   feat:添加录音插件
256
      final overall = result['overall'].toString();
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
257
      _voiceTestResult = {'overall': overall};
31d42f06   liangchengyou   feat:视频跟读录音修改
258
      _xSCheckState = XSVoiceCheckState.result;
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
259
260
261
262
      emitter(XSVoiceTestState());
    }
  
    ///播放声音
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
263
264
    void _recordeVoicePlay(RecordeVoicePlayEvent event,
        Emitter<RepeatAfterContentState> emitter) async {
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
265
266
267
268
      if (await _fileExists(_path)) {
        if (_soundPlayer.isPlaying) {
          _soundPlayer.stopPlayer();
        }
7d6a6ddc   liangchengyou   feat:1.0.1优化
269
  
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
270
        await _soundPlayer.startPlayer(
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
271
            fromURI: path, codec: Codec.pcm16WAV, whenFinished: () {});
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
272
273
      }
    }
7d6a6ddc   liangchengyou   feat:1.0.1优化
274
275
  
    ///更改播放的视频
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
276
277
    void _changeVideoPlayIndex(ChangeVideoPlayIndexEvent event,
        Emitter<RepeatAfterContentState> emitter) async {
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
278
279
280
281
      if (_entityList == null || _entityList!.isEmpty) {
        return;
      }
      if (event.isNext) {
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
282
        if (_currentPlayIndex < _entityList!.length - 1) {
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
283
284
285
          _currentPlayIndex++;
        }
      } else {
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
286
        if (_currentPlayIndex > 0) {
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
287
288
289
          _currentPlayIndex--;
        }
      }
7d6a6ddc   liangchengyou   feat:1.0.1优化
290
291
      emitter(ChangeVideoPlayIndexState(event.isNext));
    }
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
292
293
  
    ///开始录音
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
294
295
    void _starRecordVoice(StarRecordVoiceEvent event,
        Emitter<RepeatAfterContentState> emitter) async {
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
296
297
298
299
300
301
302
303
      try {
        await getPermissionStatus().then((value) async {
          if (!value) {
            debugPrint('失败$value');
            return;
          }
          Directory tempDir = await getTemporaryDirectory();
          var time = DateTime.now().millisecondsSinceEpoch;
31d42f06   liangchengyou   feat:视频跟读录音修改
304
          String path = '${tempDir.path}/$time${ext[Codec.pcm16WAV.index]}';
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
305
306
307
308
309
  
          _path = path;
          debugPrint('=====> 准备开始录音');
          await _soundRecorder.startRecorder(
            toFile: path,
31d42f06   liangchengyou   feat:视频跟读录音修改
310
            codec: Codec.pcm16WAV,
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
311
312
313
          );
          debugPrint('=====> 开始录音');
          _voiceRecordState = VoiceRecordState.voiceRecording;
934e2b47   liangchengyou   feat:权限调整+课程进度接口对接
314
          _xSCheckState = XSVoiceCheckState.unKnow;
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
315
316
317
318
319
320
321
322
          emitter(VoiceRecordStateChange());
        });
      } catch (error) {
        await _soundRecorder.stopRecorder();
      }
    }
  
    ///停止录音
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
323
324
    void _stopRecordVoice(StopRecordVoiceEvent event,
        Emitter<RepeatAfterContentState> emitter) async {
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
      debugPrint('=====> 停止录音');
      await _soundRecorder.stopRecorder();
      _voiceRecordState = VoiceRecordState.voiceRecordEnd;
      emitter(VoiceRecordStateChange());
    }
  
    /// 判断文件是否存在
    Future<bool> _fileExists(String path) async {
      return await File(path).exists();
    }
  
    ///获取权限
    Future<bool> getPermissionStatus() async {
      Permission permission = Permission.microphone;
      PermissionStatus status = await permission.status;
      if (status.isGranted) {
        return true;
      } else if (status.isDenied) {
        requestPermission(permission);
      } else if (status.isPermanentlyDenied) {
934e2b47   liangchengyou   feat:权限调整+课程进度接口对接
345
        showDialog();
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
346
347
      } else if (status.isRestricted) {
        requestPermission(permission);
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
348
      } else {}
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
349
350
351
352
353
354
355
356
357
358
359
360
      return false;
    }
  
    /// 释放录音
    Future<void> _releaseFlauto() async {
      await _soundRecorder.closeRecorder();
    }
  
    ///申请权限
    void requestPermission(Permission permission) async {
      PermissionStatus status = await permission.request();
      if (status.isPermanentlyDenied) {
934e2b47   liangchengyou   feat:权限调整+课程进度接口对接
361
        showDialog();
9efff6ae   liangchengyou   feat:视频跟读逻辑修改
362
363
      }
    }
934e2b47   liangchengyou   feat:权限调整+课程进度接口对接
364
365
  
    void showDialog() async {
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
366
      showTwoActionDialog('提示', '取消', '去设置', '请进入设置页打开麦克风权限', leftTap: () {
934e2b47   liangchengyou   feat:权限调整+课程进度接口对接
367
        popPage();
0fa4e405   吴启风   feat: 将语音评测从先声迁移至驰声
368
      }, rightTap: () {
934e2b47   liangchengyou   feat:权限调整+课程进度接口对接
369
370
371
372
        popPage();
        openAppSettings();
      });
    }
cb38bc90   liangchengyou   feat:视频跟读逻辑处理
373
  }