Browse Source

feat(agent语音识别): 新增支持多语言设置功能并优化蓝牙配对流程

- 在AgentService插件中新增setSupportedLanguages方法用于设置语音识别支持的语言
- 重构AgentController中的语言配置逻辑到独立方法_configureSpeechRecognitionLanguages
- 更新蓝牙配对流程,增加设备连接状态刷新步骤
- 替换首页耳机图片资源并调整显示高度
- 将调试输出语句统一替换为Logger日志记录
weicu
lxm 1 year ago
parent
commit
04a0a67188
  1. BIN
      assets/images/headphone_new3.png
  2. BIN
      assets/images/headphone_new4.png
  3. 103
      lib/modules/agent/controllers/agent_controller.dart
  4. 6
      lib/modules/home/views/home_view.dart
  5. 9
      lib/modules/pairing/controllers/pairing_controller.dart
  6. 2
      lib/modules/settings/views/settings_view.dart
  7. 48
      local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift
  8. 10
      local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServicePlugin.swift
  9. 6
      pubspec.yaml

BIN
assets/images/headphone_new3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 KiB

BIN
assets/images/headphone_new4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 904 KiB

103
lib/modules/agent/controllers/agent_controller.dart

@ -153,46 +153,7 @@ class AgentController extends GetxController {
_setupMediaSessionCallbacks(); //设置媒体会话回调 _setupMediaSessionCallbacks(); //设置媒体会话回调
_setupTextInputListener(); //设置文本输入监听器 _setupTextInputListener(); //设置文本输入监听器
try { _configureSpeechRecognitionLanguages(); //配置语音识别语言
// 1. 使用 GetX 获取 APP 当前语言环境 (Locale),而不是设备语言
final appLocale = Get.locale;
List<String> languagesToSupport = [];
if (appLocale != null) {
final lang = appLocale.languageCode; // e.g., 'en'
final country = appLocale.countryCode; // e.g., 'US'
// 2. 构造 Azure SDK 需要的格式 (e.g., 'en-US')
if (country != null && country.isNotEmpty) {
languagesToSupport.add('$lang-$country');
} else {
languagesToSupport.add(lang); // fallback only language code
}
}
// 3. 添加 en-US 作为备选
if (!languagesToSupport.contains('en-US')) {
languagesToSupport.add('en-US');
}
// 4. 最终兜底
if (languagesToSupport.isEmpty) {
languagesToSupport.add('en-US');
}
// 5. 调用原生设置
Logger.i(TAG, '支持的语言: ${languagesToSupport.join(', ')}');
AgentService.setSupportedLanguages(languagesToSupport).then((success) {
if (success) {
Logger.i(TAG, '支持的语言已根据 APP 设置语言: ${languagesToSupport.join(', ')}');
} else {
Logger.w(TAG, '根据 APP 设置语言失败');
}
});
} catch (e) {
Logger.e(TAG, "获取 APP 语言并设置失败: $e");
AgentService.setSupportedLanguages(['zh-CN', 'en-US']);
}
startVoiceInput(); //开启语音输入 startVoiceInput(); //开启语音输入
} }
@ -507,7 +468,7 @@ class AgentController extends GetxController {
case AgentServiceEventType.recognitionStarted: case AgentServiceEventType.recognitionStarted:
isListening.value = true; isListening.value = true;
isTextInputMode.value = false; isTextInputMode.value = false;
print("订阅代理服务事件识别开始"); Logger.i(TAG, '订阅代理服务事件识别开始');
// isTextInputMode.refresh(); // isTextInputMode.refresh();
// 只标记识别开始,不添加消息 // 只标记识别开始,不添加消息
Logger.i(TAG, '识别开始'); Logger.i(TAG, '识别开始');
@ -1052,10 +1013,54 @@ class AgentController extends GetxController {
} }
} }
//根据APP语言配置语音识别语言
Future<void> _configureSpeechRecognitionLanguages() async {
try {
// 1. 使用 GetX 获取 APP 当前语言环境 (Locale)
final appLocale = Get.locale;
List<String> languagesToSupport = [];
if (appLocale != null) {
final lang = appLocale.languageCode; // e.g., 'en'
final country = appLocale.countryCode; // e.g., 'US'
// 2. 构造 Azure SDK 需要的格式 (e.g., 'en-US')
if (country != null && country.isNotEmpty) {
languagesToSupport.add('$lang-$country');
} else {
languagesToSupport.add(lang); // fallback only language code
}
}
// 3. 添加 en-US 作为备选
if (!languagesToSupport.contains('en-US')) {
languagesToSupport.add('en-US');
}
// 4. 最终兜底
if (languagesToSupport.isEmpty) {
languagesToSupport.add('en-US');
}
// 5. 调用原生设置
Logger.i(TAG, '支持的语言: ${languagesToSupport.join(', ')}');
AgentService.setSupportedLanguages(languagesToSupport).then((success) {
if (success) {
Logger.i(TAG, 'asr的语言已根据 APP 设置语言: ${languagesToSupport.join(', ')}');
} else {
Logger.w(TAG, '根据 APP 设置asr语言失败');
}
});
} catch (e) {
Logger.e(TAG, "获取 APP 语言并设置失败: $e");
AgentService.setSupportedLanguages(['zh-CN', 'en-US']);
}
}
// 开始语音输入 // 开始语音输入
Future<void> startVoiceInput() async { Future<void> startVoiceInput() async {
if (isListening.value) return; if (isListening.value) return;
print('开始语音输入'); // 开始语音输入 Logger.i(TAG, '开始语音输入'); // 开始语音输入
final hasPermission = await PermissionUtil.instance.requestPermission( final hasPermission = await PermissionUtil.instance.requestPermission(
// context: Get.context!, // context: Get.context!,
permissionType: Permission.microphone, permissionType: Permission.microphone,
@ -1351,7 +1356,7 @@ class AgentController extends GetxController {
// 打电话模式不使用按住说话模式,而是持续语音输入 // 打电话模式不使用按住说话模式,而是持续语音输入
//isPushToTalkMode.value = false; //isPushToTalkMode.value = false;
// 开始语音识别 // 开始语音识别
print("开始打电话模式,开始语音输入"); Logger.i(TAG, '开始打电话模式,开始语音输入');
await AgentService.startConversation(); await AgentService.startConversation();
//} //}
} }
@ -1488,11 +1493,12 @@ class AgentController extends GetxController {
var status = await _musiceManager.getStatus(); var status = await _musiceManager.getStatus();
isPlaying.value = status['isPlaying'] ?? false; isPlaying.value = status['isPlaying'] ?? false;
currentId.value = status['id'] as int; currentId.value = status['id'] as int;
Logger.d('AgentController', Logger.d(
'音乐状态更新: playing=${isPlaying.value}, url=${currentId.value}'); TAG, '音乐状态更新: playing=${isPlaying.value}, url=${currentId.value}');
print("音乐状态更新: playing=${isPlaying.value}, url=${currentId.value}"); Logger.d(
TAG, '音乐状态更新: playing=${isPlaying.value}, url=${currentId.value}');
} catch (e) { } catch (e) {
Logger.e('AgentController', '获取音乐状态失败: $e'); Logger.e(TAG, '获取音乐状态失败: $e');
} }
} }
@ -1529,7 +1535,8 @@ class AgentController extends GetxController {
var response = await Api.getMusicUrl({ var response = await Api.getMusicUrl({
"ids": [id] "ids": [id]
}); });
print('getMusicUrl 响应数据: ${response.toString()}'); // 调试输出原始数据 Logger.d(
TAG, 'getMusicUrl 响应数据: ${response.toString()}'); // 调试输出原始数据
final urldata = response['urls'][0] as Map<String, dynamic>; final urldata = response['urls'][0] as Map<String, dynamic>;
song["url"] = urldata['url'] ?? ''; song["url"] = urldata['url'] ?? '';
// 修复:确保currentId接收int类型 // 修复:确保currentId接收int类型

