Browse Source

Merge branch 'new_dev' of https://github.com/deepcloud2048/deep_voice into new_dev

weicu
liwei1dao 1 year ago
parent
commit
70b575c251
  1. 27
      lib/core/translations/language/en_us.dart
  2. 27
      lib/core/translations/language/zh_cn.dart
  3. 10
      lib/core/utils/permission_util.dart
  4. 57
      lib/modules/devices/views/devices_view.dart
  5. 10
      lib/modules/meeting/controllers/meeting_connect_controller.dart
  6. 35
      lib/modules/meeting/controllers/meeting_home_controller.dart
  7. 9
      lib/modules/meeting/views/tabs/overview_tab.dart
  8. 70
      lib/modules/settings/controllers/settings_controller.dart
  9. 8
      lib/modules/translation/controllers/translation_controller.dart
  10. 4
      local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift

27
lib/core/translations/language/en_us.dart

@ -485,11 +485,11 @@ const Map<String, String> enUS = {
'networkErrorRetry': 'Network error, please try again later',
// 设备管理相关
'clearPairedDevices': 'Clear Paired Devices',
'clearPairedDevicesConfirm':
'Are you sure you want to clear all paired devices?',
'clearPairedDevicesSuccess': 'Clear paired devices successfully',
'clearPairedDevicesFailed': 'Clear paired devices failed',
// 'clearPairedDevices': 'Clear Paired Devices',
// 'clearPairedDevicesConfirm':
// 'Are you sure you want to clear all paired devices?',
// 'clearPairedDevicesSuccess': 'Clear paired devices successfully',
// 'clearPairedDevicesFailed': 'Clear paired devices failed',
// 设置页面 - 实时语音对话
'realtimeVoiceChat': 'Real-time Voice Chat',
@ -498,11 +498,20 @@ const Map<String, String> enUS = {
'loading': 'Loading...',
//设备管理页面
"earphone": "Earphone", // 耳机
"voicerecorder": "Voice Recorder", // 录音笔
"notBound": "Not Bound", // 未绑定
"deviceNotBoundYet": "Device not bound yet", // 还未绑定设备
"deviceTypeUnknown": "Unknown Device",
"deviceTypeClassicBluetoothHeadset": "Classic Bluetooth Headset",
"deviceTypeBleBluetoothHeadset": "BLE Bluetooth Headset",
"deviceTypeSmartVoiceRecorder": "Smart Voice Recorder",
"deviceTypeSmartEye": "Smart Glasses",
"unbind": "Unbind", // 解绑
"noDevicesBound": "No devices bound yet",
"pleaseBindDeviceFirst": "Please bind a device first to manage",
"noMacAddress": "No MAC Address",
"licenseCode": "License Code",
"confirmUnbind": "Confirm Unbind",
"confirmUnbindMessage":
"Are you sure you want to unbind device \"{deviceName}\"?",
"confirmUnbindAction": "Confirm Unbind",
// Agent模块
//Agent View

27
lib/core/translations/language/zh_cn.dart

@ -399,7 +399,8 @@ const Map<String, String> zhCN = {
// Meeting模块 - 云端同步相关
'syncToCloud': '同步到云端',
'syncToCloudTip': '自动将你的录音同步到云端,你可以通过任意VOITRANS APP或VOITRANS Web(web.voitrans.com)播放、管理和分享录音。',
'syncToCloudTip':
'自动将你的录音同步到云端,你可以通过任意VOITRANS APP或VOITRANS Web(web.voitrans.com)播放、管理和分享录音。',
'syncToVoitransApp': '同步VOITRANS APP',
'transcriptionRecord': '转写记录',
@ -458,10 +459,10 @@ const Map<String, String> zhCN = {
'networkErrorRetry': '网络错误,请稍后重试',
// 设备管理相关
'clearPairedDevices': '清除配对设备',
'clearPairedDevicesConfirm': '确定要清除所有配对设备吗?',
'clearPairedDevicesSuccess': '清除配对设备成功',
'clearPairedDevicesFailed': '清除配对设备失败',
// 'clearPairedDevices': '清除配对设备',
// 'clearPairedDevicesConfirm': '确定要清除所有配对设备吗?',
// 'clearPairedDevicesSuccess': '清除配对设备成功',
// 'clearPairedDevicesFailed': '清除配对设备失败',
// 设置页面 - 实时语音对话
"realtimeVoiceChat": "实时语音对话",
@ -469,11 +470,19 @@ const Map<String, String> zhCN = {
"loading": "加载中...",
//设备管理页面
"earphone": "耳机",
"voicerecorder": "录音笔",
"notBound": "未绑定",
"deviceNotBoundYet": "还未绑定设备",
"deviceTypeUnknown": "未知设备",
"deviceTypeClassicBluetoothHeadset": "经典蓝牙耳机",
"deviceTypeBleBluetoothHeadset": "BLE蓝牙耳机",
"deviceTypeSmartVoiceRecorder": "智能录音笔",
"deviceTypeSmartEye": "智能眼镜",
"unbind": "解绑",
"noDevicesBound": "暂未绑定任何设备",
"pleaseBindDeviceFirst": "请先绑定设备后再进行管理",
"noMacAddress": "无MAC地址",
"licenseCode": "授权码",
"confirmUnbind": "确认解绑",
"confirmUnbindMessage": "确定要解绑设备 \"{deviceName}\" 吗?",
"confirmUnbindAction": "确认解绑",
//Agent 模块
// agent View

10
lib/core/utils/permission_util.dart

@ -27,7 +27,7 @@ class PermissionUtil {
final _storage = GetStorage();
/// 权限拒绝次数阈值(达到此次数后视为永久拒绝)
static const int _denialThreshold = 2;
static const int _denialThreshold = 5;
/// 获取权限拒绝次数的存储键
String _getDenialCountKey(Permission permission) {
@ -91,7 +91,8 @@ class PermissionUtil {
Logger.d('PermissionUtil', '$permissionName 当前拒绝次数: $denialCount');
// 如果拒绝次数达到阈值或系统返回了 permanentlyDenied,直接视为永久拒绝
if (status.isPermanentlyDenied || denialCount >= _denialThreshold) {
Logger.w('PermissionUtil', '$permissionName 已被系统永久拒绝或自定义计数达到阈值($denialCount),触发设置引导');
Logger.w('PermissionUtil',
'$permissionName 已被系统永久拒绝或自定义计数达到阈值($denialCount),触发设置引导');
await _showPermanentDenialDialog(
permissionName: permissionName,
permanentDenialText: permanentDenialText,
@ -109,8 +110,6 @@ class PermissionUtil {
);
isOverlayShown = true;
final PermissionStatus newStatus = await permissionType.request();
Logger.d('PermissionUtil', '请求后 $permissionName 的新权限状态: $newStatus');
@ -137,7 +136,8 @@ class PermissionUtil {
if (newStatus.isDenied) {
Logger.w('PermissionUtil', '$permissionName 权限被拒绝,增加拒绝计数');
await _storage.write(_getDenialCountKey(permissionType), denialCount + 1);
await _storage.write(
_getDenialCountKey(permissionType), denialCount + 1);
}
// 所有未授权的情况最终都返回 false

57
lib/modules/devices/views/devices_view.dart

@ -10,15 +10,30 @@ import '../controllers/devices_controller.dart';
// 设备类型枚举
enum DeviceType {
Unknown(0, "未知设备"),
Classic_bluetooth_Headset(1, "经典蓝牙耳机"),
BLE_Bluetooth_Headset(2, "BLE蓝牙耳机"),
Smart_Voice_Recorder(3, "智能录音笔"),
SmartEye(4, "智能眼镜");
Unknown(0),
Classic_bluetooth_Headset(1),
BLE_Bluetooth_Headset(2),
Smart_Voice_Recorder(3),
SmartEye(4);
const DeviceType(this.value, this.displayName);
const DeviceType(this.value);
final int value;
final String displayName;
// 使用getter获取多语言显示名称
String get displayName {
switch (this) {
case DeviceType.Unknown:
return "deviceTypeUnknown".tr;
case DeviceType.Classic_bluetooth_Headset:
return "deviceTypeClassicBluetoothHeadset".tr;
case DeviceType.BLE_Bluetooth_Headset:
return "deviceTypeBleBluetoothHeadset".tr;
case DeviceType.Smart_Voice_Recorder:
return "deviceTypeSmartVoiceRecorder".tr;
case DeviceType.SmartEye:
return "deviceTypeSmartEye".tr;
}
}
static DeviceType fromValue(int value) {
return DeviceType.values.firstWhere(
@ -168,7 +183,7 @@ class DevicesView extends GetView<DevicesController> {
),
SizedBox(height: 20.h),
Text(
"暂未绑定任何设备",
"noDevicesBound".tr,
style: TextStyle(
fontSize: 16.sp,
color: isDarkMode ? Colors.white70 : Colors.grey[600],
@ -176,7 +191,7 @@ class DevicesView extends GetView<DevicesController> {
),
SizedBox(height: 12.h),
Text(
"请先绑定设备后再进行管理",
"pleaseBindDeviceFirst".tr,
style: TextStyle(
fontSize: 14.sp,
color: isDarkMode ? Colors.white54 : Colors.grey[500],
@ -230,7 +245,9 @@ class DevicesView extends GetView<DevicesController> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
device.devicename.isNotEmpty ? device.devicename : "未知设备",
device.devicename.isNotEmpty
? device.devicename
: "deviceTypeUnknown".tr,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w500,
@ -239,7 +256,9 @@ class DevicesView extends GetView<DevicesController> {
),
SizedBox(height: 4.h),
Text(
device.devicemac.isNotEmpty ? device.devicemac : "无MAC地址",
device.devicemac.isNotEmpty
? device.devicemac
: "noMacAddress".tr,
style: TextStyle(
fontSize: 14.sp,
color: isDarkMode ? Colors.white70 : Colors.grey[600],
@ -248,7 +267,7 @@ class DevicesView extends GetView<DevicesController> {
if (device.license.isNotEmpty) ...[
SizedBox(height: 4.h),
Text(
"授权码: ${device.license}",
"${"licenseCode".tr}: ${device.license}",
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.white54 : Colors.grey[500],
@ -294,13 +313,15 @@ class DevicesView extends GetView<DevicesController> {
AlertDialog(
backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white,
title: Text(
"确认解绑",
"confirmUnbind".tr,
style: TextStyle(
color: isDarkMode ? Colors.white : Colors.black87,
),
),
content: Text(
"确定要解绑设备 \"${device.devicename}\" 吗?",
"confirmUnbindMessage"
.tr
.replaceAll('{deviceName}', device.devicename),
style: TextStyle(
color: isDarkMode ? Colors.white70 : Colors.black54,
),
@ -309,7 +330,7 @@ class DevicesView extends GetView<DevicesController> {
TextButton(
onPressed: () => Get.back(),
child: Text(
"取消",
"cancel".tr,
style: TextStyle(
color: isDarkMode ? Colors.white70 : Colors.grey[600],
),
@ -320,9 +341,9 @@ class DevicesView extends GetView<DevicesController> {
Get.back();
controller.unbindearphone(device);
},
child: const Text(
"确认解绑",
style: TextStyle(color: Colors.red),
child: Text(
"confirmUnbindAction".tr,
style: const TextStyle(color: Colors.red),
),
),
],

10
lib/modules/meeting/controllers/meeting_connect_controller.dart

@ -227,8 +227,11 @@ class MeetingConnectController extends GetxController {
(sum, entry) => sum + (entry.size ?? 0),
);
for (var i = 0; i < listDirectory.length; i++) {
importedFilesStr.value =
'downloadingFileProgress'.trParams({'total': '${listDirectory.length}', 'current': '${i + 1}'}); // 对应中文:共{total}个文件,正在下载第{current}个文件
importedFilesStr.value = 'downloadingFileProgress'
.tr
.replaceAll('{total}', '${listDirectory.length}')
.replaceAll('{current}',
'${i + 1}'); // 对应中文:共{total}个文件,正在下载第{current}个文件
int previousSize = i > 0 ? listDirectory[i - 1].size ?? 0 : 0;
FTPEntry element = listDirectory[i];
await _downloadFile(totalSize, previousSize, element);
@ -236,7 +239,8 @@ class MeetingConnectController extends GetxController {
}
await _disconnect();
stateStr.value = 'downloadComplete'.tr; // 对应中文:下载完成
importedFilesStr.value = 'downloadCompleteInfo'.trParams({'count': '${listDirectory.length}'}); // 对应中文:下载完成,共{count}个文件,2秒后自动返回
importedFilesStr.value = 'downloadCompleteInfo'.tr.replaceAll('{count}',
'${listDirectory.length}'); // 对应中文:下载完成,共{count}个文件,2秒后自动返回
Future.delayed(const Duration(seconds: 2), () {
_executeUpload();
Get.back();

35
lib/modules/meeting/controllers/meeting_home_controller.dart

@ -95,7 +95,7 @@ class MeetingHomeController extends GetxController {
final deviceInfo = DeviceInfoPlugin();
final androidInfo = await deviceInfo.androidInfo;
final sdkVersion = androidInfo.version.sdkInt;
// Android 11+ (API 30+) 不需要存储权限
if (sdkVersion >= 30) {
Logger.d("Permission", "Android 11+版本,无需请求存储权限");
@ -106,13 +106,15 @@ class MeetingHomeController extends GetxController {
// 如果获取版本失败,为了兼容性还是请求权限
}
}
PermissionUtil.instance
.requestPermission(
permissionType: Permission.storage,
permissionName: "meetingStoragePermission".tr, // 对应中文:存储
explanationText: "meetingStoragePermissionExplanation".tr, // 对应中文:我们需要访问存储权限,以便您可以选择并上传音频文件。
permanentDenialText: "meetingStoragePermissionPermanentDenial".tr, // 对应中文:检测到您已永久拒绝存储权限。如需继续使用此功能,请前往应用设置中手动开启存储权限。
explanationText: "meetingStoragePermissionExplanation"
.tr, // 对应中文:我们需要访问存储权限,以便您可以选择并上传音频文件。
permanentDenialText: "meetingStoragePermissionPermanentDenial"
.tr, // 对应中文:检测到您已永久拒绝存储权限。如需继续使用此功能,请前往应用设置中手动开启存储权限。
)
.then((granted) {
if (granted) {
@ -169,7 +171,9 @@ class MeetingHomeController extends GetxController {
EasyLoading.showToast('shareComplete'.tr); // 对应中文:分享完成!
} catch (e) {
EasyLoading.showToast('shareFailed'.trParams({'error': e.toString()})); // 对应中文:分享失败: {error}
EasyLoading.showToast('shareFailed'
.tr
.replaceAll('{error}', e.toString())); // 对应中文:分享失败: {error}
}
}
@ -258,12 +262,21 @@ class MeetingHomeController extends GetxController {
addUp++;
}
} catch (e) {
print('fileProcessingFailed'.trParams({'filePath': fileEntity.path, 'error': e.toString()})); // 对应中文:文件处理失败: {filePath}, 错误: {error}
print('fileProcessingFailed'
.tr
.replaceAll('{filePath}', fileEntity.path)
.replaceAll('{error}',
e.toString())); // 对应中文:文件处理失败: {filePath}, 错误: {error}
}
}
if (addUp > 0) EasyLoading.showSuccess('importSuccess'.trParams({'count': '$addUp'})); // 对应中文:成功导入本地 {count} 条录音
if (addUp > 0)
EasyLoading.showSuccess('importSuccess'
.tr
.replaceAll('{count}', '$addUp')); // 对应中文:成功导入本地 {count} 条录音
} catch (e) {
EasyLoading.showError('importError'.trParams({'error': e.toString()})); // 对应中文:导入失败: {error}
EasyLoading.showError('importError'
.tr
.replaceAll('{error}', e.toString())); // 对应中文:导入失败: {error}
} finally {
EasyLoading.dismiss();
}
@ -308,7 +321,11 @@ class MeetingHomeController extends GetxController {
}
}
} catch (e) {
Logger.error('importFailed'.trParams({'filePath': filePath, 'error': e.toString()})); // 对应中文:导入文件失败: {filePath}, error: {error}
Logger.error('importFailed'
.tr
.replaceAll('{filePath}', filePath)
.replaceAll('{error}',
e.toString())); // 对应中文:导入文件失败: {filePath}, error: {error}
}
}
EasyLoading.dismiss();

9
lib/modules/meeting/views/tabs/overview_tab.dart

@ -71,7 +71,8 @@ class _OverviewTabState extends State<OverviewTab> {
Padding(
padding: EdgeInsets.symmetric(vertical: 10.w),
child: Text(
'keyEventsAnalyzed'.trParams({'count': '${_dataList.length}'}), // 对应中文:分析出{count}个关键事件。
'keyEventsAnalyzed'.tr.replaceAll('{count}',
'${_dataList.length}'), // 对应中文:分析出{count}个关键事件。
style: TextStyle(
color: isDarkMode ? Colors.grey[400] : Colors.grey,
fontSize: 12.sp,
@ -106,7 +107,8 @@ class _OverviewTabState extends State<OverviewTab> {
Get.bottomSheet(
const AIBottomSheet(),
isScrollControlled: true,
backgroundColor: isDarkMode ? Colors.grey[800] : Colors.white,
backgroundColor:
isDarkMode ? Colors.grey[800] : Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(12.r),
@ -125,7 +127,8 @@ class _OverviewTabState extends State<OverviewTab> {
Get.bottomSheet(
const GenerateBottomSheet(),
isScrollControlled: true,
backgroundColor: isDarkMode ? Colors.grey[800] : Colors.grey[100],
backgroundColor:
isDarkMode ? Colors.grey[800] : Colors.grey[100],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(12.r),

70
lib/modules/settings/controllers/settings_controller.dart

@ -466,42 +466,42 @@ class SettingsController extends GetxController {
}
/// 清除BLE配对设备
Future<void> clearBleDevices() async {
// 显示确认对话框
final confirmed = await Get.dialog<bool>(
AlertDialog(
title: const Text('清除配对设备'),
content: const Text('确定要清除所有BLE配对设备吗?此操作不可撤销。'),
actions: [
TextButton(
onPressed: () => Get.back(result: false),
child: Text('cancel'.tr),
),
TextButton(
onPressed: () => Get.back(result: true),
style: TextButton.styleFrom(
foregroundColor: Colors.red,
),
child: Text('confirm'.tr), //确认
),
],
),
);
// Future<void> clearBleDevices() async {
// // 显示确认对话框
// final confirmed = await Get.dialog<bool>(
// AlertDialog(
// title: const Text('清除配对设备'),
// content: const Text('确定要清除所有BLE配对设备吗?此操作不可撤销。'),
// actions: [
// TextButton(
// onPressed: () => Get.back(result: false),
// child: Text('cancel'.tr),
// ),
// TextButton(
// onPressed: () => Get.back(result: true),
// style: TextButton.styleFrom(
// foregroundColor: Colors.red,
// ),
// child: Text('confirm'.tr), //确认
// ),
// ],
// ),
// );
if (confirmed == true) {
try {
final bleManager = Get.find<BleManager>();
final result = await bleManager.clearPairedDevices();
if (result) {
Get.snackbar('成功', 'BLE配对设备已清除');
} else {
Get.snackbar('失败', '清除BLE配对设备失败');
}
} catch (e) {
Get.snackbar('错误', '清除BLE配对设备时发生错误');
}
}
}
// if (confirmed == true) {
// try {
// final bleManager = Get.find<BleManager>();
// final result = await bleManager.clearPairedDevices();
// if (result) {
// Get.snackbar('成功', 'BLE配对设备已清除');
// } else {
// Get.snackbar('失败', '清除BLE配对设备失败');
// }
// } catch (e) {
// Get.snackbar('错误', '清除BLE配对设备时发生错误');
// }
// }
// }
/// 手动检查版本更新
Future<void> checkForUpdate() async {

8
lib/modules/translation/controllers/translation_controller.dart

@ -198,6 +198,10 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
Future<void> _stopFaceToFaceRecognition() async {
try {
await pauseRecording(); //暂停
if (faceToFaceRecognitionText.value.isNotEmpty) {
handleFinalResult(faceToFaceRecognitionText.value);
faceToFaceRecognitionText.value = '';
}
await _asrService.stopContinuousRecognition();
_stopAsrActiveTracking();
isRecognizing.value = false;
@ -671,7 +675,9 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
if (text.isEmpty) return;
print(
"handleIntermediateResult:${translationHistory.isEmpty}${translationHistory.isNotEmpty ? translationHistory.last.isIntermediate : 'N/A'}");
if (translationHistory.isEmpty || (translationHistory.isNotEmpty && !translationHistory.last.isIntermediate)) {
if (translationHistory.isEmpty ||
(translationHistory.isNotEmpty &&
!translationHistory.last.isIntermediate)) {
currentSessionId ??= DateTime.now().millisecondsSinceEpoch.toString();
final newItem = TranslationItem(

4
local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift

@ -941,7 +941,7 @@ extension AzureSpeechPlugin: BleService.Callback {
os_log("音频流未初始化", type: .error)
return
}
print("liwei--------------接收到音频数据回调")
// print("liwei--------------接收到音频数据回调")
audioStream.saveAudioDataTo(data: data)
}
@ -953,7 +953,7 @@ extension AzureSpeechPlugin: BleService.Callback {
os_log("音频流未初始化", type: .error)
return
}
print("liwei--------------接收到音频数据回调1")
// print("liwei--------------接收到音频数据回调1")
audioStream.saveAudioDataTo(data: data)
}

Loading…
Cancel
Save