Ver Fonte

[new]增加导入音频流程

zk há 1 ano atrás
pai
commit
5b37c6d18f

+ 5 - 0
assets/string/base/string.xml

@@ -92,4 +92,9 @@
     <string name="record_notification_channel_name">录音</string>
     <string name="record_notification_channel_description">用于展示录音状态</string>
     <string name="home_charge_txt">充电</string>
+    <string name="file_choice_size_limit">文件最大不能超过500MB</string>
+    <string name="file_audio_duration_cannot_obtained">录音时长无法获取</string>
+    <string name="file_audio_duration_limit">录音时长不能超过5小时</string>
+    <string name="file_import_fail">导入失败</string>
+    <string name="file_importing">正在导入...</string>
 </resources>

+ 8 - 1
lib/data/api/request/talk_create_request.dart

@@ -12,7 +12,14 @@ class TalkCreateRequest extends AppBaseRequest {
   @JsonKey(name: 'requestId')
   final String requestId;
 
-  TalkCreateRequest(this.duration, this.requestId);
+  @JsonKey(name: 'localAudioUrl')
+  String? localAudioUrl;
+
+  @JsonKey(name: 'uploadType')
+  int? uploadType;
+
+  TalkCreateRequest(this.duration, this.requestId,
+      {this.localAudioUrl, this.uploadType});
 
   @override
   Map<String, dynamic> toJson() => _$TalkCreateRequestToJson(this);

+ 26 - 13
lib/data/bean/talks.dart

@@ -41,19 +41,24 @@ class TalkBean {
   @JsonKey(name: 'oversizeFile')
   bool? oversizeFile;
 
-  TalkBean(
-      {required this.id,
-      this.taskId,
-      this.ssid,
-      this.audioUrl,
-      this.duration,
-      this.characters,
-      required this.status,
-      required this.title,
-      required this.summary,
-      this.createTime,
-      this.isExample,
-      this.oversizeFile});
+  @JsonKey(name: 'uploadType')
+  int? uploadType;
+
+  TalkBean({
+    required this.id,
+    this.taskId,
+    this.ssid,
+    this.audioUrl,
+    this.duration,
+    this.characters,
+    required this.status,
+    required this.title,
+    required this.summary,
+    this.createTime,
+    this.isExample,
+    this.oversizeFile,
+    this.uploadType,
+  });
 
   factory TalkBean.fromJson(Map<String, dynamic> json) =>
       _$TalkBeanFromJson(json);
@@ -74,6 +79,7 @@ class TalkBean {
     createTime = talkBean.createTime;
     isExample = talkBean.isExample;
     oversizeFile = talkBean.oversizeFile;
+    uploadType = talkBean.uploadType;
   }
 }
 
@@ -87,3 +93,10 @@ class TalkStatus {
   static int analysisFail = 3;
   static int notAnalysis = 4;
 }
+
+class TalkUploadType {
+  TalkUploadType._();
+
+  static int record = 0;
+  static int localUpload = 1;
+}

+ 10 - 3
lib/data/repositories/talk_repository.dart

@@ -119,10 +119,17 @@ class TalkRepository {
         .then(HttpHandler.handle(true));
   }
 
-  Future<TalkBean> talkCreate(String requestId, int duration) {
+  Future<TalkBean> talkCreate(String requestId, int duration,
+      {String? localAudioUrl, int? uploadType}) {
     return atmobApi
-        .talkCreate(TalkCreateRequest(duration, requestId))
-        .then(HttpHandler.handle(true));
+        .talkCreate(TalkCreateRequest(duration, requestId,
+            localAudioUrl: localAudioUrl, uploadType: uploadType))
+        .then(HttpHandler.handle(true))
+        .then((bean) {
+      //添加新的录音到最新记录
+      talkRepository.addNewTalkData(bean);
+      return bean;
+    });
   }
 
   Future<String> uploadTalkFile(String talkId, double duration, File file) {

+ 11 - 0
lib/dialog/loading_dialog.dart

@@ -0,0 +1,11 @@
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+
+class LoadingDialog {
+  static void show(String msg) {
+    SmartDialog.showLoading(msg: msg, backDismiss: false);
+  }
+
+  static void hide() {
+    SmartDialog.dismiss();
+  }
+}

+ 103 - 0
lib/module/home/controller.dart

@@ -1,19 +1,28 @@
+import 'dart:io';
+
 import 'package:electronic_assistant/base/base_controller.dart';
 import 'package:electronic_assistant/data/bean/talks.dart';
 import 'package:electronic_assistant/data/repositories/agenda_repository.dart';
 import 'package:electronic_assistant/data/repositories/config_repository.dart';
 import 'package:electronic_assistant/data/repositories/talk_repository.dart';
 import 'package:electronic_assistant/module/main/controller.dart';
+import 'package:electronic_assistant/module/talk/view.dart';
 import 'package:electronic_assistant/resource/string.gen.dart';
+import 'package:electronic_assistant/router/app_pages.dart';
 import 'package:electronic_assistant/utils/event_bus.dart';
 import 'package:electronic_assistant/widget/pull_to_refresh.dart';
+import 'package:file_picker/file_picker.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:get/get.dart';
+import 'package:just_audio/just_audio.dart';
+import 'package:uuid/uuid.dart';
 import '../../data/api/response/example_info_response.dart';
 import '../../data/bean/agenda.dart';
 import '../../data/repositories/account_repository.dart';
+import '../../dialog/loading_dialog.dart';
 import '../../utils/error_handler.dart';
 import '../../utils/toast_util.dart';
+import 'package:path_provider/path_provider.dart';
 
 class HomePageController extends BaseController {
   get isLogin => accountRepository.isLogin.value;
@@ -109,4 +118,98 @@ class HomePageController extends BaseController {
       ErrorHandler.toastError(error);
     });
   }
+
+  void onPickerAudioFile() async {
+    if (!accountRepository.isLogin.value) {
+      Get.toNamed(RoutePath.login);
+      ToastUtil.showToast(StringName.errorCodeNoLogin.tr);
+      return;
+    }
+
+    FilePickerResult? result = await FilePicker.platform
+        .pickFiles(type: FileType.custom, allowedExtensions: [
+      'wav',
+      'mp3',
+      'm4a',
+      'flv',
+      'mp4',
+      'wma',
+      '3gp',
+      'amr',
+      'aac',
+      'ogg-opus',
+      'flac'
+    ]);
+    if (result != null) {
+      LoadingDialog.show(StringName.fileImporting.tr);
+      AudioPlayer? player;
+      try {
+        String filePath = result.files.single.path!;
+        File file = File(filePath);
+        //文件不能超过500M
+        if (file.lengthSync() > 500 * 1024 * 1024) {
+          ToastUtil.showToast(StringName.fileChoiceSizeLimit.tr);
+          return;
+        }
+        player = AudioPlayer();
+        player.setAudioSource(AudioSource.uri(file.uri));
+        Duration? duration = await player.durationStream
+            .firstWhere((duration) => duration != null);
+        if (duration == null) {
+          ToastUtil.showToast(StringName.fileAudioDurationCannotObtained.tr);
+          return;
+        }
+        //录音时长不能超过5小时
+        if (duration.inHours > 5) {
+          ToastUtil.showToast(StringName.fileAudioDurationLimit.tr);
+          return;
+        }
+        late TalkBean bean;
+        try {
+          bean = await talkRepository.talkCreate(
+              const Uuid().v4(), duration.inSeconds,
+              localAudioUrl: filePath, uploadType: 1);
+        } catch (e) {
+          ErrorHandler.toastError(e);
+          return;
+        }
+        String childDirName = bean.id;
+        Directory dir = await _getChoiceUploadDir(childDirName);
+
+        await moveFileToDirectory(file, dir);
+
+        TalkPage.start(bean);
+      } catch (e) {
+        ErrorHandler.toastError(e, message: StringName.fileImportFail.tr);
+      } finally {
+        player?.dispose();
+        LoadingDialog.hide();
+      }
+    }
+  }
+
+  Future<void> moveFileToDirectory(File file, Directory dir) async {
+    if (!dir.existsSync()) {
+      dir.createSync(recursive: true);
+    }
+    String newFilePath = '${dir.path}/${file.uri.pathSegments.last}';
+    file.renameSync(newFilePath);
+  }
+}
+
+Future<Directory> _getChoiceUploadDir(String talkId) async {
+  Directory documentDir = await getApplicationDocumentsDirectory();
+  return Directory("${documentDir.path}/.atmob/choice/$talkId");
+}
+
+Future<File?> getChoiceUploadFile(String talkId) async {
+  Directory dir = await _getChoiceUploadDir(talkId);
+  if (!dir.existsSync()) {
+    return null;
+  }
+  List<FileSystemEntity> list = dir.listSync();
+  if (list.isEmpty) {
+    return null;
+  }
+  return list.first as File;
 }

+ 3 - 2
lib/module/home/view.dart

@@ -127,8 +127,9 @@ class HomePage extends BasePage<HomePageController> {
                   [
                     "#5869ED".toColor(),
                     "#6E8AF7".toColor(),
-                  ],
-                  onTap: () {}),
+                  ], onTap: () {
+                controller.onPickerAudioFile();
+              }),
             ),
           ],
         ),

+ 0 - 2
lib/module/record/controller.dart

@@ -235,8 +235,6 @@ class RecordController extends BaseController {
     talkRepository
         .talkCreate(_lastRecordId, currentDuration.value.toInt())
         .then((talkInfo) async {
-      //添加新的录音到最新记录
-      talkRepository.addNewTalkData(talkInfo);
       File pcmFile = await _getCurrentRecordFile();
       if (pcmFile.existsSync()) {
         File wavFile = await getRecordFile(talkInfo.id);

+ 30 - 10
lib/module/talk/controller.dart

@@ -4,6 +4,7 @@ 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/view.dart';
 import 'package:electronic_assistant/resource/assets.gen.dart';
@@ -21,6 +22,7 @@ import '../../data/bean/talks.dart';
 import '../../data/repositories/agenda_repository.dart';
 import '../../data/repositories/talk_repository.dart';
 import '../../dialog/alert_dialog.dart';
+import '../../router/app_pages.dart';
 import '../../utils/toast_util.dart';
 import '../record/controller.dart';
 import 'original/view.dart';
@@ -126,12 +128,15 @@ class TalkController extends BaseController {
       isUploading.value = false;
     }
     try {
-      Uri uri;
+      Uri? uri;
       if (bean?.isExample == true && bean?.audioUrl != null) {
         uri = Uri.parse(bean!.audioUrl!);
       } else {
-        File file = await RecordController.getRecordFile(id);
-        uri = file.uri;
+        File? file = await getFileByTalk(id, bean?.uploadType);
+        uri = file?.uri;
+      }
+      if (uri == null) {
+        throw '音频文件不存在';
       }
       await _audioPlayer.setAudioSource(AudioSource.uri(uri));
       audioFileIsExist = true;
@@ -176,8 +181,9 @@ class TalkController extends BaseController {
     if (id == null) {
       return;
     }
-    File file = await RecordController.getRecordFile(id);
-    if (!file.existsSync()) {
+    File? file =
+        await getFileByTalk(talkBean.value?.id, talkBean.value?.uploadType);
+    if (file == null || !file.existsSync()) {
       ToastUtil.showToast(StringName.talkUploadFileNotExist.tr);
       return;
     }
@@ -258,7 +264,7 @@ class TalkController extends BaseController {
         });
   }
 
-  void checkCanAnalyze() async {
+  void checkCanAnalyze() {
     String? id = talkBean.value?.id;
     double? duration = talkBean.value?.duration;
     if (id == null || duration == null) {
@@ -294,10 +300,6 @@ class TalkController extends BaseController {
     });
   }
 
-  void goElectricStore() {
-    //TODO 跳转至商店页
-  }
-
   void refreshAgendaAllData() {
     String? id = talkBean.value?.id;
     if (id == null || agendaAllList.isNotEmpty) {
@@ -322,4 +324,22 @@ class TalkController extends BaseController {
     _talkBeanListener?.cancel();
     _audioPlayer.dispose();
   }
+
+  void onGoElectricStore() {
+    Get.toNamed(RoutePath.store);
+    Future.delayed(const Duration(milliseconds: 250), () {
+      isShowElectricLow.value = false;
+    });
+  }
+}
+
+Future<File?> 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);
+  }
 }

+ 35 - 33
lib/module/talk/view.dart

@@ -199,37 +199,39 @@ class TalkPage extends BasePage<TalkController> {
   }
 
   Widget buildElectricLowView() {
-    return Container(
-      color: const Color(0xFFDFE4FC),
-      padding: EdgeInsets.only(left: 16.w, right: 12.w, top: 8.h, bottom: 8.h),
-      child: Row(
-        children: [
-          SizedBox(
-              width: 46.w,
-              height: 56.w,
-              child: Assets.images.iconTalkElectricLow.image()),
-          SizedBox(width: 10.w),
-          IntrinsicHeight(
-            child: Column(
-              crossAxisAlignment: CrossAxisAlignment.start,
-              children: [
-                SizedBox(
-                    width: 90.w,
-                    height: 21.w,
-                    child: Assets.images.iconTalkElectricLowTxt.image()),
-                SizedBox(width: 1.w),
-                Text(StringName.talkElectricLow.tr,
-                    style: TextStyle(
-                        fontSize: 12.sp, color: ColorName.secondaryTextColor)),
-              ],
+    return GestureDetector(
+      onTap: () {
+        controller.onGoElectricStore();
+      },
+      child: Container(
+        color: const Color(0xFFDFE4FC),
+        padding:
+            EdgeInsets.only(left: 16.w, right: 12.w, top: 8.h, bottom: 8.h),
+        child: Row(
+          children: [
+            SizedBox(
+                width: 46.w,
+                height: 56.w,
+                child: Assets.images.iconTalkElectricLow.image()),
+            SizedBox(width: 10.w),
+            IntrinsicHeight(
+              child: Column(
+                crossAxisAlignment: CrossAxisAlignment.start,
+                children: [
+                  SizedBox(
+                      width: 90.w,
+                      height: 21.w,
+                      child: Assets.images.iconTalkElectricLowTxt.image()),
+                  SizedBox(width: 1.w),
+                  Text(StringName.talkElectricLow.tr,
+                      style: TextStyle(
+                          fontSize: 12.sp,
+                          color: ColorName.secondaryTextColor)),
+                ],
+              ),
             ),
-          ),
-          const Spacer(),
-          GestureDetector(
-            onTap: () {
-              controller.goElectricStore();
-            },
-            child: Container(
+            const Spacer(),
+            Container(
                 decoration: getPrimaryBtnDecoration(8),
                 width: 100.w,
                 height: 36.w,
@@ -237,9 +239,9 @@ class TalkPage extends BasePage<TalkController> {
                   child: Text(StringName.talkGoStore.tr,
                       style:
                           TextStyle(fontSize: 16.sp, color: ColorName.white)),
-                )),
-          )
-        ],
+                ))
+          ],
+        ),
       ),
     );
   }

+ 4 - 0
pubspec.yaml

@@ -87,6 +87,10 @@ dependencies:
   device_info_plus: ^10.1.2
   custom_platform_device_id: ^1.0.8
 
+  #文件选择
+  file_picker: ^8.1.2
+
+
 dev_dependencies:
   flutter_test:
     sdk: flutter