| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637 |
- import 'dart:async';
- import 'dart:developer';
- import 'dart:io';
- import 'package:connectivity_plus/connectivity_plus.dart';
- import 'package:electronic_assistant/base/base_controller.dart';
- import 'package:electronic_assistant/data/consts/event_report_id.dart';
- import 'package:electronic_assistant/data/repositories/account_repository.dart';
- import 'package:electronic_assistant/data/repositories/task_repository.dart';
- import 'package:electronic_assistant/handler/event_handler.dart';
- import 'package:electronic_assistant/module/chat/view.dart';
- import 'package:electronic_assistant/module/login/view.dart';
- import 'package:electronic_assistant/module/record/record_handler.dart';
- import 'package:electronic_assistant/module/store/view.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/module/talk/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/router/app_pages.dart';
- import 'package:electronic_assistant/utils/audio_picker_utils.dart';
- import 'package:electronic_assistant/utils/error_handler.dart';
- import 'package:electronic_assistant/utils/expand.dart';
- import 'package:electronic_assistant/utils/file_upload_check_helper.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:flutter_smart_dialog/flutter_smart_dialog.dart';
- import 'package:get/get.dart';
- import 'package:just_audio/just_audio.dart';
- import 'package:share_plus/share_plus.dart';
- import 'package:wakelock_plus/wakelock_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 '../../dialog/talk_share_dialog.dart';
- import '../../utils/common_utils.dart';
- import '../../utils/event_bus.dart';
- import '../../utils/system_share_util.dart';
- import '../../utils/toast_util.dart';
- import 'original/view.dart';
- class TalkController extends BaseController {
- static const String argumentItem = 'argument_item';
- static const String argumentTalkId = 'argument_talk_id';
- static const String argumentEventTag = 'argument_event_tag';
- final String uploadNoPrompts = "UPLOAD_NO_PROMPTS";
- final Rxn<TalkBean> talkBean = Rxn();
- StreamSubscription? _talkUploadListener;
- final RxDouble uploadProgress = RxDouble(0);
- final isShowElectricLow = false.obs;
- bool isAudioLoading = false;
- final double sliderMax = 1;
- bool? audioFileIsExist;
- bool? isUploadedFile;
- Rxn<bool> isUploading = Rxn();
- final isAudioPlaying = false.obs;
- final audioProgressValue = 0.0.obs;
- final audioDuration = Duration.zero.obs;
- final agendaOriginalAllList = <AgendaListAllBean>[];
- final agendaAllList = <AgendaListAllBean>[].obs;
- final _isEditModel = false.obs;
- final TextEditingController editTalkNameController = TextEditingController();
- final tabIndex = 0.obs;
- final List<String> tabBeans = [
- StringName.talkTabSummary.tr,
- '思维导图',
- StringName.talkTabMyTask.tr,
- StringName.talkTabOriginal.tr
- ];
- bool get isEditModel => _isEditModel.value;
- RxBool get isEditModelRx => _isEditModel;
- final _audioPlayer = AudioPlayer();
- StreamSubscription? _talkBeanListener;
- TextEditingController? _agendaContentController;
- TextEditingController? _agendaNameController;
- TextEditingController get agendaContentController {
- _agendaContentController ??= TextEditingController();
- return _agendaContentController!;
- }
- TextEditingController get agendaNameController {
- _agendaNameController ??= TextEditingController();
- return _agendaNameController!;
- }
- String? paramId;
- String? eventTag;
- bool isLocalFileHas = false;
- final Rxn<Duration> playingDuration = Rxn();
- @override
- void onReady() {
- super.onReady();
- _initAudioPlayer();
- _getArguments();
- eventReport(EventId.event_101001, params: {EventId.id: eventTag});
- }
- void eventReport(String eventId, {Map<String, dynamic>? params}) {
- if (talkBean.value == null || talkBean.value?.isExample == true) {
- return;
- }
- EventHandler.report(eventId, params: params);
- }
- 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((duration) {
- debugPrint('音频播放时长 == ${duration.inMilliseconds}');
- playingDuration.value = duration;
- if (audioDuration.value.inMilliseconds > 0) {
- audioProgressValue.value =
- (duration.inMilliseconds / audioDuration.value.inMilliseconds)
- .clamp(0.0, sliderMax);
- }
- });
- }
- void _dealTalk(TalkBean? bean) async {
- debugPrint('talkBean == $bean');
- String? id = bean?.id;
- if (id == null) {
- return;
- }
- _loadAudioFile(bean);
- if (bean?.status.value == TalkStatus.notAnalysis) {
- setUploadingProgress(id);
- }
- if (bean?.status.value == TalkStatus.notAnalysis &&
- talkRepository.isUploadingTalk(id)) {
- isUploading.value = true;
- } else {
- isUploading.value = false;
- }
- }
- void setUploadingProgress(String id) {
- talkRepository.getUploadProgress(id).listen((progress) {
- uploadProgress.value = (progress * 20).toFormattedDouble(1);
- });
- }
- Future<void> _loadAudioFile(TalkBean? bean, {bool? loadPlay}) async {
- try {
- Uri? uri;
- if (bean?.isExample == true && bean?.audioUrl != null) {
- uri = Uri.parse(bean!.audioUrl!);
- } else {
- File? file = await getFileByTalk(talkBean.value);
- if (file?.existsSync() == true) {
- uri = file?.uri;
- }
- }
- if (uri == null) {
- throw '音频文件不存在';
- }
- await _audioPlayer.setAudioSource(AudioSource.uri(uri));
- if (loadPlay == true) {
- clickPlayAudio();
- }
- audioFileIsExist = true;
- } catch (e) {
- audioFileIsExist = false;
- debugPrint('音频设置异常 == $e');
- }
- }
- void _getArguments() {
- TalkBean? bean = parameters?[argumentItem];
- if (bean != null) {
- talkBean.value = bean;
- _dealTalk(bean);
- } else {
- paramId = parameters?[argumentTalkId];
- if (paramId != null) {
- talkRepository.talkInfo(paramId!).then((data) {
- talkBean.value = data.talkInfo;
- _dealTalk(data.talkInfo);
- }).catchError((error) {
- ErrorHandler.toastError(error);
- });
- }
- }
- eventTag = parameters?[argumentEventTag];
- }
- void updateProgress(double value) {
- final newPosition = Duration(
- milliseconds: (value * audioDuration.value.inMilliseconds).toInt());
- _audioPlayer.seek(newPosition);
- }
- void clickPlayAudio() async {
- if (audioFileIsExist != true && isLocalFileHas == false) {
- ToastUtil.showToast(StringName.talkFileNotFind.tr);
- return;
- }
- if (isLocalFileHas == true &&
- audioFileIsExist == false &&
- !await AudioPickerUtils.hasPermission()) {
- bool has = await AudioPickerUtils.requestPermissionExtend();
- if (has == false) {
- ToastUtil.showToast(StringName.authorizationFailed.tr);
- return;
- }
- //重新加载
- _loadAudioFile(talkBean.value, loadPlay: true);
- 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);
- if (isLocalFileHas == true &&
- audioFileIsExist == false &&
- !await AudioPickerUtils.hasPermission()) {
- bool has = await AudioPickerUtils.requestPermissionExtend();
- if (has == false) {
- ToastUtil.showToast(StringName.authorizationFailed.tr);
- return;
- }
- //重新上传
- _checkFileSizeAndNet();
- return;
- }
- 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> 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;
- }
- eventReport(EventId.event_101002);
- talkRepository.checkElectric(duration).then((data) {
- if (data.enough) {
- //检查网络以及文件大小
- _checkFileSizeAndNet();
- } else {
- ToastUtil.showToast(StringName.talkAnalyseLowToast.tr);
- isShowElectricLow.value = true;
- }
- }).catchError((error) {
- ErrorHandler.toastError(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;
- WakelockPlus.enable();
- talkRepository.uploadTalkFile(talkId, duration, file).then((taskId) {
- isUploadedFile = true;
- isUploading.value = false;
- talkBean.value?.progressContent.value = '录音上传中,请勿关闭小听';
- talkBean.value?.progress.value = 20;
- talkBean.value?.status.value = TalkStatus.analysing;
- taskRepository.addTask(taskId);
- }).catchError((error) {
- isUploading.value = false;
- ErrorHandler.toastError(error);
- }).whenComplete(() => WakelockPlus.disable());
- }
- 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() async {
- if (!await checkLogin()) {
- return;
- }
- if (talkBean.value != null) {
- eventReport(EventId.event_101003);
- ChatPage.startByTalk(
- talkBean.value!.isExample == true
- ? ChatFromType.fromTalkExample
- : ChatFromType.fromTalkDetail,
- talkBean.value!);
- }
- }
- void onGoElectricStore() {
- StorePage.start(fromType: StoreFromType.analyse);
- Future.delayed(const Duration(milliseconds: 250), () {
- isShowElectricLow.value = false;
- });
- }
- void onEditModelClick() async {
- if (!await checkLogin()) {
- return;
- }
- _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<AgendaUpdateBean> 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<Agenda>? 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<Agenda> 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();
- }
- Future<File?> getFileByTalk(TalkBean? bean) async {
- isLocalFileHas = false;
- if (bean == null) {
- return null;
- }
- if (bean.uploadType == TalkUploadType.localUpload) {
- String? audioId =
- FileUploadCheckHelper.getLocalAudioId(bean.localAudioUrl);
- if (audioId != null && audioId.isNotEmpty) {
- isLocalFileHas = true;
- return await AudioPickerUtils.getAssetFile(audioId);
- } else {
- return await FileUploadCheckHelper.getChoiceUploadFile(bean.id);
- }
- } else {
- return await RecordHandler.getRecordFile(bean.id);
- }
- }
- Future<bool> checkLogin() async {
- if (!accountRepository.isLogin.value) {
- bool isLogin = await LoginPage.start(fromType: LoginFromType.talkDetail);
- if (isLogin) {
- backToSpecificPage(RoutePath.mainTab);
- }
- return false;
- }
- return true;
- }
- void onShareClick() async {
- if (!await checkLogin()) {
- return;
- }
- if (talkBean.value?.status.value != TalkStatus.analysisSuccess) {
- return;
- }
- eventReport(EventId.event_101004);
- showTalkShareDialog(talkBean.value?.title.value,
- (type, shareTo, fileName, tag) {
- talkRepository
- .talkExport(talkBean.value!.id, fileName, type)
- .then((file) async {
- if (shareTo == ShareTo.ios) {
- await Share.shareXFiles([XFile(file.path)], subject: fileName);
- } else if (shareTo == ShareTo.wechat) {
- await SystemShareUtil.shareWechatFile(file.path);
- } else {
- await SystemShareUtil.shareQQFile(file.path);
- }
- SmartDialog.dismiss(tag: tag);
- }).catchError((error) {
- if (error is SystemShareException) {
- ToastUtil.showToast(error.message);
- } else {
- ErrorHandler.toastError(error);
- }
- });
- });
- }
- @override
- void onClose() {
- super.onClose();
- _talkUploadListener?.cancel();
- _talkBeanListener?.cancel();
- _audioPlayer.dispose();
- _agendaContentController?.dispose();
- _agendaNameController?.dispose();
- }
- void seekTo(int? startMs) {
- if (startMs == null) {
- return;
- }
- _audioPlayer.seek(Duration(milliseconds: startMs));
- }
- }
|