import Foundation import AVFoundation /// 蓝牙事件类型 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 { // 状态监控相关 private var isMonitoring = false private var stateCallback: ((BluetoothEventType, [String: Any]) -> Void)? // 音频路由变化观察者 private var routeChangeObserver: NSObjectProtocol? deinit { stopBluetoothStateMonitoring() } /// 检查蓝牙是否启用(通过检查是否有连接的蓝牙音频设备) func isBluetoothEnabled() -> Bool { // 检查是否有连接的蓝牙音频设备 let hasBtDevices = !getConnectedAudioDevices().isEmpty // 如果有连接的蓝牙设备,则认为蓝牙是开启状态 if hasBtDevices { print("蓝牙状态检查: 已检测到蓝牙设备,蓝牙已开启") return true } // 如果没有连接设备,返回false (无法100%确定蓝牙是否打开,只能知道没有连接设备) print("蓝牙状态检查: 未检测到蓝牙设备") 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) ] 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 reportCurrentBluetoothState() { // 检查当前是否有蓝牙设备连接 let devices = getConnectedAudioDevices() if !devices.isEmpty { print("蓝牙状态: 已检测到蓝牙设备,报告状态为开启") reportBluetoothState(.on) } else { print("蓝牙状态: 未检测到蓝牙设备,报告状态为未知") reportBluetoothState(.unknown) } } // 报告蓝牙状态变化 private func reportBluetoothState(_ state: BluetoothState) { print("向Flutter端报告的蓝牙状态: \(state.rawValue)") stateCallback?(.stateChanged, ["state": state.rawValue]) } }