From 745cb5b7730613b9ecf9140bd9fea49831f92f89 Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Wed, 10 Sep 2025 16:04:11 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E6=89=AB=E6=8F=8F?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=E6=9D=A1=E4=BB=B6=EF=BC=8C=E6=9A=82=E6=97=B6?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=EF=BC=8C=E7=AD=89=E5=9B=BA=E4=BB=B6=E5=85=A8?= =?UTF-8?q?=E9=83=A8=E5=8D=87=E7=BA=A7=E5=90=8E=E6=94=BE=E5=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yunqiinnovation/ble_service/BleService.kt | 104 +++++++++++------- 1 file changed, 65 insertions(+), 39 deletions(-) diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt index e25c554fb..7933eac90 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt @@ -140,6 +140,21 @@ object BleService { // 初始化状态 private var isInitialized = false + + /** + * 检查是否有蓝牙扫描权限 + */ + private fun hasBluetoothScanPermission(): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + // Android 12+ 需要 BLUETOOTH_SCAN 权限 + context.checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN) == + android.content.pm.PackageManager.PERMISSION_GRANTED + } else { + // Android 12 以下使用 ACCESS_FINE_LOCATION 权限 + context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == + android.content.pm.PackageManager.PERMISSION_GRANTED + } + } // 音频数据统计相关变量 private var bytesReceivedInCurrentSecond = 0 @@ -280,18 +295,22 @@ object BleService { /** * 开始主动扫描BLE设备 + * @param uuids 要扫描的服务UUID列表 * @param timeout 扫描超时时间(毫秒) * @return 是否成功开始扫描 */ fun startScan(uuids: List, timeout: Long = 15000): Boolean { -// Log.i(TAG, "liwei---------startScan 1 $deviceName") if (!isInitialized) { Log.e(TAG, "BLE服务未初始化") return false } -// Log.i(TAG, "liwei---------startScan 2") - // 获取一个局部引用,避免并发访问问题 -// val scanner = bluetoothLeScanner ?: return false + + // 检查蓝牙扫描权限 + if (!hasBluetoothScanPermission()) { + Log.e(TAG, "开始扫描失败: 缺少 BLUETOOTH_SCAN 权限") + return false + } + // 💡 每次都重新取最新实例 val scanner = bluetoothLeScanner ?: run { Log.e(TAG, "bluetoothLeScanner == null,尝试重启蓝牙") @@ -299,58 +318,65 @@ object BleService { return false } -// Log.i(TAG, "liwei---------startScan 3") if (isScanning) return false -// Log.i(TAG, "liwei---------startScan 4") + Log.i(TAG, "开始主动扫描BLE设备...") scanResults.clear() -// Log.i(TAG, "liwei---------startScan 5") - val filter = ScanFilter.Builder() - // .setServiceUuid(ParcelUuid(BleConst.PRIMARY_SERVICE_UUID)) -// .setServiceUuid(ParcelUuid(java.util.UUID.fromString("00000201-0000-1000-8000-00805f9b34fb"))) - .build() -// val filters = uuids.mapNotNull { uuidStr -> -// try { -// // 清理UUID字符串,移除非十六进制字符 -// val cleanUuidStr = uuidStr.replace("[^0-9A-Fa-f]".toRegex(), "") -// -// // 验证清理后的字符串长度(应该是4位十六进制字符) -// if (cleanUuidStr.length != 4) { -// Log.w(TAG, "无效的UUID格式: $uuidStr,清理后: $cleanUuidStr,长度: ${cleanUuidStr.length}") -// return@mapNotNull null -// } -// -// val fullUuid = "0000$cleanUuidStr-0000-1000-8000-00805f9b34fb" -// Log.d(TAG, "扫描uuid: $fullUuid") -// -// ScanFilter.Builder() -// .setServiceUuid(ParcelUuid(UUID.fromString(fullUuid))) -// .build() -// } catch (e: IllegalArgumentException) { -// Log.e(TAG, "构造UUID失败: $uuidStr", e) -// null -// } -// } + + // 构建扫描过滤器 - 参考 registerBackgroundScan 的实现 + val filters = mutableListOf() + + // 如果提供了额外的UUID,也添加到过滤器中 + uuids.forEach { uuidStr -> + try { + val uuid = when { + uuidStr.length == 4 -> { + // 短UUID,转换为完整UUID + val fullUuid = "0000$uuidStr-0000-1000-8000-00805f9b34fb" + UUID.fromString(fullUuid) + } + uuidStr.length == 36 -> { + // 完整UUID + UUID.fromString(uuidStr) + } + else -> { + Log.w(TAG, "无效的UUID格式: $uuidStr") + return@forEach + } + } + + val filter = ScanFilter.Builder() + .setServiceUuid(ParcelUuid(uuid)) + .build() + filters.add(filter) + Log.d(TAG, "添加扫描过滤器UUID: $uuid") + } catch (e: IllegalArgumentException) { + Log.e(TAG, "构造UUID失败: $uuidStr", e) + } + } + + // 扫描设置 - 参考 registerBackgroundScan 的配置 val settings = ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER) - .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) // 推荐:不会触发权限问题 + .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) .setReportDelay(0) .build() -// Log.i(TAG, "liwei---------startScan 7") + try { - scanner.startScan(listOf(filter), settings, scanCallback) + // 使用过滤器列表进行扫描 + scanner.startScan(filters, settings, scanCallback) isScanning = true -// Log.i(TAG, "liwei---------startScan 8") + // 设置扫描超时 scanHandler.postDelayed({ Log.d(TAG, "扫描超时,停止扫描") stopScan() }, timeout) -// Log.i(TAG, "liwei---------startScan 9") + + Log.i(TAG, "已启动BLE扫描,过滤器数量: ${filters.size}") return true } catch (e: Exception) { Log.e(TAG, "启动扫描失败: ${e.message}", e) -// Log.i(TAG, "liwei---------startScan 10") isScanning = false return false } From 26be7d4efbbc22aa57f5afc223727f95eb24ef2c Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Wed, 10 Sep 2025 16:42:58 +0800 Subject: [PATCH 2/5] 1 --- .../main/kotlin/com/yunqiinnovation/ble_service/BleService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt index 7933eac90..ebad04d7e 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt @@ -346,7 +346,7 @@ object BleService { } val filter = ScanFilter.Builder() - .setServiceUuid(ParcelUuid(uuid)) + //.setServiceUuid(ParcelUuid(uuid)) .build() filters.add(filter) Log.d(TAG, "添加扫描过滤器UUID: $uuid") From 1df6c36b82f462d5ff1a8b9b3ddd0e05027ff113 Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Wed, 10 Sep 2025 17:33:05 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8DIos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/ble_service/BleService.swift | 46 +++++++++++++++---- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift b/local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift index 2b1245106..3ce8a26aa 100644 --- a/local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift +++ b/local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift @@ -249,7 +249,10 @@ private var cmdReplyType: UInt8 = 0 clearPairedMacAddress() } /// 开始扫描BLE设备 - public func startScan(timeout: TimeInterval = 30.0) -> Bool { + /// @param timeout 扫描超时时间,默认30秒 + /// @param uuids 要扫描的服务UUID列表 + /// @return 是否成功开始扫描 + public func startScan(timeout: TimeInterval = 30.0, uuids: [String]) -> Bool { if centralManager == nil { initializeCentralManager() } @@ -269,16 +272,41 @@ private var cmdReplyType: UInt8 = 0 isScanning = true - // // 移除服务UUID过滤,扫描所有设备 - // manager.scanForPeripherals(withServices: nil, options: [ - // CBCentralManagerScanOptionAllowDuplicatesKey: true // 允许重复以获取更新的广播数据 - // ]) - let serviceUUIDs = [BleConst.SERVICE_UUID, BleConst.SERVICE_UUID1] - - manager.scanForPeripherals(withServices: nil, options: [ + // 构建服务UUID数组进行过滤 + var serviceUUIDs: [CBUUID] = [] + + // // 添加默认的服务UUID + // serviceUUIDs.append(BleConst.SERVICE_UUID) + // serviceUUIDs.append(BleConst.SERVICE_UUID1) + + // 处理传入的UUID参数 + for uuidStr in uuids { + do { + let cbuuid: CBUUID + if uuidStr.count == 4 { + // 短UUID,直接使用 + cbuuid = CBUUID(string: uuidStr) + } else if uuidStr.count == 36 { + // 完整UUID + cbuuid = CBUUID(string: uuidStr) + } else { + os_log("⚠️ 无效的UUID格式: %{public}@", log: logger, type: .warning, uuidStr) + continue + } + serviceUUIDs.append(cbuuid) + os_log("✅ 添加扫描过滤器UUID: %{public}@", log: logger, type: .info, uuidStr) + } catch { + os_log("❌ 构造UUID失败: %{public}@, 错误: %{public}@", log: logger, type: .error, uuidStr, error.localizedDescription) + } + } + + // 开始扫描,使用构建的服务UUID数组进行过滤 + manager.scanForPeripherals(withServices: serviceUUIDs.isEmpty ? nil : serviceUUIDs, options: [ CBCentralManagerScanOptionAllowDuplicatesKey: true // 允许重复以获取更新的广播数据 ]) - os_log("🔍 开始扫描所有BLE设备(无过滤),超时时间: %.1f秒", log: logger, type: .info, timeout) + + let filterInfo = serviceUUIDs.isEmpty ? "无过滤" : "过滤器数量: \(serviceUUIDs.count)" + os_log("🔍 开始扫描BLE设备(%{public}@),超时时间: %.1f秒", log: logger, type: .info, filterInfo, timeout) // 设置扫描超时 scanTimer?.invalidate() From f18a005166a27baefab0d18c966793300dde799d Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Wed, 10 Sep 2025 18:10:29 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/pairing_controller.dart | 38 +++++++++---------- .../chat_api/ChatApiService.kt | 24 +++++++++++- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/lib/modules/pairing/controllers/pairing_controller.dart b/lib/modules/pairing/controllers/pairing_controller.dart index 90735dddc..df9198b89 100644 --- a/lib/modules/pairing/controllers/pairing_controller.dart +++ b/lib/modules/pairing/controllers/pairing_controller.dart @@ -1,18 +1,17 @@ -import 'dart:io'; -import 'dart:async'; - import 'package:app_settings/app_settings.dart'; +import 'package:voitrans/data/models/appconfig_model.dart'; +import '/data/models/appconfig.dart'; +import '/data/models/user_Info.dart'; +import '/data/services/network/api.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; +import 'dart:async'; import 'package:permission_handler/permission_handler.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../../routes/app_routes.dart'; import '../../../core/utils/logger.dart'; -import '/data/models/appconfig_model.dart'; -import '/data/models/appconfig.dart'; -import '/data/models/user_Info.dart'; -import '/data/services/network/api.dart'; +import 'dart:io'; import '../../../data/services/bluetooth_manager.dart'; import '../../../data/services/ble_manager.dart'; import '../../../core/utils/permission_util.dart'; @@ -348,7 +347,7 @@ class PairingController extends GetxController with WidgetsBindingObserver { void _handleBleStateChange(int state) { if (_currentState.value == PairingState.waitingBle) { if (state == 2) { - // 2代表BLE已连接 + // BLE已连接 _bleTimeoutTimer?.cancel(); _transition(PairingState.completed); } @@ -502,33 +501,32 @@ class PairingController extends GetxController with WidgetsBindingObserver { /// 检查当前状态并决定下一步 void _checkCurrentStatus() async { try { - // 1. 并行检查蓝牙状态、权限、设备连接 + // 1. 检查蓝牙状态 final results = await Future.wait([ - _checkBluetoothState(), // 检查蓝牙是否开启 - _checkPermissions(), // 检查蓝牙相关权限 - _bluetoothManager.refreshDeviceConnection(), // 刷新设备连接状态 + _checkBluetoothState(), // 检查蓝牙状态 + _checkPermissions(), // 检查权限 + _bluetoothManager.refreshDeviceConnection(), // 刷新设备连接 ]); final isBluetoothOk = results[0] as bool; final arePermissionsOk = results[1] as bool; - //蓝牙没开 if (!isBluetoothOk) { _setError( ErrorType.bluetoothOff, 'connectDeepSoundViaSystemBluetooth'.tr); return; } - //权限不够 + // 2. 检查权限 if (!arePermissionsOk) { _setError(ErrorType.noPermission, 'pleaseGrantNecessaryPermissions'.tr); return; } - // 2. 先刷新设备连接状态,确保获取最新的连接信息 - //await _bluetoothManager.refreshDeviceConnection(); + // 3. 先刷新设备连接状态,确保获取最新的连接信息 + await _bluetoothManager.refreshDeviceConnection(); - // 3. 检查经典蓝牙连接 + // 4. 检查经典蓝牙连接 final classicDevice = _bluetoothManager.currentDevice; if (classicDevice == null) { _transition(PairingState.waitingClassic); @@ -545,7 +543,7 @@ class PairingController extends GetxController with WidgetsBindingObserver { } } - /// 检查蓝牙状态是否开启 + /// 新增一个辅助方法来检查蓝牙状态 Future _checkBluetoothState() async { return _bluetoothManager.bluetoothState != 'off'; } @@ -729,7 +727,7 @@ class PairingController extends GetxController with WidgetsBindingObserver { // 检查设备名称是否包含 DeepSound 或 DeapSound final deviceName = device.name; DBProduct? product; - + // 检查是否已有BLE绑定的设备 /// 检查是否已有BLE绑定的设备 if (AppConfig.config.products.isNotEmpty) { final match = AppConfig.config.products.where( @@ -898,7 +896,7 @@ class PairingController extends GetxController with WidgetsBindingObserver { await Future.delayed(Duration(seconds: 1)); } _bleManager.stopScan(); - if (product != null && product.devicetype == 2) { + if (product != null && product.devicetype != 0) { //ble设备 必须要扫描到结果 return 0; } else { diff --git a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt index 866248973..83e3a3522 100644 --- a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt +++ b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt @@ -600,7 +600,29 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor _mcpClient?.parseJsonArguments(argumentsJson) ?: emptyMap() } // 调用MCP工具 - val toolResult = _mcpClient?.callTool(functionName, arguments) + val toolResult = try { + withTimeout(60000) { // 60秒超时 + _mcpClient?.callTool(functionName, arguments) + } + } catch (e: TimeoutCancellationException) { + Log.w("ChatApiService", "[Session: $sessionid] MCP工具调用超时: $functionName") + mapOf( + "content" to listOf(mapOf( + "type" to "text", + "text" to "Tool call timeout: $functionName" + )), + "isError" to true + ) + } catch (e: Exception) { + Log.e("ChatApiService", "[Session: $sessionid] MCP工具调用异常: $functionName", e) + mapOf( + "content" to listOf(mapOf( + "type" to "text", + "text" to "Tool call failed: ${e.message}" + )), + "isError" to true + ) + } Log.d("ChatApiService", "[Session: $sessionid] AI调用工具结果: $functionName, 参数: $arguments, 结果: $toolResult") // 处理结果 if (toolResult != null) { From 54a9c943eb85b521e137ca744b03fd2d8832f906 Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Wed, 10 Sep 2025 18:39:32 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ble_service/Sources/ble_service/BleService.swift | 10 ++++++---- .../Sources/ble_service/SwiftBleServicePlugin.swift | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift b/local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift index 3ce8a26aa..87c242ef8 100644 --- a/local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift +++ b/local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift @@ -290,7 +290,7 @@ private var cmdReplyType: UInt8 = 0 // 完整UUID cbuuid = CBUUID(string: uuidStr) } else { - os_log("⚠️ 无效的UUID格式: %{public}@", log: logger, type: .warning, uuidStr) + os_log("⚠️ 无效的UUID格式: %{public}@", log: logger, type: .default, uuidStr) continue } serviceUUIDs.append(cbuuid) @@ -300,11 +300,13 @@ private var cmdReplyType: UInt8 = 0 } } - // 开始扫描,使用构建的服务UUID数组进行过滤 - manager.scanForPeripherals(withServices: serviceUUIDs.isEmpty ? nil : serviceUUIDs, options: [ + // // 开始扫描,使用构建的服务UUID数组进行过滤 + // manager.scanForPeripherals(withServices: serviceUUIDs.isEmpty ? nil : serviceUUIDs, options: [ + // CBCentralManagerScanOptionAllowDuplicatesKey: true // 允许重复以获取更新的广播数据 + // ]) + manager.scanForPeripherals(withServices: nil , options: [ CBCentralManagerScanOptionAllowDuplicatesKey: true // 允许重复以获取更新的广播数据 ]) - let filterInfo = serviceUUIDs.isEmpty ? "无过滤" : "过滤器数量: \(serviceUUIDs.count)" os_log("🔍 开始扫描BLE设备(%{public}@),超时时间: %.1f秒", log: logger, type: .info, filterInfo, timeout) diff --git a/local_plugins/ble_service/ios/ble_service/Sources/ble_service/SwiftBleServicePlugin.swift b/local_plugins/ble_service/ios/ble_service/Sources/ble_service/SwiftBleServicePlugin.swift index dd9d07f14..eb7fc5215 100644 --- a/local_plugins/ble_service/ios/ble_service/Sources/ble_service/SwiftBleServicePlugin.swift +++ b/local_plugins/ble_service/ios/ble_service/Sources/ble_service/SwiftBleServicePlugin.swift @@ -112,9 +112,10 @@ public class SwiftBleServicePlugin: NSObject, FlutterPlugin { case "startScan": let arguments = call.arguments as? [String: Any] let timeout = (arguments?["timeout"] as? Double) ?? 15.0 + let uuids = (arguments?["uuids"] as? [String]) ?? [] os_log("🔍 开始传统BLE扫描,超时: %.1f秒", log: logger, type: .info, timeout) - BleService.shared.startScan(timeout: timeout) + BleService.shared.startScan(timeout: timeout, uuids: uuids) result(true) case "stopScan":