Browse Source

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

newdev_shunjiawei
fdp 1 year ago
parent
commit
fd9489b77e
  1. 27
      lib/core/translations/language/en_us.dart
  2. 27
      lib/core/translations/language/zh_cn.dart
  3. 2
      lib/modules/agent/views/music_card.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. 131
      lib/modules/translation/views/translation_view.dart
  10. 5
      local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift
  11. 1
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCompanionManager.kt
  12. 6
      local_plugins/music_service/ios/music_service/Sources/music_service/MusicService.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', 'networkErrorRetry': 'Network error, please try again later',
// 设备管理相关 // 设备管理相关
'clearPairedDevices': 'Clear Paired Devices', // 'clearPairedDevices': 'Clear Paired Devices',
'clearPairedDevicesConfirm': // 'clearPairedDevicesConfirm':
'Are you sure you want to clear all paired devices?', // 'Are you sure you want to clear all paired devices?',
'clearPairedDevicesSuccess': 'Clear paired devices successfully', // 'clearPairedDevicesSuccess': 'Clear paired devices successfully',
'clearPairedDevicesFailed': 'Clear paired devices failed', // 'clearPairedDevicesFailed': 'Clear paired devices failed',
// 设置页面 - 实时语音对话 // 设置页面 - 实时语音对话
'realtimeVoiceChat': 'Real-time Voice Chat', 'realtimeVoiceChat': 'Real-time Voice Chat',
@ -498,11 +498,20 @@ const Map<String, String> enUS = {
'loading': 'Loading...', 'loading': 'Loading...',
//设备管理页面 //设备管理页面
"earphone": "Earphone", // 耳机 "deviceTypeUnknown": "Unknown Device",
"voicerecorder": "Voice Recorder", // 录音笔 "deviceTypeClassicBluetoothHeadset": "Classic Bluetooth Headset",
"notBound": "Not Bound", // 未绑定 "deviceTypeBleBluetoothHeadset": "BLE Bluetooth Headset",
"deviceNotBoundYet": "Device not bound yet", // 还未绑定设备 "deviceTypeSmartVoiceRecorder": "Smart Voice Recorder",
"deviceTypeSmartEye": "Smart Glasses",
"unbind": "Unbind", // 解绑 "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模块
//Agent View //Agent View

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

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

2
lib/modules/agent/views/music_card.dart

@ -233,7 +233,7 @@ Widget buildMusicContent(
final id = item["id"]; final id = item["id"];
final name = item["name"] ?? "未知歌曲"; final name = item["name"] ?? "未知歌曲";
final url = item["url"] ?? ""; final url = item["url"] ?? "";
final singer = item["sgener"] ?? "未知歌手"; final singer = item["singer"] ?? "未知歌手";
final image = item["image"] ?? ""; final image = item["image"] ?? "";
final ts = item["ts"] ?? 0; final ts = item["ts"] ?? 0;
return Column( return Column(

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

@ -10,15 +10,30 @@ import '../controllers/devices_controller.dart';
// 设备类型枚举 // 设备类型枚举
enum DeviceType { enum DeviceType {
Unknown(0, "未知设备"), Unknown(0),
Classic_bluetooth_Headset(1, "经典蓝牙耳机"), Classic_bluetooth_Headset(1),
BLE_Bluetooth_Headset(2, "BLE蓝牙耳机"), BLE_Bluetooth_Headset(2),
Smart_Voice_Recorder(3, "智能录音笔"), Smart_Voice_Recorder(3),
SmartEye(4, "智能眼镜"); SmartEye(4);
const DeviceType(this.value, this.displayName); const DeviceType(this.value);
final int 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) { static DeviceType fromValue(int value) {
return DeviceType.values.firstWhere( return DeviceType.values.firstWhere(
@ -168,7 +183,7 @@ class DevicesView extends GetView<DevicesController> {
), ),
SizedBox(height: 20.h), SizedBox(height: 20.h),
Text( Text(
"暂未绑定任何设备", "noDevicesBound".tr,
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
color: isDarkMode ? Colors.white70 : Colors.grey[600], color: isDarkMode ? Colors.white70 : Colors.grey[600],
@ -176,7 +191,7 @@ class DevicesView extends GetView<DevicesController> {
), ),
SizedBox(height: 12.h), SizedBox(height: 12.h),
Text( Text(
"请先绑定设备后再进行管理", "pleaseBindDeviceFirst".tr,
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: isDarkMode ? Colors.white54 : Colors.grey[500], color: isDarkMode ? Colors.white54 : Colors.grey[500],
@ -230,7 +245,9 @@ class DevicesView extends GetView<DevicesController> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
device.devicename.isNotEmpty ? device.devicename : "未知设备", device.devicename.isNotEmpty
? device.devicename
: "deviceTypeUnknown".tr,
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -239,7 +256,9 @@ class DevicesView extends GetView<DevicesController> {
), ),
SizedBox(height: 4.h), SizedBox(height: 4.h),
Text( Text(
device.devicemac.isNotEmpty ? device.devicemac : "无MAC地址", device.devicemac.isNotEmpty
? device.devicemac
: "noMacAddress".tr,
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: isDarkMode ? Colors.white70 : Colors.grey[600], color: isDarkMode ? Colors.white70 : Colors.grey[600],
@ -248,7 +267,7 @@ class DevicesView extends GetView<DevicesController> {
if (device.license.isNotEmpty) ...[ if (device.license.isNotEmpty) ...[
SizedBox(height: 4.h), SizedBox(height: 4.h),
Text( Text(
"授权码: ${device.license}", "${"licenseCode".tr}: ${device.license}",
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.white54 : Colors.grey[500], color: isDarkMode ? Colors.white54 : Colors.grey[500],
@ -294,13 +313,15 @@ class DevicesView extends GetView<DevicesController> {
AlertDialog( AlertDialog(
backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white, backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white,
title: Text( title: Text(
"确认解绑", "confirmUnbind".tr,
style: TextStyle( style: TextStyle(
color: isDarkMode ? Colors.white : Colors.black87, color: isDarkMode ? Colors.white : Colors.black87,
), ),
), ),
content: Text( content: Text(
"确定要解绑设备 \"${device.devicename}\" 吗?", "confirmUnbindMessage"
.tr
.replaceAll('{deviceName}', device.devicename),
style: TextStyle( style: TextStyle(
color: isDarkMode ? Colors.white70 : Colors.black54, color: isDarkMode ? Colors.white70 : Colors.black54,
), ),
@ -309,7 +330,7 @@ class DevicesView extends GetView<DevicesController> {
TextButton( TextButton(
onPressed: () => Get.back(), onPressed: () => Get.back(),
child: Text( child: Text(
"取消", "cancel".tr,
style: TextStyle( style: TextStyle(
color: isDarkMode ? Colors.white70 : Colors.grey[600], color: isDarkMode ? Colors.white70 : Colors.grey[600],
), ),
@ -320,9 +341,9 @@ class DevicesView extends GetView<DevicesController> {
Get.back(); Get.back();
controller.unbindearphone(device); controller.unbindearphone(device);
}, },
child: const Text( child: Text(
"确认解绑", "confirmUnbindAction".tr,
style: TextStyle(color: Colors.red), 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), (sum, entry) => sum + (entry.size ?? 0),
); );
for (var i = 0; i < listDirectory.length; i++) { for (var i = 0; i < listDirectory.length; i++) {
importedFilesStr.value = importedFilesStr.value = 'downloadingFileProgress'
'downloadingFileProgress'.trParams({'total': '${listDirectory.length}', 'current': '${i + 1}'}); // 对应中文:共{total}个文件,正在下载第{current}个文件 .tr
.replaceAll('{total}', '${listDirectory.length}')
.replaceAll('{current}',
'${i + 1}'); // 对应中文:共{total}个文件,正在下载第{current}个文件
int previousSize = i > 0 ? listDirectory[i - 1].size ?? 0 : 0; int previousSize = i > 0 ? listDirectory[i - 1].size ?? 0 : 0;
FTPEntry element = listDirectory[i]; FTPEntry element = listDirectory[i];
await _downloadFile(totalSize, previousSize, element); await _downloadFile(totalSize, previousSize, element);
@ -236,7 +239,8 @@ class MeetingConnectController extends GetxController {
} }
await _disconnect(); await _disconnect();
stateStr.value = 'downloadComplete'.tr; // 对应中文:下载完成 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), () { Future.delayed(const Duration(seconds: 2), () {
_executeUpload(); _executeUpload();
Get.back(); Get.back();

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

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

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

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

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

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

131
lib/modules/translation/views/translation_view.dart

@ -206,16 +206,35 @@ class TranslationView extends GetView<TranslationController> {
: Colors.grey[100], : Colors.grey[100],
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
), ),
child: Obx(() => Text( child: Obx(() => Row(
'${controller.targetLanguage.value.split(' ')[0]}' mainAxisAlignment: MainAxisAlignment.center,
.tr, // Just show the English part children: [
style: TextStyle( Flexible(
fontSize: 16.sp, child: Text(
fontWeight: FontWeight.w600, '${controller.targetLanguage.value.split(' ')[0]}'
color: isDarkMode ? Colors.white : Colors.black, .tr,
), style: TextStyle(
textAlign: TextAlign.center, fontSize: 16.sp,
overflow: TextOverflow.ellipsis, fontWeight: FontWeight.w600,
color:
isDarkMode ? Colors.white : Colors.black,
),
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
),
),
//只在通话翻译模式下显示电话图标
if (controller.currentMode.value == 'call') ...[
SizedBox(width: 4.w),
Icon(
Icons.phone_in_talk,
size: 20.sp,
color: isDarkMode
? Colors.white70
: Colors.grey[600],
),
],
],
)), )),
), ),
), ),
@ -418,23 +437,27 @@ class TranslationView extends GetView<TranslationController> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
// 耳机图标按钮 (说话者1) // 耳机图标按钮 (说话者1)-显示源语言
_buildHoldToTalkButton( Obx(() => _buildHoldToTalkButton(
isDarkMode: isDarkMode, isDarkMode: isDarkMode,
speakerId: 1, speakerId: 1,
activeSpeaker: controller.activeSpeaker.value, activeSpeaker: controller.activeSpeaker.value,
icon: Icons.headset, icon: Icons.mic_none,
label: "headsetUser".tr, label: controller.sourceLanguage.value
), .split(' ')[0]
SizedBox(width: 40.w), .tr, // 源语言名称
// 手机图标按钮 (说话者2) )),
_buildHoldToTalkButton( SizedBox(width: 16.w),
isDarkMode: isDarkMode, // 手机图标按钮 (说话者2)-显示目标语言
speakerId: 2, Obx(() => _buildHoldToTalkButton(
activeSpeaker: controller.activeSpeaker.value, isDarkMode: isDarkMode,
icon: Icons.phone_android, speakerId: 2,
label: "phoneUser".tr, activeSpeaker: controller.activeSpeaker.value,
), icon: Icons.mic_none,
label: controller.targetLanguage.value
.split(' ')[0]
.tr, // 目标语言名称
)),
], ],
) )
else else
@ -569,15 +592,15 @@ class TranslationView extends GetView<TranslationController> {
}, },
child: AnimatedContainer( child: AnimatedContainer(
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
width: 80.w, width: 160.w, // 长条形宽度
height: 80.w, height: 54.h, // 长条形高度
decoration: BoxDecoration( decoration: BoxDecoration(
color: isActive color: isActive
? (isDarkMode ? Colors.green[700] : Colors.green) ? (isDarkMode ? Colors.green[700] : Colors.green)
: (isDisabled : (isDisabled
? (isDarkMode ? Colors.grey[800] : Colors.grey[300]) ? (isDarkMode ? Colors.grey[800] : Colors.grey[300])
: (isDarkMode ? AppColors.primary : Colors.blue)), : (isDarkMode ? AppColors.primary : Colors.blue)),
shape: BoxShape.circle, borderRadius: BorderRadius.circular(25.r), // 圆角长条形
boxShadow: isActive boxShadow: isActive
? [ ? [
BoxShadow( BoxShadow(
@ -586,32 +609,42 @@ class TranslationView extends GetView<TranslationController> {
spreadRadius: 3, spreadRadius: 3,
) )
] ]
: null, : [
BoxShadow(
color: (isDarkMode ? Colors.black : Colors.grey)
.withOpacity(0.2),
blurRadius: 8,
spreadRadius: 1,
offset: Offset(0, 2),
)
],
), ),
child: Column( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Stack( // 左边图标
alignment: Alignment.center, Icon(
children: [ icon,
// 设备类型图标 color: isActive
Icon( ? Colors.white
icon, : Colors.white.withOpacity(isDisabled ? 0.3 : 0.9),
size: 20.sp,
),
SizedBox(width: 8.w), // 图标和文字间距
// 右边语言名称
Flexible(
child: Text(
label,
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w500,
color: isActive color: isActive
? Colors.white ? Colors.white
: Colors.white.withOpacity(isDisabled ? 0.3 : 0.9), : Colors.white.withOpacity(isDisabled ? 0.3 : 0.9),
size: 24.sp,
), ),
], textAlign: TextAlign.center,
), maxLines: 1,
SizedBox(height: 4.h), overflow: TextOverflow.ellipsis, // 防止文本溢出
Text(
label,
style: TextStyle(
fontSize: 12.sp,
color: isActive
? Colors.white
: Colors.white.withOpacity(isDisabled ? 0.3 : 0.9),
), ),
), ),
], ],

5
local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift

@ -1450,6 +1450,7 @@ extension AgentServiceImpl: TtsEventListener {
restartIdleCheck() restartIdleCheck()
sendEvent(name: "tts_started", data: ["status": "started"]) sendEvent(name: "tts_started", data: ["status": "started"])
audioPlayer?.stopAwaitSound() audioPlayer?.stopAwaitSound()
case .synthesisCompleted: case .synthesisCompleted:
isSpeaking = false isSpeaking = false
restartIdleCheck() restartIdleCheck()
@ -1458,11 +1459,11 @@ extension AgentServiceImpl: TtsEventListener {
case .playbackStarted: case .playbackStarted:
restartIdleCheck() restartIdleCheck()
sendEvent(name: "playback_started", data: ["status": "playback_started"]) sendEvent(name: "playback_started", data: ["status": "playback_started"])
MusicService.shared.reduceVolume()
case .playbackCompleted: case .playbackCompleted:
restartIdleCheck() restartIdleCheck()
sendEvent(name: "playback_completed", data: ["status": "playback_completed"]) sendEvent(name: "playback_completed", data: ["status": "playback_completed"])
MusicService.shared.restoreVolume()
case .synthesisCanceled: case .synthesisCanceled:
isSpeaking = false isSpeaking = false
restartIdleCheck() restartIdleCheck()

1
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCompanionManager.kt

@ -395,6 +395,7 @@ class BleCompanionManager(private val context: Context) {
// 构建过滤器 // 构建过滤器
val filter = ScanFilter.Builder() val filter = ScanFilter.Builder()
.setDeviceAddress(mac) .setDeviceAddress(mac)
// .setServiceUuid(ParcelUuid(java.util.UUID.fromString("0000FEAA-0000-1000-8000-00805F9B34FB")))
.setManufacturerData(0xEFAB, byteArrayOf(0xA0.toByte(), 0x01)) .setManufacturerData(0xEFAB, byteArrayOf(0xA0.toByte(), 0x01))
.build() .build()

6
local_plugins/music_service/ios/music_service/Sources/music_service/MusicService.swift

@ -273,10 +273,10 @@ public class MusicService: NSObject {
} }
/// 降低音量(AI说话时) /// 降低音量(AI说话时)
func reduceVolume() { public func reduceVolume() {
if !isVolumeReduced { if !isVolumeReduced {
originalVolume = currentVolume originalVolume = currentVolume
let reducedVolume = max(0.0, min(1.0, currentVolume * 0.3)) // 降低到30% let reducedVolume = max(0.0, min(1.0, currentVolume * 0.2)) // 降低到30%
setVolume(volume: reducedVolume) setVolume(volume: reducedVolume)
isVolumeReduced = true isVolumeReduced = true
print("AI说话开始,音量从 \(originalVolume) 降低到 \(reducedVolume)") print("AI说话开始,音量从 \(originalVolume) 降低到 \(reducedVolume)")
@ -284,7 +284,7 @@ public class MusicService: NSObject {
} }
/// 恢复原始音量(AI说话结束时) /// 恢复原始音量(AI说话结束时)
func restoreVolume() { public func restoreVolume() {
if isVolumeReduced { if isVolumeReduced {
setVolume(volume: originalVolume) setVolume(volume: originalVolume)
isVolumeReduced = false isVolumeReduced = false

Loading…
Cancel
Save