From 1f1a48b9c89ed773a9c9cb42917973a30d10b860 Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Thu, 19 Jun 2025 10:08:23 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=92=AD=E6=8A=A5?= =?UTF-8?q?=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agent/controllers/agent_controller.dart | 10 ++++++++ .../agent_service/AgentService.kt | 25 +++++++++++-------- .../com/yunqiinnovation/chat_api/MCPClient.kt | 5 +++- .../chat_api/SystemFunctionHandler.kt | 12 ++++----- 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/lib/modules/agent/controllers/agent_controller.dart b/lib/modules/agent/controllers/agent_controller.dart index e322eefaf..4a0fd14ef 100644 --- a/lib/modules/agent/controllers/agent_controller.dart +++ b/lib/modules/agent/controllers/agent_controller.dart @@ -409,6 +409,16 @@ class AgentController extends GetxController { Map metaResult = {}; metaResult = jsonDecode(metaStr) as Map; if (metaResult.isNotEmpty) { + var notreply = false; + if (metaResult.containsKey("notreply")) { + // 如果有导航卡片,直接添加 + notreply = metaResult["notreply"] as bool; + } + if (notreply) { + Logger.i(TAG, 'Flutter 结束调用 不回复'); + //不回复直接返回 + break; + } // 判断是否为新的回复或响应ID是否改变 if (_isNewAssistantResponse || _currentResponseId != responseId) { // 添加新消息 diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt index adccc22f8..a99c84f5f 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt @@ -612,7 +612,7 @@ object AgentService : CoroutineScope { val responseBuilder = StringBuilder() var aiMetadata:String = "" var userMetadata = JSONObject() - var broadcast = false //是否播发 + var broadcast = true //是否播发 // 如果有图片,添加图片信息到元数据 if (hasImage) { userMetadata.put("hasImage", true) @@ -739,13 +739,13 @@ object AgentService : CoroutineScope { override fun onFunctionCallResult(functionCall: JSONObject, functionCallResult: JSONObject) { audioPlayer?.stopAudio() val name = functionCall.get("name") as String; - val (metestr, _broadcast)= autoHandleFcunCallResult(name,functionCallResult); - aiMetadata = metestr; - broadcast = _broadcast + val resultmap = autoHandleFcunCallResult(name,functionCallResult); + aiMetadata = resultmap["meta"] as String; + broadcast = resultmap["broadcast"] as Boolean; sendEvent("function_call_result", mapOf( "function_call" to functionCall.toString(), "result" to functionCallResult.toString(), - "meta" to metestr, + "meta" to aiMetadata, )) } } @@ -1116,7 +1116,7 @@ object AgentService : CoroutineScope { /* * 自动播放音乐 * */ - fun autoHandleFcunCallResult(toolname:String,functionCallResult: JSONObject): Pair{ + fun autoHandleFcunCallResult(toolname:String,functionCallResult: JSONObject): Map{ println("协议工具返回数据 检查是否存在卡片! functionCallResult: ${functionCallResult}") try { val contentStr = functionCallResult.optString("context") @@ -1126,13 +1126,18 @@ object AgentService : CoroutineScope { println("协议工具返回数据 检查是否存在卡片! textStr: ${textStr}") val metadata: MutableMap = mutableMapOf() var broadcast = true; + var notreply = false; if (textStr.isNotEmpty()) { val meta = JSONObject(textStr) - metadata[toolname] = meta // 现在可以赋值 - val metaStr = JSONObject(metadata as Map<*, *>).toString() if (meta.has("broadcast")){ broadcast = meta["broadcast"] as Boolean } + if (meta.has("notreply")){ + notreply = meta["notreply"] as Boolean + } + metadata[toolname] = meta // 现在可以赋值 + metadata["notreply"] = notreply // 现在可以赋值 + val metaStr = JSONObject(metadata as Map<*, *>).toString() // Log.d(TAG, "mcp调用结果: $metaStr") if (meta.has("card_music")) { //音乐卡片 val cardMusic = meta.getJSONObject("card_music") @@ -1190,13 +1195,13 @@ object AgentService : CoroutineScope { val end = cardNavigation.optString("end", "") processNavigation(start, end); } - return Pair(metaStr, broadcast); + return mapOf("meta" to metaStr, "broadcast" to broadcast,"notreply" to notreply); } } catch (e: Exception) { // 捕获其他类型的异常(可选) println("为解析到卡片数据!") } - return Pair("", true); + return mapOf("meta" to "", "broadcast" to true,"notreply" to false); } /** diff --git a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/MCPClient.kt b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/MCPClient.kt index 23c538115..c727a8f82 100644 --- a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/MCPClient.kt +++ b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/MCPClient.kt @@ -237,7 +237,10 @@ class MCPClient(private val context: Context? = null) : AutoCloseable { localFunctions[name]?.let { handler -> return try { val result = handler.handle(arguments) - mapOf("context" to result) + mapOf("context" to mapOf( + "type" to "text", + "text" to result, + )) } catch (e: Exception) { mapOf( "content" to listOf(mapOf( diff --git a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt index 672c29634..80820bece 100644 --- a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt +++ b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt @@ -234,7 +234,7 @@ private class ExitNavigationHandler(private val context: Context?) : FunctionHan setPackage(context?.packageName) // 设置包名使其成为显式 Intent } context?.sendBroadcast(intent) - return "{\"result\": \"已结束当前导航服务\"}" + return "{\"broadcast\":false,\"result\": \"已结束当前导航服务\"}" } } @@ -435,7 +435,7 @@ private class MediaPlayHandler(private val context: Context?) : FunctionHandler setPackage(context?.packageName) // 设置包名使其成为显式 Intent } context?.sendBroadcast(intent) - return "{\"result\": \"已开始播放媒体\"}" + return "{\"notreply\":true,\"broadcast\":false,\"result\": \"已开始播放媒体\"}" } } @@ -450,7 +450,7 @@ private class MediaPauseHandler(private val context: Context?) : FunctionHandler setPackage(context?.packageName) // 设置包名使其成为显式 Intent } context?.sendBroadcast(intent) - return "{\"result\": \"已暂停媒体播放\"}" + return "{\"notreply\":true,\"broadcast\":false,\"result\": \"已暂停媒体播放\"}" } } /** @@ -464,7 +464,7 @@ private class MediaCloseHandler(private val context: Context?) : FunctionHandler setPackage(context?.packageName) // 设置包名使其成为显式 Intent } context?.sendBroadcast(intent) - return "{\"result\": \"已关闭媒体播放\"}" + return "{\"notreply\":true,\"broadcast\":false,\"result\": \"已关闭媒体播放\"}" } } /** @@ -478,7 +478,7 @@ private class MediaPreviousHandler(private val context: Context?) : FunctionHand setPackage(context?.packageName) // 设置包名使其成为显式 Intent } context?.sendBroadcast(intent) - return "{\"result\": \"已切换到上一首\"}" + return "{\"notreply\":true,\"broadcast\":false,\"result\": \"已切换到上一首\"}" } } @@ -493,7 +493,7 @@ private class MediaNextHandler(private val context: Context?) : FunctionHandler setPackage(context?.packageName) // 设置包名使其成为显式 Intent } context?.sendBroadcast(intent) - return "{\"result\": \"已切换到下一首\"}" + return "{\"notreply\":true,\"broadcast\":false,\"result\": \"已切换到下一首\"}" } } From efc0a05e2014008e66585d80fbb92804829d28a4 Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Thu, 19 Jun 2025 10:50:14 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E7=83=A7=E8=8B=97?= =?UTF-8?q?=E5=90=AF=E5=8A=A8angent=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/pairing_controller.dart | 182 +++++++++--------- 1 file changed, 92 insertions(+), 90 deletions(-) diff --git a/lib/modules/pairing/controllers/pairing_controller.dart b/lib/modules/pairing/controllers/pairing_controller.dart index 34574e0f0..ed62d4ae9 100644 --- a/lib/modules/pairing/controllers/pairing_controller.dart +++ b/lib/modules/pairing/controllers/pairing_controller.dart @@ -11,27 +11,27 @@ import '../../../core/utils/permission_util.dart'; /// 配对状态枚举 - 简化版本 enum PairingState { - idle, // 空闲/初始状态 - checking, // 检查中(蓝牙、权限等) - waitingClassic, // 等待经典蓝牙连接 - verifying, // 验证设备 - associating, // 正在进行BLE设备关联 - waitingBle, // 等待BLE连接 - completed, // 配对完成 - error, // 错误状态 + idle, // 空闲/初始状态 + checking, // 检查中(蓝牙、权限等) + waitingClassic, // 等待经典蓝牙连接 + verifying, // 验证设备 + associating, // 正在进行BLE设备关联 + waitingBle, // 等待BLE连接 + completed, // 配对完成 + error, // 错误状态 } /// 错误类型枚举 enum ErrorType { none, - bluetoothOff, // 蓝牙关闭 - noPermission, // 无权限 - noClassicDevice, // 无经典蓝牙设备 - deviceNotMatch, // 设备不匹配 - deviceBound, // 设备已被绑定 + bluetoothOff, // 蓝牙关闭 + noPermission, // 无权限 + noClassicDevice, // 无经典蓝牙设备 + deviceNotMatch, // 设备不匹配 + deviceBound, // 设备已被绑定 associationFailed, // 关联失败 - bleTimeout, // BLE连接超时 - networkError, // 网络错误 + bleTimeout, // BLE连接超时 + networkError, // 网络错误 } /// 配对控制器 - 基于简化状态机的实现 @@ -43,7 +43,7 @@ class PairingController extends GetxController { final _currentState = PairingState.idle.obs; final _errorType = ErrorType.none.obs; final _errorMessage = ''.obs; - + PairingState get currentState => _currentState.value; ErrorType get errorType => _errorType.value; String get errorMessage => _errorMessage.value; @@ -68,7 +68,7 @@ class PairingController extends GetxController { // ========== 当前设备信息 ========== BluetoothDevice? _currentClassicDevice; String? _verifiedDeviceAddress; - + // Public getter for view access BluetoothDevice? get currentDevice => _currentClassicDevice; @@ -76,14 +76,14 @@ class PairingController extends GetxController { void onInit() { super.onInit(); Logger.i(_tag, '配对控制器初始化'); - + // 确保服务已初始化 _bluetoothManager = Get.find(); _bleManager = Get.find(); - + // 集中设置所有监听器 _setupAllListeners(); - + // 开始初始状态检查 // 延迟稍长时间,确保BluetoothManager有足够时间获取已连接设备 Future.delayed(const Duration(milliseconds: 300), () { @@ -99,10 +99,10 @@ class PairingController extends GetxController { worker.dispose(); } _workers.clear(); - + // 清理定时器 _bleTimeoutTimer?.cancel(); - + super.onClose(); } @@ -112,42 +112,37 @@ class PairingController extends GetxController { void _setupAllListeners() { // 1. 监听蓝牙开关状态 _setupBluetoothStateListener(); - + // 2. 监听经典蓝牙连接状态 _setupClassicConnectionListener(); - + // 3. 监听BLE连接状态 _setupBleConnectionListener(); } /// 监听蓝牙开关状态 void _setupBluetoothStateListener() { - _workers.add( - ever(_bluetoothManager.bluetoothStateRx, (String state) { - Logger.d(_tag, '蓝牙状态变化: $state'); - _handleBluetoothStateChange(state); - }) - ); + _workers.add(ever(_bluetoothManager.bluetoothStateRx, (String state) { + Logger.d(_tag, '蓝牙状态变化: $state'); + _handleBluetoothStateChange(state); + })); } /// 监听经典蓝牙连接状态 void _setupClassicConnectionListener() { - _workers.add( - ever(_bluetoothManager.currentDeviceRx, (BluetoothDevice? device) { - Logger.d(_tag, '经典蓝牙设备变化: ${device?.name}'); - _handleClassicDeviceChange(device); - }) - ); + _workers + .add(ever(_bluetoothManager.currentDeviceRx, (BluetoothDevice? device) { + Logger.d(_tag, '经典蓝牙设备变化: ${device?.name}'); + _handleClassicDeviceChange(device); + })); } /// 监听BLE连接状态 void _setupBleConnectionListener() { - _workers.add( - ever(_bleManager.connectionStateRx, (int state) { - Logger.d(_tag, 'BLE连接状态变化: $state'); - _handleBleStateChange(state); - }) - ); + _workers.add(ever(_bleManager.connectionStateRx, (int state) { + Logger.d(_tag, 'BLE连接状态变化: $state'); + _handleBleStateChange(state); + })); } // ==================== 状态处理 ==================== @@ -171,7 +166,7 @@ class PairingController extends GetxController { /// 处理经典蓝牙设备变化 void _handleClassicDeviceChange(BluetoothDevice? device) { _currentClassicDevice = device; - + switch (_currentState.value) { case PairingState.waitingClassic: if (device != null) { @@ -181,7 +176,7 @@ class PairingController extends GetxController { _verifyDevice(device); } break; - + case PairingState.error: // 如果在错误状态下检测到设备连接,且错误是无设备连接,则自动恢复 if (device != null && _errorType.value == ErrorType.noClassicDevice) { @@ -191,7 +186,7 @@ class PairingController extends GetxController { _verifyDevice(device); } break; - + case PairingState.verifying: case PairingState.waitingBle: case PairingState.completed: @@ -200,7 +195,7 @@ class PairingController extends GetxController { _setError(ErrorType.noClassicDevice, '设备已断开'); } break; - + default: break; } @@ -209,12 +204,13 @@ class PairingController extends GetxController { /// 处理BLE状态变化 void _handleBleStateChange(int state) { if (_currentState.value == PairingState.waitingBle) { - if (state == 2) { // BLE已连接 + if (state == 2) { + // BLE已连接 _bleTimeoutTimer?.cancel(); _transition(PairingState.completed); } } - + // 如果在完成状态下BLE断开,显示警告但不改变状态 if (_currentState.value == PairingState.completed && state != 2) { Logger.w(_tag, 'BLE连接已断开,但配对流程已完成'); @@ -228,7 +224,7 @@ class PairingController extends GetxController { final oldState = _currentState.value; _currentState.value = newState; Logger.i(_tag, '状态转换: $oldState -> $newState'); - + // 根据新状态执行相应操作 _onStateChanged(newState); } @@ -240,29 +236,29 @@ class PairingController extends GetxController { showConnectButton.value = false; showRetryButton.value = false; showSkipButton.value = false; - + switch (state) { case PairingState.idle: statusText.value = '准备开始配对'; break; - + case PairingState.checking: statusText.value = '正在检查环境...'; isLoading.value = true; break; - + case PairingState.waitingClassic: statusText.value = '请确保耳机已开启并进入配对模式'; showConnectButton.value = true; showSkipButton.value = true; connectButtonText.value = '前往蓝牙设置'; break; - + case PairingState.verifying: statusText.value = '正在验证设备...'; isLoading.value = true; break; - + case PairingState.associating: statusText.value = '正在关联设备...'; isLoading.value = true; @@ -271,7 +267,7 @@ class PairingController extends GetxController { _startDeviceAssociation(); }); break; - + case PairingState.waitingBle: statusText.value = '正在建立安全连接...'; isLoading.value = true; @@ -280,18 +276,18 @@ class PairingController extends GetxController { _startBleConnection(); }); break; - + case PairingState.completed: statusText.value = '配对成功!'; isLoading.value = false; _navigateToHome(); break; - + case PairingState.error: statusText.value = _errorMessage.value; isLoading.value = false; showSkipButton.value = true; - + // 根据错误类型决定显示哪个按钮和按钮文本 switch (_errorType.value) { case ErrorType.bluetoothOff: @@ -350,28 +346,27 @@ class PairingController extends GetxController { _setError(ErrorType.bluetoothOff, '请开启蓝牙'); return; } - + // 2. 检查权限 if (!await _checkPermissions()) { _setError(ErrorType.noPermission, '请授予必要权限'); return; } - + // 3. 先刷新设备连接状态,确保获取最新的连接信息 await _bluetoothManager.refreshDeviceConnection(); - + // 4. 检查经典蓝牙连接 final classicDevice = _bluetoothManager.currentDevice; if (classicDevice == null) { _transition(PairingState.waitingClassic); return; } - + // 5. 如果有设备,进行验证 _currentClassicDevice = classicDevice; _transition(PairingState.verifying); await _verifyDevice(classicDevice); - } catch (e) { Logger.e(_tag, '状态检查失败: $e'); _setError(ErrorType.networkError, '检查失败,请重试'); @@ -386,10 +381,10 @@ class PairingController extends GetxController { final bluetoothScan = await Permission.bluetoothScan.request(); final bluetoothConnect = await Permission.bluetoothConnect.request(); final location = await Permission.locationWhenInUse.request(); - - return bluetoothScan.isGranted && - bluetoothConnect.isGranted && - location.isGranted; + + return bluetoothScan.isGranted && + bluetoothConnect.isGranted && + location.isGranted; } return true; } @@ -398,28 +393,31 @@ class PairingController extends GetxController { Future _verifyDevice(BluetoothDevice device) async { try { Logger.i(_tag, '开始验证设备: ${device.name} (${device.address})'); - + // TODO: 实现API验证逻辑 // 暂时模拟验证通过 await Future.delayed(const Duration(seconds: 2)); - + // 检查设备名称是否包含 DeepSound 或 DeapSound final deviceName = device.name ?? ''; - if (deviceName.contains('DeepSound') || deviceName.contains('DeapSound')) { + if (deviceName.contains('DeepSound') || + deviceName.contains('DeapSound')) { Logger.i(_tag, '设备验证通过'); _verifiedDeviceAddress = device.address; - + // 检查是否已有BLE关联 final pairedMacAddress = await _bleManager.getPairedMacAddress(); Logger.i(_tag, '已关联的MAC地址: $pairedMacAddress'); - - if (pairedMacAddress != null && pairedMacAddress.toUpperCase() == device.address.toUpperCase()) { + + if (pairedMacAddress != null && + pairedMacAddress.toUpperCase() == device.address.toUpperCase()) { // 已有关联,检查BLE连接状态 Logger.i(_tag, '设备已关联,检查BLE连接状态'); final currentBleState = await _bleManager.getConnectionState(); Logger.i(_tag, '验证时BLE状态: $currentBleState'); - - if (currentBleState == 2) { // BleConnectionState.connected + + if (currentBleState == 2) { + // BleConnectionState.connected Logger.i(_tag, 'BLE已连接,直接完成配对'); _transition(PairingState.completed); } else { @@ -447,15 +445,15 @@ class PairingController extends GetxController { _setError(ErrorType.associationFailed, '设备地址无效'); return; } - + Logger.i(_tag, '开始BLE设备关联: $_verifiedDeviceAddress'); - + // 调用BLE管理器的关联方法 final success = await _bleManager.startDeviceAssociation( - deviceNamePattern: 'DeapSound', // 或者使用 _currentClassicDevice?.name + deviceNamePattern: 'DeapSound', // 或者使用 _currentClassicDevice?.name macAddress: _verifiedDeviceAddress!, ); - + if (success) { Logger.i(_tag, 'BLE设备关联成功'); // 关联成功后,转到等待BLE连接状态 @@ -479,21 +477,23 @@ class PairingController extends GetxController { _setError(ErrorType.bleTimeout, 'BLE连接超时,请重试'); } }); - + // 检查是否已连接 - 直接从原生层获取最新状态 final currentBleState = await _bleManager.getConnectionState(); Logger.i(_tag, 'BLE当前连接状态: $currentBleState'); - - if (currentBleState == 2) { // BleConnectionState.connected + + if (currentBleState == 2) { + // BleConnectionState.connected Logger.i(_tag, 'BLE已经连接,直接完成配对'); _bleTimeoutTimer?.cancel(); _transition(PairingState.completed); return; } - + // 注册后台扫描以开始BLE配对 if (_verifiedDeviceAddress != null) { - final success = await _bleManager.registerBackgroundScan(macAddress: _verifiedDeviceAddress); + final success = await _bleManager.registerBackgroundScan( + macAddress: _verifiedDeviceAddress); if (!success && _currentState.value == PairingState.waitingBle) { _setError(ErrorType.bleTimeout, 'BLE配对启动失败'); } @@ -505,7 +505,8 @@ class PairingController extends GetxController { } /// 导航到主页 - void _navigateToHome() { + void _navigateToHome() async { + await BleManager.to.startAgentService(); Future.delayed(const Duration(seconds: 1), () { Get.offAllNamed(Routes.home); }); @@ -516,7 +517,7 @@ class PairingController extends GetxController { /// 用户点击连接按钮 void onConnectTap() { Logger.i(_tag, '用户点击连接按钮'); - + // 根据当前状态或错误类型决定行为 if (_currentState.value == PairingState.error) { switch (_errorType.value) { @@ -550,7 +551,7 @@ class PairingController extends GetxController { /// 用户点击重试按钮 void onRetryTap() { Logger.i(_tag, '用户点击重试按钮'); - + // 如果是权限问题,引导去设置 if (_errorType.value == ErrorType.noPermission) { Logger.i(_tag, '引导用户去应用设置授予权限'); @@ -563,7 +564,8 @@ class PairingController extends GetxController { } /// 用户点击跳过按钮 - void onSkipTap() { + void onSkipTap() async { + await BleManager.to.startAgentService(); Logger.i(_tag, '用户选择跳过配对'); Get.offAllNamed(Routes.home); } @@ -572,7 +574,7 @@ class PairingController extends GetxController { void openBluetoothSettings() { AppSettings.openAppSettings(type: AppSettingsType.bluetooth); } - + /// 清除设备关联(在需要重新配对时调用) Future clearDeviceAssociation() async { try { @@ -582,4 +584,4 @@ class PairingController extends GetxController { Logger.e(_tag, '清除设备关联失败: $e'); } } -} \ No newline at end of file +}