Quellcode durchsuchen

[new]测试文件选择上传功能

zk vor 1 Jahr
Ursprung
Commit
61f20d3077

+ 1 - 0
android/app/src/main/AndroidManifest.xml

@@ -11,6 +11,7 @@
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
     <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
 
     <application
         android:name=".MyApplication"

+ 1 - 1
android/build.gradle

@@ -3,7 +3,7 @@ allprojects {
         compileSdkVersion = 34
         applicationId = "com.xingmeng.xiaoting"
         minSdkVersion = 23
-        targetSdkVersion = 32
+        targetSdkVersion = 33
 
         appcompat_version = "1.6.1"
         constraintlayout_version = "2.1.4"

+ 5 - 0
lib/data/bean/talks.dart

@@ -23,6 +23,9 @@ class TalkBean {
   @JsonKey(name: 'characters')
   int? characters;
 
+  @JsonKey(name: 'localAudioUrl')
+  String? localAudioUrl;
+
   @JsonKey(name: 'status', fromJson: _intFromJson, includeToJson: false)
   Rxn<int> status;
 
@@ -58,6 +61,7 @@ class TalkBean {
     this.isExample,
     this.oversizeFile,
     this.uploadType,
+    this.localAudioUrl,
   });
 
   factory TalkBean.fromJson(Map<String, dynamic> json) =>
@@ -80,6 +84,7 @@ class TalkBean {
     isExample = talkBean.isExample;
     oversizeFile = talkBean.oversizeFile;
     uploadType = talkBean.uploadType;
+    localAudioUrl = talkBean.localAudioUrl;
   }
 }
 

+ 62 - 0
lib/handler/audio_picker_handler.dart

@@ -0,0 +1,62 @@
+import 'dart:io';
+
+import 'package:photo_manager/photo_manager.dart';
+
+class AudioPickerHandler {
+  AudioPickerHandler._();
+
+  //申请权限
+  static Future<bool> requestPermissionExtend() async {
+    final PermissionState ps = await PhotoManager.requestPermissionExtend();
+    return ps.hasAccess;
+  }
+
+  //是否有权限
+  static Future<bool> hasPermission() async {
+    final PermissionState ps = await PhotoManager.getPermissionState(
+        requestOption: const PermissionRequestOption());
+    return ps.hasAccess;
+  }
+
+  //获取AssetPathEntity
+  static Future<List<AssetPathEntity>> getAssetPathList() async {
+    final PMFilter filter = FilterOptionGroup(
+      audioOption: const FilterOption(
+        sizeConstraint: SizeConstraint(ignoreSize: true),
+      ),
+    );
+    final List<AssetPathEntity> paths = await PhotoManager.getAssetPathList(
+      onlyAll: true,
+      type: RequestType.audio,
+      filterOption: filter,
+    );
+    return paths;
+  }
+
+  //获取本地音频资源列表
+  static Future<List<AssetEntity>> getAssetList(AssetPathEntity path, int page,
+      {int size = 100}) async {
+    final List<AssetEntity> entities = await path.getAssetListPaged(
+      page: page,
+      size: size,
+    );
+    return entities;
+  }
+
+  //根据id获取AssetEntity
+  static Future<AssetEntity?> getAssetEntity(String? id) async {
+    if (id == null) {
+      return null;
+    }
+    return await AssetEntity.fromId(id);
+  }
+
+  //根据id获取文件
+  static Future<File?> getAssetFile(String? id) async {
+    if (id == null) {
+      return null;
+    }
+    AssetEntity? assetEntity = await AssetEntity.fromId(id);
+    return await assetEntity?.file;
+  }
+}

+ 63 - 0
lib/module/audiopicker/controller.dart

@@ -0,0 +1,63 @@
+import 'dart:io';
+
+import 'package:electronic_assistant/base/base_controller.dart';
+import 'package:electronic_assistant/handler/audio_picker_handler.dart';
+import 'package:electronic_assistant/utils/toast_util.dart';
+import 'package:get/get.dart';
+import 'package:photo_manager/photo_manager.dart';
+import 'package:uuid/uuid.dart';
+
+import '../../data/bean/talks.dart';
+import '../../data/repositories/talk_repository.dart';
+import '../talk/view.dart';
+
+class AudioPickerController extends BaseController {
+  final audioList = RxList<AssetEntity>();
+  AssetPathEntity? currentPath;
+
+  @override
+  void onReady() async {
+    super.onReady();
+    if (!await AudioPickerHandler.hasPermission()) {
+      bool permission = await AudioPickerHandler.requestPermissionExtend();
+      if (!permission) {
+        ToastUtil.showToast('授权失败');
+        return;
+      }
+    }
+    currentPath = await initPathEntity();
+    requestList();
+  }
+
+  void requestList() {
+    if (currentPath == null) {
+      return;
+    }
+    AudioPickerHandler.getAssetList(currentPath!, 0).then((value) {
+      audioList.addAll(value);
+    });
+  }
+
+  Future<AssetPathEntity?> initPathEntity() async {
+    List<AssetPathEntity> listEntity =
+        await AudioPickerHandler.getAssetPathList();
+    if (listEntity.isEmpty) {
+      return null;
+    }
+    return listEntity.first;
+  }
+
+  void onItemClick(AssetEntity entity) async {
+    File? file = await entity.file;
+    if (file == null) {
+      ToastUtil.showToast('文件不存在');
+      return;
+    }
+    //上传文件
+    TalkBean bean = await talkRepository.talkCreate(
+        const Uuid().v4(), entity.duration,
+        localAudioUrl: entity.id, uploadType: 1);
+    Get.back();
+    TalkPage.start(bean);
+  }
+}

+ 56 - 0
lib/module/audiopicker/view.dart

