topic_picture_bloc.dart 10.3 KB
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:wow_english/common/extension/string_extension.dart';
import 'package:wow_english/common/request/dao/listen_dao.dart';
import 'package:wow_english/common/request/exception.dart';
import 'package:wow_english/models/course_process_entity.dart';
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';
import 'package:wow_english/utils/loading.dart';
import 'package:wow_english/utils/toast_util.dart';

import '../../../common/permission/permissionRequestPage.dart';
import '../../../route/route.dart';

part 'topic_picture_event.dart';
part 'topic_picture_state.dart';

enum VoicePlayState {
  ///未知
  unKnow,
  ///播放中
  playing,
  ///播放完成
  completed,
  ///播放终止
  stop
}

class TopicPictureBloc extends BaseSectionBloc<TopicPictureEvent, TopicPictureState> {

  final PageController pageController;

  final String courseLessonId;

  int _currentPage = 0;

  int _selectItem = -1;

  CourseProcessEntity? _entity;

  ///正在评测
  bool _isVoicing = false;

  ///正在播放音频
  VoicePlayState _voicePlayState = VoicePlayState.unKnow;

  // 是否是回答(选择)结果音效
  bool _isResultSoundPlaying = false;

  bool get isResultSoundPlaying => _isResultSoundPlaying;

  // 答对播放音效时禁止任何点击(选择)操作
  bool _forbiddenWhenCorrect = false;

  bool get forbiddenWhenCorrect => _forbiddenWhenCorrect;

  CourseProcessEntity? get entity => _entity;

  int get currentPage => _currentPage + 1;

  int get selectItem => _selectItem;

  bool get isVoicing => _isVoicing;

  VoicePlayState get voicePlayState  => _voicePlayState;

  late MethodChannel methodChannel;

  late AudioPlayer audioPlayer;

  final BuildContext context;

  TopicPictureBloc(this.context, this.pageController, this.courseLessonId) : super(TopicPictureInitial()) {
    on<CurrentPageIndexChangeEvent>(_pageControllerChange);
    on<VoicePlayStateChangeEvent>(_voicePlayStateChange);
    on<XSVoiceResultEvent>(_voiceXsResult);
    on<XSVoiceInitEvent>(_initVoiceSdk);
    on<SelectItemEvent>(_selectItemLoad);
    on<RequestDataEvent>(_requestData);
    on<XSVoiceTestEvent>(_voiceXsTest);
    on<XSVoiceStopEvent>(_voiceXsStop);
    on<VoicePlayEvent>(_questionVoicePlay);
    on<InitBlocEvent>((event, emit) {
      //音频播放器
      audioPlayer = AudioPlayer();
      audioPlayer.onPlayerStateChanged.listen((event) async {
        debugPrint('播放状态变化 _voicePlayState=$_voicePlayState event=$event _isResultSoundPlaying=$_isResultSoundPlaying _forbiddenWhenCorrect=$_forbiddenWhenCorrect');
        if (_isResultSoundPlaying) {
          if (event != PlayerState.playing) {
            _isResultSoundPlaying = false;
            if (_forbiddenWhenCorrect) {
              _forbiddenWhenCorrect = false;
              debugPrint('播放完成后解除禁止');
              if (event == PlayerState.completed) {
                if (isLastPage()) {
                  showStepPage();
                } else {
                  // 答对后且播放完自动翻页
                  pageController.nextPage(
                    duration: const Duration(milliseconds: 500),
                    curve: Curves.ease,
                  );
                }
              }
            }
          }
        } else {
          if (event == PlayerState.completed) {
            debugPrint('播放完成');
            _voicePlayState = VoicePlayState.completed;
          }
          if (event == PlayerState.stopped) {
            debugPrint('播放结束');
            _voicePlayState = VoicePlayState.stop;
          }

          if (event == PlayerState.playing) {
            debugPrint('正在播放中');
            _voicePlayState = VoicePlayState.playing;
          }
          if (isClosed) {
            return;
          }
          add(VoicePlayStateChangeEvent());
        }
      });

      methodChannel = const MethodChannel('wow_english/sing_sound_method_channel');
      methodChannel.setMethodCallHandler((call) async {
        if (call.method == 'voiceResult') {//评测结果
          add(XSVoiceResultEvent(call.arguments));
          return;
        }

        if (call.method == 'voiceStart') {//评测开始
          if (kDebugMode) {
            print('评测开始');
          }
          return;
        }

        if (call.method == 'voiceEnd') {//评测结束
          if (kDebugMode) {
            print('评测结束');
          }
          return;
        }

        if (call.method == 'voiceFail') {//评测失败
          EasyLoading.showToast('评测失败');
          return;
        }
      });
    });
  }

  @override
  Future<void> close(){
    pageController.dispose();
    audioPlayer.release();
    audioPlayer.dispose();
    _isResultSoundPlaying = false;
    _forbiddenWhenCorrect = false;
    _voiceXsCancel();
    return super.close();
  }

  ///请求数据
  void _requestData(RequestDataEvent event,Emitter<TopicPictureState> emitter) async {
    try {
      await loading(() async {
        _entity = await ListenDao.process(courseLessonId);
        emitter(RequestDataState());
      });
    } catch (e) {
      if (e is ApiException) {
        showToast(e.message??'请求失败,请检查网络连接');
      }
    }
  }

