import Foundation import AVFoundation import CoreBluetooth /// 蓝牙事件类型 enum BluetoothEventType { case stateChanged case deviceConnected case deviceDisconnected } /// 蓝牙状态枚举 enum BluetoothState: String { case unknown = "unknown" case off = "off" case on = "on" case unauthorized = "unauthorized" case unsupported = "unsupported" } /// 简化的蓝牙助手类,专注于iOS上的蓝牙耳机检测 class ClassicBluetoothHelper: NSObject, CBCentralManagerDelegate { private var centralManager: CBCentralManager? private var bluetoothState: CBManagerState = .unknown // 添加缺少的属性声明 private var stateCallback: ((BluetoothEventType, [String: Any]) -> Void)? private var isMonitoring: Bool = false // 音频路由变化观察者 private var routeChangeObserver: NSObjectProtocol? override init() { super.init() centralManager = CBCentralManager(delegate: self, queue: nil) } deinit { stopBluetoothStateMonitoring() } func centralManagerDidUpdateState(_ central: CBCentralManager) { bluetoothState = central.state print("lxm---蓝牙状态更新: \(bluetoothState.rawValue)") // 向Flutter端报告蓝牙状态变化 if isMonitoring { let currentState = getBluetoothAvailability() reportBluetoothState(currentState) } } func isBluetoothEnabled() -> Bool { switch bluetoothState { case .poweredOn: return true case .poweredOff, .unsupported, .unauthorized, .resetting: return false case .unknown: // 如果状态未知,回退到原来的音频设备检测方法 return !getConnectedAudioDevices().isEmpty @unknown default: return false } } /// 获取已连接的蓝牙耳机 func getConnectedDevices(completion: @escaping ([[String: String]], Error?) -> Void) { // 获取音频设备(主要是蓝牙耳机) let audioDevices = getConnectedAudioDevices() // 日志输出 if !audioDevices.isEmpty { print("已找到\(audioDevices.count)个蓝牙音频设备:") for device in audioDevices { print("- \(device["name"] ?? "未知") (\(device["address"] ?? "未知地址"))") } } else { print("未找到蓝牙音频设备") } completion(audioDevices, nil) } // 获取连接的蓝牙音频设备 private func getConnectedAudioDevices() -> [[String: String]] { var result: [[String: String]] = [] // 获取当前音频路由 let outputs = AVAudioSession.sharedInstance().currentRoute.outputs // 过滤蓝牙相关设备 for output in outputs where isBluetoothOutput(output.portType) { let device: [String: String] = [ "name": output.portName, "address": cleanDeviceAddress(output.uid), "isActive": "true" // 在 currentRoute.outputs 中的蓝牙设备都是活跃的 ] result.append(device) } return result } // 检查是否为蓝牙输出 private func isBluetoothOutput(_ portType: AVAudioSession.Port) -> Bool { return portType == .bluetoothA2DP || portType == .bluetoothHFP || portType == .bluetoothLE } // 清理设备地址 private func cleanDeviceAddress(_ address: String) -> String { // 移除类似 "-tacl" 的后缀 if let range = address.range(of: "-", options: .backwards) { return String(address[.. Void) { self.stateCallback = callback if !isMonitoring { isMonitoring = true // 观察音频路由变化 let notificationCenter = NotificationCenter.default routeChangeObserver = notificationCenter.addObserver( forName: AVAudioSession.routeChangeNotification, object: nil, queue: nil ) { [weak self] notification in self?.handleAudioRouteChange(notification) } // 激活音频会话 activateAudioSession() // 报告当前状态 reportCurrentBluetoothState() } } // 处理音频路由变化 private func handleAudioRouteChange(_ notification: Notification) { guard let userInfo = notification.userInfo, let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt, let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else { return } switch reason { case .newDeviceAvailable: // 新音频设备已连接 let audioDevices = getConnectedAudioDevices() for device in audioDevices { stateCallback?(.deviceConnected, ["device": device]) } // 如果有蓝牙设备连接,报告蓝牙状态为开启 if !audioDevices.isEmpty { reportBluetoothState(.on) } case .oldDeviceUnavailable: // 获取断开之前的路由 if let previousRoute = userInfo[AVAudioSessionRouteChangePreviousRouteKey] as? AVAudioSessionRouteDescription { for output in previousRoute.outputs where isBluetoothOutput(output.portType) { let device: [String: String] = [ "name": output.portName, "address": cleanDeviceAddress(output.uid) ] stateCallback?(.deviceDisconnected, ["device": device]) } // 检查当前是否还有蓝牙设备 let currentDevices = getConnectedAudioDevices() if currentDevices.isEmpty { // 如果没有任何蓝牙设备连接,可以认为蓝牙状态变为未知 reportBluetoothState(.unknown) } } default: break } } // 激活音频会话 private func activateAudioSession() { do { let audioSession = AVAudioSession.sharedInstance() try audioSession.setCategory(.playback, mode: .default) try audioSession.setActive(true) } catch { print("设置音频会话失败: \(error.localizedDescription)") } } /// 停止监听蓝牙状态变化 func stopBluetoothStateMonitoring() { if isMonitoring { isMonitoring = false // 移除音频路由观察者 if let observer = routeChangeObserver { NotificationCenter.default.removeObserver(observer) routeChangeObserver = nil } // 关闭音频会话 deactivateAudioSession() } // 清除回调 stateCallback = nil } // 关闭音频会话 private func deactivateAudioSession() { do { try AVAudioSession.sharedInstance().setActive(false) } catch { print("关闭音频会话失败: \(error.localizedDescription)") } } /// 获取蓝牙可用性状态(优化后的统一状态判断方法) func getBluetoothAvailability() -> BluetoothState { switch bluetoothState { case .poweredOn: return .on case .poweredOff: return .off case .unauthorized: return .unauthorized case .unsupported: return .unsupported case .unknown, .resetting: // 当CoreBluetooth状态未知时,回退到音频设备检测 return getConnectedAudioDevices().isEmpty ? .unknown : .on @unknown default: return .unknown } } /// 报告当前蓝牙状态(优化后使用统一的状态判断方法) func reportCurrentBluetoothState() { // 使用优化后的统一状态判断方法 let currentState = getBluetoothAvailability() // 根据状态输出相应的日志信息 switch currentState { case .on: print("蓝牙状态: 蓝牙已开启") case .off: print("蓝牙状态: 蓝牙已关闭") case .unauthorized: print("蓝牙状态: 应用未授权使用蓝牙") case .unsupported: print("蓝牙状态: 设备不支持蓝牙") case .unknown: print("蓝牙状态: 蓝牙状态未知") } // 向Flutter端报告状态 reportBluetoothState(currentState) } // 报告蓝牙状态变化 private func reportBluetoothState(_ state: BluetoothState) { print("向Flutter端报告的蓝牙状态: \(state.rawValue)") stateCallback?(.stateChanged, ["state": state.rawValue]) } }