@@ -0,0 +1,56 @@
+import 'package:electronic_assistant/base/base_page.dart';
+import 'package:electronic_assistant/data/bean/store_item.dart';
+import 'package:electronic_assistant/utils/expand.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart';
+import 'package:photo_manager/photo_manager.dart';
+
+import 'controller.dart';
+
+class AudioPickerPage extends BasePage<AudioPickerController> {
+  const AudioPickerPage({super.key});
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Container(
+      child: Column(
+        children: [
+          Text('AudioPickerPage'),
+          Expanded(child: Obx(() {
+            return ListView.builder(
+              itemBuilder: _buildItem,
+              itemCount: controller.audioList.length,
+            );
+          }))
+        ],
+      ),
+    );
+  }
+
+  Widget _buildItem(BuildContext context, int index) {
+    AssetEntity entity = controller.audioList[index];
+    return GestureDetector(
+      onTap: () {
+        controller.onItemClick(entity);
+      },
+      child: Container(
+        margin: EdgeInsets.only(bottom: 10),
+        padding: EdgeInsets.all(10),
+        child: Row(
+          children: [
+            Column(
+              crossAxisAlignment: CrossAxisAlignment.start,
+              children: [
+                Text(entity.title ?? ''),
+                Text(
+                    '创建时间:${entity.createDateSecond?.toFormattedDate('yyyy-MM-dd HH:mm:ss')}'),
+                Text('时长:${entity.duration}'),
+              ],
+            )
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 69 - 67
lib/module/home/controller.dart

@@ -122,73 +122,75 @@ class HomePageController extends BaseController {
   }
 
   void onPickerAudioFile() async {
-    EventHandler.report(EventId.event_100030);
-    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();
-      }
-    }
+    Get.toNamed(RoutePath.audioPicker);
+
+    // EventHandler.report(EventId.event_100030);
+    // 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 {

+ 9 - 9
lib/module/talk/controller.dart

@@ -6,6 +6,7 @@ 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/audio_picker_handler.dart';
 import 'package:electronic_assistant/handler/event_handler.dart';
 import 'package:electronic_assistant/module/chat/view.dart';
 import 'package:electronic_assistant/module/home/controller.dart';
@@ -35,7 +36,6 @@ 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';
@@ -188,7 +188,7 @@ class TalkController extends BaseController {
       if (bean?.isExample == true && bean?.audioUrl != null) {
         uri = Uri.parse(bean!.audioUrl!);
       } else {
-        File? file = await getFileByTalk(id, bean?.uploadType);
+        File? file = await getFileByTalk(talkBean.value);
         if (file?.existsSync() == true) {
           uri = file?.uri;
         }
@@ -241,8 +241,7 @@ class TalkController extends BaseController {
     if (id == null) {
       return;
     }
-    File? file =
-        await getFileByTalk(talkBean.value?.id, talkBean.value?.uploadType);
+    File? file = await getFileByTalk(talkBean.value);
     if (file == null || !file.existsSync()) {
       ToastUtil.showToast(StringName.talkUploadFileNotExist.tr);
       return;
@@ -510,13 +509,14 @@ class TalkController extends BaseController {
   }
 }
 
-Future<File?> getFileByTalk(String? talkId, int? uploadType) async {
-  if (talkId == null) {
+Future<File?> getFileByTalk(TalkBean? bean) async {
+  if (bean == null) {
     return null;
   }
-  if (uploadType == TalkUploadType.localUpload) {
-    return await getChoiceUploadFile(talkId);
+  if (bean.uploadType == TalkUploadType.localUpload) {
+    return await AudioPickerHandler.getAssetFile(bean.localAudioUrl);
+    // return await getChoiceUploadFile(bean.id);
   } else {
-    return await RecordController.getRecordFile(talkId);
+    return await RecordController.getRecordFile(bean.id);
   }
 }

+ 6 - 0
lib/router/app_pages.dart

@@ -1,3 +1,5 @@
+import 'package:electronic_assistant/module/audiopicker/controller.dart';
+import 'package:electronic_assistant/module/audiopicker/view.dart';
 import 'package:electronic_assistant/module/browser/controller.dart';
 import 'package:electronic_assistant/module/browser/view.dart';
 import 'package:electronic_assistant/module/chat/start/controller.dart';
@@ -64,6 +66,8 @@ abstract class RoutePath {
   static const browser = '/browser';
 
   static const agendaDetail = '/agendaDetail';
+
+  static const audioPicker = '/audioPicker';
 }
 
 class AppBinding extends Bindings {
@@ -87,6 +91,7 @@ class AppBinding extends Bindings {
     lazyPut(() => BrowserController());
     lazyPut(() => MainDrawerController());
     lazyPut(() => AgendaDetailController());
+    lazyPut(() => AudioPickerController());
   }
 
   void lazyPut<S>(InstanceBuilderCallback<S> builder) {
@@ -108,4 +113,5 @@ final generalPages = [
   GetPage(name: RoutePath.talkDetail, page: () => const TalkPage()),
   GetPage(name: RoutePath.browser, page: () => const BrowserPage()),
   GetPage(name: RoutePath.agendaDetail, page: () => const AgendaDetailPage()),
+  GetPage(name: RoutePath.audioPicker, page: () => const AudioPickerPage()),
 ];

+ 17 - 0
lib/utils/expand.dart

@@ -1,5 +1,22 @@
 import 'dart:ui';
 
+extension IntExtensions on int? {
+  String toFormattedDate(String format) {
+    if (this == null) {
+      return '';
+    }
+    DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(this! * 1000);
+    String formattedDate = format
+        .replaceAll('yyyy', dateTime.year.toString())
+        .replaceAll('MM', dateTime.month.toString().padLeft(2, '0'))
+        .replaceAll('dd', dateTime.day.toString().padLeft(2, '0'))
+        .replaceAll('HH', dateTime.hour.toString().padLeft(2, '0'))
+        .replaceAll('mm', dateTime.minute.toString().padLeft(2, '0'))
+        .replaceAll('ss', dateTime.second.toString().padLeft(2, '0'));
+    return formattedDate;
+  }
+}
+
 extension HexColor on String {
   Color get color => toColor();
 

+ 8 - 0
pubspec.lock

@@ -1003,6 +1003,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "6.0.2"
+  photo_manager:
+    dependency: "direct main"
+    description:
+      name: photo_manager
+      sha256: "32a1ce1095aeaaa792a29f28c1f74613aa75109f21c2d4ab85be3ad9964230a4"
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.5.0"
   platform:
     dependency: transitive
     description:

+ 3 - 0
pubspec.yaml

@@ -111,6 +111,9 @@ dependencies:
   #屏幕常亮
   wakelock_plus: ^1.2.8
 
+  #获取音频资源列表
+  photo_manager: ^3.5.0
+
 
 dev_dependencies:
   flutter_test: