From 16076adb2fc19233e366e3e54822f3067ea7f409 Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Fri, 18 Jul 2025 16:55:01 +0800 Subject: [PATCH] =?UTF-8?q?ble=E5=91=BD=E4=BB=A4=E9=98=9F=E5=88=97?= =?UTF-8?q?=E5=8F=91=E9=80=81=EF=BC=8C=E5=8A=A0=E5=85=A5=E5=BD=95=E9=9F=B3?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ios/Classes/AmapServicePlugin.swift | 129 +++++++++++++----- .../Sources/azure_speech/AzureAsrHelper.swift | 11 +- .../Sources/azure_speech/RecordFile.swift | 3 +- .../Sources/ble_service/BleService.swift | 40 +++++- 4 files changed, 141 insertions(+), 42 deletions(-) diff --git a/local_plugins/amap_service/ios/Classes/AmapServicePlugin.swift b/local_plugins/amap_service/ios/Classes/AmapServicePlugin.swift index 196285394..05e7c0ec4 100644 --- a/local_plugins/amap_service/ios/Classes/AmapServicePlugin.swift +++ b/local_plugins/amap_service/ios/Classes/AmapServicePlugin.swift @@ -2,46 +2,103 @@ import Flutter import UIKit import AMapNaviKit -public class AmapServicePlugin: NSObject, FlutterPlugin { - private static var channel: FlutterMethodChannel? - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "com.yunqiinnovation.amap_service", binaryMessenger: registrar.messenger()) - let instance = AmapServicePlugin() - // 必须先设置隐私合规 - DispatchQueue.main.async { - // 2. API Key - AMapServices.shared()?.apiKey = "aa60a03be9f6531bcc64029e60f95d03" - AMapServices.shared().enableHTTPS = true - // 导航SDK隐私合规(如果使用导航功能) - AMapNaviManagerConfig.shared().updatePrivacyShow(.didShow, privacyInfo: .didContain) - AMapNaviManagerConfig.shared().updatePrivacyAgree(.didAgree) - // 3. 打印验证(调试用) - print("高德SDK初始化完成") +public class AmapServicePlugin: NSObject, FlutterPlugin, FlutterStreamHandler { + private static var channel: FlutterMethodChannel? + private var eventSink: FlutterEventSink? + + public static func register(with registrar: FlutterPluginRegistrar) { + let methodChannel = FlutterMethodChannel( + name: "com.yunqiinnovation.amap_service", + binaryMessenger: registrar.messenger() + ) + + let eventChannel = FlutterEventChannel( + name: "com.yunqiinnovation.amap_service/events", + binaryMessenger: registrar.messenger() + ) + + let instance = AmapServicePlugin() + eventChannel.setStreamHandler(instance) + + // 高德SDK初始化 + DispatchQueue.main.async { + AMapServices.shared()?.apiKey = "aa60a03be9f6531bcc64029e60f95d03" + AMapServices.shared()?.enableHTTPS = true + + if #available(iOS 13.0, *) { + AMapNaviManagerConfig.shared().updatePrivacyShow(.didShow, privacyInfo: .didContain) + AMapNaviManagerConfig.shared().updatePrivacyAgree(.didAgree) + } + print("高德SDK初始化完成") + } + + registrar.addMethodCallDelegate(instance, channel: methodChannel) + self.channel = methodChannel } - registrar.addMethodCallDelegate(instance, channel: channel) - self.channel = channel - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - switch call.method { - case "open": - print("liwei------------------ 打开导航页面") - if let args = call.arguments as? [String: Any], - let startpos = args["startpos"] as? String, - let endpos = args["endpos"] as? String, - let ntype = args["ntype"] as? String{ - print("liwei------------------ 打开导航页面1 \(startpos) \(endpos)") - DispatchQueue.main.async { - let navVC = AmapViewController(startpos: startpos, endpos: endpos) + + // MARK: - FlutterStreamHandler + public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { + self.eventSink = events + return nil + } + + public func onCancel(withArguments arguments: Any?) -> FlutterError? { + eventSink = nil + return nil + } + + // MARK: - 事件发送方法 + private func sendEvent(eventName: String, data: [String: Any]) { + guard let sink = eventSink else { return } + let eventData: [String: Any] = [ + "event": eventName, + "data": data + ] + sink(eventData) + } + + // MARK: - 方法处理 + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "open": + guard let args = call.arguments as? [String: Any], + let startpos = args["startpos"] as? String, + let endpos = args["endpos"] as? String, + let ntype = args["ntype"] as? String else { + result(FlutterError(code: "INVALID_ARGUMENT", message: "Missing arguments", details: nil)) + return + } + + print("打开导航页面: \(startpos) → \(endpos)") + DispatchQueue.main.async { + let navVC = AmapViewController(startpos: startpos, endpos: endpos) navVC.modalPresentationStyle = .fullScreen UIApplication.shared.keyWindow?.rootViewController?.present(navVC, animated: true) } result(nil) - } else { - result(FlutterError(code: "INVALID_ARGUMENT", message: "Missing 'open' map", details: nil)) + + case "start": + guard let args = call.arguments as? [String: Any], + let startpos = args["startpos"] as? String, + let endpos = args["endpos"] as? String, + let ntype = args["ntype"] as? String else { + result(FlutterError(code: "INVALID_ARGUMENT", message: "Missing arguments", details: nil)) + return + } + + // 这里添加实际导航启动逻辑 + print("开始导航: \(startpos) → \(endpos), 类型: \(ntype)") + sendEvent(eventName: "navigation_started", data: ["status": "success"]) + result(nil) + + case "stop": + // 这里添加停止导航逻辑 + print("停止导航") + sendEvent(eventName: "navigation_stopped", data: ["status": "success"]) + result(nil) + + default: + result(FlutterMethodNotImplemented) } - default: - result(FlutterMethodNotImplemented) } - } -} +} \ No newline at end of file diff --git a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift index 480810d69..9425fa62d 100644 --- a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift +++ b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift @@ -232,7 +232,8 @@ public class AzureAsrHelper: NSObject { // 停止音频处理 audioStream?.releaseAudioResources() - + // 停止录音 + recordfile?.closeFile(isSave: true) // 释放资源 recognizer = nil speechConfig = nil @@ -304,6 +305,9 @@ public class AzureAsrHelper: NSObject { // 明确指定使用外部音频源(推流模式) audioStream = AudioStream() audioStream?.initAudioRecord() + audioStream?.onAudioData = { [weak self] data in + self?.recordfile?.saveAudioDataToWav(data) + } } // 创建音频配置 @@ -508,7 +512,7 @@ private func detectLanguage(from result: SPXSpeechRecognitionResult) -> String { private let bufferSize: Int = 4096 private var writeThread: DispatchQueue? private var currentRoute: AudioOutputRoute? - + public var onAudioData: ((Data) -> Void)? /** * 初始化 */ @@ -566,6 +570,7 @@ private func detectLanguage(from result: SPXSpeechRecognitionResult) -> String { print("写入数据长度: \(dataToWrite.count)") self.pushAudioStream?.write(dataToWrite) + self.onAudioData?(dataToWrite) } } } @@ -739,7 +744,7 @@ private func detectLanguage(from result: SPXSpeechRecognitionResult) -> String { try audioSession.setCategory( .playAndRecord, mode: .voiceChat, - options: [.allowBluetooth] + options: [.allowBluetooth, .mixWithOthers, .defaultToSpeaker] ) try audioSession.overrideOutputAudioPort(.none) diff --git a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/RecordFile.swift b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/RecordFile.swift index 0df34f085..ba6733fc9 100644 --- a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/RecordFile.swift +++ b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/RecordFile.swift @@ -21,7 +21,7 @@ public class RecordFile { let fileURL = URL(fileURLWithPath: path) let directoryURL = fileURL.deletingLastPathComponent() - + print("创建音频文件path: \(path)") // 创建目录 do { try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true) @@ -223,6 +223,7 @@ public class RecordFile { try updateHandle.close() lastSavedFile = fileURL + print("关闭文件path: \(fileURL)") return true } catch { print("关闭文件失败: \(error.localizedDescription)") 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 e27a14f95..5c10e0399 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 @@ -70,6 +70,19 @@ public class BleService: NSObject { // MARK: - Opus音频处理器 private var opusProcessor: SwiftOpusAudioProcessor? + // MARK: - 新增命令队列相关属性 + private var commandQueue = [Data]() + //是否回复 + private var isReply = true + // 是否回复指令码 +private var cmdReplyType: UInt8 = 0 + + //private var currentCommand: Command? + // 命令结构体 + private struct Command { + let cmd: UInt8 + let data: Data + } // MARK: - 初始化 private override init() { @@ -650,8 +663,20 @@ public class BleService: NSObject { os_log("发送命令: CMD=0x%02X, 长度=%d, 数据=[%@], CRC=0x%02X", log: logger, type: .debug, cmd, cmdLength, dataHexString, crc) + + + // 加入队列并触发发送 + + if isReply{ + peripheralDevice?.writeValue(cmdPacket, for: characteristic, type: .withoutResponse) + isReply = false; + return true + + } else { + commandQueue.append(cmdPacket) + } // 发送数据(使用无响应写入,与Android端保持一致) - peripheralDevice?.writeValue(cmdPacket, for: characteristic, type: .withoutResponse) + return true } catch { os_log("发送命令异常: %@", log: logger, type: .error, error.localizedDescription) @@ -691,6 +716,11 @@ public class BleService: NSObject { /// 处理接收到的响应数据 private func handleResponseData(_ data: Data) { + guard let characteristic = writeCharacteristic, + connectionState == BleConst.STATE_CONNECTED else { + os_log(" 设备未连接", log: logger, type: .error) + return + } guard data.count >= 4 else { os_log("响应数据格式错误:数据长度过短", log: logger, type: .error) return @@ -732,7 +762,13 @@ public class BleService: NSObject { // 提取响应数据 let responseData = length > 0 ? data.subdata(in: 3..<(3 + Int(length))) : Data() - + isReply = true; + cmdReplyType = cmd; + if !commandQueue.isEmpty { + let nextCmd = commandQueue.removeFirst() + peripheralDevice?.writeValue(nextCmd, for: characteristic, type: .withoutResponse) +} + // 根据命令类型处理设备响应 switch cmd { case BleConst.CMD_GET_VERSION: