import 'dart:async'; import 'dart:io'; import 'package:electronic_assistant/base/base_controller.dart'; import 'package:electronic_assistant/data/repositories/task_repository.dart'; import 'package:electronic_assistant/module/chat/view.dart'; import 'package:electronic_assistant/module/home/controller.dart'; import 'package:electronic_assistant/module/talk/summary/view.dart'; import 'package:electronic_assistant/module/talk/todo/controller.dart'; import 'package:electronic_assistant/module/talk/todo/view.dart'; import 'package:electronic_assistant/resource/assets.gen.dart'; import 'package:electronic_assistant/resource/colors.gen.dart'; import 'package:electronic_assistant/resource/string.gen.dart'; import 'package:electronic_assistant/utils/error_handler.dart'; import 'package:electronic_assistant/utils/expand.dart'; import 'package:electronic_assistant/utils/mmkv_util.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import '../../data/api/request/agenda_update_bean.dart'; import '../../data/bean/agenda.dart'; import '../../data/bean/agenda_list_all_bean.dart'; import '../../data/bean/talks.dart'; import '../../data/repositories/agenda_repository.dart'; import '../../data/repositories/talk_repository.dart'; import '../../dialog/add_agenda_dialog.dart'; import '../../dialog/alert_dialog.dart'; import '../../router/app_pages.dart'; import '../../utils/event_bus.dart'; import '../../utils/toast_util.dart'; import '../record/controller.dart'; import 'original/view.dart'; import 'package:just_audio/just_audio.dart'; class TalkController extends BaseController { final String uploadNoPrompts = "UPLOAD_NO_PROMPTS"; final Rxn talkBean = Rxn(); final isShowElectricLow = false.obs; bool isAudioLoading = false; final double sliderMax = 1; bool? audioFileIsExist; bool? isUploadedFile; Rxn isUploading = Rxn(); final isAudioPlaying = false.obs; final audioProgressValue = 0.0.obs; final audioDuration = Duration.zero.obs; final agendaOriginalAllList = []; final agendaAllList = [].obs; final _isEditModel = false.obs; final TextEditingController editTalkNameController = TextEditingController(); final tabIndex = 0.obs; final List tabBeans = [ StringName.talkTabSummary.tr, StringName.talkTabMyTask.tr, StringName.talkTabOriginal.tr ]; bool get isEditModel => _isEditModel.value; RxBool get isEditModelRx => _isEditModel; final _audioPlayer = AudioPlayer(); StreamSubscription? _talkBeanListener; final pages = [const SummaryView(), const TodoView(), const OriginalView()]; TextEditingController? _agendaContentController; TextEditingController? _agendaNameController; TextEditingController get agendaContentController { _agendaContentController ??= TextEditingController(); return _agendaContentController!; } TextEditingController get agendaNameController { _agendaNameController ??= TextEditingController(); return _agendaNameController!; } @override void onReady() { super.onReady(); _initAudioPlayer(); _initListener(); _getArguments(); } void _initAudioPlayer() { _audioPlayer.playerStateStream.listen((playerState) { if (playerState.processingState == ProcessingState.loading || playerState.processingState == ProcessingState.buffering) { isAudioLoading = true; debugPrint('音频load = true'); } else { debugPrint('音频load = false'); isAudioLoading = false; if (playerState.processingState == ProcessingState.completed) { _audioPlayer.stop(); _audioPlayer.seek(Duration.zero); isAudioPlaying.value = false; debugPrint('音频 播放结束了'); } } isAudioPlaying.value = playerState.playing; }, onError: (Object e, StackTrace stackTrace) { debugPrint('音频加载异常 == $e'); }); _audioPlayer.durationStream.listen((duration) { if (duration != null) { debugPrint('音频总播放时长 == ${duration.inMilliseconds}'); audioDuration.value = duration; } }); _audioPlayer.positionStream.listen((position) { if (audioDuration.value.inMilliseconds > 0) { audioProgressValue.value = (position.inMilliseconds / audioDuration.value.inMilliseconds) .clamp(0.0, sliderMax); } }); } void _initListener() { _talkBeanListener = talkBean.listen((bean) { _dealTalkUpdate(bean); }); } void _dealTalkUpdate(TalkBean? bean) async { String? id = talkBean.value?.id; if (id == null) { return; } if (talkRepository.isUploadingTalk(id) && talkBean.value?.status.value == TalkStatus.notAnalysis) { isUploading.value = true; } else { isUploading.value = false; } try { Uri? uri; if (bean?.isExample == true && bean?.audioUrl != null) { uri = Uri.parse(bean!.audioUrl!); } else { File? file = await getFileByTalk(id, bean?.uploadType); if (file?.existsSync() == true) { uri = file?.uri; } } if (uri == null) { throw '音频文件不存在'; } await _audioPlayer.setAudioSource(AudioSource.uri(uri)); audioFileIsExist = true; } catch (e) { audioFileIsExist = false; debugPrint('音频设置异常 == $e'); } } void _getArguments() { if (Get.arguments is TalkBean) { talkBean.value = Get.arguments as TalkBean; } } void updateProgress(double value) { final newPosition = Duration( milliseconds: (value * audioDuration.value.inMilliseconds).toInt()); _audioPlayer.seek(newPosition); } void clickPlayAudio() { if (audioFileIsExist != true) { ToastUtil.showToast(StringName.talkFileNotFind.tr); return; } if (isAudioLoading) { ToastUtil.showToast(StringName.talkAudioLoading.tr); return; } if (_audioPlayer.playing) { _audioPlayer.pause(); isAudioPlaying.value = false; } else { _audioPlayer.play(); isAudioPlaying.value = true; } } void _checkFileSizeAndNet() async { String? id = talkBean.value?.id; if (id == null) { return; } File? file = await getFileByTalk(talkBean.value?.id, talkBean.value?.uploadType); if (file == null || !file.existsSync()) { ToastUtil.showToast(StringName.talkUploadFileNotExist.tr); return; } bool isCheckRemind = KVUtil.getBool(uploadNoPrompts, false); if (isCheckRemind) { _requestAnalyze(file); return; } //如果文件大小低于250MB 不弹窗提醒 if (file.lengthSync() < 250 * 1024 * 1024) { _requestAnalyze(file); return; } final List connectivityResult = await (Connectivity().checkConnectivity()); if (connectivityResult.contains(ConnectivityResult.wifi)) { _requestAnalyze(file); } else { _showTrafficRemindDialog(file.lengthSync().toReadableSize(), confirmOnTap: (isCheckRemind) { if (isCheckRemind) { KVUtil.putBool(uploadNoPrompts, true); } _requestAnalyze(file); }); } } void _showTrafficRemindDialog(String holderTxt, {void Function(bool isCheckRemind)? confirmOnTap}) { final remindTrafficConsume = false.obs; Widget getSelectIcon() { return Obx(() { return remindTrafficConsume.value ? Assets.images.iconSelectTrue.image() : Assets.images.iconSelectFalse.image(); }); } Assets.images.iconSelectTrue.image(); EAAlertDialog.show( contentWidget: Column( children: [ Text( StringName.talkTrafficRemindTitle.tr .replacePlaceholders([holderTxt]), style: TextStyle(fontSize: 15.sp, color: ColorName.primaryTextColor), ), SizedBox(height: 8.h), GestureDetector( onTap: () { remindTrafficConsume.value = !remindTrafficConsume.value; }, child: IntrinsicWidth( child: Row( children: [ SizedBox(width: 20.w, height: 20.w, child: getSelectIcon()), SizedBox(width: 5.w), Text( StringName.talkTrafficRemindTips.tr, style: TextStyle( fontSize: 15.sp, color: ColorName.tertiaryTextColor), ) ], ), ), ) ], ), cancelText: StringName.cancel.tr, confirmText: StringName.sure.tr, confirmOnTap: () { confirmOnTap?.call(remindTrafficConsume.value); }); } void checkCanAnalyze() { String? id = talkBean.value?.id; double? duration = talkBean.value?.duration; if (id == null || duration == null) { return; } talkRepository.checkElectric(duration).then((data) { if (data.enough) { //检查网络以及文件大小 _checkFileSizeAndNet(); } else { ToastUtil.showToast(StringName.talkAnalyseLowToast.tr); isShowElectricLow.value = true; } }).catchError((error) { ToastUtil.showToast(error); }); } void _requestAnalyze(File file) { String? talkId = talkBean.value?.id; double? duration = talkBean.value?.duration; if (talkId == null || duration == null || isUploadedFile == true) { return; } isUploading.value = true; talkRepository.uploadTalkFile(talkId, duration, file).then((taskId) { isUploadedFile = true; taskRepository.addTask(taskId); }).catchError((error) { isUploading.value = false; ErrorHandler.toastError(error); }); } void refreshAgendaAllData({bool isForceRefresh = false}) { String? id = talkBean.value?.id; if (id == null || (!isForceRefresh && agendaAllList.isNotEmpty)) { return; } agendaRepository.agendaListAll(id).then((agenda) { agendaAllList.clear(); agendaOriginalAllList.clear(); if (agenda.list != null) { agendaOriginalAllList.addAll( agenda.list!.map((item) => AgendaListAllBean.from(item)).toList()); agendaAllList.addAll(agenda.list!); } }); } void clickAIAnalysis() { if (talkBean.value != null) { ChatPage.startByTalk(talkBean.value!); } } void onGoElectricStore() { Get.toNamed(RoutePath.store); Future.delayed(const Duration(milliseconds: 250), () { isShowElectricLow.value = false; }); } void onEditModelClick() { _isEditModel.value = true; if (_audioPlayer.playing) { _audioPlayer.pause(); isAudioPlaying.value = false; } editTalkNameController.text = talkBean.value?.title.value ?? ''; } void updateTabIndex(int index) { tabIndex.value = index; } void onEditCancel() { _isEditModel.value = false; agendaAllList.assignAll(agendaOriginalAllList .map((item) => AgendaListAllBean.from(item)) .toList()); } void onEditDoneClick() { if (talkBean.value == null) { return; } List list = []; for (AgendaListAllBean item in agendaAllList) { if (item.list != null) { for (Agenda agenda in item.list!) { list.add(AgendaUpdateBean(agenda.id, agenda.name, agenda.content)); } } } agendaRepository.agendaUpdate(talkBean.value!.id, list).then((data) { refreshAgendaAllData(isForceRefresh: true); eventBus.emit(TodoController.refreshTalkMineTask); isEditModelRx.value = false; }).catchError((error) { ErrorHandler.toastError(error); }); if (tabIndex.value == 0) { String updateName = editTalkNameController.text; talkRepository.talkRename(talkBean.value!.id, updateName).then((data) { talkBean.value?.title.value = updateName; }).catchError((error) { ErrorHandler.toastError(error); }); } } void removeTalkAgenda(List? list, Agenda agenda) { list?.remove(agenda); agendaAllList.refresh(); } void showSingleAddAgendaDialog(BuildContext context) { showAddAgendaDialog(context, agendaContentController, agendaNameController, list: agendaAllList.map((e) => e.name ?? "").toList(), callback: () { if (agendaContentController.text.isEmpty) { ToastUtil.showToast(StringName.talkAddAgendaContentHint.tr); return; } if (agendaNameController.text.isEmpty) { ToastUtil.showToast(StringName.talkAddAgendaNameHint.tr); return; } Get.back(); _dealAddProcedureList(); }); } void _dealAddProcedureList() { String name = agendaNameController.text; final addItem = Agenda( id: "", talkId: "", name: name, ); addItem.content = agendaContentController.text; for (AgendaListAllBean item in agendaAllList) { if (item.name == name) { List list = item.list ?? []; list.add(addItem); item.list = list; agendaAllList.refresh(); agendaContentController.clear(); agendaNameController.clear(); return; } } agendaAllList.add(AgendaListAllBean(name: name, list: [addItem])); agendaContentController.clear(); agendaNameController.clear(); } @override void onClose() { super.onClose(); _talkBeanListener?.cancel(); _audioPlayer.dispose(); _agendaContentController?.dispose(); _agendaNameController?.dispose(); } } Future getFileByTalk(String? talkId, int? uploadType) async { if (talkId == null) { return null; } if (uploadType == TalkUploadType.localUpload) { return await getChoiceUploadFile(talkId); } else { return await RecordController.getRecordFile(talkId); } }