  ///页面切换
  void _pageControllerChange(CurrentPageIndexChangeEvent event,Emitter<TopicPictureState> emitter) async {
    await closePlayerResource();
    debugPrint('翻页 $_currentPage->${event.pageIndex}');
    if (_currentPage == _entity?.topics?.length) {
      return;
    }
    _currentPage = event.pageIndex;
    final topics = _entity?.topics?[_currentPage];
    if (topics?.type != 3 && topics?.type != 4) {
      if (topics?.audioUrl != null) {
        final urlStr = topics?.audioUrl??'';
        if (urlStr.isNotEmpty) {
          debugPrint(urlStr);
          await audioPlayer.play(UrlSource(urlStr));
        }
      }
    }
    _selectItem = -1;
    emitter(CurrentPageIndexState());
  }

  ///选择
  void _selectItemLoad(SelectItemEvent event,Emitter<TopicPictureState> emitter) async {
    if (_forbiddenWhenCorrect) {
      return;
    }
    _selectItem = event.selectIndex;
    CourseProcessTopics? topics = _entity?.topics?[_currentPage];
    CourseProcessTopicsTopicAnswerList? answerList = topics?.topicAnswerList?[_selectItem];
    if (answerList?.correct == 0) {
      _playResultSound(false);
      // showToast('继续加油哦',duration: const Duration(seconds: 2));
    } else {
      _playResultSound(true);
      // showToast('恭喜你,答对啦!',duration: const Duration(seconds: 2));
    }
    emitter(SelectItemChangeState());
  }

  ///初始化SDK
  _initVoiceSdk(XSVoiceInitEvent event,Emitter<TopicPictureState> emitter) async {
    methodChannel.invokeMethod('initVoiceSdk',event.data);
  }

  ///先声测试
  void _voiceXsTest(XSVoiceTestEvent event,Emitter<TopicPictureState> emitter) async {
    await audioPlayer.stop();
    // 调用封装好的权限检查和请求方法
    bool result = await permissionCheckAndRequest(
        context,
        Permission.microphone,
        "录音"
    );
    if (result) {
      methodChannel.invokeMethod(
          'startVoice',
          {
            'word': event.testWord,
            'type': event.type,
            'userId': event.userId.toString()
          }
      );
      _isVoicing = true;
      emitter(XSVoiceTestState());
    }
  }

  ///终止评测
  void _voiceXsStop(XSVoiceStopEvent event,Emitter<TopicPictureState> emitter) async {
    methodChannel.invokeMethod('stopVoice');
  }

  ///取消评测(用于处理退出页面后录音未停止等异常情况的保护操作)
  void _voiceXsCancel() {
    methodChannel.invokeMethod('cancelVoice');
  }

  ///先声评测结果
  void _voiceXsResult(XSVoiceResultEvent event,Emitter<TopicPictureState> emitter) async {
    final Map args = event.message as Map;
    final result = args['result'] as Map;
    final overall = result['overall'].toString();
    showToast('测评成功,分数是$overall',duration: const Duration(seconds: 5));
    _isVoicing = false;
    emitter(XSVoiceTestState());
    if (isLastPage()) {
      showStepPage();
    }
  }

  // 暂时没用上
  void _voicePlayStateChange(VoicePlayStateChangeEvent event,Emitter<TopicPictureState> emitter) async {
    emitter(VoicePlayStateChange());
  }

  // 题目音频播放
  void _questionVoicePlay(VoicePlayEvent event,Emitter<TopicPictureState> emitter) async {
    if (_forbiddenWhenCorrect) {
      return;
    }
    _forbiddenWhenCorrect = false;
    await closePlayerResource();
    final topics = _entity?.topics?[_currentPage];
    final urlStr = topics?.audioUrl??'';
    await audioPlayer.play(UrlSource(urlStr));
  }

  Future<void> closePlayerResource() async {
    if (voicePlayState == VoicePlayState.playing || _isResultSoundPlaying) {
      await audioPlayer.stop();
    }
  }

  ///播放选择结果音效
  void _playResultSound(bool isCorrect) async {
    // await audioPlayer.stop();
    if (audioPlayer.state == PlayerState.playing && _isResultSoundPlaying == false) {
      _voicePlayState = VoicePlayState.stop;
    }
    debugPrint("_playResultSound isCorrect=$isCorrect");
    _isResultSoundPlaying = true;
    _forbiddenWhenCorrect = isCorrect;
    if (isCorrect) {
      await audioPlayer.play(AssetSource('correct_voice'.assetMp3));
    } else {
      await audioPlayer.play(AssetSource('incorrect_voice'.assetMp3));
    }
  }

  ///是否是最后一页
  bool isLastPage() {
    return currentPage == _entity?.topics?.length;
  }

  ///展示过渡页
  void showStepPage() {
    ///如果最后一页是语音问答题,评测完后自动翻页
    sectionComplete(() {
      popPage(
          data:{
            'currentStep':currentPage.toString(),
            'courseLessonId':courseLessonId,
            'isLastPage': true,
            'nextSection': true
          });
    }, againSectionTap: () {
      debugPrint("WQF 重做");
      pageController.jumpToPage(0);
    });
  }
}