diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index d0bbc4669..c9050a312 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -85,7 +85,7 @@ PODS:
- SDWebImage/Core (~> 5.17)
- spotify_sdk (0.0.1):
- Flutter
- - tencentcloud_cos_sdk_plugin (1.2.3):
+ - tencentcloud_cos_sdk_plugin (1.2.4):
- Flutter
- QCloudCOSXML (= 6.4.7)
- WechatOpenSDK-XCFramework (2.0.4)
@@ -203,7 +203,7 @@ SPEC CHECKSUMS:
SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
spotify_sdk: a48400bb29f70c4fe251ebfdc9135c37097ac5ca
- tencentcloud_cos_sdk_plugin: c3d78b2c3bae42b5a20009dc047f3f1621e6272b
+ tencentcloud_cos_sdk_plugin: 7bed564dbe72df23e7b5cacb1e51b1a20cb1639c
WechatOpenSDK-XCFramework: 36fb2bea0754266c17184adf4963d7e6ff98b69f
wifi_iot: f645260a2be8608517b2a9bf4c39b98e97003acc
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 4df97502d..02daa62e1 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -252,7 +252,7 @@
);
mainGroup = 97C146E51CF9000F007C117D;
packageReferences = (
- 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
+ 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */,
D013B7972DFA83E400B2B69B /* XCRemoteSwiftPackageReference "flutterfire" */,
);
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
@@ -793,7 +793,7 @@
/* End XCConfigurationList section */
/* Begin XCLocalSwiftPackageReference section */
- 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
+ 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = {
isa = XCLocalSwiftPackageReference;
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
};
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index bc3f4a793..b2cdd80bb 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -85,10 +85,6 @@
语音助理耳机
- NSAccessorySetupKitSupports
-
- Bluetooth
-
NSAppTransportSecurity
NSAllowsArbitraryLoads
@@ -117,10 +113,25 @@
+
NSBluetoothAlwaysUsageDescription
需要蓝牙权限用于连接和管理音频设备
NSBluetoothPeripheralUsageDescription
需要蓝牙权限用于连接和管理音频设备
+
+
+ UIBackgroundModes
+
+ bluetooth-central
+ audio
+ location
+
+
+
+ NSAccessorySetupKitSupports
+
+ Bluetooth
+
NSCalendarsUsageDescription
需要日历权限用于安排和管理您的音频会议和录音任务
NSCameraUsageDescription
@@ -137,12 +148,6 @@
需要相册权限用于保存录音文件和导入音频文件
UIApplicationSupportsIndirectInputEvents
- UIBackgroundModes
-
- bluetooth-central
- audio
- location
-
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
diff --git a/lib/data/services/ble_manager.dart b/lib/data/services/ble_manager.dart
index 3cbc033c3..5d131da39 100644
--- a/lib/data/services/ble_manager.dart
+++ b/lib/data/services/ble_manager.dart
@@ -859,4 +859,14 @@ class BleManager extends GetxService {
}
return Future.value(Get.find());
}
+
+ /// 请求蓝牙权限
+ Future requestBluetoothPermission() async {
+ try {
+ return await _bleService.requestBluetoothPermission();
+ } catch (e) {
+ Logger.error('请求蓝牙权限失败: ${e.toString()}');
+ return false;
+ }
+ }
}
diff --git a/lib/modules/pairing/controllers/pairing_controller.dart b/lib/modules/pairing/controllers/pairing_controller.dart
index 53b1690fb..bed5baf39 100644
--- a/lib/modules/pairing/controllers/pairing_controller.dart
+++ b/lib/modules/pairing/controllers/pairing_controller.dart
@@ -418,17 +418,45 @@ class PairingController extends GetxController {
/// 检查权限
Future _checkPermissions() async {
- // Android权限检查
if (Platform.isAndroid) {
- // 检查蓝牙相关权限
- 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;
+ // Android 平台权限检查
+ Map statuses = await [
+ Permission.bluetoothScan,
+ Permission.bluetoothConnect,
+ Permission.locationWhenInUse,
+ ].request();
+
+ return statuses[Permission.bluetoothScan]!.isGranted &&
+ statuses[Permission.bluetoothConnect]!.isGranted &&
+ statuses[Permission.locationWhenInUse]!.isGranted;
+ } else if (Platform.isIOS) {
+ // iOS 平台权限检查
+ Map statuses = await [
+ Permission.locationWhenInUse,
+ Permission.bluetooth, // 添加蓝牙权限检查
+ ].request();
+
+ // 检查位置权限
+ bool locationGranted = statuses[Permission.locationWhenInUse]!.isGranted;
+
+ // 检查蓝牙权限
+ bool bluetoothGranted = statuses[Permission.bluetooth]!.isGranted;
+
+ // 如果蓝牙权限未确定,强制触发权限申请
+ if (!bluetoothGranted) {
+ try {
+ await _bleManager.requestBluetoothPermission();
+ // 重新检查蓝牙权限状态
+ final newBluetoothStatus = await Permission.bluetooth.status;
+ bluetoothGranted = newBluetoothStatus.isGranted;
+ } catch (e) {
+ Logger.e(_tag, '蓝牙权限申请失败: $e');
+ }
+ }
+
+ return locationGranted && bluetoothGranted;
}
+
return true;
}
diff --git a/local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleScanManager.swift b/local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleScanManager.swift
new file mode 100644
index 000000000..1be2b0c46
--- /dev/null
+++ b/local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleScanManager.swift
@@ -0,0 +1,586 @@
+import Foundation
+import CoreBluetooth
+import UIKit // 添加UIKit导入
+import os.log
+
+/// 简化的 BLE 扫描管理器
+/// 只提供扫描和获取设备列表功能
+@available(iOS 13.0, *)
+public class BleScanManager: NSObject {
+
+ // MARK: - 单例
+ public static let shared = BleScanManager()
+
+ // MARK: - 数据模型
+ public struct BleDeviceInfo {
+ public let identifier: UUID
+ public let name: String?
+ public let rssi: NSNumber
+ public let advertisementData: [String: Any]
+ public let discoveryMethod: DiscoveryMethod
+ public let lastSeen: Date
+
+ public enum DiscoveryMethod {
+ case traditional
+ case accessorySetupKit
+ }
+ }
+
+ // MARK: - 属性
+ private var centralManager: CBCentralManager!
+ private let logger = OSLog(subsystem: "com.yunqiinnovation.ble_service", category: "BleScanManager")
+
+ // 扫描状态
+ private var isScanning = false
+ private var scanTimeout: Timer?
+
+ // 设备列表
+ private var discoveredDevices: [UUID: BleDeviceInfo] = [:]
+
+
+ // MARK: - 初始化
+ private override init() {
+ super.init()
+ setupCentralManager()
+ }
+
+ // 在setupCentralManager后添加权限检查
+ private func setupCentralManager() {
+ let options: [String: Any] = [
+ CBCentralManagerOptionShowPowerAlertKey: true,
+ CBCentralManagerOptionRestoreIdentifierKey: "BleScanManager"
+ ]
+
+ centralManager = CBCentralManager(delegate: self, queue: nil, options: options)
+
+ // 添加权限检查和触发逻辑
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
+ self?.checkAndTriggerPermission()
+ }
+ }
+
+ private func checkAndTriggerPermission() {
+ // 检查蓝牙权限状态,兼容不同iOS版本
+ let authorization: CBManagerAuthorization
+ if #available(iOS 13.1, *) {
+ authorization = CBManager.authorization
+ } else if #available(iOS 13.0, *) {
+ // iOS 13.0 使用实例方法,需要确保centralManager已初始化
+ authorization = centralManager?.authorization ?? .notDetermined
+ } else {
+ // iOS 13.0以下版本没有权限检查API,默认为未确定
+ authorization = .notDetermined
+ }
+
+ os_log("📊 蓝牙权限状态: %d", log: logger, type: .info, authorization.rawValue)
+ os_log("📊 CBCentralManager状态: %d", log: logger, type: .info, centralManager?.state.rawValue ?? -1)
+
+ // 只在权限未确定且蓝牙状态为关闭时触发扫描
+ if authorization == .notDetermined && centralManager?.state == .poweredOff {
+ os_log("📊 主动触发一次扫描来申请权限", log: logger, type: .info)
+ // 主动触发一次扫描来申请权限
+ centralManager?.scanForPeripherals(withServices: nil, options: nil)
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
+ self?.centralManager?.stopScan()
+ }
+ }
+ }
+
+
+
+ // MARK: - 公共接口
+
+ /// 开始扫描 BLE 设备
+ /// - Parameter timeout: 扫描超时时间,默认 10 秒
+ /// - Returns: 是否成功开始扫描
+ public func startScan(timeout: TimeInterval = 30.0) -> Bool {
+ os_log("🔍 开始 BLE 扫描,超时时间: %.1f 秒", log: logger, type: .info, timeout)
+
+ guard !isScanning else {
+ os_log("⚠️ 扫描已在进行中", log: logger, type: .info)
+ return false
+ }
+
+ // 清空之前的设备列表
+ discoveredDevices.removeAll()
+
+ // iOS 18 兼容性检查
+ if #available(iOS 18.0, *) {
+ // iOS 18 使用新的权限检查方式
+ return startScanForIOS18(timeout: timeout)
+ } else {
+ // iOS 17 及以下使用传统方式
+ return startScanLegacy(timeout: timeout)
+ }
+ }
+
+ @available(iOS 18.0, *)
+ private func startScanForIOS18(timeout: TimeInterval) -> Bool {
+ // 对于 iOS 18,直接检查 CBCentralManager 状态
+ guard let centralManager = centralManager else {
+ os_log("❌ CBCentralManager 未初始化", log: logger, type: .error)
+ return false
+ }
+
+ let currentState = centralManager.state
+ os_log("📊 iOS 18 蓝牙状态: %d (%{public}@)", log: logger, type: .info, currentState.rawValue, getBluetoothStateString(currentState))
+
+ switch currentState {
+ case .poweredOn:
+ return performScanWithPermission(timeout: timeout)
+ case .unauthorized:
+ os_log("❌ 蓝牙权限被拒绝", log: logger, type: .error)
+ showPermissionAlert()
+ return false
+ case .unknown:
+ os_log("⚠️ 蓝牙状态未知,等待状态更新", log: logger, type: .info)
+ // 设置监听器等待状态更新
+ waitForBluetoothState(timeout: timeout)
+ return true
+ default:
+ os_log("❌ 蓝牙不可用: %{public}@", log: logger, type: .error, getBluetoothStateString(currentState))
+ return false
+ }
+ }
+
+ private func startScanLegacy(timeout: TimeInterval) -> Bool {
+ // 检查并申请蓝牙权限
+ let authorization: CBManagerAuthorization = CBManager.authorization
+ os_log("📊 当前蓝牙权限状态: %d", log: logger, type: .info, authorization.rawValue)
+
+ switch authorization {
+ case .allowedAlways:
+ return performScanWithPermission(timeout: timeout)
+ case .denied, .restricted:
+ os_log("❌ 蓝牙权限被拒绝或受限", log: logger, type: .error)
+ showPermissionAlert()
+ return false
+ case .notDetermined:
+ // 权限未确定,需要主动触发蓝牙操作来显示权限弹窗
+ if centralManager == nil {
+ setupCentralManager()
+ }
+
+ // 等待CBCentralManager状态更新后,主动开始扫描来触发权限弹窗
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
+ guard let self = self else { return }
+ if self.centralManager.state == .poweredOn {
+ // 开始一个短暂的扫描来触发权限申请
+ self.centralManager.scanForPeripherals(withServices: nil, options: nil)
+ // 立即停止扫描
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
+ self.centralManager.stopScan()
+ }
+ } else if self.centralManager.state == .unauthorized {
+ // 权限被拒绝,显示设置提示
+ self.showPermissionAlert()
+ }
+ }
+ return true
+ @unknown default:
+ os_log("⚠️ 未知的蓝牙权限状态", log: logger, type: .info)
+ return false
+ }
+ }
+
+ private func waitForBluetoothState(timeout: TimeInterval) {
+ var checkCount = 0
+ let maxChecks = 20
+
+ let timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { [weak self] timer in
+ guard let self = self else {
+ timer.invalidate()
+ return
+ }
+
+ checkCount += 1
+ let currentState = self.centralManager?.state ?? .unknown
+
+ os_log("🔍 检查蓝牙状态 (%d/%d): %{public}@", log: self.logger, type: .info, checkCount, maxChecks, self.getBluetoothStateString(currentState))
+
+ switch currentState {
+ case .poweredOn:
+ timer.invalidate()
+ _ = self.performScanWithPermission(timeout: timeout)
+ case .unauthorized:
+ timer.invalidate()
+ self.showPermissionAlert()
+ case .poweredOff, .unsupported:
+ timer.invalidate()
+ os_log("❌ 蓝牙不可用", log: self.logger, type: .error)
+ default:
+ if checkCount >= maxChecks {
+ timer.invalidate()
+ os_log("⏰ 等待蓝牙状态超时", log: self.logger, type: .error)
+ }
+ }
+ }
+ }
+
+ private func forceTriggerPermissionRequest(timeout: TimeInterval) {
+ // 完全清理旧的 centralManager
+ centralManager?.delegate = nil
+ centralManager = nil
+
+ // 强制重新初始化,确保触发权限申请
+ let options: [String: Any] = [
+ CBCentralManagerOptionShowPowerAlertKey: true,
+ CBCentralManagerOptionRestoreIdentifierKey: "BleScanManager"
+ ]
+
+ centralManager = CBCentralManager(delegate: self, queue: nil, options: options)
+
+ // 设置一个监听器来等待权限状态更新
+ waitForPermissionResponse(timeout: timeout)
+ }
+
+ private func waitForPermissionResponse(timeout: TimeInterval) {
+ // 设置一个定时器来检查权限状态变化
+ var checkCount = 0
+ let maxChecks = 20 // 最多检查20次,每次间隔0.5秒,总共10秒
+
+ let timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { [weak self] timer in
+ guard let self = self else {
+ timer.invalidate()
+ return
+ }
+
+ checkCount += 1
+ let currentAuth = CBManager.authorization
+
+ os_log("🔍 检查权限状态 (%d/%d): %d", log: self.logger, type: .info, checkCount, maxChecks, currentAuth.rawValue)
+
+ switch currentAuth {
+ case .allowedAlways:
+ timer.invalidate()
+ os_log("✅ 蓝牙权限已获得,开始扫描", log: self.logger, type: .info)
+ _ = self.performScanWithPermission(timeout: timeout)
+ case .denied, .restricted:
+ timer.invalidate()
+ os_log("❌ 蓝牙权限被拒绝", log: self.logger, type: .error)
+ self.showPermissionAlert()
+ case .notDetermined:
+ if checkCount >= maxChecks {
+ timer.invalidate()
+ os_log("⏰ 等待权限响应超时", log: self.logger, type: .error)
+ self.showPermissionAlert()
+ }
+ // 继续等待
+ @unknown default:
+ timer.invalidate()
+ os_log("⚠️ 未知权限状态", log: self.logger, type: .error)
+ }
+ }
+ }
+
+ private func reinitializeCentralManager() {
+ // 清理旧的 centralManager
+ centralManager?.delegate = nil
+ centralManager = nil
+
+ // 重新初始化
+ setupCentralManager()
+ }
+
+ /// 检查蓝牙权限状态
+ private func checkBluetoothPermissionStatus() -> (hasPermission: Bool, needsRequest: Bool) {
+ // 检查权限状态
+ let authorization: CBManagerAuthorization
+ if #available(iOS 13.1, *) {
+ authorization = CBManager.authorization
+ } else if #available(iOS 13.0, *) {
+ if centralManager == nil {
+ setupCentralManager()
+ }
+ authorization = centralManager?.authorization ?? .notDetermined
+ } else {
+ // iOS 13以下没有权限概念
+ return (true, false)
+ }
+
+ os_log("📊 蓝牙权限状态: %d, CBCentralManager状态: %d", log: logger, type: .info, authorization.rawValue, centralManager?.state.rawValue ?? -1)
+
+ switch authorization {
+ case .allowedAlways:
+ return (true, false)
+ case .denied, .restricted:
+ return (false, false)
+ case .notDetermined:
+ return (false, true)
+ @unknown default:
+ return (false, true)
+ }
+ }
+
+ private func performScanWithPermission(timeout: TimeInterval) -> Bool {
+ // 首先检查权限状态
+ let permissionStatus = checkBluetoothPermissionStatus()
+
+ if !permissionStatus.hasPermission {
+ if permissionStatus.needsRequest {
+ os_log("🔐 需要申请蓝牙权限", log: logger, type: .info)
+ _ = requestBluetoothPermission()
+ return false
+ } else {
+ os_log("❌ 蓝牙权限被拒绝", log: logger, type: .error)
+ showPermissionAlert()
+ return false
+ }
+ }
+
+ // 权限已获得,检查蓝牙硬件状态
+ let currentState = centralManager.state
+ os_log("📊 蓝牙硬件状态: %d (%{public}@)", log: logger, type: .info, currentState.rawValue, getBluetoothStateString(currentState))
+
+ switch currentState {
+ case .poweredOn:
+ // 蓝牙已开启,可以扫描
+ break
+ case .unknown:
+ os_log("⚠️ 蓝牙状态未知,等待状态更新...", log: logger, type: .info)
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
+ _ = self?.performScanWithPermission(timeout: timeout)
+ }
+ return false
+ case .poweredOff:
+ // 区分是权限问题还是硬件关闭
+ if permissionStatus.hasPermission {
+ os_log("❌ 蓝牙硬件已关闭,请在设备设置中开启蓝牙", log: logger, type: .error)
+ } else {
+ os_log("❌ 可能是权限问题导致的蓝牙状态异常", log: logger, type: .error)
+ _ = requestBluetoothPermission()
+ }
+ return false
+ case .unauthorized:
+ os_log("❌ 应用未授权使用蓝牙", log: logger, type: .error)
+ showPermissionAlert()
+ return false
+ case .unsupported:
+ os_log("❌ 设备不支持蓝牙", log: logger, type: .error)
+ return false
+ default:
+ os_log("⚠️ 其他蓝牙状态: %d", log: logger, type: .error, currentState.rawValue)
+ return false
+ }
+
+ // 开始BLE扫描
+ centralManager.scanForPeripherals(withServices: nil, options: [
+ CBCentralManagerScanOptionAllowDuplicatesKey: true
+ ])
+
+ isScanning = true
+
+ // 设置扫描超时
+ scanTimeout = Timer.scheduledTimer(withTimeInterval: timeout, repeats: false) { [weak self] _ in
+ self?.stopScan()
+ }
+
+ os_log("✅ 开始BLE扫描,超时时间: %.1f 秒", log: logger, type: .info, timeout)
+ return true
+ }
+
+ private func showPermissionAlert() {
+ guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
+ let window = windowScene.windows.first else {
+ os_log("❌ 无法获取当前窗口", log: logger, type: .error)
+ return
+ }
+
+ let alert = UIAlertController(
+ title: "需要蓝牙权限",
+ message: "请在设置中开启蓝牙权限以使用设备扫描功能",
+ preferredStyle: .alert
+ )
+
+ alert.addAction(UIAlertAction(title: "去设置", style: .default) { _ in
+ if let settingsUrl = URL(string: UIApplication.openSettingsURLString) {
+ UIApplication.shared.open(settingsUrl)
+ }
+ })
+
+ alert.addAction(UIAlertAction(title: "取消", style: .cancel))
+
+ window.rootViewController?.present(alert, animated: true)
+ }
+
+ /// 停止扫描
+ public func stopScan() {
+ guard isScanning else { return }
+
+ os_log("🛑 停止 BLE 扫描", log: logger, type: .info)
+
+ centralManager.stopScan() // 直接使用,不需要可选绑定
+ isScanning = false
+
+ scanTimeout?.invalidate()
+ scanTimeout = nil
+ }
+
+ /// 获取已发现的设备列表
+ /// - Returns: 按最后发现时间排序的设备列表
+ public func getDiscoveredDevices() -> [BleDeviceInfo] {
+ return Array(discoveredDevices.values).sorted { $0.lastSeen > $1.lastSeen }
+ }
+
+ /// 清除设备列表
+ public func clearDeviceList() {
+ discoveredDevices.removeAll()
+ os_log("🗑️ 已清除设备列表", log: logger, type: .info)
+ }
+
+ /// 检查是否正在扫描
+ public func isScanningActive() -> Bool {
+ return isScanning
+ }
+
+ /// 获取蓝牙状态
+ public func getBluetoothState() -> CBManagerState {
+ return centralManager.state // 直接返回,不需要 ?? .unknown
+ }
+
+ // MARK: - 私有方法
+
+
+
+ private func addOrUpdateDevice(_ deviceInfo: BleDeviceInfo) {
+ let identifier = deviceInfo.identifier
+ discoveredDevices[identifier] = deviceInfo
+
+ os_log("📱 发现设备: %{public}@", log: logger, type: .debug, deviceInfo.name ?? "Unknown")
+ }
+
+ /// 强制触发蓝牙权限申请
+ public func requestBluetoothPermission() -> Bool {
+ os_log("🔐 强制触发蓝牙权限申请", log: logger, type: .info)
+
+ // 检查当前权限状态
+ let authorization: CBManagerAuthorization
+ if #available(iOS 13.1, *) {
+ authorization = CBManager.authorization
+ } else if #available(iOS 13.0, *) {
+ // 确保centralManager已初始化
+ if centralManager == nil {
+ setupCentralManager()
+ }
+ authorization = centralManager.authorization
+ } else {
+ // iOS 13以下没有权限概念,直接返回true
+ return true
+ }
+
+ os_log("📊 当前权限状态: %d", log: logger, type: .info, authorization.rawValue)
+
+ switch authorization {
+ case .notDetermined:
+ // 权限未确定,需要主动触发蓝牙操作来显示权限弹窗
+ if centralManager == nil {
+ os_log("📊 初始化蓝牙管理对象", log: logger, type: .info, "")
+ setupCentralManager()
+ }
+
+ // 等待CBCentralManager状态更新后,主动开始扫描来触发权限弹窗
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
+ guard let self = self else { return }
+
+ if self.centralManager.state == .poweredOn {
+ // 开始一个短暂的扫描来触发权限申请
+ self.centralManager.scanForPeripherals(withServices: nil, options: nil)
+ // 立即停止扫描
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
+ self.centralManager.stopScan()
+ }
+ } else if self.centralManager.state == .unauthorized {
+ // 权限被拒绝,显示设置提示
+ self.showPermissionAlert()
+ }
+ }
+ return true
+ case .allowedAlways:
+ os_log("✅ 权限已授予", log: logger, type: .info)
+ return true
+ case .denied, .restricted:
+ os_log("❌ 权限被拒绝,显示设置提示", log: logger, type: .error)
+ showPermissionAlert()
+ return false
+ @unknown default:
+ os_log("⚠️ 未知权限状态", log: logger, type: .info)
+ return false
+ }
+ }
+}
+
+// MARK: - CBCentralManagerDelegate
+@available(iOS 13.0, *)
+extension BleScanManager: CBCentralManagerDelegate {
+
+ public func centralManagerDidUpdateState(_ central: CBCentralManager) {
+ let stateString = getBluetoothStateString(central.state)
+ os_log("🔧 CBCentralManager 状态更新: %{public}@ (原始值: %d)", log: logger, type: .info, stateString, central.state.rawValue)
+
+ switch central.state {
+ case .poweredOn:
+ os_log("✅ 蓝牙已开启,可以开始扫描", log: logger, type: .info)
+ // 如果有待处理的扫描请求,现在可以执行
+ case .poweredOff:
+ os_log("❌ 蓝牙已关闭", log: logger, type: .error)
+ if isScanning {
+ stopScan()
+ }
+ case .unauthorized:
+ os_log("❌ 蓝牙权限被拒绝", log: logger, type: .error)
+ if isScanning {
+ stopScan()
+ }
+ case .unsupported:
+ os_log("❌ 设备不支持蓝牙", log: logger, type: .error)
+ if isScanning {
+ stopScan()
+ }
+ case .unknown:
+ os_log("⚠️ 蓝牙状态未知,等待进一步更新", log: logger, type: .error)
+ case .resetting:
+ os_log("⚠️ 蓝牙正在重置", log: logger, type: .error)
+ if isScanning {
+ stopScan()
+ }
+ @unknown default:
+ os_log("⚠️ 未知蓝牙状态: %d", log: logger, type: .error, central.state.rawValue)
+ }
+ }
+
+ public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral,
+ advertisementData: [String : Any], rssi RSSI: NSNumber) {
+
+ let deviceInfo = BleDeviceInfo(
+ identifier: peripheral.identifier,
+ name: peripheral.name ?? advertisementData[CBAdvertisementDataLocalNameKey] as? String,
+ rssi: RSSI,
+ advertisementData: advertisementData,
+ discoveryMethod: .traditional,
+ lastSeen: Date()
+ )
+
+ addOrUpdateDevice(deviceInfo)
+ }
+
+ public func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
+ os_log("🔄 恢复 CBCentralManager 状态", log: logger, type: .info)
+
+ // 恢复扫描状态
+ if dict[CBCentralManagerRestoredStateScanServicesKey] != nil {
+ isScanning = true
+ }
+ }
+
+ private func getBluetoothStateString(_ state: CBManagerState) -> String {
+ switch state {
+ case .unknown: return "未知状态"
+ case .resetting: return "重置中"
+ case .unsupported: return "设备不支持蓝牙"
+ case .unauthorized: return "应用未授权使用蓝牙"
+ case .poweredOff: return "蓝牙已关闭"
+ case .poweredOn: return "蓝牙已开启"
+ @unknown default: return "未知状态"
+ }
+ }
+}
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 99e8340cc..fb7f92d49 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
@@ -230,7 +230,31 @@ public class SwiftBleServicePlugin: NSObject, FlutterPlugin {
os_log("当前环境不支持AccessorySetupKit", log: logger, type: .info)
result(false)
#endif
+ case "startScan":
+ let arguments = call.arguments as? [String: Any]
+ let devicename = (arguments?["devicename"] as? String) ?? ""
+ // 修复:支持所有iOS版本
+ if #available(iOS 18.0, *) {
+ // iOS 18+ 使用新的BleScanManager
+ os_log("iOS 18+ 使用 BleScanManager 进行扫描", log: logger, type: .info)
+ let succ = BleScanManager.shared.startScan(timeout: 15.0)
+ result(succ)
+ } else {
+ // iOS 17及以下使用传统BleService
+ os_log("iOS 17及以下使用传统 BleService 进行扫描", log: logger, type: .info)
+ BleService.shared.startScan()
+ result(true)
+ }
+
+ case "getDeviceInfo":
+ let arguments = call.arguments as? [String: Any]
+ let address = (arguments?["address"] as? String) ?? ""
+ let info = BleScanManager.shared.getDiscoveredDevices()
+ result(info)
+ case "requestBluetoothPermission":
+ BleScanManager.shared.requestBluetoothPermission();
+ result(true)
default:
result(FlutterMethodNotImplemented)
}
@@ -394,4 +418,4 @@ class BleStatusStreamHandler: NSObject, FlutterStreamHandler {
plugin?.statusEventSink = nil
return nil
}
-}
+}
diff --git a/local_plugins/ble_service/lib/ble_service.dart b/local_plugins/ble_service/lib/ble_service.dart
index a33ea1e94..844c9c594 100644
--- a/local_plugins/ble_service/lib/ble_service.dart
+++ b/local_plugins/ble_service/lib/ble_service.dart
@@ -435,4 +435,15 @@ class BleService {
return false;
}
}
+
+ /// 请求蓝牙权限(仅iOS)
+ Future requestBluetoothPermission() async {
+ try {
+ final result = await _methodChannel.invokeMethod('requestBluetoothPermission');
+ return result ?? false;
+ } catch (e) {
+ print('请求蓝牙权限失败: $e');
+ return false;
+ }
+ }
}
diff --git a/local_plugins/ota/ios/Classes/BleManager/JLBleManager.m b/local_plugins/ota/ios/Classes/BleManager/JLBleManager.m
index 1147c552d..cb22ecfa8 100644
--- a/local_plugins/ota/ios/Classes/BleManager/JLBleManager.m
+++ b/local_plugins/ota/ios/Classes/BleManager/JLBleManager.m
@@ -218,6 +218,7 @@ NSString *FLT_BLE_RCSP_R = @"AE02"; //命令“读”通道
}
- (void)addPeripheral:(CBPeripheral*)peripheral Info:(NSString*)macStr {
+ kJLLog(JLLOG_DEBUG, @"BLE addPeripheral ---> Device %@", peripheral.name);
if (self.lastAddress && [macStr isEqualToString:self.lastAddress]) {
[self stopScanBLE];
[self connectBLE:peripheral];