import Foundation import CoreBluetooth import os.log import AVFoundation import AccessorySetupKit import UIKit class BleAccessorySetup: NSObject { // MARK: - 属性 static let shared = BleAccessorySetup() private var bleService: BleService? private var session: ASAccessorySession? private var isSessionActivated = false private var isInitializing = false private let logger = OSLog(subsystem: "com.yunqiinnovation.ble_service", category: "BleAccessorySetup") // 杰理蓝牙设备的公司标识符 private let deviceCompanyIdentifier: UInt16 = 0xEFAB private let deviceDisplayName = "语音助理耳机" typealias DeviceDiscoveredCallback = (UUID) -> Void private var onDeviceDiscovered: DeviceDiscoveredCallback? // MARK: - 初始化 private override init() { super.init() } // MARK: - 公共方法 func setBleService(_ service: Any) { self.bleService = service as? BleService } func setDeviceDiscoveredCallback(_ callback: @escaping (UUID) -> Void) { self.onDeviceDiscovered = callback } // 处理发现的设备并连接 func connectToAccessory(uuid: UUID) { guard let service = bleService else { return } service.connectToAccessory(uuid: uuid) } func addToReconnectQueue(uuid: UUID) { guard let service = bleService else { return } service.addToReconnectQueue(uuid: uuid) } // 从视图控制器显示设备选择器 - 主要入口点 func showDevicePickerFromViewController(_ viewController: UIViewController) { initializeSession() } // 清除蓝牙状态(仅用于测试或重置,不会清除系统授权) func resetBluetoothState() { if let service = bleService { service.disconnect() } } // MARK: - 会话管理 func initializeSession() { if isInitializing || (session != nil && isSessionActivated) { return } isInitializing = true do { session = try ASAccessorySession() session?.activate(on: .main) { [weak self] event in guard let self = self else { return } self.handleSessionEvent(event) } } catch { handleSessionInitError(error) } } func resetSession() { session?.invalidate() session = nil isSessionActivated = false isInitializing = false } // MARK: - 设备选择器显示 private func showDevicePicker() { guard let session = session, isSessionActivated else { return } let descriptor = ASDiscoveryDescriptor() descriptor.bluetoothCompanyIdentifier = ASBluetoothCompanyIdentifier(deviceCompanyIdentifier) let deviceImage = UIImage(systemName: "headphones") ?? UIImage() let displayItem = ASPickerDisplayItem( name: deviceDisplayName, productImage: deviceImage, descriptor: descriptor ) DispatchQueue.main.async { session.showPicker(for: [displayItem]) { [weak self] error in guard let self = self else { return } if let error = error { let nsError = error as NSError os_log("设备选择器错误: %{public}@", log: self.logger, type: .error, error.localizedDescription) } } } } // MARK: - 私有辅助方法 private func handleSessionEvent(_ event: ASAccessoryEvent) { switch event.eventType { case .activated: os_log("会话已激活", log: logger, type: .info) handleSessionActivated() case .accessoryAdded: os_log("发现新设备: %{public}@", log: logger, type: .info, event.accessory?.bluetoothIdentifier?.uuidString ?? "Unknown") handleAccessoryAdded(event.accessory) case .accessoryRemoved: handleAccessoryRemoved(event.accessory) case .accessoryChanged: break case .pickerDidPresent: break case .pickerDidDismiss: break case .pickerSetupFailed: os_log("选择器设置失败: %{public}@", log: logger, type: .error, event.error?.localizedDescription ?? "未知错误") default: break } } private func handleSessionActivated() { isSessionActivated = true isInitializing = false if let accessories = session?.accessories, !accessories.isEmpty { if let service = bleService { let _ = service.getCentralManager() for accessory in accessories { if let uuid = accessory.bluetoothIdentifier { onDeviceDiscovered?(uuid) connectToAccessory(uuid: uuid) } } } } else { showDevicePicker() } } private func handleAccessoryAdded(_ accessory: ASAccessory?) { guard let accessory = accessory, let uuid = accessory.bluetoothIdentifier else { return } onDeviceDiscovered?(uuid) connectToAccessory(uuid: uuid) } private func handleAccessoryRemoved(_ accessory: ASAccessory?) { guard let accessory = accessory, let uuid = accessory.bluetoothIdentifier else { return } if let service = bleService { service.disconnect() } } private func handleSessionInitError(_ error: Error) { isInitializing = false session = nil } // MARK: - 蓝牙状态检查 func isBluetoothAvailable() -> Bool { do { let audioSession = AVAudioSession.sharedInstance() let outputs = audioSession.currentRoute.outputs for output in outputs { if output.portType == .bluetoothA2DP || output.portType == .bluetoothHFP || output.portType == .bluetoothLE { return true } } } catch { os_log("检查蓝牙状态出错: %{public}@", log: logger, type: .error, error.localizedDescription) } return true } // MARK: - 清除配对关系 func clearAssociations() { resetBluetoothState() #if canImport(AccessorySetupKit) && swift(>=5.9) if #available(iOS 18.0, *) { resetSession() initializeSession() } #endif } }