From cca3c4484b311a20c5e74850d38e2de031fde2ef Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Mon, 23 Jun 2025 20:33:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96ui=E7=AD=89=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/meeting_controller.dart | 31 +++--- .../meeting_record_controller.dart | 17 +++- .../controllers/translation_controller.dart | 62 ++++++++---- .../translation/views/translation_view.dart | 95 +++++++++++++------ 4 files changed, 140 insertions(+), 65 deletions(-) diff --git a/lib/modules/meeting/controllers/meeting_controller.dart b/lib/modules/meeting/controllers/meeting_controller.dart index 0058b31d8..fc7f46938 100644 --- a/lib/modules/meeting/controllers/meeting_controller.dart +++ b/lib/modules/meeting/controllers/meeting_controller.dart @@ -182,27 +182,34 @@ class MeetingController extends GetxController { void addMeetingAudio() async { EasyLoading.show(status: '正在导入本地录音...'); try { + int addUp = 0; final appDir = await appDocDir; - final sourceDir = Directory("${appDir.path}/MeetingAudio/"); - // 检查源目录是否存在 - if (!await sourceDir.exists()) { - EasyLoading.dismiss(); - return; - } +// 定义源目录列表 + final sourceDirs = [ + Directory("${appDir.path}/MeetingAudio/"), + Directory("${appDir.path}/DestAudio/"), + ]; - // 创建目标目录(如果不存在) +// 创建目标目录(如果不存在) final destDir = Directory("${appDir.path}/LocalAudio/"); if (!await destDir.exists()) { await destDir.create(recursive: true); } - // 获取目录下所有文件 - final files = - await sourceDir.list().where((entity) => entity is File).toList(); + List allFiles = []; - int addUp = 0; - for (final fileEntity in files) { + for (final dir in sourceDirs) { + if (await dir.exists()) { + final files = + await dir.list().where((entity) => entity is File).toList(); + allFiles.addAll(files); + } + } + +// 此时 allFiles 包含 MeetingAudio 和 DestAudio 目录下所有的文件 + + for (final fileEntity in allFiles) { try { final file = File(fileEntity.path); final filePath = file.path; diff --git a/lib/modules/meeting/controllers/meeting_record_controller.dart b/lib/modules/meeting/controllers/meeting_record_controller.dart index ee4640b18..f9f66fc7e 100644 --- a/lib/modules/meeting/controllers/meeting_record_controller.dart +++ b/lib/modules/meeting/controllers/meeting_record_controller.dart @@ -335,10 +335,19 @@ class MeetingRecordController extends GetxController final appDir = await appDocDir; final dir = Directory("${appDir.path}/MeetingAudio/"); if (!await dir.exists()) await dir.create(recursive: true); - await _asrService.renameFile( - "${dir.path}/${fileName.value}.wav", - newName.value, - ); + if (isMarked.value) { + final Newdir = Directory("${appDir.path}/DestAudio/"); + if (!await Newdir.exists()) await Newdir.create(recursive: true); + await _asrService.moveFile( + "${dir.path}/${fileName.value}.wav", + "${Newdir.path}/${fileName.value}.wav", + ); + } else { + await _asrService.renameFile( + "${dir.path}/${fileName.value}.wav", + newName.value, + ); + } } await _asrService.stopContinuousRecognition(); diff --git a/lib/modules/translation/controllers/translation_controller.dart b/lib/modules/translation/controllers/translation_controller.dart index dd307a50f..0fb6d465d 100644 --- a/lib/modules/translation/controllers/translation_controller.dart +++ b/lib/modules/translation/controllers/translation_controller.dart @@ -36,6 +36,11 @@ class TranslationController extends GetxController { final bleManager = Get.find(); // 音频输入源 bool _audioSourceType = false; + // 倒计时器 + + final RxString recordDurationText = '00:00'.obs; + Timer? _recordTimer; + int _seconds = 0; // 存储相关 static const String _historyKey = 'translation_history'; static const String _sourceLanguageKey = 'translation_source_language'; @@ -198,7 +203,7 @@ class TranslationController extends GetxController { return true; } else { Logger.d("Permission", "用户未授予存储权限"); - return false; + return true; } } catch (e) { Logger.e("Permission", "请求存储权限时出错: $e"); @@ -267,16 +272,45 @@ class TranslationController extends GetxController { Future toggleisRecord() async { isRecording.toggle(); if (isRecording.value && isRecognizing.value) { - // 使用可读性更好的时间格式 - final formattedTime = - DateFormat('yyyyMMdd_HHmmss').format(DateTime.now()); - await _asrService.enableRecord( - "${dir.path}/${currentModeTitle.value.tr}_$formattedTime.wav"); + startRecording(); } else if (!isRecording.value) { - await _asrService.stopRecord(true); + stopRecording(); } else {} } + Future startRecording() async { + final bool storage = await _requestStoragePermission(); + if (!storage) { + Logger.d("Permission", "未授予存储权限,无法提供录音功能"); + return; + } + //isRecording.value = true; + _seconds = 0; + recordDurationText.value = '00:00'; + _recordTimer = Timer.periodic(Duration(seconds: 1), (_) { + _seconds++; + final minutes = (_seconds ~/ 60).toString().padLeft(2, '0'); + final seconds = (_seconds % 60).toString().padLeft(2, '0'); + recordDurationText.value = '$minutes:$seconds'; + }); + + // 启动实际录音逻辑 + // ... + final formattedTime = DateFormat('yyyyMMdd_HHmmss').format(DateTime.now()); + await _asrService.enableRecord( + "${dir.path}/${currentModeTitle.value.tr}_$formattedTime.wav"); + } + + Future stopRecording() async { + _recordTimer?.cancel(); + _recordTimer = null; + isRecording.value = false; + recordDurationText.value = '00:00'; + await _asrService.stopRecord(true); + // 停止实际录音逻辑 + // ... + } + // 开始语音识别 Future startRecognition() async { Logger.info('开始语音识别'); @@ -343,15 +377,7 @@ class TranslationController extends GetxController { await _asrService.startContinuousRecognition(_audioSourceType); isRecognizing.value = true; if (isRecording.value && isRecognizing.value) { - final bool storage = await _requestStoragePermission(); - if (!storage) { - Logger.d("Permission", "未授予存储权限,无法提供录音功能"); - return; - } - final formattedTime = - DateFormat('yyyyMMdd_HHmmss').format(DateTime.now()); - await _asrService.enableRecord( - "${dir.path}/${currentModeTitle.value.tr}_$formattedTime.wav"); + startRecording(); } // 监听识别结果 _recognitionSubscription?.cancel(); @@ -411,7 +437,7 @@ class TranslationController extends GetxController { saveTranslationHistory(); } - + stopRecording(); currentSessionId = null; } catch (e) { Logger.error('停止语音识别失败: ${e.toString()}'); @@ -607,7 +633,7 @@ class TranslationController extends GetxController { if (isRecognizing.value) { await stopRecognition(); } - await _asrService.stopRecord(true); + stopRecording(); _translationDebounceTimer?.cancel(); await _ttsService.stop(); } catch (e) { diff --git a/lib/modules/translation/views/translation_view.dart b/lib/modules/translation/views/translation_view.dart index 3a9df2239..221bab5a7 100644 --- a/lib/modules/translation/views/translation_view.dart +++ b/lib/modules/translation/views/translation_view.dart @@ -107,37 +107,70 @@ class TranslationView extends GetView { )), centerTitle: true, actions: [ - // TTS toggle button - Obx(() => IconButton( - icon: Icon( - controller.isRecording.value - ? Icons.mic - : Icons.fiber_manual_record, - color: isDarkMode ? Colors.white : Colors.black, - size: 24.sp, + Obx(() { + if (controller.isRecording.value) { + // 录音中:显示红色计时器样式,点击可停止录音 + return Padding( + padding: EdgeInsets.only(right: 16.w), + child: GestureDetector( + onTap: controller.toggleisRecord, // 点击结束录音 + child: Container( + padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), + decoration: BoxDecoration( + color: Colors.red, + borderRadius: BorderRadius.circular(20.r), + ), + child: Row( + children: [ + Icon( + Icons.fiber_manual_record, + color: Colors.white, + size: 18.sp, + ), + SizedBox(width: 4.w), + Text( + controller.recordDurationText.value, + style: TextStyle( + color: Colors.white, + fontSize: 14.sp, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), ), - // onPressed: controller.toggleTts,默认播放 - onPressed: controller.toggleisRecord, - tooltip: '是否开启录音', - )), - - // 历史记录按钮 - IconButton( - icon: Icon( - Icons.folder, - color: isDarkMode ? Colors.white : Colors.black, - size: 24.sp, - ), - onPressed: () async { - // 停止语音识别和TTS,释放资源 - await controller.stopAll(); - Get.toNamed(Routes.translationList); - - // 返回时不自动开始翻译,只需重新设置滚动监听器 - controller.setupScrollListener(); - }, - tooltip: '查看录音文件', - ), + ); + } else { + // 未录音:显示红色录音按钮 + 文件夹按钮 + return Row( + children: [ + IconButton( + icon: Icon( + Icons.radio_button_checked, + color: Colors.red, + size: 24.sp, + ), + onPressed: controller.toggleisRecord, + tooltip: '是否开启录音', + ), + IconButton( + icon: Icon( + Icons.folder, + color: isDarkMode ? Colors.white : Colors.black, + size: 24.sp, + ), + onPressed: () async { + await controller.stopAll(); + Get.toNamed(Routes.translationList); + controller.setupScrollListener(); + }, + tooltip: '查看录音文件', + ), + ], + ); + } + }), ], bottom: PreferredSize( preferredSize: Size.fromHeight(50.h), @@ -328,7 +361,7 @@ class TranslationView extends GetView { onTap: () => controller.playTranslationItem(item), child: Icon( Icons.volume_up, - size: 18, + size: 20, color: Theme.of(context).colorScheme.primary, ), ),