6
lib/modules/home/views/home_view.dart

@ -173,9 +173,9 @@ class HomeView extends GetView<HomeController> {
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
child: Image.asset( child: Image.asset(
isDarkMode isDarkMode
? 'assets/images/headphone_dark.png' ? 'assets/images/headphone_new3.png'
: 'assets/images/headphone_light.png', : 'assets/images/headphone_new4.png',
height: 180.h, height: 220.h,
fit: BoxFit.contain, fit: BoxFit.contain,
), ),
), ),

9
lib/modules/pairing/controllers/pairing_controller.dart

@ -384,7 +384,7 @@ class PairingController extends GetxController {
/// 检查当前状态并决定下一步 /// 检查当前状态并决定下一步
void _checkCurrentStatus() async { void _checkCurrentStatus() async {
try { try {
// 使用 Future.wait 并行执行检查,提升启动速度 // 1. 检查蓝牙状态
final results = await Future.wait([ final results = await Future.wait([
_checkBluetoothState(), // 检查蓝牙状态 _checkBluetoothState(), // 检查蓝牙状态
_checkPermissions(), // 检查权限 _checkPermissions(), // 检查权限
@ -399,18 +399,23 @@ class PairingController extends GetxController {
return; return;
} }
// 2. 检查权限
if (!arePermissionsOk) { if (!arePermissionsOk) {
_setError(ErrorType.noPermission, 'pleaseGrantNecessaryPermissions'.tr); _setError(ErrorType.noPermission, 'pleaseGrantNecessaryPermissions'.tr);
return; return;
} }
// 后续逻辑保持不变,确保所有设备都经过验证 // 3. 先刷新设备连接状态,确保获取最新的连接信息
await _bluetoothManager.refreshDeviceConnection();
// 4. 检查经典蓝牙连接
final classicDevice = _bluetoothManager.currentDevice; final classicDevice = _bluetoothManager.currentDevice;
if (classicDevice == null) { if (classicDevice == null) {
_transition(PairingState.waitingClassic); _transition(PairingState.waitingClassic);
return; return;
} }
// 5. 如果有设备,进行验证
_currentClassicDevice = classicDevice; _currentClassicDevice = classicDevice;
_transition(PairingState.verifying); _transition(PairingState.verifying);
await _verifyDevice(classicDevice); await _verifyDevice(classicDevice);

2
lib/modules/settings/views/settings_view.dart

@ -813,7 +813,7 @@ class SettingsView extends GetView<SettingsController> {
//------------测试使用标记 start---------------// //------------测试使用标记 start---------------//
SizedBox(height: 4.h), SizedBox(height: 4.h),
Text( Text(
'${Platform.operatingSystem}-test-0707', '${Platform.operatingSystem}-test-0721',
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: const Color.fromARGB(255, 143, 94, 94), color: const Color.fromARGB(255, 143, 94, 94),

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

@ -742,6 +742,54 @@ class AgentServiceImpl: NSObject {
return "通过test插件集成MacPaw OpenAI SDK v0.4.3 - 集成成功" return "通过test插件集成MacPaw OpenAI SDK v0.4.3 - 集成成功"
} }
/**
* 设置支持的语音识别语言
*
* @param languages 支持的语言列表,如 ["zh-CN", "en-US"]
* @return 是否设置成功
*/
func setSupportedLanguages(_ languages: [String]) -> Bool {
os_log("设置支持的语言: %{public}@", log: logger, type: .info, languages.joined(separator: ", "))
if !isInitialized {
os_log("服务未初始化,无法设置语言", log: logger, type: .error)
sendError("服务未初始化", code: "NOT_INITIALIZED")
return false
}
if languages.isEmpty {
os_log("语言列表为空,使用默认语言", log: logger, type: .warning)
return false
}
// 如果正在识别,需要先停止
let wasRecognizing = isRecognizing
if wasRecognizing {
_ = stopRecognition()
}
// 重新初始化ASR引擎,使用新的语言设置
guard let success = azureAsrHelper?.initialize(
subscriptionKey: azureSpeechKey,
region: azureSpeechRegion,
supportedLanguages: languages,
audioSourceType: .microphone
), success else {
os_log("重新初始化ASR引擎失败", log: logger, type: .error)
sendError("重新初始化ASR引擎失败", code: "ASR_INIT_ERROR")
return false
}
os_log("成功设置支持的语言: %{public}@", log: logger, type: .info, languages.joined(separator: ", "))
// 如果之前正在识别,重新开始
if wasRecognizing {
_ = startRecognition()
}
return true
}
func dispose() -> Bool { func dispose() -> Bool {
if isRecognizing { if isRecognizing {
stopRecognition() stopRecognition()

10
local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServicePlugin.swift

@ -97,6 +97,16 @@ public class AgentServicePlugin: NSObject, FlutterPlugin {
let success = impl.dispose() let success = impl.dispose()
result(success) result(success)
case "setSupportedLanguages":
guard let arguments = call.arguments as? [String: Any],
let languages = arguments["languages"] as? [String] else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "语言列表不能为空", details: nil))
return
}
let success = impl.setSupportedLanguages(languages)
result(success)
default: default:
result(FlutterMethodNotImplemented) result(FlutterMethodNotImplemented)
} }

6
pubspec.yaml

@ -125,8 +125,8 @@ flutter:
shaders: shaders:
- shaders/mic_flow.frag - shaders/mic_flow.frag
assets: assets:
- assets/images/headphone_light.png # - assets/images/headphone_light.png
- assets/images/headphone_dark.png # - assets/images/headphone_dark.png
- assets/images/robot_light.png - assets/images/robot_light.png
- assets/images/glasses_light.png - assets/images/glasses_light.png
- assets/images/agents/ - assets/images/agents/
@ -136,5 +136,7 @@ flutter:
- assets/images/mine-background.png - assets/images/mine-background.png
- assets/lottie/ - assets/lottie/
- assets/js/ - assets/js/
- assets/images/headphone_new4.png
- assets/images/headphone_new3.png
- .env - .env
- .mcp.json - .mcp.json

Loading…
Cancel
Save