diff --git a/lib/data/services/ble_manager.dart b/lib/data/services/ble_manager.dart index 7c7477378..d8c160df3 100644 --- a/lib/data/services/ble_manager.dart +++ b/lib/data/services/ble_manager.dart @@ -311,6 +311,11 @@ class BleManager extends GetxService { _handleExpandFunction(event); } else if (event is TranslationAudioEvent) { _handleTranslationAudio(event); + } else if (event is TranslationLogEvent) { + print('[BLE] [EVT] TranslationLog modeId=${event.modeId} ${event.content}'); + } else if (event is TranslationErrorEvent) { + print('[BLE] [EVT] TranslationError modeId=${event.modeId} ' + 'code=${event.code} msg=${event.message}'); } else if (event is UnknownJieliEvent) { print('[BLE] [EVT] 未识别事件: ${event.raw}'); } @@ -322,11 +327,17 @@ class BleManager extends GetxService { } void _handleConnectionState(ConnectionStateEvent e) { + // 关键诊断日志:原生 → Dart 链路上的最终一站。如果这里没打印,意味着 + // 原生侧 `connectionState` 事件根本没到达 Dart(看 iOS `[JieliDispatcher] + // send #N type=connectionState` 是否成功 sink-ok)。 + print('[BLE] [EVT] >>> _handleConnectionState addr=${e.address} state=${e.state} ' + '(0=disconnect 1=ok 2=connecting)'); Logger.i('BleManager', 'JL 连接状态: ${e.address} → ${e.state}'); if (e.state == ConnectionStateEvent.connectionOk) { currentDeviceAddress = e.address; _connectionState.value = BleConnectionState.connected; _isConnected.value = true; + print('[BLE] [EVT] >>> isConnected 翻成 true, currentDeviceAddress=${e.address}'); _ensureDeviceInfoStub(); // 用扫描期间最新一条 advertisement 立刻填上电量(SDK BleScanMessage 里就有 // leftDeviceQuantity / rightDeviceQuantity / chargingBinQuantity / 各 charging 位) @@ -335,11 +346,13 @@ class BleManager extends GetxService { // 改到 RcspInitEvent 成功时再拉一次 + 之后再轮询几次保险。 } else if (e.state == ConnectionStateEvent.connectionConnecting) { _connectionState.value = BleConnectionState.connecting; + print('[BLE] [EVT] >>> 连接中 (state=2)'); } else { _connectionState.value = BleConnectionState.disconnected; _isConnected.value = false; _isCodecActive.value = false; _stopBatteryWatchdog(); + print('[BLE] [EVT] >>> isConnected 翻成 false (state=${e.state})'); } } @@ -489,16 +502,29 @@ class BleManager extends GetxService { '[BAT] 广播不再携带电量字段,连接后将通过 RCSP/BatteryEvent 补齐'); } + int _uplinkRxCount = 0; + int _downlinkRxCount = 0; + void _handleTranslationAudio(TranslationAudioEvent e) { // streamId 来自 native TranslationStreams:in.uplink / in.downlink / in.mic ... // 仅转发通话翻译两条输入流;out.* 由这里产生回灌指令而不是接收。 switch (e.streamId) { case 'in.uplink': + _uplinkRxCount++; + if (_uplinkRxCount <= 5 || _uplinkRxCount % 100 == 1) { + Logger.i('CALL-RX', + '上行 #$_uplinkRxCount len=${e.pcm.length} sr=${e.sampleRate} ch=${e.channels} bits=${e.bitsPerSample} seq=${e.seq}'); + } if (!_inUplinkPcmController.isClosed) { _inUplinkPcmController.add(e.pcm); } break; case 'in.downlink': + _downlinkRxCount++; + if (_downlinkRxCount <= 5 || _downlinkRxCount % 100 == 1) { + Logger.i('CALL-RX', + '下行 #$_downlinkRxCount len=${e.pcm.length} sr=${e.sampleRate} ch=${e.channels} bits=${e.bitsPerSample} seq=${e.seq}'); + } if (!_inDownlinkPcmController.isClosed) { _inDownlinkPcmController.add(e.pcm); } @@ -761,15 +787,16 @@ class BleManager extends GetxService { } Future openA2DPDecoder() async { - Logger.i('CALL_TRANS_CMD', '启动通话翻译模式 (杰理 startTranslation)'); + Logger.i('CALL_TRANS_CMD', '【启动】通话翻译模式 (杰理 startTranslation modeId=3)'); try { await Jielihome.instance.startTranslation( modeId: TranslationModeIds.callTranslation, ); _isCodecActive.value = true; + Logger.i('CALL_TRANS_CMD', '✅【启动成功】通话翻译模式已下发 — 接下来等耳机回上行音频'); return true; } catch (e) { - Logger.e('CALL_TRANS_CMD', '启动通话翻译模式失败: $e'); + Logger.e('CALL_TRANS_CMD', '❌【启动失败】通话翻译模式: $e'); _isCodecActive.value = false; return false; } diff --git a/lib/data/services/spotify_service.dart b/lib/data/services/spotify_service.dart index fa9816e9f..21e2f1421 100644 --- a/lib/data/services/spotify_service.dart +++ b/lib/data/services/spotify_service.dart @@ -1,3 +1,4 @@ +/* 暂时关闭:spotify_sdk plugin 已在 pubspec.yaml 注释掉,恢复时把本文件首尾的 block comment 解开即可。 import 'dart:async'; import 'dart:io'; import 'dart:convert'; @@ -207,3 +208,4 @@ class SpotifService { } } } +*/ diff --git a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServicePlugin.swift b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServicePlugin.swift index b8d998ee4..197df852a 100644 --- a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServicePlugin.swift +++ b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServicePlugin.swift @@ -74,9 +74,18 @@ public class AgentServicePlugin: NSObject, FlutterPlugin { result(true) return } - print("liwei----------AgentServicePlugin startConversation") - let mode = (call.arguments as? [String: Any])?["mode"] as? String ?? "normal" - impl.startRecognition(mode: mode) + // 关键:Dart 端传过来的 `isExternalActive=true` 表示"音频从外部 + // 设备(杰理耳机 RCSP 上行)灌进来,不要启动手机麦"。Android 在 + // `AgentServicePlugin.kt:160` 里正确提取并传给 native;iOS 这里 + // 之前完全没读这个参数,导致 `startRecognition` 用默认值 false, + // 永远走手机麦 → AI 对话即使耳机已连接也不会用耳机麦上行,TTS + // 也按 .microphone 路径配 audio session(默认到手机扬声器)。 + // 现在补齐:取出来透传成 `useBle`,与 Android 行为对齐。 + let args = call.arguments as? [String: Any] + let mode = args?["mode"] as? String ?? "normal" + let isExternalActive = args?["isExternalActive"] as? Bool ?? false + print("liwei----------AgentServicePlugin startConversation mode=\(mode) isExternalActive=\(isExternalActive)") + impl.startRecognition(useBle: isExternalActive, mode: mode) result(true) case "isInterrupt": if let args = call.arguments as? [String: Any], @@ -92,6 +101,26 @@ public class AgentServicePlugin: NSObject, FlutterPlugin { case "recognizeCallback": impl.recognizeCallback() result(true) + + case "pushAudioData": + // Dart 端通过 device_jieli AssistantAudioEvent 拿到 PCM 后调用本方法 + // 注入到 ASR。`isExternalActive=true` 模式下的唯一上行通路。 + // 之前这里漏了 case,调用直接落到 default 抛 MissingPluginException, + // Dart 侧 catchError 静默吞掉 —— 结果 BLE 帧一直发但讯飞 audioFrames=0。 + guard let args = call.arguments as? [String: Any] else { + result(FlutterError(code: "INVALID_ARGUMENTS", message: "参数缺失", details: nil)) + return + } + let pcmData: Data + if let typed = args["data"] as? FlutterStandardTypedData { + pcmData = typed.data + } else if let bytes = args["data"] as? [UInt8] { + pcmData = Data(bytes) + } else { + result(FlutterError(code: "INVALID_ARGUMENTS", message: "data 参数缺失或类型错误", details: nil)) + return + } + result(impl.pushAudioData(pcmData)) case "stopConversation": // 防抖:短时间重复调用直接忽略 diff --git a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift index ec7560fd8..b8aa70749 100644 --- a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift +++ b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift @@ -128,6 +128,10 @@ import ble_service // 设备产品配置 broadcastpeertranslate:true=播报并启用 gate;false/未下发=不播报且 gate 永不触发。默认 false。 private var broadcastPeerTranslate: Bool = false + // 杰理 SCO PCM 推送计数(每 100 帧打一次日志,避免刷屏) + private var astAudioPushCountA: Int64 = 0 + private var astAudioPushCountB: Int64 = 0 + fileprivate func pushCallBPcm(_ data: Data) { guard broadcastPeerTranslate else { return } callBPcmPlayer.feed(data) @@ -145,6 +149,42 @@ import ble_service return Data(count: data.count) } + /// 把杰理 SCO 上行 PCM(己方麦克风)按当前 provider 推到 A 路 AST。 + fileprivate func pushAstAudioToA(_ data: Data) { + astAudioPushCountA &+= 1 + if astAudioPushCountA % 100 == 1 { + os_log("pushAstAudioToA: provider=%{public}@ size=%d count=%lld", + log: ctLog, type: .info, currentAstProvider, data.count, astAudioPushCountA) + } + let payload = gateAgainstBPlayback(data) + switch currentAstProvider { + case "volcano": + _ = doubaoAstHelperA.pushAudioData(payload) + case "alibaba": + _ = bailianAstHelperA.pushAudioData(payload) + default: + azureAstHelperA.pushAudioData(audioData: payload) + } + } + + /// 把杰理 SCO 下行 PCM(对端通话音)按当前 provider 推到 B 路 AST。 + fileprivate func pushAstAudioToB(_ data: Data) { + astAudioPushCountB &+= 1 + if astAudioPushCountB % 100 == 1 { + os_log("pushAstAudioToB: provider=%{public}@ size=%d count=%lld", + log: ctLog, type: .info, currentAstProvider, data.count, astAudioPushCountB) + } + let payload = gateAgainstBPlayback(data) + switch currentAstProvider { + case "volcano": + _ = doubaoAstHelperB.pushAudioData(payload) + case "alibaba": + _ = bailianAstHelperB.pushAudioData(payload) + default: + azureAstHelperB.pushAudioData(audioData: payload) + } + } + // 音频数据相关 private var audioDataEventChannel: FlutterEventChannel? internal var audioDataEventSink: FlutterEventSink? @@ -875,6 +915,31 @@ private func sendAudioDataEvent(_ event: [String: Any]) { os_log("[CALL] setBroadcastPeerTranslate=%{public}@", log: ctLog, type: .info, enabled ? "true" : "false") result(true) + // Dart 把杰理 runtime 解码后的 SCO PCM 推进来(与 Android `pushExternalAudio` 对齐): + // leg = "A" / "UPLINK" → 本机麦克风 → A 路 AST + // leg = "B" / "DOWNLINK" → 对端通话音 → B 路 AST + // pcm 必须是 16kHz / mono / 16bit / little-endian。 + case "pushExternalAudio": + let args = call.arguments as? [String: Any] + let leg = (args?["leg"] as? String)?.uppercased() ?? "" + let pcm: Data = { + if let typed = args?["pcm"] as? FlutterStandardTypedData { return typed.data } + return Data() + }() + if pcm.isEmpty { + result(false); return + } + switch leg { + case "A", "UPLINK": + pushAstAudioToA(pcm) + case "B", "DOWNLINK": + pushAstAudioToB(pcm) + default: + result(FlutterError(code: "BAD_LEG", message: "leg must be A/uplink or B/downlink, got=\(leg)", details: nil)) + return + } + result(true) + case "dispose": // 让 init 的 Task 在 sleep 之后比对 generation 时直接 return,避免落到已 dispose 的 helper 上 _ = nextAstInitGeneration() diff --git a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureTtsHelper.swift b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureTtsHelper.swift index 87c112dc6..d767eeac6 100644 --- a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureTtsHelper.swift +++ b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureTtsHelper.swift @@ -1610,16 +1610,25 @@ private func applyAudioSessionForTTS() -> Bool { if self.currentRecognitionMode == "phone_call" { + // 设计:对齐 Android `BleAgent` 的"设备录音模式" —— 上行 BLE,下行 + // 让系统自由路由。Android 走 `AudioTrack` USAGE_MEDIA,系统连了 A2DP + // 自然走 A2DP,没连就走手机扬声器,不强制任何一边。 + // + // 之前 iOS 在没耳机分支里塞 `.defaultToSpeaker` + 下面 1667 行还会再来 + // 一次 `overrideOutputAudioPort(.speaker)` —— 等于"无论如何都打到手机 + // 扬声器"。结果就是用户配对了 A2DP 也没用(被 override 抢回去), + // AI 对话播报永远从手机出。 + // + // 改成 `.allowBluetoothA2DP` 两个分支都加;没配对 A2DP 的设备上 + // iOS 默认走手机扬声器(不需要我们 override),配对了就自动走耳机。 desiredCategory = .playAndRecord + desiredMode = .default + desiredOptions = [.allowBluetoothA2DP] if hasHeadphones { - desiredMode = .default - desiredOptions = [.allowBluetoothA2DP] + // 用手机麦做兜底(设备录音模式下其实不用,但保留与之前一致) if let inputs = audioSession.availableInputs, let builtInMic = inputs.first(where: { $0.portType == .builtInMic }) { try? audioSession.setPreferredInput(builtInMic) } - } else { - desiredMode = .voiceChat - desiredOptions = [.allowBluetooth, .defaultToSpeaker] } // 不再 mixWithOthers:TTS 期间需要打断外部/内部其他音频 } else if self.currentRecognitionMode == "push_to_talk" || self.currentRecognitionMode == "normal" || self.currentRecognitionMode == "ble_wakeup" { @@ -1664,9 +1673,9 @@ private func applyAudioSessionForTTS() -> Bool { } try audioSession.setActive(true) self.internalSessionActive = true - if self.currentRecognitionMode == "phone_call", !hasHeadphones { - _ = try? audioSession.overrideOutputAudioPort(.speaker) - } + // 注:原来这里在 phone_call 没耳机时 override 到 speaker, + // 跟"设备录音模式 下行让系统自由路由"的设计冲突,已删除。 + // 想强制扬声器的场景请在调用方显式做,不要藏在 TTS 内部。 break } catch { if attempt == 2 { diff --git a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/DoubaoE2ETranslateHelper.swift b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/DoubaoE2ETranslateHelper.swift index 6bbf935f7..f37318904 100644 --- a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/DoubaoE2ETranslateHelper.swift +++ b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/DoubaoE2ETranslateHelper.swift @@ -75,6 +75,11 @@ class DoubaoE2ETranslateHelper: NSObject, URLSessionWebSocketDelegate { private var pumpTimer: DispatchSourceTimer? private let pumpQueue = DispatchQueue(label: "com.azure.speech.DoubaoE2E.pump", qos: .userInitiated) + // pushAudioData 日志节流:累计调用次数与字节数,每 50 次打印一次 + private var pushAudioLogCount: Int = 0 + private var pushAudioLogBytes: Int = 0 + private let pushAudioLogInterval: Int = 50 + /** * 初始化助手(异步) * 返回是否成功触发了连接逻辑 @@ -202,20 +207,45 @@ class DoubaoE2ETranslateHelper: NSObject, URLSessionWebSocketDelegate { * 等首条服务端消息到达再 flush 进 audioSendBuffer。 */ func pushAudioData(_ data: Data) -> Bool { - if isDisposed { return false } - if data.isEmpty { return true } - if !isStarted || !isWebSocketConnected { + if isDisposed { + os_log("pushAudioData skipped: isDisposed=true, size=%{public}d", log: log, type: .info, data.count) + return false + } + if data.isEmpty { + os_log("pushAudioData received empty data", log: log, type: .info) + return true + } + let routedToPending = (!isStarted || !isWebSocketConnected) + if routedToPending { lock.lock() while pendingAudioData.count >= maxPendingAudioChunks { pendingAudioData.removeFirst() } pendingAudioData.append(data) + let pendingCount = pendingAudioData.count lock.unlock() + pushAudioLogCount += 1 + pushAudioLogBytes += data.count + if pushAudioLogCount % pushAudioLogInterval == 1 { + os_log("pushAudioData -> pending: size=%{public}d, pendingCount=%{public}d, totalCalls=%{public}d, totalBytes=%{public}d, isStarted=%{public}@, isWsConnected=%{public}@", + log: log, type: .info, + data.count, pendingCount, pushAudioLogCount, pushAudioLogBytes, + isStarted ? "true" : "false", + isWebSocketConnected ? "true" : "false") + } return true } audioSendBufferLock.lock() audioSendBuffer.append(data) + let bufferSize = audioSendBuffer.count audioSendBufferLock.unlock() + pushAudioLogCount += 1 + pushAudioLogBytes += data.count + if pushAudioLogCount % pushAudioLogInterval == 1 { + os_log("pushAudioData -> buffer: size=%{public}d, bufferSize=%{public}d, totalCalls=%{public}d, totalBytes=%{public}d", + log: log, type: .info, + data.count, bufferSize, pushAudioLogCount, pushAudioLogBytes) + } return true } diff --git a/local_plugins/azure_speech/ios/azure_speech/Sources/tools/SimpleAudioReceiver.swift b/local_plugins/azure_speech/ios/azure_speech/Sources/tools/SimpleAudioReceiver.swift index c620aacfe..c9e43525b 100644 --- a/local_plugins/azure_speech/ios/azure_speech/Sources/tools/SimpleAudioReceiver.swift +++ b/local_plugins/azure_speech/ios/azure_speech/Sources/tools/SimpleAudioReceiver.swift @@ -139,12 +139,19 @@ public class SimpleAudioReceiver: NSObject { do { try configureAudioSession() - - let isHybridExternalMode = audioSourceType == .external && (currentRecognitionMode == "phone_call" || currentRecognitionMode == "push_to_talk") - if audioSourceType == .microphone || isHybridExternalMode { + // 见 configureAudioSession 上方注释:iOS 已对齐 Android `BleAgent` — + // useBle=true → audioSourceType=.external → 永远走 runExternalCapture, + // 由 device_jieli 的 AssistantAudioEvent → AgentService.pushAudioData 注入。 + // 之前这里有个 isHybridExternalMode(external + phone_call/push_to_talk) + // 会错路到 runMicrophoneCapture(),试图开 AVAudioEngine 抓手机麦 —— 但 + // configureAudioSession 在 .external 分支已经把 session 设成 .playback, + // 不允许 input,AVAudioEngine 起不来抛 OSStatus -50 paramErr,日志里 + // 看到的 "配置或启动音频捕获失败" + "Failed to set properties, error: -50" + // 就是这条线。 + if audioSourceType == .microphone { runMicrophoneCapture() - } else { // True external mode + } else { // .external runExternalCapture() } } catch { @@ -161,10 +168,26 @@ public class SimpleAudioReceiver: NSObject { var desiredCategory: AVAudioSession.Category var desiredMode: AVAudioSession.Mode var desiredOptions: AVAudioSession.CategoryOptions - - let isHybridExternalMode = audioSourceType == .external && (currentRecognitionMode == "phone_call" || currentRecognitionMode == "push_to_talk") - if audioSourceType == .microphone || isHybridExternalMode { + // 设计:参考 Android `BleAgent` 路径(上行 BLE / 下行交给系统路由)。 + // + // 之前 iOS 把 (.external && phone_call/push_to_talk) 当成"混合模式", + // 仍按 .playAndRecord + 强制 builtInMic 走 — 等价于"用 BLE 上行 + 手机麦 + // 兜底"。结果两个副作用: + // 1) 设了 `setPreferredInput(builtInMic)`,iOS 把音频会话归到 voice + // record 类,下行 TTS 默认路由就被拉到内置扬声器; + // 2) 在非 A2DP 配对场景里加了 `.defaultToSpeaker`,再次把 A2DP 出口顶掉。 + // Android `BleAgent.kt` 这个模式根本不碰手机麦 — `useBle=true` → + // ASR 用 .external,全部 PCM 由 device_jieli 的 AssistantAudioEvent + // 经 `pushAudioData` 灌入。下行 TTS 直接 `AudioTrack` USAGE_MEDIA, + // 系统自己决定走 A2DP 还是手机扬声器。 + // + // 所以 iOS 也按"纯播放"配置:.playback + .spokenAudio + .allowBluetoothA2DP, + // 不再申请录音权限通道,也不再强制 builtInMic / defaultToSpeaker。 + // 这样耳机以经典蓝牙配对过的话 TTS 走 A2DP;没配过的话回退手机扬声器 + // —— 跟 Android 行为对齐。 + + if audioSourceType == .microphone { // --- 麦克风输入逻辑 (包括混合模式) --- if currentRecognitionMode == "phone_call" { if isHeadphones { @@ -217,7 +240,10 @@ public class SimpleAudioReceiver: NSObject { try audioSession.setActive(true, options: .notifyOthersOnDeactivation) // --- 路由设置 --- - if audioSourceType == .microphone || isHybridExternalMode { + // 见上方注释:iOS 已对齐 Android `BleAgent` —— 设备录音模式下 + // audioSourceType 永远是 .external,根本不进 .microphone 分支, + // 所以路由设置只在"真的用手机麦"时才执行。 + if audioSourceType == .microphone { // 为需要麦克风的场景设置输入输出 if desiredCategory == .playAndRecord { if isHeadphones { @@ -235,12 +261,6 @@ public class SimpleAudioReceiver: NSObject { try audioSession.setPreferredInput(builtInMic) print("耳机模式下,为 \(currentRecognitionMode) 模式强制设置内置麦克风") } - } else if isHybridExternalMode { - // 混合模式: 强制使用内置麦克风 - if let builtInMic = inputs.first(where: { $0.portType == .builtInMic }) { - try audioSession.setPreferredInput(builtInMic) - print("混合外部模式: 首选输入已设置为内置麦克风") - } } else if let btInput = inputs.first(where: { $0.portType == .bluetoothHFP }), isHeadphones { // 其他耳机模式: 优先使用蓝牙耳机麦克风 try audioSession.setPreferredInput(btInput) diff --git a/local_plugins/ble_service/ios/ble_service/Package.swift b/local_plugins/ble_service/ios/ble_service/Package.swift index e5d2b6c9b..2b42832f5 100644 --- a/local_plugins/ble_service/ios/ble_service/Package.swift +++ b/local_plugins/ble_service/ios/ble_service/Package.swift @@ -13,8 +13,11 @@ let package = Package( .target( name: "ble_service", dependencies: [ - "opus", - "JLAudioUnitKit" + "opus" + // 注:JLAudioUnitKit 已移除 —— ble_service 整体在向 device_jieli + // 迁移,PCM→OPUS 编码路径砍掉以解开 SPM "Multiple commands produce + // JLAudioUnitKit.framework" 冲突(device_jieli 已经 vendor 同名 framework)。 + // 详见 Sources/ble_service/SwiftOpusAudioProcessor.swift 顶部注释。 ], path: "Sources/ble_service", linkerSettings: [ @@ -27,10 +30,6 @@ let package = Package( .binaryTarget( name: "opus", // 改为与 xcframework 内部模块名称一致 path: "opus.xcframework" - ), - .binaryTarget( - name: "JLAudioUnitKit", - path: "JLAudioUnitKit.xcframework" ) ] ) \ No newline at end of file diff --git a/local_plugins/ble_service/ios/ble_service/Sources/ble_service/SwiftOpusAudioProcessor.swift b/local_plugins/ble_service/ios/ble_service/Sources/ble_service/SwiftOpusAudioProcessor.swift index 3c84f0a62..9e314f5f7 100644 --- a/local_plugins/ble_service/ios/ble_service/Sources/ble_service/SwiftOpusAudioProcessor.swift +++ b/local_plugins/ble_service/ios/ble_service/Sources/ble_service/SwiftOpusAudioProcessor.swift @@ -1,7 +1,24 @@ import Foundation import AVFoundation import opus -import JLAudioUnitKit + +// MARK: - ble_service 弃用迁移期说明 +// ---------------------------------------------------------------------------- +// 整个 ble_service plugin 在迁移到 device_jieli。 +// +// 编码路径(PCM → OPUS → BLE 下发到耳机外放)原本由 JLAudioUnitKit 提供, +// 现已被砍掉以解开 "Multiple commands produce JLAudioUnitKit.framework" 编译 +// 冲突(两个 SPM plugin 都 vendor 同名 binaryTarget)。 +// +// 砍掉后受影响的 native 调用(暂时变成 noop): +// - AzureSpeechPlugin.writeExternalAudioData → BleService.shared.writeExternalAudioData +// (通话翻译 A 路 / TTS 把 PCM 编码发到耳机外放给对方) +// - AgentServiceImpl 在 ble_wakeup 播报结束后 BleService.shared.openB1Encoder() +// (重启耳机端编码器) +// +// 迁移后这些路径应改走 device_jieli 的 RcspTranslationRuntime / AssistantBridge 下行。 +// 短期内保留方法签名让所有调用方编译通过;废弃时机由 plugin 整体下线决定。 +// ---------------------------------------------------------------------------- @available(iOS 13.0, *) class SwiftOpusAudioProcessor: NSObject { @@ -25,20 +42,12 @@ class SwiftOpusAudioProcessor: NSObject { private var channels: Int32 = 1 private let maxFrameSize: Int32 = 640 // 为16kHz优化:40ms * 16000Hz = 640 samples - // MARK: - JLOpusEncoder 编码器相关属性 - private var jlOpusEncoder: JLOpusEncoder? - private var encoderFormat: JLOpusFormat? - - // MARK: - PCM数据队列相关属性 - /// PCM数据缓存队列 - private var pcmDataQueue = Data() - /// 队列访问锁,确保线程安全 - private let queueLock = NSLock() - /// 每次处理的数据块大小(字节) - private let chunkSize = 640 - /// 异步处理队列 - private var processingQueue: DispatchQueue? - + // MARK: - 编码器(已废弃) + // 原本通过 JLAudioUnitKit 提供 OPUS encoding,迁移到 device_jieli 后这里全部 + // 不再做实际工作。保留 isEncodingActive 仅用于让外部状态查询不变。 + // 上次 noop 调用日志的时间(节流,避免 encodePCMData 高频调用时日志刷屏) + private var lastNoopEncodeLogMs: Int64 = 0 + // MARK: - 初始化和清理 override init() { @@ -367,145 +376,38 @@ class SwiftOpusAudioProcessor: NSObject { } - /// 启动JLOpusEncoder编码(完善版本) - /// - Parameters: - /// - sampleRate: 采样率(默认16000Hz) - /// - channels: 声道数(默认1) - /// - frameRate: 帧长度(默认20ms) - /// - isHeader: 是否包含头部信息 - /// - Returns: 是否启动成功 - func startEncoding(sampleRate: Int32 = 16000, - channels: Int32 = 1, - frameRate: Int32 = 20, - isHeader: Bool = false) -> Bool { - if isEncodingActive { - return true - } - - print("启动JLOpusEncoder编码 - 采样率: \(sampleRate)Hz, 声道: \(channels), 帧长度: \(frameRate)ms, 头部: \(isHeader)") - - // 创建JLOpusFormat配置 - encoderFormat = JLOpusFormat.defaultFormats() - encoderFormat?.sampleRate = Int32(sampleRate) - encoderFormat?.channels = Int32(channels) - encoderFormat?.frameDuration = Int32(frameRate) - encoderFormat?.hasDataHeader = isHeader - - // 根据配置计算数据大小 - if channels == 1 { - encoderFormat?.dataSize = frameRate == 20 ? 40 : 80 // 20ms=40bytes, 40ms=80bytes - } else { - encoderFormat?.dataSize = frameRate == 20 ? 80 : 160 // 双声道翻倍 - } - - guard let format = encoderFormat else { - print("编码器格式配置失败") - return false - } - - // 初始化JLOpusEncoder - jlOpusEncoder = JLOpusEncoder(format: format, delegate: self) - - guard jlOpusEncoder != nil else { - print("JLOpusEncoder初始化失败") - return false - } - + /// 启动 OPUS 编码(已废弃 / noop) + /// 迁移到 device_jieli 后保留方法签名让 native 调用方编译通过。 + /// 调用方应改走 Jielihome / device_jieli 的下行链路。 + @discardableResult + func startEncoding(sampleRate: Int32 = 16000, + channels: Int32 = 1, + frameRate: Int32 = 20, + isHeader: Bool = false) -> Bool { + if isEncodingActive { return true } isEncodingActive = true - print("JLOpusEncoder启动成功") - + print("[ble_service:deprecated] startEncoding noop (sampleRate=\(sampleRate) ch=\(channels) frameRate=\(frameRate)) — migrate to device_jieli") return true } - - /// 停止JLOpusEncoder编码(完善版本) - /// - Returns: 是否停止成功 + + /// 停止 OPUS 编码(已废弃 / noop) + @discardableResult func stopEncoding() -> Bool { - if !isEncodingActive { - return false - } - + guard isEncodingActive else { return false } isEncodingActive = false - - // 清空PCM数据队列 - clearPCMQueue() - - // 释放JLOpusEncoder资源 - jlOpusEncoder?.opusOnRelease() - jlOpusEncoder = nil - encoderFormat = nil - processingQueue = nil - - print("JLOpusEncoder已停止") - + print("[ble_service:deprecated] stopEncoding noop — migrate to device_jieli") return true } - - /// 编码PCM音频数据 - /// - Parameter pcmData: PCM音频数据 - func encodePCMData(_ pcmData: Data) { - guard isEncodingActive, let encoder = jlOpusEncoder else { return } - guard !pcmData.isEmpty else { return } - - // 将新数据加入队列 - queueLock.lock() - pcmDataQueue.append(pcmData) - queueLock.unlock() - - // 异步处理队列中的数据 - processQueuedData() - } - - /// 处理队列中的PCM数据 - private func processQueuedData() { - guard isEncodingActive, let encoder = jlOpusEncoder else { - return - } - - // 使用专用队列进行异步处理 - if processingQueue == nil { - processingQueue = DispatchQueue(label: "com.opus.processing", qos: .userInitiated) - } - - processingQueue?.async { [weak self, weak encoder] in - guard let self = self, let encoder = encoder else { return } - - while self.isEncodingActive { - var chunkData: Data? - self.queueLock.lock() - if self.pcmDataQueue.count >= self.chunkSize { - chunkData = self.pcmDataQueue.subdata(in: 0.. 0 { - let availableBytes = self.pcmDataQueue.count - let silentBytesNeeded = self.chunkSize - availableBytes - let silentData = Data(repeating: 0, count: silentBytesNeeded) - chunkData = Data() - chunkData?.append(self.pcmDataQueue) - chunkData?.append(silentData) - self.pcmDataQueue.removeAll() - } - self.queueLock.unlock() - - if let dataToEncode = chunkData { - guard self.isEncodingActive else { break } - encoder.opusEncode(dataToEncode) - } else { - break - } - } + /// 编码 PCM 音频数据(已废弃 / noop,日志节流到 1s 一条) + func encodePCMData(_ pcmData: Data) { + let now = Int64(Date().timeIntervalSince1970 * 1000) + if now - lastNoopEncodeLogMs >= 1000 { + lastNoopEncodeLogMs = now + print("[ble_service:deprecated] encodePCMData dropped \(pcmData.count) B (encoder removed; migrate to device_jieli)") } } - - /// 清空PCM数据队列 - private func clearPCMQueue() { - queueLock.lock() - pcmDataQueue.removeAll() - queueLock.unlock() - print("PCM数据队列已清空") - } - - + /// 清理所有资源 func cleanup() { stopDecoding() @@ -513,29 +415,5 @@ class SwiftOpusAudioProcessor: NSObject { } } -// MARK: - JLOpusEncoderDelegate -@available(iOS 13.0, *) -extension SwiftOpusAudioProcessor: JLOpusEncoderDelegate { - - /// JLOpusEncoder编码回调 - /// - Parameters: - /// - encoder: 编码器实例 - /// - data: 编码后的Opus数据 - /// - error: 错误信息 - func opusEncoder(_ encoder: JLOpusEncoder, data: Data?, error: (any Error)?) { - if let error = error { - print("Opus编码错误: \(error.localizedDescription)") - return - } - - guard let opusData = data, !opusData.isEmpty else { return } - - // 回调编码后的数据到主线程 - DispatchQueue.main.async { [weak self] in - self?.callback?.onEncodedDataReceived(data: opusData) - } - } -} - diff --git a/local_plugins/device_jieli/CHANGELOG.md b/local_plugins/device_jieli/CHANGELOG.md index 41cc7d819..c41ea00ea 100644 --- a/local_plugins/device_jieli/CHANGELOG.md +++ b/local_plugins/device_jieli/CHANGELOG.md @@ -1,3 +1,87 @@ +## 0.0.2 + +### iOS 端补全 / 与 Android 对齐(参考 `JIELI-IOS-Release`) + +#### 框架 +- 拷贝 `JIELI-IOS-Release/Libs/` 下全部 `.xcframework` + sample 项目里的 `JLAudioUnitKit.xcframework`(OPUS 编解码所需)到 `ios/Frameworks/`,通过 `vendored_frameworks` 引入。 +- `pubspec.yaml` 新增 `ios` 平台条目;`ios/device_jieli.podspec` 配置完成。 + +#### 与 Android 同名 / 同形态的核心通路(按 user 要求全量对齐) + +| 通路 | Android | iOS | +|---|---|---| +| 设备连接 | `ConnectFeature` → `JL_BluetoothManager.connect` + `setUseDeviceAuth(true)` | `ConnectFeature` → `JL_BLEMultiple.connectEntity` + `authEnable=true`;paired 后**自动调 `cmdTargetFeatureResult`** + `cmdGetSystemInfo(.COMMON)` 把 model 拉满,再外抛 `rcspInit` | +| 电量推送 | `OnRcspEventListener.onBatteryChange` → `battery` 事件 | `DeviceInfoEventForwarder` 订阅 `kJL_MANAGER_HEADSET_ADV` / `kJL_MANAGER_SYSTEM_INFO`,合成 `battery` 事件(带 `level/left/right/case` 字段,去重) | +| 通话状态 | `onPhoneCallStatusChange` → `phoneCallStatus` | `DeviceInfoEventForwarder` 订阅 `kJL_MANAGER_CALL_STATUS` | +| 通话翻译 mode=3 | `CallTranslationModeHandler` (uplink/downlink × OPUS 编解码) | `CallTranslationModeHandler` 完整复刻:`OpusStreamDecoder(channels=1, dataSize=40)` 分流 escoUp/escoDown,下行 PCM `OpusStreamEncoder` 编码后 `trWriteAudio(.escoUp/.escoDown)` | +| 通话翻译 mode=6(立体声)| `StereoCallTranslationModeHandler` + `PcmKit.splitStereo16` | `StereoCallTranslationModeHandler` 完整复刻:`OpusStreamDecoder(channels=2, dataSize=80)` → `PcmKit.splitStereo16` → uplink/downlink 两路 | +| MODE_CALL_TRANSLATION + OPUS 自动升级到 stereo | ✅ | ✅(`bypassStereoUpgrade=true` 可禁用,行为与 Android 一致) | +| AI 助理 | `JieliAssistantPort` (MODE_RECORD + STRATEGY_DEVICE_ALWAYS_RECORDING + OPUS) | `AssistantBridge` 复刻:通过 `TranslationSession.acquire` 拿独占所有权,`trStartTranslateMode(.onlyRecord, OPUS, ch=1)` + `recordtype=.byDevice`,`OpusStreamDecoder(channels=1, dataSize=40)` 解出 16k mono PCM → `assistantAudio` | +| 设备录音 | `JieliDeviceRecordPort` (mode=6 stereo + DEVICE_ALWAYS_RECORDING) | `DeviceRecordFeature` 复刻:`trStartTranslateMode(.callTranslateStereo, OPUS, ch=2)` + `recordtype=.byDevice`,`OpusStreamDecoder(channels=2, dataSize=80)` 解出 stereo PCM → `deviceRecordAudio` | +| OTA | 自实现 cmd-level 状态机 | 直接走 `JL_BLEMultiple.otaFunc(...)` 高层 API,结果回调映射到 `otaState` / `otaError` | + +#### TranslationSession 互斥模型 +iOS SDK 一台设备只有一个 `JLTranslationManager`,与 Android 同样要求"翻译 / 助理 / 设备录音"三选一互斥。新增 [TranslationSession](ios/Classes/core/TranslationSession.swift): +- 内部持有 `JLTranslationManager` 实例,自身作为 `JLTranslationManagerDelegate` +- 通过 `acquire(owner:)` / `release(owner:)` 协调当前会话所有者 +- 所有 SDK 回调(`onReceiveAudioData` / `onModeChange` / `onError` / `onSendAudioQueueOver` / `isOnCalling`)fan-out 到当前 owner + +#### 文件清单(`ios/Classes/`) +``` +JielihomePlugin.swift # Flutter 入口 +MethodRouter.swift # MethodChannel 路由 +EventDispatcher.swift # EventChannel 总线 +JieliHomeServer.swift # 单例 + per-device session + +core/TranslationSession.swift # 设备级翻译会话所有者协调 +audio/OpusStreamCodec.swift # JLOpusDecoder/Encoder 包装 +audio/PcmKit.swift # 16-bit stereo split / interleave + +event/BluetoothEventForwarder.swift # BLE 状态 / 连接通知 +event/CustomEventForwarder.swift # 厂商扩展指令 +event/DeviceInfoEventForwarder.swift # 电量 / 通话 / 系统信息 + +feature/ScanFeature.swift +feature/ConnectFeature.swift +feature/DeviceInfoFeature.swift +feature/CustomCmdFeature.swift +feature/OtaFeature.swift +feature/TranslationFeature.swift # 6-mode dispatcher +feature/translation/TranslationConstants.swift +feature/translation/TranslationModeHandler.swift +feature/translation/mode/RecordModeHandler.swift (mode=1 + RecordingTranslation mode=2) +feature/translation/mode/CallTranslationModeHandler.swift (mode=3) +feature/translation/mode/StereoCallTranslationModeHandler.swift (mode=6) +feature/translation/mode/AudioAndFaceModeHandlers.swift (mode=4 + mode=5) +feature/AssistantBridge.swift +feature/DeviceRecordFeature.swift +feature/SpeechFeature.swift # 状态机骨架(详见下方) +``` + +### 与 Android 实际差异 + +| 项 | Android | iOS | 影响 | +|---|---|---|---| +| `address` 含义 | BLE MAC | CoreBluetooth identifier (UUID) | iOS 系统对 BLE MAC 永久匿名;上层若用 address 做主键需要做厂商适配 | +| 录音策略 | 三选一:CUSTOM/ALWAYS/AUTO | 二选一:`.byPhone`(=CUSTOM) / `.byDevice`(=ALWAYS) | iOS SDK 不暴露 AUTO(VAD 自动启停),调用 `strategy="auto"` 时回退到 default | +| `versionCode` 字段 | SDK 有 numeric versionCode | SDK 只有 versionFirmware(字符串) | `deviceSnapshot` 里 versionCode 为 null | +| `sendCustomCmd` opCode | SDK 有专门字段 | iOS `JL_CustomManager.cmdCustomData:` 无 opCode 字段 | iOS 实现把 Dart 传入的 opCode 拼到 payload 头部(业务双方约定) | +| `OTA` 状态机 | 插件自实现 cmd 级状态机 | 直接走 SDK 高层 API | Dart `otaStart` 的 `blockSize` / `fileFlag` 参数 iOS 端忽略 | +| `speechStart/Stop`(cmd=4/5 通路) | 完整实现 | 仅状态机外抛 `speechStart/End`,**不拉真实音频** | iOS SDK 4.2 beta 没把 cmd=4/5 通路暴露成公开 Swift API;耳机硬件唤醒的上行帧通过翻译通路接管 | + +### 真机验证状态 + +| 子系统 | 单元自测 | 真机验证 | +|---|---|---| +| 扫描 / 连接 / 断开 | ✅ 编译通过 | ⬜ | +| `battery` / `phoneCallStatus` 推送 | ✅ | ⬜ | +| `deviceSnapshot` / `queryTargetInfo` / `sendCustomCmd` | ✅ | ⬜ | +| OTA | ✅ | ⬜ | +| 通话翻译 mode=3 + OPUS 上下行 | ✅ | ⬜ | +| 通话翻译 mode=6 立体声 | ✅ | ⬜ | +| AI 助理 (MODE_RECORD + byDevice + OPUS) | ✅ | ⬜ | +| 设备录音 (mode=6 + stereo + byDevice + OPUS) | ✅ | ⬜ | + ## 0.0.1 * TODO: Describe initial release. diff --git a/local_plugins/device_jieli/README.md b/local_plugins/device_jieli/README.md index d5eb0fde6..bc38ec0c9 100644 --- a/local_plugins/device_jieli/README.md +++ b/local_plugins/device_jieli/README.md @@ -1,15 +1,42 @@ -# jielihome +# device_jieli -JieLi Home SDK Flutter plugin +Flutter plugin around the JieLi (杰理) RCSP Bluetooth SDK. Provides a vendor-agnostic +MethodChannel / EventChannel surface (`device_jieli/method`, `device_jieli/event`) +so Dart-side code can scan / connect / query / OTA / translate against JieLi earpieces +on both Android and iOS. -## Getting Started +## Platforms -This project is a starting point for a Flutter -[plug-in package](https://flutter.dev/to/develop-plugins), -a specialized package that includes platform-specific implementation code for -Android and/or iOS. +| Platform | Native SDK | Status | +|---|---|---| +| Android | `android/libs/jl_bluetooth_rcsp_*.aar` etc. | Functional (used in production demo) | +| iOS | `ios/Frameworks/JL_BLEKit.xcframework` etc. (from `JIELI-IOS-Release/Libs`) | **Compiles,真机验证未完成** — see [CHANGELOG.md](CHANGELOG.md) for the per-feature status matrix. | -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +## iOS setup +The plugin ships every required `.xcframework` under `ios/Frameworks/`. When the upstream +SDK in `JIELI-IOS-Release/Libs/` updates, mirror the new `.xcframework` directories on +top of `ios/Frameworks/` (overwrite is safe) and `pod install` again in the host app. + +Minimum target: iOS 12. + +The host app must declare in `Info.plist`: + +```xml +NSBluetoothAlwaysUsageDescription +用于连接杰理蓝牙耳机 +NSBluetoothPeripheralUsageDescription +用于连接杰理蓝牙耳机 +NSMicrophoneUsageDescription +用于翻译 / 语音助手录音 +``` + +## Channel surface + +Dart API lives in [`lib/device_jieli.dart`](lib/device_jieli.dart). Native code on either +platform implements the same set of method calls and emits the same event payload schema — +the iOS implementation lives in [`ios/Classes/`](ios/Classes/), the Android +implementation in [`android/src/main/kotlin/com/jielihome/jielihome/`](android/src/main/kotlin/com/jielihome/jielihome/). + +For background on the RCSP commands and translation pipeline see +[`JIELI_SDK_COMMANDS.md`](JIELI_SDK_COMMANDS.md). diff --git a/local_plugins/device_jieli/android/libs/jl_bluetooth_rcsp_V4.2.0_beta2_40214_20251224.aar b/local_plugins/device_jieli/android/libs/jl_bluetooth_rcsp_V4.2.0_beta2_40214_20251224.aar deleted file mode 100644 index 15eb8cf5e..000000000 Binary files a/local_plugins/device_jieli/android/libs/jl_bluetooth_rcsp_V4.2.0_beta2_40214_20251224.aar and /dev/null differ diff --git a/local_plugins/device_jieli/android/src/main/kotlin/com/jielihome/jielihome/feature/record/JieliDeviceRecordPort.kt b/local_plugins/device_jieli/android/src/main/kotlin/com/jielihome/jielihome/feature/record/JieliDeviceRecordPort.kt index 1a26be54b..8a2a953b0 100644 --- a/local_plugins/device_jieli/android/src/main/kotlin/com/jielihome/jielihome/feature/record/JieliDeviceRecordPort.kt +++ b/local_plugins/device_jieli/android/src/main/kotlin/com/jielihome/jielihome/feature/record/JieliDeviceRecordPort.kt @@ -287,7 +287,7 @@ class JieliDeviceRecordPort( val addr = device?.address if (impl != null) { runCatching { - Log.i(TAG, "[APP->SDK] exitMode addr=$addr mode=${TranslationMode.MODE_CALL_TRANSLATION_WITH_STEREO}") + Log.i(TAG, "[APP->SDK] exitMode addr=$addr mode=${TranslationMode.MODE_CALL_RECORD}") impl.exitMode(object : OnRcspActionCallback { override fun onSuccess(d: BluetoothDevice?, t: Int?) { Log.i(TAG, "[APP->SDK] exitMode onSuccess addr=${d?.address} t=$t") diff --git a/local_plugins/device_jieli/android/src/main/kotlin/com/jielihome/jielihome/feature/translation/runtime/JieliAITranslationBridge.kt b/local_plugins/device_jieli/android/src/main/kotlin/com/jielihome/jielihome/feature/translation/runtime/JieliAITranslationBridge.kt new file mode 100644 index 000000000..cfb04d34f --- /dev/null +++ b/local_plugins/device_jieli/android/src/main/kotlin/com/jielihome/jielihome/feature/translation/runtime/JieliAITranslationBridge.kt @@ -0,0 +1,281 @@ +package com.jielihome.jielihome.feature.translation.runtime + +import android.util.Log +import com.jieli.bluetooth.bean.translation.AudioData +import com.jieli.bluetooth.bean.translation.TranslationMode +import com.jieli.bluetooth.bean.translation.TranslationResult +import com.jieli.bluetooth.constant.Constants +import com.jieli.bluetooth.interfaces.rcsp.translation.AITranslationCallback +import com.jieli.bluetooth.interfaces.rcsp.translation.IAITranslationApi +import com.jieli.jl_audio_decode.callback.OnStateCallback +import com.jieli.jl_audio_decode.opus.OpusManager +import com.jielihome.jielihome.audio.OpusStreamDecoder +import com.jielihome.jielihome.feature.translation.TranslationStreams +import java.io.ByteArrayOutputStream +import java.io.File +import java.util.concurrent.atomic.AtomicLong + +/** + * 杰理 SDK 的 [IAITranslationApi] 实现 —— call translation 路径专用的「上下行接力桥」。 + * + * # 为什么要有这个 bridge + * 早先的实现(旧 [RcspTranslationRuntime])走的是"自己手写 writeAudioData 队列"的方案: + * 1. 用 [com.jieli.bluetooth.interfaces.rcsp.translation.TranslationCallback.onReceiveAudioData] + * 拿上行 OPUS,自己解码; + * 2. 把外部翻译服务回送的 PCM 周期切片 → OPUS 编码 → 用自己的 WriteScheduler 串行调 + * [com.jieli.bluetooth.impl.rcsp.translation.TranslationImpl.writeAudioData] 推回耳机。 + * + * 实测踩到的两个坑: + * - 周期切片(每 1s)→ 多段独立 OPUS → 耳机端解码器频繁 reset → 杂音 / 断续; + * - 自己维护 in-flight=1 的写队列,溢出丢最老 → utterance 中段被截掉。 + * + * 官方 demo 的真正用法([com.jieli.bt.sdk.tool.translation.AITranslationImpl]):实现 + * [IAITranslationApi],把整段 utterance 编一次 OPUS,包成 [AudioData] 塞进 + * [TranslationResult.translationTTSData] 然后调 [AITranslationCallback.onTranslateResult], + * **SDK 内部** 接管 cmd=52 切包 / 发送时序 / 缓冲水位。 + * + * # 上下行职责 + * - **上行**(headset → app):SDK 主动调 [writeAudio],audioData.type = OPUS / PCM; + * 按 [AudioData.source] 分流到 up/down/stereo decoder,解码后通过 [onPcm] 抛出去。 + * - **下行**(app → headset):[feedTtsPcm] 累积外部翻译服务回送的 PCM,**纯 isFinal 驱动**: + * `isFinal=true` 时把累积 PCM 编成一段 OPUS,组装 [TranslationResult] 丢给 SDK。 + * 不再有周期 flush。如果上层一直不发 isFinal,缓冲到 [PCM_BUFFER_HARD_LIMIT_BYTES] + * 兜底强制 flush,避免内存爆。 + * + * # 与 [NoOpAITranslationApi] 的区别 + * - [NoOpAITranslationApi]:纯录音通路(JieliAssistantPort / JieliDeviceRecordPort 等) + * 只想拿原始 PCM,不希望 SDK 触发 AI 流程。 + * - 本类:通话翻译需要"上行解码 + 下行 SDK 接管 TTS 注入",要走 SDK 的标准 AI hook。 + * + * # 线程 + * - SDK 回调线程不固定;解码器 / 编码器调用都不假设线程。 + * - [feedTtsPcm] 可被任意线程调用,per-leg 缓冲用 [bufferLock] 互斥。 + * - OPUS 文件编码异步([OpusManager.encodeFile] 内部线程),完成后回到 SDK callback。 + */ +class JieliAITranslationBridge( + private val mode: TranslationMode, + private val tempDir: File, + /** 解码后的上行 PCM 出口;source 为 SDK 原值(SOURCE_E_SCO_UP_LINK / DOWN_LINK / MIX)。 */ + private val onPcm: (source: Int, pcm: ByteArray) -> Unit, + /** 解码 / 编码失败的统一出口。 */ + private val onError: (code: Int, msg: String?) -> Unit, + private val opusPacketSize: Int = if (mode.channel == 2) 80 else 200, +) : IAITranslationApi { + + companion object { + private const val TAG = "JieliAITranslationBridge" + /** 单段 utterance PCM 上限:≈ 60s 16k mono 16bit = 1.92MB。超限强制 flush 兜底。 */ + private const val PCM_BUFFER_HARD_LIMIT_BYTES = 2 * 1024 * 1024 + } + + private val isPcmMode = mode.audioType == Constants.AUDIO_TYPE_PCM + private val sampleRateHz = mode.sampleRate.takeIf { it > 0 } ?: 16000 + + @Volatile private var sdkCallback: AITranslationCallback? = null + @Volatile private var working = false + + /** 上行 OPUS 解码器:通话翻译有 up/down 两路单声道,stereo 模式只一路双声道。 */ + private val upDecoder: OpusStreamDecoder? = if (isPcmMode) null else OpusStreamDecoder( + channel = 1, + packetSize = opusPacketSize, + sampleRate = sampleRateHz, + onPcm = { pcm -> onPcm(AudioData.SOURCE_E_SCO_UP_LINK, pcm) }, + onError = { c, m -> onError(c, "upDecoder: $m") }, + ) + private val downDecoder: OpusStreamDecoder? = + if (!isPcmMode && mode.mode == TranslationMode.MODE_CALL_TRANSLATION) OpusStreamDecoder( + channel = 1, + packetSize = opusPacketSize, + sampleRate = sampleRateHz, + onPcm = { pcm -> onPcm(AudioData.SOURCE_E_SCO_DOWN_LINK, pcm) }, + onError = { c, m -> onError(c, "downDecoder: $m") }, + ) else null + private val stereoDecoder: OpusStreamDecoder? = + if (!isPcmMode && mode.mode == TranslationMode.MODE_CALL_TRANSLATION_WITH_STEREO) OpusStreamDecoder( + channel = 2, packetSize = 80, + sampleRate = sampleRateHz, + onPcm = { pcm -> onPcm(AudioData.SOURCE_E_SCO_MIX, pcm) }, + onError = { c, m -> onError(c, "stereoDecoder: $m") }, + ) else null + + /** Per-leg PCM 缓冲:key = outputStreamId(OUT_UPLINK / OUT_DOWNLINK / OUT_SPEAKER)。 */ + private val pcmBuffers = mutableMapOf() + private val bufferLock = Any() + private val encodeSeq = AtomicLong(0) + + /** 调试统计 */ + @Volatile private var rxFirstLogged = false + private val rxFirstPerSource = java.util.concurrent.ConcurrentHashMap() + + // ─── IAITranslationApi 实现 ─────────────────────────────────────────── + + override fun isWorking(): Boolean = working + + override fun startTranslating(mode: TranslationMode, callback: AITranslationCallback) { + Log.i(TAG, "[SDK->APP] startTranslating mode=${mode.mode} type=${mode.audioType} sr=${mode.sampleRate} ch=${mode.channel}") + sdkCallback = callback + if (!tempDir.exists()) tempDir.mkdirs() + upDecoder?.start() + downDecoder?.start() + stereoDecoder?.start() + working = true + callback.onStart() + } + + override fun stopTranslating() { + Log.i(TAG, "[SDK->APP] stopTranslating") + working = false + runCatching { upDecoder?.stop() } + runCatching { downDecoder?.stop() } + runCatching { stereoDecoder?.stop() } + // 兜底:剩余 buffer 直接丢,不补发 onTranslateResult(utterance 已被打断)。 + synchronized(bufferLock) { + pcmBuffers.values.forEach { it.reset() } + pcmBuffers.clear() + } + sdkCallback?.onStop(0, "stopTranslating") + sdkCallback = null + } + + override fun writeAudio(data: AudioData) { + if (!working) return + if (data.type != mode.audioType) { + // SDK 极少数情况下会推与 mode.audioType 不一致的帧(例如握手期残留);忽略不报错。 + return + } + val payload = data.audioData ?: return + if (payload.isEmpty()) return + + // 首帧(总) + if (!rxFirstLogged) { + rxFirstLogged = true + Log.i(TAG, "[SDK->APP] writeAudio FIRST source=${data.source} type=${data.type} size=${payload.size}") + } + // 首帧(按 source) + if (rxFirstPerSource.putIfAbsent(data.source, true) == null) { + Log.i(TAG, "[SDK->APP] writeAudio FIRST source=${data.source} size=${payload.size}") + } + + if (isPcmMode) { + onPcm(data.source, payload) + return + } + when (data.source) { + AudioData.SOURCE_E_SCO_UP_LINK -> upDecoder?.feedEncoded(payload) + AudioData.SOURCE_E_SCO_DOWN_LINK -> downDecoder?.feedEncoded(payload) + AudioData.SOURCE_E_SCO_MIX -> stereoDecoder?.feedEncoded(payload) + else -> Log.w(TAG, "[SDK->APP] writeAudio unknown source=${data.source} size=${payload.size}") + } + } + + // ─── 下行:外部翻译服务的 TTS PCM 接力 ───────────────────────────────── + + /** + * 把外部翻译服务回送的 TTS PCM 接力给 SDK。 + * + * 策略: + * - PCM 模式:每次调用作为一段 [AudioData] 直接交给 SDK。 + * - OPUS 模式:累积 per-leg buffer;`isFinal=true` 或缓冲到 [PCM_BUFFER_HARD_LIMIT_BYTES] + * 时整段编码 → 通过 [AITranslationCallback.onTranslateResult] 交给 SDK,SDK 内部 + * 完成 cmd=52 切包 / 发送 / 速率控制。 + * + * @return 是否接受本帧(false 表示丢弃,例如未启动 / 缓冲爆 / SDK callback 缺失) + */ + fun feedTtsPcm(outputStreamId: String, pcm: ByteArray, isFinal: Boolean): Boolean { + if (!working) return false + val cb = sdkCallback ?: return false + val source = sourceOf(outputStreamId) + + if (isPcmMode) { + postTranslationResult(cb, source, Constants.AUDIO_TYPE_PCM, pcm) + return true + } + // OPUS 模式:累积 + isFinal/超限触发整段编码 + val pendingFlush: ByteArray? = synchronized(bufferLock) { + val buf = pcmBuffers.getOrPut(outputStreamId) { ByteArrayOutputStream() } + if (pcm.isNotEmpty()) { + if (buf.size() + pcm.size > PCM_BUFFER_HARD_LIMIT_BYTES) { + Log.w(TAG, "feedTtsPcm leg=$outputStreamId buffer hit hard limit ${PCM_BUFFER_HARD_LIMIT_BYTES}, force flush") + val out = buf.toByteArray() + pcm + buf.reset() + return@synchronized out + } + buf.write(pcm) + } + if (isFinal) { + val out = buf.toByteArray() + buf.reset() + if (out.isEmpty()) null else out + } else null + } + if (pendingFlush != null) { + encodeAndDeliverAsync(cb, source, outputStreamId, pendingFlush) + } + return true + } + + private fun postTranslationResult( + cb: AITranslationCallback, + source: Int, + audioType: Int, + bytes: ByteArray, + ) { + val result = TranslationResult().also { + it.translationTTSData = AudioData(source, audioType, bytes) + } + runCatching { cb.onTranslateResult(result) }.onFailure { + Log.w(TAG, "onTranslateResult threw: ${it.message}") + } + } + + private fun encodeAndDeliverAsync( + cb: AITranslationCallback, + source: Int, + leg: String, + pcmBytes: ByteArray, + ) { + val seq = encodeSeq.incrementAndGet() + val pcmFile = File(tempDir, "tts_${source}_$seq.pcm") + val opusFile = File(tempDir, "tts_${source}_$seq.opus") + try { + pcmFile.writeBytes(pcmBytes) + } catch (e: Throwable) { + Log.w(TAG, "encodeAndDeliver leg=$leg seq=$seq write pcm failed: ${e.message}") + return + } + // OpusManager 默认 OpusOption(带 head),与 demo `MachineTranslation.tryToTTS` 一致; + // SDK 接收端按"完整带 head 的 OPUS 段"解析,所以一次 onTranslateResult = 一段 utterance。 + val encoder = OpusManager() + encoder.encodeFile(pcmFile.absolutePath, opusFile.absolutePath, object : OnStateCallback { + override fun onStart() {} + override fun onComplete(path: String?) { + try { + val opusBytes = if (opusFile.exists()) opusFile.readBytes() else null + if (opusBytes == null || opusBytes.isEmpty()) { + Log.w(TAG, "encodeAndDeliver leg=$leg seq=$seq: empty opus output") + } else { + Log.d(TAG, "encodeAndDeliver leg=$leg seq=$seq pcm=${pcmBytes.size}B opus=${opusBytes.size}B → onTranslateResult") + postTranslationResult(cb, source, Constants.AUDIO_TYPE_OPUS, opusBytes) + } + } finally { + runCatching { encoder.release() } + runCatching { pcmFile.delete() } + runCatching { opusFile.delete() } + } + } + override fun onError(code: Int, message: String?) { + Log.w(TAG, "encodeAndDeliver leg=$leg seq=$seq encodeFile error code=$code msg=$message") + runCatching { encoder.release() } + runCatching { pcmFile.delete() } + runCatching { opusFile.delete() } + onError(code, "encodeFile $leg: $message") + } + }) + } + + private fun sourceOf(outputStreamId: String): Int = when (outputStreamId) { + TranslationStreams.OUT_UPLINK -> AudioData.SOURCE_E_SCO_UP_LINK + TranslationStreams.OUT_DOWNLINK -> AudioData.SOURCE_E_SCO_DOWN_LINK + else -> AudioData.SOURCE_PHONE_MIC + } +} diff --git a/local_plugins/device_jieli/android/src/main/kotlin/com/jielihome/jielihome/feature/translation/runtime/NoOpAITranslationApi.kt b/local_plugins/device_jieli/android/src/main/kotlin/com/jielihome/jielihome/feature/translation/runtime/NoOpAITranslationApi.kt index 448c4deed..8cb7eee7d 100644 --- a/local_plugins/device_jieli/android/src/main/kotlin/com/jielihome/jielihome/feature/translation/runtime/NoOpAITranslationApi.kt +++ b/local_plugins/device_jieli/android/src/main/kotlin/com/jielihome/jielihome/feature/translation/runtime/NoOpAITranslationApi.kt @@ -6,8 +6,21 @@ import com.jieli.bluetooth.interfaces.rcsp.translation.AITranslationCallback import com.jieli.bluetooth.interfaces.rcsp.translation.IAITranslationApi /** - * SDK 的 [TranslationImpl] 构造函数要求一个 [IAITranslationApi]。 - * 我们走的是「外部翻译服务」路线,SDK 自带的 AI 流程不启用,所以这里给个空实现。 + * SDK 的 `TranslationImpl` 构造函数要求一个 [IAITranslationApi]。本类是给**纯录音通路** + * 占位用的空实现:上层只想拿原始 OPUS / PCM 流(自己挂 `TranslationCallback.onReceiveAudioData`), + * 不希望 SDK 触发任何 AI 翻译流程 / TTS 注入。 + * + * # 谁在用 + * - [com.jielihome.jielihome.feature.assistant.JieliAssistantPort](AI 助理上行通路) + * - [com.jielihome.jielihome.feature.record.JieliDeviceRecordPort](通话录音 MODE_CALL_RECORD) + * - [com.jielihome.jielihome.feature.translation.mode.RecordModeHandler](MODE_RECORD) + * - [com.jielihome.jielihome.feature.translation.mode.RecordingTranslationModeHandler](MODE_RECORDING_TRANSLATION) + * - [com.jielihome.jielihome.feature.translation.TranslationFeature](兜底 TranslationImpl 实例) + * + * # 谁**不在**用了 + * Call translation(mode 3 / 6)由 [JieliAITranslationBridge] 接管——那条路要的是 + * "SDK 接管 TTS 切包注入",必须给真正的 IAITranslationApi 实现,避免自己写 writeAudioData + * 队列触发的"耳机端解码器频繁 reset → 杂音 / 断续"。 */ internal class NoOpAITranslationApi : IAITranslationApi { override fun isWorking(): Boolean = false diff --git a/local_plugins/device_jieli/android/src/main/kotlin/com/jielihome/jielihome/feature/translation/runtime/RcspTranslationRuntime.kt b/local_plugins/device_jieli/android/src/main/kotlin/com/jielihome/jielihome/feature/translation/runtime/RcspTranslationRuntime.kt index 4a6c3545c..aedd7bdc5 100644 --- a/local_plugins/device_jieli/android/src/main/kotlin/com/jielihome/jielihome/feature/translation/runtime/RcspTranslationRuntime.kt +++ b/local_plugins/device_jieli/android/src/main/kotlin/com/jielihome/jielihome/feature/translation/runtime/RcspTranslationRuntime.kt @@ -1,6 +1,7 @@ package com.jielihome.jielihome.feature.translation.runtime import android.bluetooth.BluetoothDevice +import android.util.Log import com.jieli.bluetooth.bean.translation.AudioData import com.jieli.bluetooth.bean.translation.TranslationMode import com.jieli.bluetooth.constant.Constants @@ -8,42 +9,28 @@ import com.jieli.bluetooth.impl.JL_BluetoothManager import com.jieli.bluetooth.impl.rcsp.translation.TranslationImpl import com.jieli.bluetooth.interfaces.rcsp.callback.OnRcspActionCallback import com.jieli.bluetooth.interfaces.rcsp.translation.TranslationCallback -import android.util.Log -import com.jielihome.jielihome.audio.OpusStreamDecoder -import com.jielihome.jielihome.feature.translation.TranslationStreams -import com.jieli.jl_audio_decode.callback.OnStateCallback -import com.jieli.jl_audio_decode.opus.OpusManager -import java.io.ByteArrayOutputStream import java.io.File -import java.util.ArrayDeque -import java.util.concurrent.Executors -import java.util.concurrent.ScheduledExecutorService -import java.util.concurrent.TimeUnit -import java.util.concurrent.atomic.AtomicBoolean /** - * RCSP 翻译模式运行时。 - * - * audioType: - * - [Constants.AUDIO_TYPE_OPUS](默认):上行 OPUS → 解码 PCM;下行 PCM → **整段** 编码 OPUS 写回 - * - [Constants.AUDIO_TYPE_PCM]:上下行直接走 PCM,不经编解码 + * RCSP 翻译模式运行时(call translation 路径专用)。 * - * # TTS 回灌策略(与官方 demo `MachineTranslation.tryToTTS` / `OpusHelper.encodeFile` 对齐) + * # 角色 + * 把"进入/退出 SDK 翻译模式"的 RCSP 状态机生命周期,跟「上下行音频接力」绑在一起。 + * 真正的音频路由由 [JieliAITranslationBridge] 这个 [com.jieli.bluetooth.interfaces.rcsp.translation.IAITranslationApi] + * 实现负责(见它头注释)。 * - * 之前是流式 [com.jielihome.jielihome.audio.OpusStreamEncoder]:每出一帧 OPUS 立即包成 - * AudioData 调 [TranslationImpl.writeAudioData],导致耳机端 RCSP 把每帧当成"独立的一段 - * TTS 起点",解码器频繁 reset → 杂音 / 断续。 + * # 与旧实现的区别 + * 之前的 runtime 大约 500 行:自己用 [TranslationCallback.onReceiveAudioData] 拿上行 OPUS、 + * 自己 OPUS 编码 TTS、自己维护 `WriteScheduler` 串行队列调 [TranslationImpl.writeAudioData]。 + * 实测耳机端解码器频繁 reset 导致杂音 / 断续。 * - * 现在按 demo:[feedTtsPcm] 把 PCM 累积到 per-leg 缓冲;上层在每段 utterance 末尾把 - * `isFinal=true` 透传过来;runtime 这一刻: - * 1. 把累积 PCM 写到临时 .pcm 文件; - * 2. `OpusManager.encodeFile(pcmFile, opusFile, callback)` 离线整段编码(带 head 的默认 OpusOption); - * 3. 整段读出 → **一个** [AudioData] → [WriteScheduler] 单次入队下发。 + * 现在按官方 demo `AITranslationImpl` 的标准做法: + * 1. 构造 [TranslationImpl] 时传入真正的 [JieliAITranslationBridge],SDK 通过它把上行 + * OPUS 推给我们解码; + * 2. 下行 TTS PCM 累积成段后通过 [AITranslationCallback.onTranslateResult] 交给 SDK, + * 由 SDK 内部完成 cmd=52 切包 / 写时序 / 缓冲水位([TranslationImpl.PushDataWrapper])。 * - * # 文件命名 - * 临时文件位于 [tempDir],按 `tts__.` 命名;编码完成异步删除。 - * - * # source 字段写回方向 + * # 上下行 source 字段语义 * - 通话翻译给「对端听」 → AudioData.source = SOURCE_E_SCO_UP_LINK * - 通话翻译给「本机听」 → AudioData.source = SOURCE_E_SCO_DOWN_LINK * - 录音/音视频/面对面 → AudioData.source = SOURCE_PHONE_MIC(SDK 按 mode 自分发) @@ -56,90 +43,44 @@ class RcspTranslationRuntime( /** 解码(或直传 PCM)后的音频上行;source 为 SDK 原值 */ private val onPcm: (source: Int, pcm: ByteArray) -> Unit, private val onError: (code: Int, msg: String?) -> Unit, + /** OPUS 解码 packetSize;mono call=200,stereo=80 */ private val opusPacketSize: Int = if (mode.channel == 2) 80 else 200, ) { - private val translationImpl = TranslationImpl(btManager, NoOpAITranslationApi(), device) - private val isPcmMode = mode.audioType == Constants.AUDIO_TYPE_PCM - private val sampleRateHz = mode.sampleRate.takeIf { it > 0 } ?: 16000 + companion object { + private const val TAG = "RcspTranslationRuntime" + } - /** 入栈解码器:仅 OPUS 模式下创建 */ - private val upDecoder: OpusStreamDecoder? = if (isPcmMode) null else OpusStreamDecoder( - channel = 1, - packetSize = opusPacketSize, - sampleRate = sampleRateHz, - onPcm = { pcm -> onPcm(AudioData.SOURCE_E_SCO_UP_LINK, pcm) }, - onError = { c, m -> onError(c, "upDecoder: $m") }, + /** SDK 的 AI hook 实现:上行解码、下行 TTS 接力都在这里。 */ + private val bridge = JieliAITranslationBridge( + mode = mode, + tempDir = tempDir, + onPcm = onPcm, + onError = onError, + opusPacketSize = opusPacketSize, ) - private val downDecoder: OpusStreamDecoder? = - if (!isPcmMode && mode.mode == TranslationMode.MODE_CALL_TRANSLATION) OpusStreamDecoder( - channel = 1, - packetSize = opusPacketSize, - sampleRate = sampleRateHz, - onPcm = { pcm -> onPcm(AudioData.SOURCE_E_SCO_DOWN_LINK, pcm) }, - onError = { c, m -> onError(c, "downDecoder: $m") }, - ) else null - - private val stereoDecoder: OpusStreamDecoder? = - if (!isPcmMode && mode.mode == TranslationMode.MODE_CALL_TRANSLATION_WITH_STEREO) OpusStreamDecoder( - channel = 2, packetSize = 80, - sampleRate = sampleRateHz, - onPcm = { pcm -> onPcm(AudioData.SOURCE_E_SCO_MIX, pcm) }, - onError = { c, m -> onError(c, "stereoDecoder: $m") }, - ) else null - - /** 调试统计:SDK onReceiveAudioData 被回调多少次、按 (source,type) 分桶 */ - @Volatile private var rxCount = 0L - @Volatile private var rxTypeMismatch = 0L - @Volatile private var rxNullPayload = 0L - @Volatile private var rxLastReportMs = 0L - /** 首帧 flag:首次收到任意 AudioData 时立即打一条 INFO 级别日志(不受 1s 节流影响)。 */ - @Volatile private var rxFirstLogged = false - /** 首帧(按 source 维度):uplink / downlink / mix 分别各打一条,确认链路通畅。 */ - private val rxFirstPerSource = java.util.concurrent.ConcurrentHashMap() + private val translationImpl = TranslationImpl(btManager, bridge, device) + private val isPcmMode = mode.audioType == Constants.AUDIO_TYPE_PCM + /** + * 仅订阅 mode 变化事件用于日志 / 异常上报;不在这里消费音频 + * (音频走 [bridge] 的 `IAITranslationApi.writeAudio`,避免双路重复消费)。 + */ private val translationCallback = object : TranslationCallback { override fun onModeChange(d: BluetoothDevice, m: TranslationMode) { - Log.i(TAG, "[SDK<-DEV] onModeChange addr=${d.address} mode=${m.mode} type=${m.audioType} sr=${m.sampleRate} ch=${m.channel} strategy=${m.recordingStrategy}") + Log.i( + TAG, + "[SDK<-DEV] onModeChange addr=${d.address} mode=${m.mode} type=${m.audioType} " + + "sr=${m.sampleRate} ch=${m.channel} strategy=${m.recordingStrategy}" + ) + if (m.mode == TranslationMode.MODE_IDLE && mode.mode != TranslationMode.MODE_IDLE) { + onError(-1, "headset exited mode=${mode.mode} → MODE_IDLE") + } } override fun onReceiveAudioData(d: BluetoothDevice, data: AudioData) { - rxCount++ - val payloadSize = data.audioData?.size ?: 0 - // 首帧(总):立即打点,避免节流导致"完全静默"误判 - if (!rxFirstLogged) { - rxFirstLogged = true - Log.i(TAG, "[SDK<-DEV] onReceiveAudioData FIRST FRAME addr=${d.address} source=${data.source} type=${data.type} size=$payloadSize expect.type=${mode.audioType}") - } - // 首帧(按 source):分别打点,快速判断上行/下行是否都在推 - if (rxFirstPerSource.putIfAbsent(data.source, true) == null) { - Log.i(TAG, "[SDK<-DEV] onReceiveAudioData FIRST source=${data.source} type=${data.type} size=$payloadSize") - } - val now = System.currentTimeMillis() - if (now - rxLastReportMs >= 1000L) { - Log.d(TAG, "[SDK<-DEV] RX stats (last 1s): total=$rxCount typeMismatch=$rxTypeMismatch nullPayload=$rxNullPayload expect.type=${mode.audioType} last.source=${data.source} last.type=${data.type} payloadSize=$payloadSize") - rxCount = 0; rxTypeMismatch = 0; rxNullPayload = 0; rxLastReportMs = now - } - if (data.type != mode.audioType) { - rxTypeMismatch++ - return - } - val payload = data.audioData - if (payload == null) { - rxNullPayload++ - return - } - if (isPcmMode) { - onPcm(data.source, payload) - return - } - when (data.source) { - AudioData.SOURCE_E_SCO_UP_LINK -> upDecoder?.feedEncoded(payload) - AudioData.SOURCE_E_SCO_DOWN_LINK -> downDecoder?.feedEncoded(payload) - AudioData.SOURCE_E_SCO_MIX -> stereoDecoder?.feedEncoded(payload) - else -> Log.w(TAG, "[SDK<-DEV] onReceiveAudioData unknown source=${data.source} dropped") - } + // 音频走 bridge.writeAudio;这里不消费,避免与 bridge 双路重复解码。 } override fun onError(d: BluetoothDevice, code: Int, msg: String) { @@ -148,129 +89,7 @@ class RcspTranslationRuntime( } } - companion object { - private const val TAG = "RcspTranslationRuntime" - /** 每腿 writeAudioData 缓冲队列上限。整段下发后队列里几乎不会堆积,留 8 个槽足够。 */ - private const val WRITE_QUEUE_LIMIT = 8 - /** 单段 PCM 上限:≈ 60s 16k mono 16bit = 1.92MB,正常 utterance 远低于此值。 */ - private const val PCM_BUFFER_HARD_LIMIT_BYTES = 2 * 1024 * 1024 - /** - * 周期性 flush 间隔:每隔此毫秒数巡检一次 buffer,**只要非空就 flush**。 - * - * 历史实测:200ms 时耳机端 RCSP 解码器会被频繁 reset 直至死机;1s 长期稳定。 - * 当前实验:500ms —— 通话翻译"播报中间断一下"的优化尝试。需真机长时压测 - * (≥10 分钟连续讲话)确认无解码器 reset 风暴,否则回退到 1_000L。 - */ - private const val PERIODIC_FLUSH_MS = 1000L - /** - * 首段优先 flush 阈值(路线 B):每个 utterance 的**首段**达到此毫秒数对应字节量 - * 就立即 flush,不等周期。让用户开口到对方听到的延迟尽可能小(< 200ms)。 - * - * 触发后转入周期模式直到 isFinal=true 重置。火山不发 isFinal 的会话里, - * 只有第一句享受首段优先;后续句统一走 [PERIODIC_FLUSH_MS] 周期。这是为了 - * 避免连续讲话被反复识别成"新 utterance"导致每 100ms 切一段、reset 频率失控。 - */ - private const val FIRST_SEGMENT_FLUSH_MS = 100L - } - - /** - * 串行化的 writeAudioData 调度器(每个 source 一个)。 - * - * RCSP/SPP 写入要求:上一帧 [writeAudioData] 的回调返回(成功或失败)之前不要再发, - * 否则 SDK 会拒收新请求并报 "Operation in progress"。这里维护一个 in-flight ≤ 1 - * 的 FIFO 队列,溢出时丢最老的帧(保实时性,不堆积延迟)。 - */ - private class WriteScheduler( - private val tag: String, - private val send: (AudioData, OnRcspActionCallback) -> Unit, - ) { - private val queue = ArrayDeque(WRITE_QUEUE_LIMIT) - private val inFlight = AtomicBoolean(false) - private val lock = Any() - @Volatile private var droppedSinceReport = 0L - @Volatile private var lastReportMs = 0L - - fun enqueue(data: AudioData) { - val toSend: AudioData? = synchronized(lock) { - if (inFlight.compareAndSet(false, true)) { - data - } else { - if (queue.size >= WRITE_QUEUE_LIMIT) { - queue.pollFirst() // 丢最老 - droppedSinceReport++ - val now = System.currentTimeMillis() - if (now - lastReportMs >= 1000L) { - Log.w(TAG, "writeAudioData[$tag] queue full, dropped=$droppedSinceReport (last 1s)") - droppedSinceReport = 0 - lastReportMs = now - } - } - queue.offerLast(data) - null - } - } - if (toSend != null) dispatch(toSend) - } - - private fun dispatch(data: AudioData) { - send(data, object : OnRcspActionCallback { - override fun onSuccess(d: BluetoothDevice?, ok: Boolean?) = onComplete() - override fun onError(d: BluetoothDevice?, err: com.jieli.bluetooth.bean.base.BaseError?) { - val msg = err?.message.orEmpty() - if (!msg.contains("Operation in progress", ignoreCase = true)) { - Log.w(TAG, "writeAudioData[$tag] error: code=${err?.code} msg=$msg") - } - onComplete() - } - }) - } - - private fun onComplete() { - val next: AudioData? = synchronized(lock) { - val n = queue.pollFirst() - if (n == null) inFlight.set(false) - n - } - if (next != null) dispatch(next) - } - - fun clear() { - synchronized(lock) { - queue.clear() - inFlight.set(false) - } - } - } - - private val uplinkWriter = WriteScheduler("uplink") { data, cb -> - translationImpl.writeAudioData(data, cb) - } - private val downlinkWriter = WriteScheduler("downlink") { data, cb -> - translationImpl.writeAudioData(data, cb) - } - private val phoneMicWriter = WriteScheduler("phoneMic") { data, cb -> - translationImpl.writeAudioData(data, cb) - } - /** 兜底:PCM 直传 / 未识别 source 走这条;和 OPUS 编码器互不抢 in-flight 槽位。 */ - private val rawWriter = WriteScheduler("raw") { data, cb -> - translationImpl.writeAudioData(data, cb) - } - - /** - * Per-leg PCM 缓冲。`feedTtsPcm` 累积写入,`isFinal=true` 时整段编码下发后清空。 - * - * key = outputStreamId(OUT_UPLINK / OUT_DOWNLINK / OUT_SPEAKER 等) - */ - private val pcmBuffers = mutableMapOf() - /** 每条 leg 是否仍处于"等待首段触发"状态(路线 B 首段优先 flush 用)。 */ - private val firstSegmentPending = mutableMapOf() - private val bufferLock = Any() - private var encodeSeq = 0L - - /** 周期性 flush 巡检线程;start() 启动,stop() 关闭。 */ - private var flushWatcher: ScheduledExecutorService? = null - - /** 启动前置校验 + 进入翻译模式 */ + /** 启动前置校验 + 进入翻译模式。 */ fun start(): Result { if (!translationImpl.isInit) { return Result.failure(IllegalStateException("RCSP not init for ${device.address}")) @@ -285,172 +104,31 @@ class RcspTranslationRuntime( } if (!tempDir.exists()) tempDir.mkdirs() - upDecoder?.start() - downDecoder?.start() - stereoDecoder?.start() - translationImpl.addTranslationCallback(translationCallback) - Log.i(TAG, "[APP->SDK] enterMode addr=${device.address} mode=${mode.mode} type=${mode.audioType} sr=${mode.sampleRate} ch=${mode.channel} strategy=${mode.recordingStrategy} (waiting for onModeChange/onReceiveAudioData)") + Log.i( + TAG, + "[APP->SDK] enterMode addr=${device.address} mode=${mode.mode} type=${mode.audioType} " + + "sr=${mode.sampleRate} ch=${mode.channel} strategy=${mode.recordingStrategy} " + + "(SDK will drive bridge.startTranslating)" + ) translationImpl.enterMode(mode, translationCallback) - - // OPUS 模式启动周期性 flush 巡检:每 [PERIODIC_FLUSH_MS] 切一次缓冲。 - if (!isPcmMode) startPeriodicFlushWatcher() return Result.success(Unit) } - private fun startPeriodicFlushWatcher() { - flushWatcher?.shutdownNow() - val exec = Executors.newSingleThreadScheduledExecutor { r -> - Thread(r, "rcsp-tts-flush").apply { isDaemon = true } - } - flushWatcher = exec - exec.scheduleAtFixedRate( - { runCatching { periodicFlush() } }, - PERIODIC_FLUSH_MS, PERIODIC_FLUSH_MS, TimeUnit.MILLISECONDS, - ) - } - - /** 每秒巡检:每条 leg 的 buffer 只要非空就立即切走 + 编码 + 下发(不依赖任何外部信号)。 */ - private fun periodicFlush() { - val toEncode = mutableListOf>() - synchronized(bufferLock) { - for ((leg, buf) in pcmBuffers) { - if (buf.size() == 0) continue - val source = when (leg) { - TranslationStreams.OUT_UPLINK -> AudioData.SOURCE_E_SCO_UP_LINK - TranslationStreams.OUT_DOWNLINK -> AudioData.SOURCE_E_SCO_DOWN_LINK - else -> AudioData.SOURCE_PHONE_MIC - } - val out = buf.toByteArray() - buf.reset() - Log.d(TAG, "periodicFlush: leg=$leg bytes=${out.size}") - toEncode.add(Triple(source, leg, out)) - } - } - // encode 走 async,必须释放锁后做。 - for ((source, leg, pcm) in toEncode) { - encodeAndDispatchAsync(source, leg, pcm) - } - } - /** - * 把外部翻译服务回送的 PCM 注入回耳机。 + * 把外部翻译服务回送的 PCM 接力给 SDK。 * - * 策略: - * - PCM 模式:每次调用直接当作一段 AudioData 下发(SDK 内部按 blockMtu 分片)。 - * - OPUS 模式:纯时间驱动 flush —— 火山等端到端服务的段尾事件不可靠, - * 不能依赖 isFinal 或字节量阈值。改为: - * a) feedTtsPcm 仅追加 buffer,不主动 flush(除非 isFinal=true); - * b) [PERIODIC_FLUSH_MS] (1s) 巡检线程每秒切走 buffer 并整段编码下发; - * c) `isFinal=true` 仍短路立即 flush(兼容主动信号,不再依赖)。 - * 最大听感延迟 ≤ 1s,且短句 / 没有段尾事件的服务都不会卡死。 + * 行为完全委托给 [JieliAITranslationBridge.feedTtsPcm]: + * - PCM 模式:每次调用作为一段 [AudioData] 立即交付给 SDK; + * - OPUS 模式:累积,`isFinal=true` 时整段编码 → `onTranslateResult` 交给 SDK。 * - * @param outputStreamId 决定 source: - * [TranslationStreams.OUT_UPLINK] / [TranslationStreams.OUT_DOWNLINK] 用于通话翻译; - * 其它(speaker/localPlayback)由 ModeHandler 自己处理,不会落到这里。 - * @param isFinal 本帧是否为当前 utterance 的最后一帧;触发 buffer 残余立即 flush。 + * 调用方契约:**必须**在 utterance 末尾调一次 `isFinal=true`,否则音频会一直累积 + * 到 2MB 兜底上限才出。 */ - fun feedTtsPcm(outputStreamId: String, pcm: ByteArray, isFinal: Boolean): Boolean { - val source = when (outputStreamId) { - TranslationStreams.OUT_UPLINK -> AudioData.SOURCE_E_SCO_UP_LINK - TranslationStreams.OUT_DOWNLINK -> AudioData.SOURCE_E_SCO_DOWN_LINK - else -> AudioData.SOURCE_PHONE_MIC - } - if (isPcmMode) { - // PCM 直传:每次调用一段 AudioData。`isFinal` 在该模式下不影响下发节奏。 - writeBack(AudioData(source, Constants.AUDIO_TYPE_PCM, pcm)) - return true - } - // OPUS 模式:累积到 buffer,三条 flush 触发链(按优先级): - // 1. isFinal=true → 立即 flush,并把首段优先重置回 true(下一句新算) - // 2. 首段优先(路线 B) → 当前 leg 处于 firstSegmentPending=true 且 buffer - // 累积到 FIRST_SEGMENT_FLUSH_MS 字节量时立即 flush, - // 让"开口到对方听见"的延迟最小化(< 200ms) - // 3. 周期 flush(路线 A) → 由 [PERIODIC_FLUSH_MS] 巡检线程接管 - val pendingFlush: ByteArray? = synchronized(bufferLock) { - val buf = pcmBuffers.getOrPut(outputStreamId) { ByteArrayOutputStream() } - if (pcm.isNotEmpty()) { - if (buf.size() + pcm.size > PCM_BUFFER_HARD_LIMIT_BYTES) { - Log.w(TAG, "feedTtsPcm: leg=$outputStreamId pcm buffer hit hard limit ${PCM_BUFFER_HARD_LIMIT_BYTES}, dropping segment") - buf.reset() - return false - } - buf.write(pcm) - } - // 1) 段尾信号:立即下发尾段并把首段标志重置回 true。 - if (isFinal) { - firstSegmentPending[outputStreamId] = true - val out = buf.toByteArray() - buf.reset() - return@synchronized if (out.isEmpty()) null else out - } - // 2) 首段优先(路线 B):当前**实验中临时屏蔽**——为观察 500ms 周期 flush 的纯效果。 - // 屏蔽后所有 flush 走两条路:a) isFinal=true 短路;b) PERIODIC_FLUSH_MS 周期。 - // 实验完成后若需恢复,把下面 if (false) 改回 if (pending) 即可。 - @Suppress("ConstantConditionIf") - if (false) { - val pending = firstSegmentPending.getOrDefault(outputStreamId, true) - if (pending) { - val firstFlushBytes = (sampleRateHz * 2 * FIRST_SEGMENT_FLUSH_MS / 1000L).toInt() - if (buf.size() >= firstFlushBytes) { - firstSegmentPending[outputStreamId] = false - val out = buf.toByteArray() - buf.reset() - Log.d(TAG, "firstSegmentFlush: leg=$outputStreamId bytes=${out.size}") - return@synchronized if (out.isEmpty()) null else out - } - } - } - null - } - if (pendingFlush != null) { - encodeAndDispatchAsync(source, outputStreamId, pendingFlush) - } - return true - } - - private fun encodeAndDispatchAsync(source: Int, leg: String, pcmBytes: ByteArray) { - val seq = synchronized(bufferLock) { ++encodeSeq } - val pcmFile = File(tempDir, "tts_${source}_$seq.pcm") - val opusFile = File(tempDir, "tts_${source}_$seq.opus") - try { - pcmFile.writeBytes(pcmBytes) - } catch (e: Throwable) { - Log.w(TAG, "encodeAndDispatch leg=$leg seq=$seq write pcm file failed: ${e.message}") - return - } - // OpusManager.encodeFile(in, out, callback) 默认 OpusOption(带 head),与 demo 一致。 - val encoder = OpusManager() - encoder.encodeFile(pcmFile.absolutePath, opusFile.absolutePath, object : OnStateCallback { - override fun onStart() {} - override fun onComplete(path: String?) { - try { - val opusBytes = if (opusFile.exists()) opusFile.readBytes() else null - if (opusBytes == null || opusBytes.isEmpty()) { - Log.w(TAG, "encodeAndDispatch leg=$leg seq=$seq: empty opus output") - } else { - Log.d(TAG, "encodeAndDispatch leg=$leg seq=$seq pcm=${pcmBytes.size}B opus=${opusBytes.size}B") - writeBack(AudioData(source, Constants.AUDIO_TYPE_OPUS, opusBytes)) - } - } finally { - runCatching { encoder.release() } - runCatching { pcmFile.delete() } - runCatching { opusFile.delete() } - } - } - override fun onError(code: Int, message: String?) { - Log.w(TAG, "encodeAndDispatch leg=$leg seq=$seq encodeFile error code=$code msg=$message") - runCatching { encoder.release() } - runCatching { pcmFile.delete() } - runCatching { opusFile.delete() } - onError(code, "encodeFile $leg: $message") - } - }) - } + fun feedTtsPcm(outputStreamId: String, pcm: ByteArray, isFinal: Boolean): Boolean = + bridge.feedTtsPcm(outputStreamId, pcm, isFinal) fun stop() { - runCatching { flushWatcher?.shutdownNow() } - flushWatcher = null runCatching { Log.i(TAG, "[APP->SDK] exitMode addr=${device.address} mode=${mode.mode}") translationImpl.exitMode(object : OnRcspActionCallback { @@ -463,34 +141,8 @@ class RcspTranslationRuntime( }) } runCatching { translationImpl.removeTranslationCallback(translationCallback) } + // bridge 由 SDK 在 exitMode 后通过 stopTranslating 自动 release decoders; + // 这里不重复调,避免双重 stop。 runCatching { translationImpl.destroy() } - runCatching { upDecoder?.stop() } - runCatching { downDecoder?.stop() } - runCatching { stereoDecoder?.stop() } - runCatching { - uplinkWriter.clear() - downlinkWriter.clear() - phoneMicWriter.clear() - rawWriter.clear() - } - synchronized(bufferLock) { - pcmBuffers.values.forEach { it.reset() } - pcmBuffers.clear() - firstSegmentPending.clear() - } - } - - /** - * 把 OPUS / PCM 帧入队到对应 source 的 [WriteScheduler],由调度器串行发起 RCSP - * writeAudioData,避免并发写入触发 "Operation in progress"。 - */ - private fun writeBack(data: AudioData) { - val scheduler = when (data.source) { - AudioData.SOURCE_E_SCO_UP_LINK -> uplinkWriter - AudioData.SOURCE_E_SCO_DOWN_LINK -> downlinkWriter - AudioData.SOURCE_PHONE_MIC -> phoneMicWriter - else -> rawWriter - } - scheduler.enqueue(data) } } diff --git a/local_plugins/device_jieli/example/android/app/libs/jl_bluetooth_rcsp_V4.2.0_beta1_40255_20260324.aar b/local_plugins/device_jieli/example/android/app/libs/jl_bluetooth_rcsp_V4.2.0_beta1_40255_20260324.aar new file mode 100644 index 000000000..0ff72f1c6 Binary files /dev/null and b/local_plugins/device_jieli/example/android/app/libs/jl_bluetooth_rcsp_V4.2.0_beta1_40255_20260324.aar differ diff --git a/local_plugins/device_jieli/example/android/app/libs/jl_bluetooth_rcsp_V4.2.0_beta2_40214_20251224.aar b/local_plugins/device_jieli/example/android/app/libs/jl_bluetooth_rcsp_V4.2.0_beta2_40214_20251224.aar deleted file mode 100644 index 15eb8cf5e..000000000 Binary files a/local_plugins/device_jieli/example/android/app/libs/jl_bluetooth_rcsp_V4.2.0_beta2_40214_20251224.aar and /dev/null differ diff --git a/local_plugins/device_jieli/ios/Classes/Audio/OpusStreamCodec.swift b/local_plugins/device_jieli/ios/Classes/Audio/OpusStreamCodec.swift deleted file mode 100644 index 1f2c3ed43..000000000 --- a/local_plugins/device_jieli/ios/Classes/Audio/OpusStreamCodec.swift +++ /dev/null @@ -1,181 +0,0 @@ -import Foundation - -#if canImport(JLAudioUnitKit) -import JLAudioUnitKit -#endif - -/// Streaming OPUS decoder + encoder built on top of `JLAudioUnitKit`. -/// -/// Mirrors the Android `audio/OpusStreamCodec.kt` (decoder side). The codec -/// pushes 16 kHz / 16-bit / mono / 20 ms PCM frames through the supplied -/// callback, matching the Android contract used by `RcspTranslationRuntime`. -/// -/// Important: `JLOpusDecoder.opusDecoderInputData(_:)` accepts **one OPUS -/// packet at a time**. The headset advertises packets at 40 bytes for 16k/20ms; -/// pass the byte stream directly — the decoder does not buffer. If the -/// upstream feed combines multiple packets in one chunk, we slice along -/// `packetSize` to keep the decoder happy. -final class OpusStreamDecoder: NSObject { - - let sampleRate: Int - let channels: Int - let packetSize: Int - - private let onPcm: (Data) -> Void - private let onError: (Int, String?) -> Void - - #if canImport(JLAudioUnitKit) - private var decoder: JLOpusDecoder? - #endif - private var started = false - private let lock = NSLock() - - init( - channels: Int = 1, - packetSize: Int = 40, - sampleRate: Int = 16000, - onPcm: @escaping (Data) -> Void, - onError: @escaping (Int, String?) -> Void = { _, _ in } - ) { - self.channels = channels - self.packetSize = packetSize - self.sampleRate = sampleRate - self.onPcm = onPcm - self.onError = onError - } - - func start() { - lock.lock(); defer { lock.unlock() } - if started { return } - #if canImport(JLAudioUnitKit) - let format = JLOpusFormat.defaultFormats() - format.sampleRate = Int32(sampleRate) - format.channels = Int32(channels) - format.frameDuration = 20 - // The headset's stream is the JieLi-flavored OPUS (no header). The - // demo's `defaultFormats` already disables the header for the headset - // direction, so we keep that default. - format.dataSize = Int32(packetSize) - decoder = JLOpusDecoder(decoder: format, delegate: self) - #endif - started = true - } - - func stop() { - lock.lock(); defer { lock.unlock() } - if !started { return } - started = false - #if canImport(JLAudioUnitKit) - decoder?.opusOnRelease() - decoder = nil - #endif - } - - /// Feed one or more OPUS packets concatenated. The decoder is fed one - /// `packetSize`-sized chunk at a time when the input length is a multiple - /// of `packetSize`; otherwise the entire buffer is forwarded as-is and - /// the decoder does its own resync. - func feedEncoded(_ data: Data) { - #if canImport(JLAudioUnitKit) - guard started, let dec = decoder else { return } - if packetSize > 0 && data.count > packetSize && data.count % packetSize == 0 { - var offset = 0 - while offset < data.count { - let end = min(offset + packetSize, data.count) - dec.opusDecoderInputData(data.subdata(in: offset.. Void - private let onError: (Int, String?) -> Void - - #if canImport(JLAudioUnitKit) - private var encoder: JLOpusEncoder? - #endif - private var started = false - private let lock = NSLock() - - init( - channels: Int = 1, - sampleRate: Int = 16000, - onEncoded: @escaping (Data) -> Void, - onError: @escaping (Int, String?) -> Void = { _, _ in } - ) { - self.sampleRate = sampleRate - self.channels = channels - self.onEncoded = onEncoded - self.onError = onError - } - - func start() { - lock.lock(); defer { lock.unlock() } - if started { return } - #if canImport(JLAudioUnitKit) - // Use the JieLi default config (no OGG header — same as the headset - // expects, matches the demo's `JLOpusEncodeConfig.default()`). - let cfg = JLOpusEncodeConfig.default() - cfg.sampleRate = Int32(sampleRate) - cfg.channels = Int32(channels) - cfg.frameDuration = 20 - encoder = JLOpusEncoder(format: cfg, delegate: self) - #endif - started = true - } - - func stop() { - lock.lock(); defer { lock.unlock() } - if !started { return } - started = false - #if canImport(JLAudioUnitKit) - encoder?.opusOnRelease() - encoder = nil - #endif - } - - /// Feed one or more raw PCM bytes (16-bit LE, `channels` interleaved when >1). - func feedPcm(_ pcm: Data) { - #if canImport(JLAudioUnitKit) - guard started, let enc = encoder else { return } - enc.opusEncode(pcm) - #endif - } -} - -#if canImport(JLAudioUnitKit) -extension OpusStreamEncoder: JLOpusEncoderDelegate { - func opusEncoder(_ encoder: JLOpusEncoder, data: Data?, error: Error?) { - if let err = error { - onError((err as NSError).code, err.localizedDescription) - return - } - guard let opus = data, !opus.isEmpty else { return } - onEncoded(opus) - } -} -#endif diff --git a/local_plugins/device_jieli/ios/Classes/Audio/PcmKit.swift b/local_plugins/device_jieli/ios/Classes/Audio/PcmKit.swift deleted file mode 100644 index b4197e24e..000000000 --- a/local_plugins/device_jieli/ios/Classes/Audio/PcmKit.swift +++ /dev/null @@ -1,27 +0,0 @@ -import Foundation - -/// PCM helpers (16-bit signed little-endian). -/// -/// Mirrors Android `audio/PcmKit.kt` — stereo split / mono mix utilities used -/// by the stereo call-translation mode handler. -enum PcmKit { - - /// Split an interleaved 16-bit LE stereo buffer into two mono buffers - /// (left, right). Each output has half the input byte length. - static func splitStereo16(_ stereo: Data) -> (Data, Data) { - var left = Data(); left.reserveCapacity(stereo.count / 2) - var right = Data(); right.reserveCapacity(stereo.count / 2) - let count = stereo.count - (stereo.count % 4) - var i = 0 - stereo.withUnsafeBytes { (raw: UnsafeRawBufferPointer) in - let p = raw.bindMemory(to: UInt8.self).baseAddress! - while i < count { - // L = bytes[i..i+1], R = bytes[i+2..i+3] - left.append(p[i]); left.append(p[i + 1]) - right.append(p[i + 2]); right.append(p[i + 3]) - i += 4 - } - } - return (left, right) - } -} diff --git a/local_plugins/device_jieli/ios/Classes/Bridge/EventDispatcher.swift b/local_plugins/device_jieli/ios/Classes/Bridge/EventDispatcher.swift deleted file mode 100644 index c50c656c9..000000000 --- a/local_plugins/device_jieli/ios/Classes/Bridge/EventDispatcher.swift +++ /dev/null @@ -1,110 +0,0 @@ -import Flutter -import Foundation - -/// Native-side event subscriber. Mirrors Android `JieliEventListener`. -/// Implementers receive the same `payload` dictionary that gets pushed to -/// the Flutter EventChannel, so native and Dart subscribers stay symmetric. -/// -/// Pure-Swift protocol (not `@objc`) because the payload's `[String: Any?]` -/// cannot be bridged to Obj-C (Obj-C dictionary values can't be nullable). -protocol JieliEventListener: AnyObject { - func onEvent(payload: [String: Any?]) -} - -/// Event bus. All forwarders / features call [send]; this fans out to: -/// - Flutter side: through the EventChannel sink (on main queue); -/// - Native side: through registered [JieliEventListener]s (sync, on the -/// originating queue — fits high-frequency audio frames). -/// -/// Mirrors Android `bridge/EventDispatcher.kt`. -class EventDispatcher: NSObject, FlutterStreamHandler { - - private let lock = NSLock() - private var sink: FlutterEventSink? - private var listeners: [JieliEventListener] = [] - - // MARK: - FlutterStreamHandler - - func onListen( - withArguments arguments: Any?, - eventSink events: @escaping FlutterEventSink - ) -> FlutterError? { - lock.lock(); defer { lock.unlock() } - sink = events - return nil - } - - func onCancel(withArguments arguments: Any?) -> FlutterError? { - lock.lock(); defer { lock.unlock() } - sink = nil - return nil - } - - // MARK: - Native listeners - - func addNativeListener(_ listener: JieliEventListener) { - lock.lock(); defer { lock.unlock() } - if !listeners.contains(where: { $0 === listener }) { - listeners.append(listener) - } - } - - func removeNativeListener(_ listener: JieliEventListener) { - lock.lock(); defer { lock.unlock() } - listeners.removeAll { $0 === listener } - } - - // MARK: - Send - - /// Broadcast a single event payload. - /// - /// Payload schema: identical to Android. `type` is the discriminator key - /// that the Dart `Jielihome._parseEvent(...)` switches on. - func send(_ payload: [String: Any?]) { - let snapshot: [JieliEventListener] - let sinkRef: FlutterEventSink? - lock.lock() - snapshot = listeners - sinkRef = sink - lock.unlock() - - for l in snapshot { - l.onEvent(payload: payload) - } - - guard let s = sinkRef else { return } - let sanitized = sanitize(payload) - if Thread.isMainThread { - s(sanitized) - } else { - DispatchQueue.main.async { s(sanitized) } - } - } - - /// Replace `nil` values with `NSNull()` and re-emit `Data` payloads as - /// `FlutterStandardTypedData.bytes(...)` so Dart receives `Uint8List` - /// rather than an opaque blob (matches Android's ByteArray → Uint8List - /// auto-conversion). - private func sanitize(_ raw: [String: Any?]) -> [String: Any] { - var out = [String: Any]() - out.reserveCapacity(raw.count) - for (k, v) in raw { - out[k] = sanitize(value: v) - } - return out - } - - private func sanitize(value: Any?) -> Any { - guard let v = value else { return NSNull() } - if let data = v as? Data { - return FlutterStandardTypedData(bytes: data) - } - if let arr = v as? [Any?] { - return arr.map { sanitize(value: $0) } - } - if let map = v as? [String: Any?] { - return sanitize(map) - } - return v - } -} diff --git a/local_plugins/device_jieli/ios/Classes/Bridge/MethodRouter.swift b/local_plugins/device_jieli/ios/Classes/Bridge/MethodRouter.swift deleted file mode 100644 index 8ab3394bf..000000000 --- a/local_plugins/device_jieli/ios/Classes/Bridge/MethodRouter.swift +++ /dev/null @@ -1,327 +0,0 @@ -import Flutter -import Foundation -import UIKit - -/// Method-channel router. Maps every Dart call to the matching feature module. -/// -/// Symmetric with Android `bridge/MethodRouter.kt` — same method names, same -/// argument keys. Where the iOS JieLi SDK does not yet have a wired -/// implementation, the handler returns a `NOT_IMPLEMENTED_IOS` error so the -/// Dart layer can downgrade gracefully (instead of crashing). -class MethodRouter { - - private let server: JieliHomeServer - - init(server: JieliHomeServer) { - self.server = server - } - - func handle(call: FlutterMethodCall, result: @escaping FlutterResult) { - let args = call.arguments as? [String: Any] ?? [:] - do { - switch call.method { - - // MARK: - lifecycle / platform - case "getPlatformVersion": - result("iOS \(UIDevice.current.systemVersion)") - - case "initialize": - try server.initialize( - multiDevice: (args["multiDevice"] as? Bool) ?? true, - skipNoNameDev: (args["skipNoNameDev"] as? Bool) ?? false, - enableLog: (args["enableLog"] as? Bool) ?? false - ) - result(true) - - // MARK: - scan - case "startScan": - let timeoutMs = (args["timeoutMs"] as? Int) ?? 30000 - let nameList = (args["nameList"] as? [String]) ?? [] - let uuidList = (args["uuidList"] as? [String]) ?? [] - let skipUnnamed = (args["skipUnnamed"] as? Bool) ?? true - try server.scanFeature.startScan( - timeoutMs: timeoutMs, - nameList: nameList, - uuidList: uuidList, - skipUnnamed: skipUnnamed - ) - result(true) - - case "stopScan": - server.scanFeature.stopScan() - result(true) - - case "isScanning": - result(server.scanFeature.isScanning()) - - // MARK: - connect - case "connect": - guard let address = args["address"] as? String else { - result(FlutterError(code: "BAD_ARG", message: "address required", details: nil)); return - } - try server.connectFeature.connect( - bleAddress: address, - edrAddress: args["edrAddr"] as? String, - deviceType: (args["deviceType"] as? Int) ?? -1, - connectWay: (args["connectWay"] as? Int) ?? 0 - ) - result(true) - - case "disconnect": - guard let address = args["address"] as? String else { - result(FlutterError(code: "BAD_ARG", message: "address required", details: nil)); return - } - try server.connectFeature.disconnect(address: address) - result(true) - - case "isConnected": - guard let address = args["address"] as? String else { - result(FlutterError(code: "BAD_ARG", message: "address required", details: nil)); return - } - result(server.connectFeature.isConnected(address: address)) - - case "connectedDevice": - if let info = server.connectFeature.connectedDeviceInfo() { - result(info) - } else { - result(nil) - } - - // MARK: - device info - case "deviceSnapshot": - guard let address = args["address"] as? String else { - result(FlutterError(code: "BAD_ARG", message: "address required", details: nil)); return - } - result(server.deviceInfoFeature.snapshot(address: address)) - - case "queryTargetInfo": - guard let address = args["address"] as? String else { - result(FlutterError(code: "BAD_ARG", message: "address required", details: nil)); return - } - let mask = (args["mask"] as? Int) ?? 0x0F - server.deviceInfoFeature.queryTargetInfo(address: address, mask: mask) { snapshot, error in - if let snapshot = snapshot { - result(snapshot) - } else { - result(FlutterError( - code: "TARGET_INFO_ERR", - message: error?.localizedDescription ?? "queryTargetInfo failed", - details: nil - )) - } - } - - // MARK: - custom RCSP - case "sendCustomCmd": - guard let address = args["address"] as? String else { - result(FlutterError(code: "BAD_ARG", message: "address required", details: nil)); return - } - guard let opCode = args["opCode"] as? Int else { - result(FlutterError(code: "BAD_ARG", message: "opCode required", details: nil)); return - } - let payloadBytes: [UInt8] - if let typed = args["payload"] as? FlutterStandardTypedData { - payloadBytes = [UInt8](typed.data) - } else if let arr = args["payload"] as? [Int] { - payloadBytes = arr.map { UInt8(truncatingIfNeeded: $0) } - } else { - payloadBytes = [] - } - server.customCmdFeature.send( - address: address, - opCode: UInt8(truncatingIfNeeded: opCode), - payload: Data(payloadBytes) - ) { response, error in - if let resp = response { - result([UInt8](resp)) - } else { - result(FlutterError( - code: "CUSTOM_CMD_ERR", - message: error?.localizedDescription ?? "custom cmd failed", - details: nil - )) - } - } - - // MARK: - translation - case "startTranslation": - guard let modeId = args["modeId"] as? Int else { - result(FlutterError(code: "BAD_ARG", message: "modeId required", details: nil)); return - } - let extra = (args["args"] as? [String: Any]) ?? [:] - if server.deviceRecordFeature.isRecording() { server.deviceRecordFeature.stop() } - try server.translationFeature.start(modeId: modeId, args: extra) - result(true) - - case "stopTranslation": - server.translationFeature.stop() - result(true) - - case "translationStatus": - result([ - "working": server.translationFeature.isWorking(), - "modeId": server.translationFeature.currentModeId() as Any, - "inputStreams": server.translationFeature.currentInputStreams(), - "outputStreams": server.translationFeature.currentOutputStreams(), - ] as [String: Any]) - - case "feedTranslatedAudio": - guard let streamId = args["streamId"] as? String else { - result(FlutterError(code: "BAD_ARG", message: "streamId required", details: nil)); return - } - let pcm: Data - if let typed = args["pcm"] as? FlutterStandardTypedData { - pcm = typed.data - } else if let arr = args["pcm"] as? [Int] { - pcm = Data(arr.map { UInt8(truncatingIfNeeded: $0) }) - } else { - result(FlutterError(code: "BAD_ARG", message: "pcm required", details: nil)); return - } - let ok = server.translationFeature.feedTranslatedAudio( - streamId: streamId, - pcm: pcm, - sampleRate: (args["sampleRate"] as? Int) ?? 16000, - channels: (args["channels"] as? Int) ?? 1, - bitsPerSample: (args["bitsPerSample"] as? Int) ?? 16, - isFinal: (args["final"] as? Bool) ?? false - ) - result(ok) - - case "feedTranslationResult": - server.translationFeature.feedTranslationResult( - srcLang: args["srcLang"] as? String, - srcText: args["srcText"] as? String, - destLang: args["destLang"] as? String, - destText: args["destText"] as? String, - requestId: args["requestId"] as? String - ) - result(true) - - case "isSupportCallTranslationWithStereo": - let address = args["address"] as? String - result(server.translationFeature.isSupportCallTranslationWithStereo(address: address)) - - case "feedAudioFilePcm": - let pcm: Data - if let typed = args["pcm"] as? FlutterStandardTypedData { - pcm = typed.data - } else if let arr = args["pcm"] as? [Int] { - pcm = Data(arr.map { UInt8(truncatingIfNeeded: $0) }) - } else { - result(FlutterError(code: "BAD_ARG", message: "pcm required", details: nil)); return - } - let sampleRate = (args["sampleRate"] as? Int) ?? 16000 - result(server.translationFeature.feedAudioFilePcm(pcm: pcm, sampleRate: sampleRate)) - - // MARK: - speech (PTT / wake-word) - case "speechIsRecording": - result(server.speechFeature.isRecording(address: args["address"] as? String)) - - case "speechStart": - server.speechFeature.start( - address: args["address"] as? String, - voiceType: (args["voiceType"] as? Int) ?? SpeechFeature.voiceTypeOpus, - sampleRate: (args["sampleRate"] as? Int) ?? SpeechFeature.sampleRate16k, - vadWay: (args["vadWay"] as? Int) ?? SpeechFeature.vadWayDevice - ) { ok, message in - if ok { result(true) } - else { - result(FlutterError(code: "SPEECH_START_ERR", message: message, details: nil)) - } - } - - case "speechStop": - server.speechFeature.stop( - address: args["address"] as? String, - reason: (args["reason"] as? Int) ?? SpeechFeature.reasonNormal - ) { ok, message in - if ok { result(true) } - else { - result(FlutterError(code: "SPEECH_STOP_ERR", message: message, details: nil)) - } - } - - // MARK: - assistant always-recording - case "assistantStart": - if server.translationFeature.isWorking() { server.translationFeature.stop() } - if server.deviceRecordFeature.isRecording() { server.deviceRecordFeature.stop() } - let ok = server.assistantBridge.start() - if ok { result(true) } - else { - result(FlutterError(code: "ASSISTANT_START_ERR", - message: "assistant bridge start failed", details: nil)) - } - - case "assistantStop": - server.assistantBridge.stop() - result(true) - - case "assistantIsRunning": - result(server.assistantBridge.isRunning()) - - // MARK: - device record - case "deviceRecordStart": - let nested = (args["args"] as? [String: Any]) ?? [:] - if server.translationFeature.isWorking() { server.translationFeature.stop() } - try server.deviceRecordFeature.start(args: nested) - result(true) - - case "deviceRecordStop": - server.deviceRecordFeature.stop() - result(true) - - case "deviceRecordStatus": - result(["recording": server.deviceRecordFeature.isRecording()]) - - // MARK: - OTA - case "otaStart": - guard let path = args["firmwareFilePath"] as? String else { - result(FlutterError(code: "BAD_ARG", message: "firmwareFilePath required", details: nil)); return - } - let address = args["address"] as? String - let blockSize = (args["blockSize"] as? Int) ?? 512 - let fileFlag: Data? - if let typed = args["fileFlag"] as? FlutterStandardTypedData { - fileFlag = typed.data - } else if let arr = args["fileFlag"] as? [Int] { - fileFlag = Data(arr.map { UInt8(truncatingIfNeeded: $0) }) - } else { - fileFlag = nil - } - server.otaFeature.start( - address: address, - firmwareFilePath: path, - blockSize: blockSize, - fileFlag: fileFlag - ) - result(true) - - case "otaCancel": - server.otaFeature.cancel() - result(true) - - case "otaIsRunning": - result(server.otaFeature.isRunning()) - - default: - result(FlutterMethodNotImplemented) - } - } catch let err as PluginError { - result(FlutterError(code: err.code, message: err.message, details: nil)) - } catch { - result(FlutterError(code: "PLUGIN_ERR", message: error.localizedDescription, details: nil)) - } - } -} - -/// Local plugin error wrapping that maps cleanly onto FlutterError. Used by -/// feature classes when they need to surface a typed error code without -/// throwing an opaque NSError. -struct PluginError: Error { - let code: String - let message: String - init(_ code: String, _ message: String) { - self.code = code - self.message = message - } -} diff --git a/local_plugins/device_jieli/ios/Classes/Core/JieliHomeServer.swift b/local_plugins/device_jieli/ios/Classes/Core/JieliHomeServer.swift deleted file mode 100644 index 62b23efab..000000000 --- a/local_plugins/device_jieli/ios/Classes/Core/JieliHomeServer.swift +++ /dev/null @@ -1,177 +0,0 @@ -import Foundation -import CoreBluetooth - -#if canImport(JL_BLEKit) -import JL_BLEKit -#endif - -/// Process-wide singleton mirroring Android `core/JieliHomeServer.kt`. -/// -/// All features (scan, connect, info, custom-cmd, translation, speech, -/// device-record, OTA, assistant) hang off this server. Both the Flutter side -/// (via `JielihomePlugin`) and any pure-iOS host code share the same instance. -/// -/// Initialization is idempotent — calling [initialize] twice returns -/// immediately on the second call. -class JieliHomeServer { - - static let shared = JieliHomeServer() - - private(set) public var initialized: Bool = false - - /// Event bus shared by Flutter EventChannel + native listeners. - let dispatcher: EventDispatcher = EventDispatcher() - - // MARK: - Underlying SDK references - // The JieLi iOS SDK exposes BLE plumbing via `JL_BLEMultiple` and per-device - // command management via `JL_ManagerM` (held inside each `JL_EntityM`). - // We keep one shared `JL_BLEMultiple` for scan/connect across features. - #if canImport(JL_BLEKit) - internal let bleMultiple: JL_BLEMultiple = JL_BLEMultiple() - #endif - - /// Shared coordinator owning the per-device-session - /// `JLTranslationManager`. Holds the active translation sink (call - /// translation / assistant / device record); modules switch sinks as they - /// enter/exit modes — only one mode at a time. - private(set) lazy var translationCoordinator: TranslationCoordinator = { - TranslationCoordinator(server: self) - }() - - // MARK: - Features (lazy-attached after initialize) - private(set) lazy var scanFeature: ScanFeature = { - ScanFeature(dispatcher: dispatcher) - }() - private(set) lazy var connectFeature: ConnectFeature = { - ConnectFeature(server: self) - }() - private(set) lazy var deviceInfoFeature: DeviceInfoFeature = { - DeviceInfoFeature(server: self) - }() - private(set) lazy var customCmdFeature: CustomCmdFeature = { - CustomCmdFeature(server: self) - }() - private(set) lazy var translationFeature: TranslationFeature = { - TranslationFeature(server: self) - }() - private(set) lazy var speechFeature: SpeechFeature = { - SpeechFeature(server: self) - }() - private(set) lazy var otaFeature: OtaFeature = { - OtaFeature(server: self) - }() - private(set) lazy var deviceRecordFeature: DeviceRecordFeature = { - DeviceRecordFeature(server: self) - }() - private(set) lazy var assistantBridge: AssistantBridge = { - AssistantBridge(server: self) - }() - /// 设备主动推送事件转发器(battery / phoneCallStatus / expandFunction), - /// 对齐 Android 的 `DeviceInfoEventForwarder` + `CustomEventForwarder`。 - private(set) lazy var eventForwarders: JieliEventForwarders = { - JieliEventForwarders(server: self) - }() - - private init() {} - - // MARK: - Lifecycle - - func initialize( - multiDevice: Bool, - skipNoNameDev: Bool, - enableLog: Bool - ) throws { - if initialized { return } - - #if canImport(JL_BLEKit) - // SDK log + auth toggles. Mirrors Android `BluetoothOption` setup. - bleMultiple.ble_FILTER_ENABLE = true - // 7s default per BleManager.swift demo. - bleMultiple.ble_TIMEOUT = 7 - // Device authentication on by default — match Android `setUseDeviceAuth(true)`. - bleMultiple.authEnable = true - #endif - - // Forward connect/disconnect → scan-feature reset, identical to the - // Android internalCleanupListener in JieliHomeServer.kt. - dispatcher.addNativeListener(internalCleanup) - - // Hook scan into shared BLE central; scan publishes its own events. - scanFeature.attach(server: self) - connectFeature.attach() - // 把 SDK 的 NSNotification(电量 / 通话状态 / 自定义协议)转发到 Dart。 - eventForwarders.attach() - - initialized = true - dispatcher.send([ - "type": "adapterStatus", - "enabled": true, - "hasBle": true, - ]) - } - - func shutdown() { - if !initialized { return } - otaFeature.cancel() - deviceRecordFeature.stop() - translationFeature.stop() - speechFeature.detach() - assistantBridge.shutdown() - eventForwarders.detach() - scanFeature.detach() - initialized = false - } - - // MARK: - Native event subscription helpers - - func addEventListener(_ listener: JieliEventListener) { - dispatcher.addNativeListener(listener) - } - - func removeEventListener(_ listener: JieliEventListener) { - dispatcher.removeNativeListener(listener) - } - - // MARK: - Internal cleanup listener - // Same role as Android `internalCleanupListener`: when the device - // disconnects or call status flips while in non-call translation mode, - // proactively stop translation/recording to avoid stale state. - private lazy var internalCleanup: InternalCleanupListener = { - InternalCleanupListener(server: self) - }() -} - -private final class InternalCleanupListener: NSObject, JieliEventListener { - weak var server: JieliHomeServer? - init(server: JieliHomeServer) { self.server = server } - - func onEvent(payload: [String: Any?]) { - guard let server = server else { return } - let type = payload["type"] as? String - switch type { - case "connectionState": - let state = payload["state"] as? Int ?? -1 - // 0 = CONNECTION_DISCONNECT (matches Android constant) - if state == 0 { - if server.translationFeature.isWorking() { server.translationFeature.stop() } - if server.deviceRecordFeature.isRecording() { server.deviceRecordFeature.stop() } - if server.assistantBridge.isRunning() { server.assistantBridge.stop() } - // Tear down the translation coordinator so the next session builds - // a fresh JLTranslationManager bound to the new device's manager. - server.translationCoordinator.tearDown() - } - case "phoneCallStatus": - let status = payload["status"] as? Int ?? 0 - if status == 0 { return } - if let mid = server.translationFeature.currentModeId() { - // 3 = MODE_CALL_TRANSLATION, 6 = MODE_CALL_TRANSLATION_WITH_STEREO - let isCallMode = mid == 3 || mid == 6 - if !isCallMode && server.translationFeature.isWorking() { - server.translationFeature.stop() - } - } - default: - break - } - } -} diff --git a/local_plugins/device_jieli/ios/Classes/Feature/AssistantBridge.swift b/local_plugins/device_jieli/ios/Classes/Feature/AssistantBridge.swift deleted file mode 100644 index 4e76fbc34..000000000 --- a/local_plugins/device_jieli/ios/Classes/Feature/AssistantBridge.swift +++ /dev/null @@ -1,208 +0,0 @@ -import Foundation - -#if canImport(JL_BLEKit) -import JL_BLEKit -#endif - -/// AI-assistant bridge — mirrors Android `feature/assistant/AssistantBridge.kt` -/// + `JieliAssistantPort.kt`. -/// -/// Pipeline: -/// 1. Enter RCSP `MODE_RECORD` (=1) with `recordtype = .byDevice` -/// (equivalent to Android `STRATEGY_DEVICE_ALWAYS_RECORDING`). The -/// headset starts pushing OPUS audio frames continuously. -/// 2. `OpusStreamDecoder` (packet size 40 — critical, otherwise 80% of -/// frames get dropped, same gotcha the Android comment calls out) emits -/// 16-bit / 16 kHz / mono / 20 ms PCM. -/// 3. Each PCM frame is published on the EventDispatcher as `assistantAudio`. -/// 4. Errors / lifecycle events as `assistantError` / `assistantStart` / -/// `assistantEnd`. -/// -/// TTS playback (stop direction) — for now we accept playback frames and -/// queue them but do not yet write back through A2DP locally. The Android -/// path writes via a `LocalPlayer` (AudioTrack USAGE_MEDIA) which the OS -/// re-routes through the connected A2DP earphone. iOS equivalent is -/// `AVAudioEngine`/`AudioQueue`; the orchestrator can drive this directly -/// or call back through `assistantPlayback` (TODO once needed). -class AssistantBridge { - - weak var server: JieliHomeServer? - init(server: JieliHomeServer) { self.server = server } - - private var running: Bool = false - private var decoder: OpusStreamDecoder? - private var sequence: Int64 = 0 - #if canImport(JL_BLEKit) - private var sinkWrapper: AssistantSinkWrapper? - #endif - - func isRunning() -> Bool { running } - - func start() -> Bool { - if running { return true } - #if canImport(JL_BLEKit) - guard let server = server else { return false } - guard let manager = server.translationCoordinator.ensureManager() else { - server.dispatcher.send([ - "type": "assistantError", - "code": "device.assistant.no_device", - "message": "no connected device", - ]) - return false - } - if !manager.trIsSupportTranslate() { - server.dispatcher.send([ - "type": "assistantError", - "code": "device.assistant.not_supported", - "message": "device does not support translation", - ]) - return false - } - - // Build the OPUS decoder that turns 40-byte JieLi OPUS packets into - // 16k / 16-bit / mono / 20 ms PCM and republish each frame. - let dec = OpusStreamDecoder( - channels: 1, - packetSize: 40, - sampleRate: 16000, - onPcm: { [weak self] pcm in self?.publishPcm(pcm) }, - onError: { [weak self] code, msg in - self?.server?.dispatcher.send([ - "type": "assistantError", - "code": "device.assistant.decoder_failed", - "message": "opus: code=\(code) msg=\(msg ?? "")", - ]) - } - ) - dec.start() - decoder = dec - - // Install ourselves as the active translation sink before triggering - // `trStartTranslate`. Reusing the call-translation runtime is overkill - // for this single-leg path; we drive the manager directly. - let wrapper = AssistantSinkWrapper(owner: self) - server.translationCoordinator.activeSink = wrapper - sinkWrapper = wrapper - - let mode = JLTranslateSetMode() - mode.modeType = .onlyRecord - mode.dataType = .OPUS - mode.channel = 1 - mode.sampleRate = 16000 - manager.recordtype = .byDevice - - // Fire-and-forget — failure surfaces through the delegate as - // `onError`, which we map to `assistantError` events. - manager.trStartTranslate(mode) { [weak self] status, err in - guard let self = self else { return } - if status != .success { - self.server?.dispatcher.send([ - "type": "assistantError", - "code": "device.assistant.enter_failed", - "message": "trStartTranslate status=\(status.rawValue) err=\(err?.localizedDescription ?? "")", - ]) - self.cleanup() - self.running = false - } - } - - running = true - server.dispatcher.send([ - "type": "assistantStart", - "sampleRate": 16000, - "tsMs": Int(Date().timeIntervalSince1970 * 1000), - ]) - return true - #else - return false - #endif - } - - func stop() { - if !running { return } - running = false - cleanup() - server?.dispatcher.send([ - "type": "assistantEnd", - "tsMs": Int(Date().timeIntervalSince1970 * 1000), - ]) - } - - func shutdown() { stop() } - - // MARK: - Internal - - private func publishPcm(_ pcm: Data) { - sequence &+= 1 - server?.dispatcher.send([ - "type": "assistantAudio", - "encoding": "pcm16", - "sampleRate": 16000, - "channels": 1, - "bitsPerSample": 16, - "sequence": sequence, - "tsMs": Int(Date().timeIntervalSince1970 * 1000), - "pcm": pcm, - ]) - } - - private func cleanup() { - decoder?.stop(); decoder = nil - #if canImport(JL_BLEKit) - if let server = server, let wrapper = sinkWrapper, - server.translationCoordinator.activeSink === wrapper { - server.translationCoordinator.activeSink = nil - } - sinkWrapper = nil - server?.translationCoordinator.manager?.trExitMode { _, _ in } - #endif - } - - // MARK: - Sink callbacks - - #if canImport(JL_BLEKit) - fileprivate func handleAudio(_ audio: JLTranslateAudio) { - let payload = audio.data - if payload.isEmpty { return } - switch audio.audioType { - case .PCM: - publishPcm(payload) - case .OPUS: - decoder?.feedEncoded(payload) - default: - // Speex / MSBC / JLA_V2 — not on the assistant path. Drop quietly. - break - } - } - - fileprivate func handleModeChange(_ mode: JLTranslateSetMode) { - if mode.modeType == .idle && running { - server?.dispatcher.send([ - "type": "assistantError", - "code": "device.assistant.mode_exited", - "message": "headset exited record mode", - ]) - } - } - - fileprivate func handleError(_ error: Error) { - server?.dispatcher.send([ - "type": "assistantError", - "code": "device.assistant.translation_error", - "message": error.localizedDescription, - ]) - } - #endif -} - -#if canImport(JL_BLEKit) -private final class AssistantSinkWrapper: TranslationManagerSink { - weak var owner: AssistantBridge? - init(owner: AssistantBridge) { self.owner = owner } - func onModeChange(uuid: String, mode: JLTranslateSetMode) { owner?.handleModeChange(mode) } - func onReceiveAudioData(uuid: String, audio: JLTranslateAudio) { owner?.handleAudio(audio) } - func onError(uuid: String, error: Error) { owner?.handleError(error) } - func onCallingStateChanged(uuid: String, isCalling: Bool) {} - func onSendAudioQueueOver(uuid: String) {} -} -#endif diff --git a/local_plugins/device_jieli/ios/Classes/Feature/ConnectFeature.swift b/local_plugins/device_jieli/ios/Classes/Feature/ConnectFeature.swift deleted file mode 100644 index b8acbf8c1..000000000 --- a/local_plugins/device_jieli/ios/Classes/Feature/ConnectFeature.swift +++ /dev/null @@ -1,193 +0,0 @@ -import Foundation -import CoreBluetooth - -#if canImport(JL_BLEKit) -import JL_BLEKit -#endif - -/// Connect / disconnect via JL_BLEMultiple. Mirrors `feature/ConnectFeature.kt`. -/// -/// Connection state events are published as `connectionState` payloads with -/// the same numeric coding as the Dart `ConnectionStateEvent` constants -/// (0=disconnect, 1=ok, 2=connecting). RCSP-init success surfaces as a -/// separate `rcspInit` event when the JieLi SDK confirms `cmdTargetFeatureResult` -/// returns successfully — same flow Android publishes. -class ConnectFeature: NSObject { - - weak var server: JieliHomeServer? - init(server: JieliHomeServer) { self.server = server } - - /// Cached id → entity for lookup by Dart-side `address` (peripheral UUID). - private var entityCache: [String: AnyObject] = [:] - private var connectedId: String? - private var connectedName: String? - - func attach() { - #if canImport(JL_BLEKit) - // Hook into the JieLi notifications. The SDK posts named NSNotifications - // (kJL_BLE_M_ENTITY_CONNECTED / kJL_BLE_M_ENTITY_DISCONNECTED). - let nc = NotificationCenter.default - nc.addObserver(self, - selector: #selector(onConnected(_:)), - name: NSNotification.Name(kJL_BLE_M_ENTITY_CONNECTED), object: nil) - nc.addObserver(self, - selector: #selector(onDisconnected(_:)), - name: NSNotification.Name(kJL_BLE_M_ENTITY_DISCONNECTED), object: nil) - nc.addObserver(self, - selector: #selector(onAdapterOff(_:)), - name: NSNotification.Name(kJL_BLE_M_OFF), object: nil) - nc.addObserver(self, - selector: #selector(onAdapterOn(_:)), - name: NSNotification.Name(kJL_BLE_M_ON), object: nil) - #endif - } - - func connect( - bleAddress: String, - edrAddress: String?, - deviceType: Int, - connectWay: Int - ) throws { - NSLog("%@", "[JieliConnect] connect() start ble=\(bleAddress) edr=\(edrAddress ?? "nil") type=\(deviceType) way=\(connectWay)") - server?.dispatcher.send([ - "type": "connectionState", - "address": bleAddress, - "state": 2, // connecting - ]) - #if canImport(JL_BLEKit) - guard let server = server else { throw PluginError("CONNECT_FAILED", "server not initialized") } - let mult = server.bleMultiple - NSLog("%@", "[JieliConnect] authEnable=\(mult.authEnable) ble_TIMEOUT=\(mult.ble_TIMEOUT) filter=\(mult.ble_FILTER_ENABLE)") - // Build / lookup entity. JL_BLEMultiple keeps a `blePeripheralArr` with - // discovered entities — we resolve by UUID, matching the iOS demo. - let entity: JL_EntityM - if let cached = entityCache[bleAddress] as? JL_EntityM { - entity = cached - NSLog("%@", "[JieliConnect] entity cache hit for \(bleAddress)") - } else if let made = mult.makeEntity(withUUID: bleAddress) { - entity = made - entityCache[bleAddress] = entity - NSLog("%@", "[JieliConnect] entity makeEntity ok for \(bleAddress)") - } else { - NSLog("%@", "[JieliConnect] entity makeEntity FAILED — peripheral not in JL_BLEMultiple cache (\(bleAddress))") - throw PluginError("CONNECT_FAILED", "remote device not found (UUID=\(bleAddress))") - } - mult.connectEntity(entity) { [weak self] status in - guard let self = self else { return } - NSLog("%@", "[JieliConnect] connectEntity callback status=\(status.rawValue) ble=\(bleAddress)") - switch status { - case .paired: - NSLog("%@", "[JieliConnect] PAIRED — issuing cmdTargetFeatureResult (RCSP init handshake) for \(bleAddress)") - self.connectedId = bleAddress - self.connectedName = entity.mPeripheral.name - // RCSP target-feature query — same role as Android's connect-success - // path that issues `cmdTargetFeatureResult` after handshake. - entity.mCmdManager.cmdTargetFeatureResult { result, response, error in - NSLog("%@", "[JieliConnect] cmdTargetFeatureResult result=\(result) response=\(String(describing: response)) error=\(String(describing: error)) for \(bleAddress)") - self.server?.dispatcher.send([ - "type": "connectionState", - "address": bleAddress, - "state": 1, // ok / link connected - ]) - self.server?.dispatcher.send([ - "type": "rcspInit", - "address": bleAddress, - "code": 0, - ]) - } - case .connecting: - NSLog("%@", "[JieliConnect] CONNECTING \(bleAddress)") - case .disconnectOk: - NSLog("%@", "[JieliConnect] DISCONNECT_OK \(bleAddress)") - self.server?.dispatcher.send([ - "type": "connectionState", - "address": bleAddress, - "state": 0, - ]) - default: - // Any other terminal failure → expose as 0/disconnect with a code. - // 注意:authEnable=true 时,未授权设备会在 RCSP 握手阶段被 SDK 主动断开, - // 通常会以 .disconnect 或 .timeout 等非 .paired 状态走到这里。 - NSLog("%@", "[JieliConnect] TERMINAL FAILURE status=\(status.rawValue) (authEnable=\(self.server?.bleMultiple.authEnable ?? false)) — surfacing as disconnect for \(bleAddress)") - self.server?.dispatcher.send([ - "type": "connectionState", - "address": bleAddress, - "state": 0, - ]) - } - } - #else - NSLog("%@", "[JieliConnect] JL_BLEKit not linked — cannot connect \(bleAddress)") - throw PluginError("CONNECT_FAILED", "JL_BLEKit framework not linked") - #endif - } - - func disconnect(address: String) throws { - #if canImport(JL_BLEKit) - guard let server = server else { return } - let mult = server.bleMultiple - if let entity = entityCache[address] as? JL_EntityM { - mult.disconnectEntity(entity) { _ in } - } - server.dispatcher.send([ - "type": "connectionState", - "address": address, - "state": 0, - ]) - if connectedId == address { - connectedId = nil - connectedName = nil - } - #endif - } - - func isConnected(address: String) -> Bool { - return connectedId == address - } - - func connectedDeviceInfo() -> [String: Any?]? { - guard let id = connectedId else { return nil } - return ["address": id, "name": connectedName ?? ""] - } - - // MARK: - Internal helpers used by sibling features - func currentEntity() -> AnyObject? { - guard let id = connectedId else { return nil } - return entityCache[id] - } - - // MARK: - Notifications - - @objc private func onConnected(_ note: Notification) { - // The SDK posts the CBPeripheral as the object on Custom-BLE flow. The - // BleMultiple flow publishes via the `paired` callback above, so the only - // job here is to ensure consistency for native subscribers. - if let cbp = note.object as? CBPeripheral { - NSLog("%@", "[JieliConnect] notif kJL_BLE_M_ENTITY_CONNECTED id=\(cbp.identifier.uuidString) name=\(cbp.name ?? "")") - connectedId = cbp.identifier.uuidString - connectedName = cbp.name - } - } - - @objc private func onDisconnected(_ note: Notification) { - if let cbp = note.object as? CBPeripheral { - let id = cbp.identifier.uuidString - NSLog("%@", "[JieliConnect] notif kJL_BLE_M_ENTITY_DISCONNECTED id=\(id) name=\(cbp.name ?? "")") - server?.dispatcher.send([ - "type": "connectionState", - "address": id, - "state": 0, - ]) - if connectedId == id { connectedId = nil; connectedName = nil } - } - } - - @objc private func onAdapterOff(_ note: Notification) { - NSLog("%@", "[JieliConnect] notif kJL_BLE_M_OFF (adapter off)") - server?.dispatcher.send(["type": "adapterStatus", "enabled": false, "hasBle": true]) - } - @objc private func onAdapterOn(_ note: Notification) { - NSLog("%@", "[JieliConnect] notif kJL_BLE_M_ON (adapter on)") - server?.dispatcher.send(["type": "adapterStatus", "enabled": true, "hasBle": true]) - } -} diff --git a/local_plugins/device_jieli/ios/Classes/Feature/CustomCmdFeature.swift b/local_plugins/device_jieli/ios/Classes/Feature/CustomCmdFeature.swift deleted file mode 100644 index b8812b562..000000000 --- a/local_plugins/device_jieli/ios/Classes/Feature/CustomCmdFeature.swift +++ /dev/null @@ -1,50 +0,0 @@ -import Foundation - -#if canImport(JL_BLEKit) -import JL_BLEKit -#endif - -/// Custom RCSP command pass-through. Mirrors `feature/CustomCmdFeature.kt`. -/// -/// Android's plugin takes (opCode: Int, payload: ByteArray) and the SDK -/// wraps them into a private RCSP frame. The iOS SDK exposes only -/// `cmdCustomData:isNeedResponse:Result:` which takes a single `data` -/// argument, so we prepend the opCode byte to the payload and call that. -/// The reply comes back through the result block. This matches the shape -/// the Dart caller expects: a `[Int]` response. -class CustomCmdFeature { - - weak var server: JieliHomeServer? - init(server: JieliHomeServer) { self.server = server } - - func send( - address: String, - opCode: UInt8, - payload: Data, - completion: @escaping (Data?, Error?) -> Void - ) { - #if canImport(JL_BLEKit) - guard let entity = server?.connectFeature.currentEntity() as? JL_EntityM, - entity.mUUID == address else { - completion(nil, PluginError("CUSTOM_CMD_ERR", "no active session")) - return - } - var frame = Data(capacity: 1 + payload.count) - frame.append(opCode) - frame.append(payload) - - entity.mCmdManager.mCustomManager.cmdCustomData( - frame, - isNeedResponse: true - ) { status, _, data in - if status == .success { - completion(data ?? Data(), nil) - } else { - completion(nil, PluginError("CUSTOM_CMD_ERR", "JL_CMDStatus=\(status.rawValue)")) - } - } - #else - completion(nil, PluginError("CUSTOM_CMD_ERR", "JL_BLEKit not linked")) - #endif - } -} diff --git a/local_plugins/device_jieli/ios/Classes/Feature/DeviceInfoFeature.swift b/local_plugins/device_jieli/ios/Classes/Feature/DeviceInfoFeature.swift deleted file mode 100644 index e5f4ccd69..000000000 --- a/local_plugins/device_jieli/ios/Classes/Feature/DeviceInfoFeature.swift +++ /dev/null @@ -1,67 +0,0 @@ -import Foundation - -#if canImport(JL_BLEKit) -import JL_BLEKit -#endif - -/// Device snapshot / target-feature queries. Mirrors `feature/DeviceInfoFeature.kt`. -/// -/// Returns a flat dictionary readable by the Dart `Jielihome.deviceSnapshot` -/// caller. The Android schema (`name`, `firmwareVersion`, `battery`, ...) is -/// preserved; iOS-specific extras (`bleAddr`, `btAddr`, `pidvid`) are added -/// for completeness — Dart consumers ignore unknown keys. -class DeviceInfoFeature { - - weak var server: JieliHomeServer? - init(server: JieliHomeServer) { self.server = server } - - func snapshot(address: String) -> [String: Any?]? { - #if canImport(JL_BLEKit) - guard let entity = server?.connectFeature.currentEntity() as? JL_EntityM, - entity.mUUID == address else { - return nil - } - let model = entity.mCmdManager.outputDeviceModel() - return [ - "address": address, - "name": entity.mPeripheral.name ?? "", - "battery": Int(model.battery), - "firmwareVersion": valueIfNotEmpty(model.versionFirmware), - "hardwareVersion": valueIfNotEmpty(model.versionUBoot), - "serialNumber": valueIfNotEmpty(model.pidvid), - "manufacturer": valueIfNotEmpty(model.license), - "model": valueIfNotEmpty(model.proCode), - "btAddr": valueIfNotEmpty(model.btAddr), - "bleAddr": valueIfNotEmpty(model.bleAddr), - ] - #else - return nil - #endif - } - - func queryTargetInfo( - address: String, - mask: Int, - completion: @escaping ([String: Any?]?, Error?) -> Void - ) { - #if canImport(JL_BLEKit) - guard let entity = server?.connectFeature.currentEntity() as? JL_EntityM, - entity.mUUID == address else { - completion(nil, PluginError("TARGET_INFO_ERR", "no active session")) - return - } - entity.mCmdManager.cmdTargetFeatureResult { [weak self] _, _, _ in - let snap = self?.snapshot(address: address) - if let s = snap { completion(s, nil) } - else { completion(nil, PluginError("TARGET_INFO_ERR", "snapshot unavailable")) } - } - #else - completion(nil, PluginError("TARGET_INFO_ERR", "JL_BLEKit not linked")) - #endif - } - - private func valueIfNotEmpty(_ s: String?) -> Any { - guard let s = s, !s.isEmpty else { return NSNull() } - return s - } -} diff --git a/local_plugins/device_jieli/ios/Classes/Feature/DeviceRecordFeature.swift b/local_plugins/device_jieli/ios/Classes/Feature/DeviceRecordFeature.swift deleted file mode 100644 index 572accfd2..000000000 --- a/local_plugins/device_jieli/ios/Classes/Feature/DeviceRecordFeature.swift +++ /dev/null @@ -1,202 +0,0 @@ -import Foundation - -#if canImport(JL_BLEKit) -import JL_BLEKit -#endif - -/// Device-record (会议记录的通话录音). Mirrors Android -/// `feature/record/DeviceRecordFeature.kt`. -/// -/// Drives RCSP `MODE_CALL_TRANSLATION` (=3) with `recordtype = .byDevice` -/// (Android equivalent of `STRATEGY_DEVICE_ALWAYS_RECORDING`). The headset -/// continuously pushes both legs (uplink = 本机麦, downlink = 对端通话音) -/// even with no real call event, which is exactly what meeting recording -/// needs. We OPUS-decode each leg into 16k mono PCM and publish: -/// -/// - `deviceRecordStart` — recording started -/// - `deviceRecordAudio` — one PCM frame, with `streamId` = "in.uplink" / -/// "in.downlink" -/// - `deviceRecordStop` — stopped -/// - `deviceRecordError` — error -/// -/// Mutually exclusive with TranslationFeature — both use the shared -/// `JLTranslationManager`. The MethodRouter stops the other before starting. -class DeviceRecordFeature { - - weak var server: JieliHomeServer? - init(server: JieliHomeServer) { self.server = server } - - private var working = false - private var deviceAddress: String? - private var sampleRate: Int = 16000 - - private var upDecoder: OpusStreamDecoder? - private var downDecoder: OpusStreamDecoder? - #if canImport(JL_BLEKit) - private var sinkWrapper: RecordSinkWrapper? - #endif - - func isRecording() -> Bool { working } - - func start(args: [String: Any]) throws { - if working { throw PluginError("DEVICE_RECORD_ERR", "already recording") } - #if canImport(JL_BLEKit) - guard let server = server else { - throw PluginError("DEVICE_RECORD_ERR", "server gone") - } - guard let manager = server.translationCoordinator.ensureManager() else { - throw PluginError("DEVICE_RECORD_ERR", "no connected device") - } - if !manager.trIsSupportTranslate() { - throw PluginError("DEVICE_RECORD_ERR", "device does not support translation") - } - - let address = (args["address"] as? String) - ?? (server.connectFeature.connectedDeviceInfo()?["address"] as? String) - sampleRate = (args["sampleRate"] as? Int) ?? 16000 - - upDecoder = makeDecoder(streamId: "in.uplink") - downDecoder = makeDecoder(streamId: "in.downlink") - upDecoder?.start() - downDecoder?.start() - - let wrapper = RecordSinkWrapper(owner: self) - server.translationCoordinator.activeSink = wrapper - sinkWrapper = wrapper - - let mode = JLTranslateSetMode() - mode.modeType = .callTranslate - mode.dataType = .OPUS - mode.channel = 1 - mode.sampleRate = sampleRate - manager.recordtype = .byDevice - - // Fire-and-forget: failures surface through the delegate's onError → - // mapped to deviceRecordError events by the sink wrapper. - manager.trStartTranslate(mode) { [weak self] status, err in - guard let self = self else { return } - if status != .success { - self.server?.dispatcher.send([ - "type": "deviceRecordError", - "address": self.deviceAddress as Any, - "code": Int(status.rawValue), - "message": "trStartTranslate failed err=\(err?.localizedDescription ?? "")", - ]) - // Tear ourselves down — same shape as the synchronous error path. - self.working = false - self.cleanup() - } - } - - working = true - deviceAddress = address - server.dispatcher.send([ - "type": "deviceRecordStart", - "address": address as Any, - "sampleRate": sampleRate, - "tsMs": Self.tsMs(), - ]) - #else - throw PluginError("DEVICE_RECORD_ERR", "JL_BLEKit not linked") - #endif - } - - func stop() { - if !working { return } - working = false - let addr = deviceAddress - deviceAddress = nil - cleanup() - server?.dispatcher.send([ - "type": "deviceRecordStop", - "address": addr as Any, - "tsMs": Self.tsMs(), - ]) - } - - // MARK: - Internal - - private func makeDecoder(streamId: String) -> OpusStreamDecoder { - OpusStreamDecoder( - channels: 1, - packetSize: 40, - sampleRate: sampleRate, - onPcm: { [weak self] pcm in - self?.publishPcm(streamId: streamId, pcm: pcm) - }, - onError: { [weak self] code, msg in - self?.server?.dispatcher.send([ - "type": "deviceRecordError", - "address": self?.deviceAddress as Any, - "code": code, - "message": "opus[\(streamId)]: \(msg ?? "")", - ]) - } - ) - } - - private func publishPcm(streamId: String, pcm: Data) { - server?.dispatcher.send([ - "type": "deviceRecordAudio", - "address": deviceAddress as Any, - "streamId": streamId, - "sampleRate": sampleRate, - "channels": 1, - "bitsPerSample": 16, - "tsMs": Self.tsMs(), - "pcm": pcm, - ]) - } - - private func cleanup() { - upDecoder?.stop(); upDecoder = nil - downDecoder?.stop(); downDecoder = nil - #if canImport(JL_BLEKit) - if let server = server, let wrapper = sinkWrapper, - server.translationCoordinator.activeSink === wrapper { - server.translationCoordinator.activeSink = nil - } - sinkWrapper = nil - server?.translationCoordinator.manager?.trExitMode { _, _ in } - #endif - } - - static func tsMs() -> Int { Int(Date().timeIntervalSince1970 * 1000) } - - // MARK: - Sink - - #if canImport(JL_BLEKit) - fileprivate func handleAudio(_ audio: JLTranslateAudio) { - let payload = audio.data - if payload.isEmpty { return } - let kind = RcspTranslationRuntime.SourceKind(rawValue: Int(audio.sourceType.rawValue)) ?? .escoUp - if audio.audioType == .PCM { - let stream = (kind == .escoDown) ? "in.downlink" : "in.uplink" - publishPcm(streamId: stream, pcm: payload) - return - } - switch kind { - case .escoDown: downDecoder?.feedEncoded(payload) - default: upDecoder?.feedEncoded(payload) - } - } - #endif -} - -#if canImport(JL_BLEKit) -private final class RecordSinkWrapper: TranslationManagerSink { - weak var owner: DeviceRecordFeature? - init(owner: DeviceRecordFeature) { self.owner = owner } - func onModeChange(uuid: String, mode: JLTranslateSetMode) {} - func onReceiveAudioData(uuid: String, audio: JLTranslateAudio) { owner?.handleAudio(audio) } - func onError(uuid: String, error: Error) { - owner?.server?.dispatcher.send([ - "type": "deviceRecordError", - "code": (error as NSError).code, - "message": error.localizedDescription, - ]) - } - func onCallingStateChanged(uuid: String, isCalling: Bool) {} - func onSendAudioQueueOver(uuid: String) {} -} -#endif diff --git a/local_plugins/device_jieli/ios/Classes/Feature/JieliEventForwarders.swift b/local_plugins/device_jieli/ios/Classes/Feature/JieliEventForwarders.swift deleted file mode 100644 index 1850988e8..000000000 --- a/local_plugins/device_jieli/ios/Classes/Feature/JieliEventForwarders.swift +++ /dev/null @@ -1,165 +0,0 @@ -import Foundation - -#if canImport(JL_BLEKit) -import JL_BLEKit -#endif - -/// 把 JL_BLEKit 的 NSNotification(设备主动推送)翻译成 EventDispatcher -/// 事件。对齐 Android `event/DeviceInfoEventForwarder` + `CustomEventForwarder`: -/// -/// - `kJL_MANAGER_SYSTEM_INFO` / `kJL_MANAGER_TARGET_INFO` → `battery` -/// (耳机端电量变化、放回仓、设备主动同步系统信息时都会触发;这里只在 -/// `outputDeviceModel().battery` 真的变化时才往 Dart 抛,避免高频空包) -/// - `kJL_MANAGER_CALL_STATUS` → `phoneCallStatus` -/// (通话状态变化;BleManager._isCall 跟 InternalCleanupListener 都依赖它) -/// - `kJL_MANAGER_CUSTOM_DATA` → `expandFunction` -/// (耳机厂商私有协议主动上报,opCode=0x16/0x17 用来发起/停止通话翻译; -/// Android 的 `CustomEventForwarder.onExpandFunction` 等价路径) -/// -/// 通知 payload 格式来自 SDK 头注释: -/// `@{ kJL_MANAGER_KEY_UUID: uuid, kJL_MANAGER_KEY_OBJECT: object }` -/// 实际可能放在 `note.object` 也可能在 `note.userInfo`,所以读取做了兜底。 -final class JieliEventForwarders: NSObject { - - weak var server: JieliHomeServer? - - private var lastBatteryLevel: Int = -1 - private var lastCallStatus: Int = -1 - - init(server: JieliHomeServer) { - self.server = server - super.init() - } - - func attach() { - #if canImport(JL_BLEKit) - let nc = NotificationCenter.default - nc.addObserver(self, selector: #selector(onSystemInfo(_:)), - name: NSNotification.Name(kJL_MANAGER_SYSTEM_INFO), object: nil) - nc.addObserver(self, selector: #selector(onTargetInfo(_:)), - name: NSNotification.Name(kJL_MANAGER_TARGET_INFO), object: nil) - nc.addObserver(self, selector: #selector(onCallStatus(_:)), - name: NSNotification.Name(kJL_MANAGER_CALL_STATUS), object: nil) - nc.addObserver(self, selector: #selector(onCustomData(_:)), - name: NSNotification.Name(kJL_MANAGER_CUSTOM_DATA), object: nil) - // 设备断开时清空去重缓存,避免下一次重连首次电量/通话状态变化被 - // "和上次一样" 误判而吞掉。 - nc.addObserver(self, selector: #selector(onEntityDisconnected(_:)), - name: NSNotification.Name(kJL_BLE_M_ENTITY_DISCONNECTED), object: nil) - #endif - } - - func detach() { - NotificationCenter.default.removeObserver(self) - lastBatteryLevel = -1 - lastCallStatus = -1 - } - - // MARK: - Helpers - - /// SDK 把 `@{UUID, OBJECT}` 字典塞在 NSNotification 的哪个字段并不一致 - /// (`object` 或 `userInfo`),这里两个位置都查一遍。 - private func payloadDict(_ note: Notification) -> [AnyHashable: Any]? { - if let dict = note.object as? [AnyHashable: Any] { return dict } - if let info = note.userInfo, !info.isEmpty { return info } - return nil - } - - #if canImport(JL_BLEKit) - private func uuid(from note: Notification) -> String { - let dict = payloadDict(note) ?? [:] - if let s = dict[kJL_MANAGER_KEY_UUID] as? String { return s } - return "" - } - - private func obj(from note: Notification) -> Any? { - payloadDict(note)?[kJL_MANAGER_KEY_OBJECT] - } - #endif - - // MARK: - Battery - - @objc private func onSystemInfo(_ note: Notification) { - publishBatteryIfChanged(note: note) - } - - @objc private func onTargetInfo(_ note: Notification) { - publishBatteryIfChanged(note: note) - } - - private func publishBatteryIfChanged(note: Notification) { - #if canImport(JL_BLEKit) - guard let server = server, server.initialized else { return } - let address = uuid(from: note) - // 当前 session 只跟踪一个已连接设备;UUID 对不上就放弃,避免抢 - // 别的 entity 的状态。 - guard let entity = server.connectFeature.currentEntity() as? JL_EntityM, - !address.isEmpty, entity.mUUID == address else { return } - let battery = Int(entity.mCmdManager.outputDeviceModel().battery) - // 0 是 SDK 没拿到信息时的占位;忽略 0 → 非 0 之外的"覆盖性 0", - // 防止短暂的握手抖动把已经显示出来的电量重置回 0。 - if battery == 0 && lastBatteryLevel > 0 { return } - if battery == lastBatteryLevel { return } - lastBatteryLevel = battery - server.dispatcher.send([ - "type": "battery", - "address": address, - "level": battery, - ]) - #endif - } - - // MARK: - Phone call status - - @objc private func onCallStatus(_ note: Notification) { - guard let server = server, server.initialized else { return } - #if canImport(JL_BLEKit) - let address = uuid(from: note) - // 杰理 iOS SDK 的 call status 通过 NSNumber 抛上来;解不出来就当 0 - // (= idle),整段链路语义都是 "status != 0 表示通话中"。 - let status: Int - if let n = obj(from: note) as? NSNumber { - status = n.intValue - } else if let n = obj(from: note) as? Int { - status = n - } else { - status = 0 - } - if status == lastCallStatus { return } - lastCallStatus = status - server.dispatcher.send([ - "type": "phoneCallStatus", - "address": address, - "status": status, - ]) - #endif - } - - // MARK: - Disconnect reset - - @objc private func onEntityDisconnected(_ note: Notification) { - lastBatteryLevel = -1 - lastCallStatus = -1 - } - - // MARK: - Expand function (vendor custom command, device-initiated) - - @objc private func onCustomData(_ note: Notification) { - guard let server = server, server.initialized else { return } - #if canImport(JL_BLEKit) - let address = uuid(from: note) - guard let data = obj(from: note) as? Data, !data.isEmpty else { return } - // Android 的 onExpandFunction(opCode, payload) 把首字节当 opCode, - // 其余作为 payload。iOS `cmdCustomData` 出站方向也是这么拼的 - // (见 CustomCmdFeature.send),按对称把入站拆回去。 - let opCode = Int(data[0]) - let payload = data.count > 1 ? data.subdata(in: 1.. Bool { running } - - func start( - address: String?, - firmwareFilePath: String, - blockSize: Int, - fileFlag: Data? - ) { - if running { return } - #if canImport(JL_BLEKit) - guard let server = server, - let entity = server.connectFeature.currentEntity() as? JL_EntityM else { - server?.dispatcher.send([ - "type": "otaError", - "code": -1, - "message": "no active session", - ]) - return - } - running = true - server.dispatcher.send([ - "type": "otaState", - "state": "INQUIRING", - "sent": 0, "total": 0, "percent": -1, "tsMs": Self.tsMs(), - ]) - // JL_OTA_RT 是 nullable closure (`JL_OTA_RT __nullable`),Swift 不接受 - // trailing closure 给 nullable closure 参数 —— 显式拼出 closure 字面量。 - let cb: JL_OTA_RT = { [weak self] result, progress in - self?.onOtaCallback(result: result, progress: progress) - } - // 注意 label:Obj-C selector `otaFuncWithEntityM:withFilePath:Result:` - // 经 Swift importer 的 omit-needless-words 规则把 `EntityM` 去掉(与 - // 参数类型 JL_EntityM 尾部重复),第一个 label 变成 `with:`。 - server.bleMultiple.otaFunc( - with: entity, - withFilePath: firmwareFilePath, - result: cb - ) - #else - server?.dispatcher.send([ - "type": "otaError", - "code": -1, - "message": "JL_OTALib not linked", - ]) - #endif - } - - func cancel() { - if !running { return } - running = false - server?.dispatcher.send([ - "type": "otaState", - "state": "CANCELLED", - "sent": 0, "total": 0, "percent": -1, "tsMs": Self.tsMs(), - ]) - } - - #if canImport(JL_OTALib) - /// Map `JL_OTAResult` (~30 distinct cases) to the smaller Android-style - /// state machine used by Dart. We funnel "in-progress / preparing / - /// upgrading" into TRANSFERRING with the float `progress` (0..1) scaled - /// to 0..100, terminal success → DONE, anything else → FAILED. - private func onOtaCallback(result: JL_OTAResult, progress: Float) { - guard let server = server else { return } - switch result { - case .preparing, .prepared: - server.dispatcher.send([ - "type": "otaState", "state": "ENTERING", - "sent": 0, "total": 0, "percent": -1, "tsMs": Self.tsMs(), - ]) - case .upgrading: - let percent = max(0, min(100, Int(progress * 100))) - server.dispatcher.send([ - "type": "otaState", "state": "TRANSFERRING", - "sent": 0, "total": 0, "percent": percent, "tsMs": Self.tsMs(), - ]) - case .reboot, .reconnect, .reconnectWithMacAddr, .reconnectUpdateSource: - server.dispatcher.send([ - "type": "otaState", "state": "REBOOTING", - "sent": 0, "total": 0, "percent": 100, "tsMs": Self.tsMs(), - ]) - case .success: - running = false - server.dispatcher.send([ - "type": "otaState", "state": "DONE", - "sent": 0, "total": 0, "percent": 100, "tsMs": Self.tsMs(), - ]) - case .cancel: - running = false - server.dispatcher.send([ - "type": "otaState", "state": "CANCELLED", - "sent": 0, "total": 0, "percent": -1, "tsMs": Self.tsMs(), - ]) - default: - // Anything else from the JL_OTAResult enum is a terminal failure. - running = false - server.dispatcher.send([ - "type": "otaState", "state": "FAILED", - "sent": 0, "total": 0, "percent": -1, "tsMs": Self.tsMs(), - ]) - server.dispatcher.send([ - "type": "otaError", - "code": Int(result.rawValue), - "message": "JL_OTAResult=\(result.rawValue)", - ]) - } - } - #endif - - private static func tsMs() -> Int { - Int(Date().timeIntervalSince1970 * 1000) - } -} diff --git a/local_plugins/device_jieli/ios/Classes/Feature/ScanFeature.swift b/local_plugins/device_jieli/ios/Classes/Feature/ScanFeature.swift deleted file mode 100644 index 7f2ad2a40..000000000 --- a/local_plugins/device_jieli/ios/Classes/Feature/ScanFeature.swift +++ /dev/null @@ -1,263 +0,0 @@ -import Foundation -import CoreBluetooth - -/// BLE scan via CoreBluetooth. Mirrors Android `feature/ScanFeature.kt`. -/// -/// Like the Android implementation, scanning is **not** delegated to the JieLi -/// SDK's built-in `scan()` (which has its own filterContent/strategy quirks); -/// instead we drive `CBCentralManager` directly and parse the advertisement -/// data ourselves. The published `deviceFound` event payload matches the -/// Android schema 1:1 so the Dart parser is unchanged: -/// -/// ``` -/// { -/// type: "deviceFound", -/// name, address, rssi, -/// edrAddr, deviceType, connectWay, // null on iOS — see note below -/// rawAdv, advFlags, manufacturerCompanyId, manufacturerData, -/// serviceUuids: [String], advRecords: [{len, type, data}] -/// } -/// ``` -/// -/// `edrAddr` / `deviceType` / `connectWay` are populated on Android by feeding -/// the raw scan record through `ParseDataUtil.isFilterBleDevice(...)`. On iOS -/// the equivalent live in `JL_AdvParse`'s `JLAdvParse` Obj-C class; until the -/// exact entry point is wired we leave them null and rely on the connection -/// path's BLE-only fallback (matches the Android "fallback when SDK can't -/// parse" behavior). -class ScanFeature: NSObject, CBCentralManagerDelegate { - - private weak var server: JieliHomeServer? - private weak var dispatcher: EventDispatcher? - - /// Dedicated central manager for scanning — separated from the SDK's own - /// `JL_BLEMultiple` central so that filter knobs do not collide. - private var centralManager: CBCentralManager? - - private var scanning: Bool = false - private var stopWorkItem: DispatchWorkItem? - - private var currentNameList: [String] = [] - private var currentSkipUnnamed: Bool = true - private var serviceFilters: [CBUUID] = [] - - // Track which addresses have been published this scan to dedupe. - private var publishedIds: Set = [] - - init(dispatcher: EventDispatcher) { - self.dispatcher = dispatcher - super.init() - } - - func attach(server: JieliHomeServer) { - self.server = server - if centralManager == nil { - centralManager = CBCentralManager(delegate: self, queue: .main) - } - } - - func detach() { - stopScan() - centralManager = nil - } - - // MARK: - Public API (mirrors Android signatures) - - func startScan( - timeoutMs: Int, - nameList: [String], - uuidList: [String], - skipUnnamed: Bool - ) throws { - guard let central = centralManager else { - throw PluginError("SCAN_FAILED", "BLE central unavailable") - } - if scanning { stopScan() } - - currentNameList = nameList.filter { !$0.isEmpty } - currentSkipUnnamed = skipUnnamed - serviceFilters = uuidList.compactMap { Self.parseUuid($0) } - publishedIds.removeAll(keepingCapacity: true) - - if central.state != .poweredOn { - // Match Android behavior — scan request from a powered-off adapter - // surfaces the same shape of error. - throw PluginError("SCAN_FAILED", "BLE adapter not powered on (state=\(central.state.rawValue))") - } - - let services: [CBUUID]? = serviceFilters.isEmpty ? nil : serviceFilters - let options: [String: Any] = [ - // Allow duplicate adverts so we keep getting RSSI updates. - CBCentralManagerScanOptionAllowDuplicatesKey: false - ] - central.scanForPeripherals(withServices: services, options: options) - scanning = true - dispatcher?.send([ - "type": "scanStatus", - "ble": true, - "started": true, - ]) - - let work = DispatchWorkItem { [weak self] in self?.stopScan() } - stopWorkItem = work - DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(timeoutMs), execute: work) - } - - func stopScan() { - if let work = stopWorkItem { work.cancel(); stopWorkItem = nil } - if let central = centralManager, central.state == .poweredOn { - central.stopScan() - } - if scanning { - dispatcher?.send([ - "type": "scanStatus", - "ble": true, - "started": false, - ]) - } - scanning = false - currentNameList = [] - serviceFilters = [] - } - - func isScanning() -> Bool { scanning } - - // MARK: - CBCentralManagerDelegate - - func centralManagerDidUpdateState(_ central: CBCentralManager) { - let on = central.state == .poweredOn - dispatcher?.send([ - "type": "adapterStatus", - "enabled": on, - "hasBle": true, - ]) - if !on && scanning { stopScan() } - } - - func centralManager( - _ central: CBCentralManager, - didDiscover peripheral: CBPeripheral, - advertisementData: [String: Any], - rssi RSSI: NSNumber - ) { - let name = peripheral.name - ?? (advertisementData[CBAdvertisementDataLocalNameKey] as? String) - - // === 调试用:打印每一条广播(过滤前),完整 dump 广播 hex === - // iOS CoreBluetooth 不暴露原始广播字节,这里把 advertisementData - // 里所有字段按 BLE AD type 拼出 hex 方便核对。 - let manufacturerData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data - let serviceUuids = advertisementData[CBAdvertisementDataServiceUUIDsKey] as? [CBUUID] - let serviceData = advertisementData[CBAdvertisementDataServiceDataKey] as? [CBUUID: Data] - let txPower = advertisementData[CBAdvertisementDataTxPowerLevelKey] as? NSNumber - let localName = advertisementData[CBAdvertisementDataLocalNameKey] as? String - let isConnectable = (advertisementData[CBAdvertisementDataIsConnectable] as? NSNumber)?.boolValue ?? false - let solicited = advertisementData[CBAdvertisementDataSolicitedServiceUUIDsKey] as? [CBUUID] - let overflow = advertisementData[CBAdvertisementDataOverflowServiceUUIDsKey] as? [CBUUID] - - let mfrHex = manufacturerData?.map { String(format: "%02X", $0) }.joined() ?? "nil" - let svcUuids = serviceUuids?.map { $0.uuidString }.joined(separator: ",") ?? "" - let svcDataHex = serviceData?.map { (k, v) in - "\(k.uuidString)=" + v.map { String(format: "%02X", $0) }.joined() - }.joined(separator: ";") ?? "nil" - let txPowerStr = txPower.map { "\($0.intValue)" } ?? "nil" - let solicitedStr = solicited?.map { $0.uuidString }.joined(separator: ",") ?? "" - let overflowStr = overflow?.map { $0.uuidString }.joined(separator: ",") ?? "" - - NSLog("%@", "================ [JieliScan] didDiscover ================") - NSLog("%@", "[JieliScan] name = \(name ?? "")") - NSLog("%@", "[JieliScan] localName = \(localName ?? "")") - NSLog("%@", "[JieliScan] id = \(peripheral.identifier.uuidString)") - NSLog("%@", "[JieliScan] rssi = \(RSSI) txPower=\(txPowerStr) connectable=\(isConnectable)") - NSLog("%@", "[JieliScan] mfr(hex) = \(mfrHex)") - if let m = manufacturerData, m.count >= 2 { - let cid = Int(m[0]) | (Int(m[1]) << 8) - let payload = m.count > 2 - ? m[2..= 2 { - mfrCompanyId = Int(m[0]) | (Int(m[1]) << 8) - if m.count > 2 { - mfrPayloadHex = m[2.. CBUUID? { - let s = raw.trimmingCharacters(in: .whitespacesAndNewlines) - guard !s.isEmpty else { return nil } - return CBUUID(string: s) - } -} diff --git a/local_plugins/device_jieli/ios/Classes/Feature/SpeechFeature.swift b/local_plugins/device_jieli/ios/Classes/Feature/SpeechFeature.swift deleted file mode 100644 index d498118be..000000000 --- a/local_plugins/device_jieli/ios/Classes/Feature/SpeechFeature.swift +++ /dev/null @@ -1,59 +0,0 @@ -import Foundation - -/// PTT / wake-word path. Mirrors `feature/voice/SpeechFeature.kt`. -/// -/// **STUBBED on iOS.** The Android implementation drives -/// `RecordOpImpl.startVoiceRecord(...)` plus an OPUS decoder that emits -/// `speechAudio` PCM frames. The iOS counterpart sits on -/// `JL_SpeechAIttsHandler` / `JL_SpeexManager`, which need explicit wiring -/// against the SDK's stream callbacks. Until that path is implemented: -/// -/// - `start(...)` / `stop(...)` invoke the completion with `false` + an -/// explanatory message; -/// - `isRecording()` returns `false`. -/// -/// The MethodChannel surface is preserved so the Dart layer (which already -/// expects errors here on missing devices) sees a clean `SPEECH_START_ERR`. -class SpeechFeature { - - static let voiceTypePcm = 0 - static let voiceTypeSpeex = 1 - static let voiceTypeOpus = 2 - - static let sampleRate8k = 8 - static let sampleRate16k = 16 - - static let vadWayDevice = 0 - static let vadWaySdk = 1 - - static let reasonNormal = 0 - static let reasonStop = 1 - - weak var server: JieliHomeServer? - init(server: JieliHomeServer) { self.server = server } - - func isRecording(address: String?) -> Bool { false } - - func start( - address: String?, - voiceType: Int, - sampleRate: Int, - vadWay: Int, - completion: @escaping (Bool, String?) -> Void - ) { - // TODO: wire to JL_SpeechAIttsHandler / JL_SpeexManager. - completion(false, "speech.start not yet implemented on iOS") - } - - func stop( - address: String?, - reason: Int, - completion: @escaping (Bool, String?) -> Void - ) { - completion(false, "speech.stop not yet implemented on iOS") - } - - func detach() { - // No resources held while stubbed. - } -} diff --git a/local_plugins/device_jieli/ios/Classes/Feature/Translation/Mode/CallTranslationModeHandler.swift b/local_plugins/device_jieli/ios/Classes/Feature/Translation/Mode/CallTranslationModeHandler.swift deleted file mode 100644 index 071ff848b..000000000 --- a/local_plugins/device_jieli/ios/Classes/Feature/Translation/Mode/CallTranslationModeHandler.swift +++ /dev/null @@ -1,217 +0,0 @@ -import Foundation - -/// MODE_CALL_TRANSLATION — eSCO uplink + downlink. Mirrors Android -/// `mode/CallTranslationModeHandler.kt`. -/// -/// Inputs: `in.uplink` (本机用户) + `in.downlink` (对端). -/// Outputs: `out.uplink` (TTS 给对端) + `out.downlink` (TTS 给本机用户). -final class CallTranslationModeHandler: BaseTranslationModeHandler { - - override var modeId: Int { TranslationModeIds.MODE_CALL_TRANSLATION } - override var inputStreams: [String] { - [TranslationStreams.IN_UPLINK, TranslationStreams.IN_DOWNLINK] - } - override var outputStreams: [String] { - [TranslationStreams.OUT_UPLINK, TranslationStreams.OUT_DOWNLINK] - } - - private var runtime: RcspTranslationRuntime? - - override func start(args: [String: Any]) throws { - guard !isWorking else { return } - guard let server = server else { - throw PluginError("TRANSLATION_ERR", "server gone") - } - let sampleRate = (args["sampleRate"] as? Int) ?? 16000 - let codec = Self.parseAudioCodec(args["audioType"]) ?? .opus - let recordByDevice = Self.parseRecordByDevice(args["strategy"]) ?? true - - let rt = RcspTranslationRuntime( - server: server, - modeId: modeId, - codec: codec, - channels: 1, - sampleRate: sampleRate, - recordByDevice: recordByDevice, - onPcm: { [weak self] source, pcm in - guard let self = self else { return } - let stream: String - switch source { - case .escoUp: stream = TranslationStreams.IN_UPLINK - case .escoDown: stream = TranslationStreams.IN_DOWNLINK - default: return - } - self.pushFrame( - streamId: stream, - pcm: pcm, - format: TranslationAudioFormat(sampleRate: sampleRate) - ) - }, - onError: { [weak self] code, msg in - self?.emitError(code: code, message: msg) - } - ) - do { - try rt.start() - runtime = rt - setWorking(true) - emitLog("CallTranslation start codec=\(codec) sr=\(sampleRate) recordByDevice=\(recordByDevice)") - } catch { - rt.stop() - throw error - } - } - - override func stop() { - if !isWorking { return } - runtime?.stop() - runtime = nil - setWorking(false) - emitLog("CallTranslation stop") - } - - @discardableResult - override func onTranslatedAudio( - outputStreamId: String, - pcm: Data, - format: TranslationAudioFormat, - isFinal: Bool - ) -> Bool { - guard outputStreamId == TranslationStreams.OUT_UPLINK || - outputStreamId == TranslationStreams.OUT_DOWNLINK else { return false } - return runtime?.feedTtsPcm( - outputStreamId: outputStreamId, - pcm: pcm, - isFinal: isFinal - ) ?? false - } -} - -/// MODE_CALL_TRANSLATION_WITH_STEREO — same I/O contract as -/// CallTranslationModeHandler but the SDK pushes a single stereo stream -/// (`escoMix`) which we split L=uplink / R=downlink in software. -final class StereoCallTranslationModeHandler: BaseTranslationModeHandler { - - override var modeId: Int { TranslationModeIds.MODE_CALL_TRANSLATION_WITH_STEREO } - override var inputStreams: [String] { - [TranslationStreams.IN_UPLINK, TranslationStreams.IN_DOWNLINK] - } - override var outputStreams: [String] { - [TranslationStreams.OUT_UPLINK, TranslationStreams.OUT_DOWNLINK] - } - - private var runtime: RcspTranslationRuntime? - - override func start(args: [String: Any]) throws { - guard !isWorking else { return } - guard let server = server else { - throw PluginError("TRANSLATION_ERR", "server gone") - } - let sampleRate = (args["sampleRate"] as? Int) ?? 16000 - let codec = Self.parseAudioCodec(args["audioType"]) ?? .opus - let recordByDevice = Self.parseRecordByDevice(args["strategy"]) ?? true - - let rt = RcspTranslationRuntime( - server: server, - modeId: modeId, - codec: codec, - channels: 2, - sampleRate: sampleRate, - recordByDevice: recordByDevice, - onPcm: { [weak self] source, stereoPcm in - guard let self = self else { return } - // The stereo decoder produces interleaved 16-bit LE; split → two mono. - if source != .escoMix && source != .escoUp { return } - let (left, right) = PcmKit.splitStereo16(stereoPcm) - let fmt = TranslationAudioFormat(sampleRate: sampleRate) - self.pushFrame(streamId: TranslationStreams.IN_UPLINK, pcm: left, format: fmt) - self.pushFrame(streamId: TranslationStreams.IN_DOWNLINK, pcm: right, format: fmt) - }, - onError: { [weak self] code, msg in - self?.emitError(code: code, message: msg) - } - ) - do { - try rt.start() - runtime = rt - setWorking(true) - emitLog("StereoCallTranslation start codec=\(codec) sr=\(sampleRate)") - } catch { - rt.stop() - throw error - } - } - - override func stop() { - if !isWorking { return } - runtime?.stop() - runtime = nil - setWorking(false) - emitLog("StereoCallTranslation stop") - } - - @discardableResult - override func onTranslatedAudio( - outputStreamId: String, - pcm: Data, - format: TranslationAudioFormat, - isFinal: Bool - ) -> Bool { - guard outputStreamId == TranslationStreams.OUT_UPLINK || - outputStreamId == TranslationStreams.OUT_DOWNLINK else { return false } - return runtime?.feedTtsPcm( - outputStreamId: outputStreamId, - pcm: pcm, - isFinal: isFinal - ) ?? false - } -} - -/// MODE_RECORD — single-leg uplink only. Used by both `JieliAssistantPort` -/// and `DeviceRecordFeature` (the latter via MODE_CALL_TRANSLATION but with -/// recordByDevice=true so eSCO frames flow with no real call). Re-exported -/// here so future mode dispatching has a placeholder. -final class RecordOnlyModeHandler: BaseTranslationModeHandler { - override var modeId: Int { TranslationModeIds.MODE_RECORD } - override var inputStreams: [String] { [TranslationStreams.IN_MIC] } - override var outputStreams: [String] { [] } - - private var runtime: RcspTranslationRuntime? - - override func start(args: [String: Any]) throws { - guard !isWorking else { return } - guard let server = server else { - throw PluginError("TRANSLATION_ERR", "server gone") - } - let sampleRate = (args["sampleRate"] as? Int) ?? 16000 - let codec = Self.parseAudioCodec(args["audioType"]) ?? .opus - let rt = RcspTranslationRuntime( - server: server, - modeId: modeId, - codec: codec, - channels: 1, - sampleRate: sampleRate, - recordByDevice: true, - onPcm: { [weak self] _, pcm in - self?.pushFrame( - streamId: TranslationStreams.IN_MIC, - pcm: pcm, - format: TranslationAudioFormat(sampleRate: sampleRate) - ) - }, - onError: { [weak self] code, msg in - self?.emitError(code: code, message: msg) - } - ) - try rt.start() - runtime = rt - setWorking(true) - } - - override func stop() { - if !isWorking { return } - runtime?.stop() - runtime = nil - setWorking(false) - } -} diff --git a/local_plugins/device_jieli/ios/Classes/Feature/Translation/RcspTranslationRuntime.swift b/local_plugins/device_jieli/ios/Classes/Feature/Translation/RcspTranslationRuntime.swift deleted file mode 100644 index 6b9027a3f..000000000 --- a/local_plugins/device_jieli/ios/Classes/Feature/Translation/RcspTranslationRuntime.swift +++ /dev/null @@ -1,415 +0,0 @@ -import Foundation - -#if canImport(JL_BLEKit) -import JL_BLEKit -#endif - -/// iOS counterpart of Android `feature/translation/runtime/RcspTranslationRuntime.kt`. -/// -/// Drives `JLTranslationManager.trStartTranslate(_:block:)` (the iOS -/// equivalent of `TranslationImpl.enterMode`) and: -/// -/// - Decodes incoming OPUS frames (`onReceiveAudioData`) to 16k mono PCM via -/// `OpusStreamDecoder`, hands them to the supplied `onPcm(source:pcm:)` -/// callback. PCM-mode (no codec) is also supported — frames pass through -/// unchanged. -/// - Buffers TTS PCM per-leg, periodically flushes (default 1 s) by encoding -/// the whole buffer with `OpusStreamEncoder` and writing it back via -/// `JLTranslationManager.trWrite(_:translate:)`. The original received -/// `JLTranslateAudio` frame is kept so we can clone its codec/CRC layout -/// into the write — exactly what the demo does (`TranslateVM.swift:295-318`). -final class RcspTranslationRuntime { - - enum AudioCodecKind { case opus, pcm, jla_v2 } - - /// SDK-equivalent of Android's `AudioData.SOURCE_*`. We export raw Int - /// values so callers can match against the same numbers Android uses. - enum SourceKind: Int { - case file = 0 // JLTranslateAudioTypeFile - case deviceMic = 1 // JLTranslateAudioTypeDeviceMic - case phoneMic = 2 // JLTranslateAudioTypePhoneMic - case escoUp = 3 // JLTranslateAudioTypeESCOUp - case escoDown = 4 // JLTranslateAudioTypeESCODown - case a2dp = 5 // JLTranslateAudioTypeA2DP - case escoMix = 6 // JLTranslateAudioTypeESCOMax - } - - weak var server: JieliHomeServer? - - /// Mode descriptor — decides what goes into `trStartTranslate`. - let modeId: Int - let codec: AudioCodecKind - let channels: Int - let sampleRate: Int - let recordByDevice: Bool - - /// Callbacks supplied by the mode handler. - private let onPcm: (SourceKind, Data) -> Void - private let onError: (Int, String?) -> Void - - /// Flush cadence for OPUS feed — 1 s matches the long-term-stable Android - /// setting (`PERIODIC_FLUSH_MS = 1000L`). Faster cadences cause the - /// headset's RCSP decoder to reset and produce dropouts. - private let periodicFlushSeconds: TimeInterval = 1.0 - private let pcmBufferHardLimitBytes: Int = 2 * 1024 * 1024 - - // OPUS decoders — one per leg in stereo, two for mono call translation, - // one for assistant / device record. - private var upDecoder: OpusStreamDecoder? - private var downDecoder: OpusStreamDecoder? - private var stereoDecoder: OpusStreamDecoder? - - // OPUS encoders for TTS write-back. One per output leg. - private var encoders: [String: OpusStreamEncoder] = [:] - private var encoderQueues: [String: Data] = [:] // pending encoded output - private let encoderLock = NSLock() - - // TTS PCM accumulators keyed by output stream id. - private var pcmBuffers: [String: Data] = [:] - private let bufferLock = NSLock() - private var flushTimer: DispatchSourceTimer? - private var sinkWrapper: SinkWrapper? - - /// Reference `JLTranslateAudio` captured from the most recent SDK uplink - /// frame. We clone it (with sourceType remapped) when writing TTS back — - /// this is the trick the demo uses (TranslateVM.swift:295-318). - #if canImport(JL_BLEKit) - private var referenceAudio: JLTranslateAudio? - #endif - - private(set) var working: Bool = false - - init( - server: JieliHomeServer, - modeId: Int, - codec: AudioCodecKind, - channels: Int, - sampleRate: Int, - recordByDevice: Bool, - onPcm: @escaping (SourceKind, Data) -> Void, - onError: @escaping (Int, String?) -> Void - ) { - self.server = server - self.modeId = modeId - self.codec = codec - self.channels = channels - self.sampleRate = sampleRate - self.recordByDevice = recordByDevice - self.onPcm = onPcm - self.onError = onError - } - - // MARK: - Public API (matches Android RcspTranslationRuntime) - - func start() throws { - guard let server = server else { - throw PluginError("TRANSLATION_ERR", "server gone") - } - #if canImport(JL_BLEKit) - guard let manager = server.translationCoordinator.ensureManager() else { - throw PluginError("TRANSLATION_ERR", "no active session") - } - if !manager.trIsSupportTranslate() { - throw PluginError("TRANSLATION_ERR", "device does not support translation") - } - - // Build OPUS decoders for the legs we expect to receive frames on. - if codec == .opus { - switch modeId { - case TranslationModeIds.MODE_CALL_TRANSLATION: - upDecoder = makeDecoder() - downDecoder = makeDecoder() - case TranslationModeIds.MODE_CALL_TRANSLATION_WITH_STEREO: - stereoDecoder = makeDecoder(channels: 2) - default: - // Assistant / record / face-to-face: a single uplink decoder. - upDecoder = makeDecoder() - } - } - upDecoder?.start() - downDecoder?.start() - stereoDecoder?.start() - - // Compose the iOS mode object & install the sink. - let mode = JLTranslateSetMode() - mode.modeType = JLTranslateSetModeType(rawValue: UInt(modeId)) ?? .idle - mode.channel = channels - mode.sampleRate = sampleRate - mode.dataType = mapCodecToSpeakDataType(codec) - manager.recordtype = recordByDevice ? .byDevice : .byPhone - - let wrapper = SinkWrapper(owner: self) - server.translationCoordinator.activeSink = wrapper - sinkWrapper = wrapper - - // Fire-and-forget start — match Android's RcspTranslationRuntime.start - // semantics: kick off enterMode, surface terminal failures via the - // delegate's onError. The mode-change confirmation arrives on - // `onModeChange` which we already track. - manager.trStartTranslate(mode) { [weak self] status, err in - if status != .success { - let msg = err?.localizedDescription ?? "trStartTranslate status=\(status.rawValue)" - self?.onError(Int(status.rawValue), msg) - } - } - if codec == .opus { startPeriodicFlushTimer() } - working = true - #else - throw PluginError("TRANSLATION_ERR", "JL_BLEKit not linked") - #endif - } - - func stop() { - cleanup() - working = false - } - - /// Outside-injected TTS PCM. Mirrors Android `feedTtsPcm` — accumulate or - /// pass-through (PCM mode), flush periodically (OPUS mode). - func feedTtsPcm(outputStreamId: String, pcm: Data, isFinal: Bool) -> Bool { - #if canImport(JL_BLEKit) - guard working else { return false } - let source = sourceForOutputStream(outputStreamId) - - if codec == .pcm { - writeBack(source: source, type: .PCM, payload: pcm) - return true - } - - var pendingFlush: Data? - bufferLock.lock() - var buf = pcmBuffers[outputStreamId] ?? Data() - if !pcm.isEmpty { - if buf.count + pcm.count > pcmBufferHardLimitBytes { - buf.removeAll(keepingCapacity: true) - } else { - buf.append(pcm) - } - } - if isFinal { - pendingFlush = buf - buf.removeAll(keepingCapacity: true) - } - pcmBuffers[outputStreamId] = buf - bufferLock.unlock() - - if let toFlush = pendingFlush, !toFlush.isEmpty { - encodeAndDispatchAsync(source: source, leg: outputStreamId, pcmBytes: toFlush) - } - return true - #else - return false - #endif - } - - // MARK: - Internal - - #if canImport(JL_BLEKit) - /// SinkWrapper exists so `RcspTranslationRuntime` (a class) can be - /// referenced weakly while still satisfying the @objc-class delegate - /// expectations of the coordinator. - private final class SinkWrapper: TranslationManagerSink { - weak var owner: RcspTranslationRuntime? - init(owner: RcspTranslationRuntime) { self.owner = owner } - func onModeChange(uuid: String, mode: JLTranslateSetMode) { - owner?.handleModeChange(mode: mode) - } - func onReceiveAudioData(uuid: String, audio: JLTranslateAudio) { - owner?.handleReceiveAudio(audio: audio) - } - func onError(uuid: String, error: Error) { - owner?.handleError(error) - } - func onCallingStateChanged(uuid: String, isCalling: Bool) {} - func onSendAudioQueueOver(uuid: String) {} - } - - private func handleModeChange(mode: JLTranslateSetMode) { - if mode.modeType == .idle { - // Headset proactively exited (e.g., button press). Surface as error - // to the orchestrator so it can decide to fully stop. - onError(-100, "headset exited mode → IDLE") - } - } - - private func handleReceiveAudio(audio: JLTranslateAudio) { - referenceAudio = audio - let payload = audio.data - if payload.isEmpty { return } - let src = SourceKind(rawValue: Int(audio.sourceType.rawValue)) ?? .escoUp - - if codec == .pcm || audio.audioType == .PCM { - onPcm(src, payload) - return - } - - switch src { - case .escoUp: upDecoder?.feedEncoded(payload) - case .escoDown: downDecoder?.feedEncoded(payload) - case .escoMix: stereoDecoder?.feedEncoded(payload) - default: - // Assistant / record paths still funnel through escoUp on most - // firmware; if not, fall back to upDecoder. - upDecoder?.feedEncoded(payload) - } - } - - private func handleError(_ error: Error) { - let nse = error as NSError - onError(nse.code, nse.localizedDescription) - } - - private func mapCodecToSpeakDataType(_ codec: AudioCodecKind) -> JL_SpeakDataType { - switch codec { - case .pcm: return .PCM - case .opus: return .OPUS - case .jla_v2: return .JLA_V2 - } - } - - private func sourceForOutputStream(_ id: String) -> SourceKind { - switch id { - case TranslationStreams.OUT_UPLINK: return .escoUp - case TranslationStreams.OUT_DOWNLINK: return .escoDown - default: return .phoneMic - } - } - - private func makeDecoder(channels: Int = 1) -> OpusStreamDecoder { - OpusStreamDecoder( - channels: channels, - // 16k mono 20ms = 40-byte JieLi packets; stereo doubles. - packetSize: channels == 2 ? 80 : 40, - sampleRate: sampleRate, - onPcm: { [weak self] pcm in - guard let self = self else { return } - // The decoder doesn't know which leg the bytes came from, but the - // higher-level handler already routed them to a specific decoder. - // Re-tag based on which decoder fired. We can't tell here; the - // handler's `onPcm` takes a SourceKind — we report the SDK source - // when it was set via referenceAudio.sourceType. - let src = self.lastReceivedSource() - self.onPcm(src, pcm) - }, - onError: { [weak self] code, msg in self?.onError(code, msg) } - ) - } - - /// Map the most recently received audio's sourceType to our enum so - /// downstream PCM consumers see the correct leg. We track it per decoder - /// firing — there's only one in-flight decode call at a time per decoder. - private func lastReceivedSource() -> SourceKind { - guard let ref = referenceAudio else { return .escoUp } - return SourceKind(rawValue: Int(ref.sourceType.rawValue)) ?? .escoUp - } - - // MARK: - TTS write-back - - private func startPeriodicFlushTimer() { - let timer = DispatchSource.makeTimerSource(queue: .global(qos: .utility)) - timer.schedule(deadline: .now() + periodicFlushSeconds, - repeating: periodicFlushSeconds) - timer.setEventHandler { [weak self] in self?.periodicFlush() } - flushTimer = timer - timer.resume() - } - - private func periodicFlush() { - var pending: [(SourceKind, String, Data)] = [] - bufferLock.lock() - for (leg, buf) in pcmBuffers where !buf.isEmpty { - pending.append((sourceForOutputStream(leg), leg, buf)) - pcmBuffers[leg] = Data() - } - bufferLock.unlock() - for (src, leg, pcm) in pending { - encodeAndDispatchAsync(source: src, leg: leg, pcmBytes: pcm) - } - } - - private func encodeAndDispatchAsync(source: SourceKind, leg: String, pcmBytes: Data) { - encoderLock.lock() - let encoder: OpusStreamEncoder - if let existing = encoders[leg] { - encoder = existing - } else { - let queueKey = leg - encoderQueues[queueKey] = Data() - let e = OpusStreamEncoder( - channels: 1, - sampleRate: sampleRate, - onEncoded: { [weak self] opus in - guard let self = self else { return } - self.encoderLock.lock() - var q = self.encoderQueues[queueKey] ?? Data() - q.append(opus) - self.encoderQueues[queueKey] = q - self.encoderLock.unlock() - }, - onError: { [weak self] c, m in self?.onError(c, "encoder[\(leg)]: \(m ?? "")") } - ) - e.start() - encoders[leg] = e - encoder = e - } - encoderLock.unlock() - - encoder.feedPcm(pcmBytes) - - // After feeding, drain whatever the encoder emitted into a single write. - encoderLock.lock() - let drained = encoderQueues[leg] ?? Data() - encoderQueues[leg] = Data() - encoderLock.unlock() - - if !drained.isEmpty { - writeBack(source: source, type: .OPUS, payload: drained) - } - } - - /// Send a packed payload back to the headset by reusing the most recent - /// reference frame, swapping its sourceType, and calling - /// `JLTranslationManager.trWrite(_:translate:)`. - private func writeBack(source: SourceKind, type: JL_SpeakDataType, payload: Data) { - guard let server = server, - let manager = server.translationCoordinator.manager else { return } - let template = referenceAudio?.copy() as? JLTranslateAudio ?? JLTranslateAudio() - template.sourceType = JLTranslateAudioSourceType(rawValue: UInt(source.rawValue)) ?? .typeESCOUp - template.audioType = type - template.data = payload - template.len = Int32(payload.count) - manager.trWrite(template, translate: payload) - } - #endif - - private func cleanup() { - flushTimer?.cancel() - flushTimer = nil - upDecoder?.stop(); upDecoder = nil - downDecoder?.stop(); downDecoder = nil - stereoDecoder?.stop(); stereoDecoder = nil - encoderLock.lock() - encoders.values.forEach { $0.stop() } - encoders.removeAll() - encoderQueues.removeAll() - encoderLock.unlock() - bufferLock.lock() - pcmBuffers.removeAll() - bufferLock.unlock() - - #if canImport(JL_BLEKit) - if let server = server, let manager = server.translationCoordinator.manager { - manager.trExitMode { _, _ in } - } - if let server = server, let wrapper = sinkWrapper { - // Only clear the active sink if it is still us — guards against - // races with a follow-up handler that already installed itself. - if server.translationCoordinator.activeSink === wrapper { - server.translationCoordinator.activeSink = nil - } - } - sinkWrapper = nil - referenceAudio = nil - #endif - } -} diff --git a/local_plugins/device_jieli/ios/Classes/Feature/Translation/TranslationAudioBridge.swift b/local_plugins/device_jieli/ios/Classes/Feature/Translation/TranslationAudioBridge.swift deleted file mode 100644 index ee14c7abb..000000000 --- a/local_plugins/device_jieli/ios/Classes/Feature/Translation/TranslationAudioBridge.swift +++ /dev/null @@ -1,134 +0,0 @@ -import Foundation - -/// Bridge between a TranslationModeHandler and the outside world (Flutter -/// EventChannel by default; can be replaced by host code for native-direct -/// orchestration). Mirrors Android `feature/translation/TranslationAudioBridge.kt`. -protocol TranslationAudioBridge: AnyObject { - - /// Plugin → outside: one captured PCM frame from the device. - func emitAudioFrame( - modeId: Int, - streamId: String, - pcm: Data, - format: TranslationAudioFormat, - sequence: Int64, - tsMs: Int64, - isFinal: Bool - ) - - /// Plugin → outside: subtitle text (purely informational). - func emitTranslationResult( - modeId: Int, - srcLang: String?, - srcText: String?, - destLang: String?, - destText: String?, - requestId: String? - ) - - /// Plugin → outside: log line. - func emitLog(modeId: Int, content: String) - - /// Plugin → outside: error. - func emitError(modeId: Int, code: Int, message: String?) - - /// Outside → plugin: TTS PCM coming back from the translation service. - /// The mode handler decides how to repackage and write it back to the - /// device (uplink / downlink / local speaker). - @discardableResult - func feedTranslatedAudio( - modeId: Int, - outputStreamId: String, - pcm: Data, - format: TranslationAudioFormat, - isFinal: Bool - ) -> Bool -} - -/// Default bridge backed by the EventDispatcher → Flutter EventChannel -/// path. Mirrors Android `feature/translation/EventChannelAudioBridge.kt`. -final class EventChannelAudioBridge: TranslationAudioBridge { - - private weak var dispatcher: EventDispatcher? - /// Closure invoked when feedTranslatedAudio is called. The default impl - /// routes back into TranslationFeature — same as Android's `injector`. - private let injector: (Int, String, Data, TranslationAudioFormat, Bool) -> Bool - - init( - dispatcher: EventDispatcher, - injector: @escaping (Int, String, Data, TranslationAudioFormat, Bool) -> Bool - ) { - self.dispatcher = dispatcher - self.injector = injector - } - - func emitAudioFrame( - modeId: Int, - streamId: String, - pcm: Data, - format: TranslationAudioFormat, - sequence: Int64, - tsMs: Int64, - isFinal: Bool - ) { - dispatcher?.send([ - "type": "translationAudio", - "modeId": modeId, - "streamId": streamId, - "sampleRate": format.sampleRate, - "channels": format.channels, - "bitsPerSample": format.bitsPerSample, - "seq": sequence, - "tsMs": tsMs, - "final": isFinal, - "pcm": pcm, - ]) - } - - func emitTranslationResult( - modeId: Int, - srcLang: String?, - srcText: String?, - destLang: String?, - destText: String?, - requestId: String? - ) { - dispatcher?.send([ - "type": "translationResult", - "modeId": modeId, - "srcLang": srcLang as Any, - "srcText": srcText as Any, - "destLang": destLang as Any, - "destText": destText as Any, - "requestId": requestId as Any, - ]) - } - - func emitLog(modeId: Int, content: String) { - dispatcher?.send([ - "type": "translationLog", - "modeId": modeId, - "content": content, - ]) - } - - func emitError(modeId: Int, code: Int, message: String?) { - dispatcher?.send([ - "type": "translationError", - "modeId": modeId, - "code": code, - "message": message as Any, - ]) - } - - @discardableResult - func feedTranslatedAudio( - modeId: Int, - outputStreamId: String, - pcm: Data, - format: TranslationAudioFormat, - isFinal: Bool - ) -> Bool { - injector(modeId, outputStreamId, pcm, format, isFinal) - } -} diff --git a/local_plugins/device_jieli/ios/Classes/Feature/Translation/TranslationCoordinator.swift b/local_plugins/device_jieli/ios/Classes/Feature/Translation/TranslationCoordinator.swift deleted file mode 100644 index 2cff76ab6..000000000 --- a/local_plugins/device_jieli/ios/Classes/Feature/Translation/TranslationCoordinator.swift +++ /dev/null @@ -1,110 +0,0 @@ -import Foundation - -#if canImport(JL_BLEKit) -import JL_BLEKit -#endif - -/// Internal sink protocol that `RcspTranslationRuntime` (and any other -/// `JLTranslationManager` consumer) implements. The coordinator routes -/// incoming SDK callbacks to the currently active sink. -protocol TranslationManagerSink: AnyObject { - #if canImport(JL_BLEKit) - func onModeChange(uuid: String, mode: JLTranslateSetMode) - func onReceiveAudioData(uuid: String, audio: JLTranslateAudio) - func onError(uuid: String, error: Error) - func onCallingStateChanged(uuid: String, isCalling: Bool) - func onSendAudioQueueOver(uuid: String) - #endif -} - -/// Owns the shared `JLTranslationManager` for the connected device. -/// -/// Why share: the JieLi iOS SDK keeps state inside `JLTranslationManager` -/// (current mode, send queue, isCalling KVO target). The demo creates one -/// per device session and reuses it across mode switches via `trStartTranslate` -/// / `trExitMode`. We follow the same pattern: lazily build the manager on -/// first use, route delegate callbacks to the active runtime sink, recreate -/// only when the device disconnects. -/// -/// Only one [TranslationManagerSink] is active at a time. Each mode handler -/// (call translation / assistant / device record) installs itself before -/// calling `trStartTranslate(...)` and removes itself on exit. -final class TranslationCoordinator: NSObject { - - weak var server: JieliHomeServer? - init(server: JieliHomeServer) { - self.server = server - super.init() - } - - #if canImport(JL_BLEKit) - private(set) var manager: JLTranslationManager? - #endif - - /// Single active sink — only set while a mode is running. - weak var activeSink: TranslationManagerSink? - - private var calledOnInitSuccess = false - - // MARK: - Manager lifecycle - - #if canImport(JL_BLEKit) - /// Build the shared manager if needed; reuse existing one if alive. - /// Returns `nil` only if the device is not connected at all. - func ensureManager() -> JLTranslationManager? { - if let m = manager { return m } - guard let server = server, - let entity = server.connectFeature.currentEntity() as? JL_EntityM else { - return nil - } - let m = JLTranslationManager( - delegate: self, - manager: entity.mCmdManager - ) { [weak self] _, _ in - // init result — we ignore the boolean because trIsSupportTranslate - // gives us the same answer when we actually try to enter a mode. - self?.calledOnInitSuccess = true - } - manager = m - return m - } - #endif - - func tearDown() { - activeSink = nil - #if canImport(JL_BLEKit) - manager?.trDestory() - manager = nil - #endif - calledOnInitSuccess = false - } -} - -#if canImport(JL_BLEKit) -extension TranslationCoordinator: JLTranslationManagerDelegate { - - func onInitSuccess(_ uuid: String) { - calledOnInitSuccess = true - } - - func onModeChange(_ uuid: String, mode: JLTranslateSetMode) { - activeSink?.onModeChange(uuid: uuid, mode: mode) - } - - func onReceiveAudioData(_ uuid: String, audioData data: JLTranslateAudio) { - activeSink?.onReceiveAudioData(uuid: uuid, audio: data) - } - - func onError(_ uuid: String, error: Error) { - activeSink?.onError(uuid: uuid, error: error) - } - - func isOnCalling(_ isCalling: Bool, uuid: String) { - activeSink?.onCallingStateChanged(uuid: uuid, isCalling: isCalling) - } - - func onSendAudioQueueOver(_ uuid: String) { - activeSink?.onSendAudioQueueOver(uuid: uuid) - } -} -#endif diff --git a/local_plugins/device_jieli/ios/Classes/Feature/Translation/TranslationModeHandler.swift b/local_plugins/device_jieli/ios/Classes/Feature/Translation/TranslationModeHandler.swift deleted file mode 100644 index 32f3eea44..000000000 --- a/local_plugins/device_jieli/ios/Classes/Feature/Translation/TranslationModeHandler.swift +++ /dev/null @@ -1,130 +0,0 @@ -import Foundation - -/// Mirrors Android `feature/translation/TranslationModeHandler.kt`. -/// -/// Each mode handler declares which input/output streams it consumes/produces, -/// owns an `RcspTranslationRuntime` (when the mode actually uses RCSP), and -/// routes incoming PCM frames to the bridge. -protocol TranslationModeHandler: AnyObject { - var modeId: Int { get } - var inputStreams: [String] { get } - var outputStreams: [String] { get } - var isWorking: Bool { get } - - func start(args: [String: Any]) throws - func stop() - - /// Outside service hands back TTS PCM; `outputStreamId` selects which - /// physical leg to write to. - @discardableResult - func onTranslatedAudio( - outputStreamId: String, - pcm: Data, - format: TranslationAudioFormat, - isFinal: Bool - ) -> Bool -} - -/// Shared scaffolding — keeps the bridge reference and a `working` flag. -class BaseTranslationModeHandler: TranslationModeHandler { - - weak var server: JieliHomeServer? - weak var bridge: TranslationAudioBridge? - - init(server: JieliHomeServer, bridge: TranslationAudioBridge) { - self.server = server - self.bridge = bridge - } - - var modeId: Int { TranslationModeIds.MODE_IDLE } - var inputStreams: [String] { [] } - var outputStreams: [String] { [] } - - private(set) var working: Bool = false - var isWorking: Bool { working } - - func start(args: [String: Any]) throws { fatalError("override") } - func stop() { working = false } - @discardableResult - func onTranslatedAudio( - outputStreamId: String, - pcm: Data, - format: TranslationAudioFormat, - isFinal: Bool - ) -> Bool { false } - - // MARK: - Helpers for subclasses - - func setWorking(_ on: Bool) { working = on } - - func pushFrame(streamId: String, pcm: Data, format: TranslationAudioFormat) { - bridge?.emitAudioFrame( - modeId: modeId, - streamId: streamId, - pcm: pcm, - format: format, - sequence: Self.nextSeq(), - tsMs: Self.tsMs(), - isFinal: false - ) - } - - func emitLog(_ content: String) { - bridge?.emitLog(modeId: modeId, content: content) - } - - func emitError(code: Int, message: String?) { - bridge?.emitError(modeId: modeId, code: code, message: message) - } - - // MARK: - Static helpers - - private static var seqGen: Int64 = 0 - private static let seqLock = NSLock() - static func nextSeq() -> Int64 { - seqLock.lock(); defer { seqLock.unlock() } - seqGen &+= 1 - return seqGen - } - - static func tsMs() -> Int64 { - Int64(Date().timeIntervalSince1970 * 1000) - } - - // MARK: - Arg parsing utilities - - static func parseAudioCodec(_ raw: Any?) -> RcspTranslationRuntime.AudioCodecKind? { - if let s = raw as? String { - switch s.lowercased() { - case "opus": return .opus - case "pcm": return .pcm - case "jla_v2", "jlav2": return .jla_v2 - default: return nil - } - } - if let n = raw as? Int { - switch n { - case 0: return .pcm - case 2: return .opus - case 4: return .jla_v2 - default: return nil - } - } - return nil - } - - /// Map "always" / "auto" / "device" / "phone" → `recordByDevice` boolean. - /// The iOS SDK collapses Android's three strategies to two - /// (`byPhone`/`byDevice`); both ALWAYS and AUTO map to byDevice. - static func parseRecordByDevice(_ raw: Any?) -> Bool? { - if let b = raw as? Bool { return b } - if let s = raw as? String { - switch s.lowercased() { - case "always", "auto", "device", "byDevice".lowercased(): return true - case "phone", "byPhone".lowercased(), "custom": return false - default: return nil - } - } - return nil - } -} diff --git a/local_plugins/device_jieli/ios/Classes/Feature/Translation/TranslationStreams.swift b/local_plugins/device_jieli/ios/Classes/Feature/Translation/TranslationStreams.swift deleted file mode 100644 index 6869ab515..000000000 --- a/local_plugins/device_jieli/ios/Classes/Feature/Translation/TranslationStreams.swift +++ /dev/null @@ -1,35 +0,0 @@ -import Foundation - -/// Stream-id constants. Mirror Android `TranslationStreams.kt` 1:1 so the -/// Dart side keeps the same routing semantics. -enum TranslationStreams { - // input streams (plugin → translation service) - static let IN_MIC = "in.mic" - static let IN_UPLINK = "in.uplink" - static let IN_DOWNLINK = "in.downlink" - static let IN_AUDIO_FILE = "in.audioFile" - // output streams (translation service → plugin → device) - static let OUT_SPEAKER = "out.speaker" - static let OUT_UPLINK = "out.uplink" - static let OUT_DOWNLINK = "out.downlink" - static let OUT_LOCAL_PLAYBACK = "out.localPlayback" -} - -/// Mode-ID constants — same numeric values as Android `TranslationModeIds` -/// and the iOS SDK enum `JLTranslateSetModeType`. -enum TranslationModeIds { - static let MODE_IDLE = 0 - static let MODE_RECORD = 1 - static let MODE_RECORDING_TRANSLATION = 2 - static let MODE_CALL_TRANSLATION = 3 - static let MODE_AUDIO_TRANSLATION = 4 - static let MODE_FACE_TO_FACE_TRANSLATION = 5 - static let MODE_CALL_TRANSLATION_WITH_STEREO = 6 -} - -/// PCM frame format — bare minimum metadata for downstream consumers. -struct TranslationAudioFormat: Equatable { - var sampleRate: Int = 16000 - var channels: Int = 1 - var bitsPerSample: Int = 16 -} diff --git a/local_plugins/device_jieli/ios/Classes/Feature/TranslationFeature.swift b/local_plugins/device_jieli/ios/Classes/Feature/TranslationFeature.swift deleted file mode 100644 index ba10e16e6..000000000 --- a/local_plugins/device_jieli/ios/Classes/Feature/TranslationFeature.swift +++ /dev/null @@ -1,156 +0,0 @@ -import Foundation - -/// Translation entry point. Mirrors Android `feature/translation/TranslationFeature.kt`. -/// -/// Dispatches `start(modeId:args:)` to the matching mode handler. The set of -/// supported modes mirrors Android — call translation (mode 3 / 6) plus the -/// record-only path used internally by AssistantBridge / DeviceRecordFeature -/// (mode 1). -/// -/// The `feedTranslatedAudio` / `feedTranslationResult` paths route to the -/// active handler / bridge, exactly like Android. -class TranslationFeature: NSObject { - - weak var server: JieliHomeServer? - - private var bridge: TranslationAudioBridge? - private var handlers: [Int: TranslationModeHandler] = [:] - private var current: TranslationModeHandler? - - init(server: JieliHomeServer) { - self.server = server - super.init() - let defaultBridge = EventChannelAudioBridge( - dispatcher: server.dispatcher, - injector: { [weak self] modeId, streamId, pcm, fmt, isFinal in - return self?.feedTranslatedAudio( - streamId: streamId, - pcm: pcm, - sampleRate: fmt.sampleRate, - channels: fmt.channels, - bitsPerSample: fmt.bitsPerSample, - isFinal: isFinal - ) ?? false - } - ) - setBridge(defaultBridge) - } - - /// Replace the active bridge — used by host code that wants to keep - /// audio inside native (mirrors Android `setTranslationBridge`). - func setBridge(_ bridge: TranslationAudioBridge) { - self.bridge = bridge - guard let server = server else { return } - handlers = [ - TranslationModeIds.MODE_CALL_TRANSLATION: - CallTranslationModeHandler(server: server, bridge: bridge), - TranslationModeIds.MODE_CALL_TRANSLATION_WITH_STEREO: - StereoCallTranslationModeHandler(server: server, bridge: bridge), - TranslationModeIds.MODE_RECORD: - RecordOnlyModeHandler(server: server, bridge: bridge), - ] - } - - // MARK: - Public API - - func isWorking() -> Bool { current?.isWorking ?? false } - func currentModeId() -> Int? { - guard let h = current, h.isWorking else { return nil } - return h.modeId - } - func currentInputStreams() -> [String] { - current?.isWorking == true ? (current?.inputStreams ?? []) : [] - } - func currentOutputStreams() -> [String] { - current?.isWorking == true ? (current?.outputStreams ?? []) : [] - } - - func start(modeId: Int, args: [String: Any]) throws { - // Match Android logic — auto-upgrade mode=3 + OPUS to mode=6 stereo when - // the device supports it (and caller did not opt out). - let bypassStereo = (args["bypassStereoUpgrade"] as? Bool) ?? false - let codecRaw = args["audioType"] - let codecIsOpus: Bool = { - if codecRaw == nil { return true } - if let s = codecRaw as? String { return s.lowercased() == "opus" } - if let n = codecRaw as? Int { return n == 2 } - return false - }() - var effectiveModeId = modeId - if !bypassStereo && - modeId == TranslationModeIds.MODE_CALL_TRANSLATION && - codecIsOpus && - isSupportCallTranslationWithStereo(address: args["address"] as? String) { - effectiveModeId = TranslationModeIds.MODE_CALL_TRANSLATION_WITH_STEREO - } - guard let handler = handlers[effectiveModeId] else { - throw PluginError("TRANSLATION_ERR", "unknown modeId=\(effectiveModeId)") - } - current?.stop() - try handler.start(args: args) - current = handler - } - - func stop() { - current?.stop() - current = nil - } - - @discardableResult - func feedTranslatedAudio( - streamId: String, - pcm: Data, - sampleRate: Int, - channels: Int, - bitsPerSample: Int, - isFinal: Bool - ) -> Bool { - guard let h = current, h.isWorking else { return false } - if !h.outputStreams.contains(streamId) { return false } - return h.onTranslatedAudio( - outputStreamId: streamId, - pcm: pcm, - format: TranslationAudioFormat( - sampleRate: sampleRate, - channels: channels, - bitsPerSample: bitsPerSample - ), - isFinal: isFinal - ) - } - - func feedTranslationResult( - srcLang: String?, srcText: String?, - destLang: String?, destText: String?, - requestId: String? - ) { - guard let mid = current?.modeId else { return } - bridge?.emitTranslationResult( - modeId: mid, - srcLang: srcLang, srcText: srcText, - destLang: destLang, destText: destText, - requestId: requestId - ) - } - - /// Mirrors Android `isSupportCallTranslationWithStereo`. The iOS SDK does - /// not expose a direct boolean for this; we use `trIsPlayWithA2dp` - /// (which is true on TWS earphones that support the stereo path) plus the - /// `JLDeviceConfigTws.isSupportOpusStereo` flag when available. As a - /// pragmatic default we just return `trIsPlayWithA2dp()`, matching the - /// demo's `setupModeForCall` heuristic. - func isSupportCallTranslationWithStereo(address: String?) -> Bool { - #if canImport(JL_BLEKit) - guard let server = server else { return false } - return server.translationCoordinator.manager?.trIsPlayWithA2dp() ?? false - #else - return false - #endif - } - - /// Audio-translation mode (file-driven) is not yet wired on iOS — the - /// dedicated handler maps to MODE_AUDIO_TRANSLATION which the demo drives - /// by starting recording-and-play. Returns `false` until implemented to - /// keep the Dart contract intact. - func feedAudioFilePcm(pcm: Data, sampleRate: Int) -> Bool { false } -} diff --git a/local_plugins/device_jieli/ios/Classes/Integration/JieliNativeDevicePlugin.swift b/local_plugins/device_jieli/ios/Classes/Integration/JieliNativeDevicePlugin.swift deleted file mode 100644 index 1b04c73fb..000000000 --- a/local_plugins/device_jieli/ios/Classes/Integration/JieliNativeDevicePlugin.swift +++ /dev/null @@ -1,31 +0,0 @@ -import Foundation - -/// Vendor registration entry — placeholder symmetric with Android's -/// `integration/JieliNativeDevicePlugin.kt` calling -/// `NativeDevicePluginRegistry.register(...)`. -/// -/// On Android the registry lives in `device_plugin_interface` (Kotlin) and is -/// a process-wide map keyed by vendor key. On iOS the equivalent registry has -/// not yet been ported (the `device_plugin_interface` iOS pod is currently a -/// no-op, matching its no-op Android side). When/if the Swift-side registry -/// lands, this method should: -/// -/// 1. Build a `VendorDescriptor(vendorKey: "jieli", displayName: "JieLi (杰理)", -/// capabilities: [.scan, .connect, .bond, .customCommand, -/// .onDeviceCallTranslation, .onDeviceFaceToFaceTranslation, -/// .onDeviceRecordingTranslation])`; -/// 2. Provide a factory that returns a `NativeDevicePlugin` Swift wrapper -/// around `JieliHomeServer.shared`; -/// 3. Hand both to the registry. -/// -/// Until then `register()` is a no-op so the plugin still loads cleanly. -enum JieliNativeDevicePlugin { - - static let kVendor = "jieli" - - static func register() { - // TODO: when device_plugin_interface iOS gains a NativeDevicePluginRegistry, - // wire the JieLi factory here, mirroring Android's - // `JieliNativeDevicePlugin.register(context)`. - } -} diff --git a/local_plugins/device_jieli/ios/Classes/JielihomePlugin.swift b/local_plugins/device_jieli/ios/Classes/JielihomePlugin.swift deleted file mode 100644 index 8972c4f4f..000000000 --- a/local_plugins/device_jieli/ios/Classes/JielihomePlugin.swift +++ /dev/null @@ -1,50 +0,0 @@ -import Flutter -import UIKit - -/// Flutter entry point — mirrors the Android `JielihomePlugin.kt`. -/// -/// Responsibilities (kept symmetric with Android): -/// 1. Bind `device_jieli/method` MethodChannel to [MethodRouter]; -/// 2. Bind `device_jieli/event` EventChannel to the shared [EventDispatcher]; -/// 3. Register the JieLi vendor with the device-manager native registry -/// (placeholder until [JieliNativeDevicePlugin.register] is wired). -/// -/// All actual business logic lives in [JieliHomeServer] and the feature/event -/// submodules under `Classes/Feature/`. -public class JielihomePlugin: NSObject, FlutterPlugin { - - private var methodChannel: FlutterMethodChannel? - private var eventChannel: FlutterEventChannel? - private var router: MethodRouter? - - public static func register(with registrar: FlutterPluginRegistrar) { - let instance = JielihomePlugin() - instance.attach(registrar: registrar) - } - - private func attach(registrar: FlutterPluginRegistrar) { - let server = JieliHomeServer.shared - let router = MethodRouter(server: server) - self.router = router - - let methodChannel = FlutterMethodChannel( - name: "device_jieli/method", - binaryMessenger: registrar.messenger() - ) - methodChannel.setMethodCallHandler { call, result in - router.handle(call: call, result: result) - } - self.methodChannel = methodChannel - - let eventChannel = FlutterEventChannel( - name: "device_jieli/event", - binaryMessenger: registrar.messenger() - ) - eventChannel.setStreamHandler(server.dispatcher) - self.eventChannel = eventChannel - - // Vendor registration with the device-manager registry. Symmetric to - // Android's `JieliNativeDevicePlugin.register(...)`. - JieliNativeDevicePlugin.register() - } -} diff --git a/local_plugins/device_jieli/ios/Frameworks/JLAV2Lib.framework/Headers/JLAV2Lib.h b/local_plugins/device_jieli/ios/Frameworks/JLAV2Lib.framework/Headers/JLAV2Lib.h deleted file mode 100644 index 27b7073db..000000000 --- a/local_plugins/device_jieli/ios/Frameworks/JLAV2Lib.framework/Headers/JLAV2Lib.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// JLAV2Lib.h -// JLAV2Lib -// -// Created by ZPC21-032 on 2025/4/9. -// - -#import - -//! Project version number for JLAV2Lib. -FOUNDATION_EXPORT double JLAV2LibVersionNumber; - -//! Project version string for JLAV2Lib. -FOUNDATION_EXPORT const unsigned char JLAV2LibVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import -#import - diff --git a/local_plugins/device_jieli/ios/Frameworks/JLAV2Lib.framework/Headers/jla_v2_codec_api.h b/local_plugins/device_jieli/ios/Frameworks/JLAV2Lib.framework/Headers/jla_v2_codec_api.h deleted file mode 100644 index 18149ae8b..000000000 --- a/local_plugins/device_jieli/ios/Frameworks/JLAV2Lib.framework/Headers/jla_v2_codec_api.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef _JLA_V2_CODEC_h -#define _JLA_V2_CODEC_h - - -#ifndef u16 -#define u32 unsigned int -#define u16 unsigned short -#define s16 short -#define u8 unsigned char -#endif - -#define SET_DECODE_MODE 0x80 -#define SET_DEC_CH_MODE 0x81 -//#define SET_FADE_IN_TMS 0x82 //for dec plc deleted -#define SET_FADE_IN_OUT 0x82 //淡入淡出 设置生效. -#define SET_NEW_BITRATE 0x90 //enc. -#define GET_EDFRAME_LEN 0x91 // -#define SET_EDFRAME_LEN 0x92 //only for dec. - -#define SET_HW_FFT_CALLBACK 0xA0 //br56_fft. - -enum -{ - JLA_V2_NORMAL_CH = 0, //仅在此模式下,音源为双声道时输出双声道pcm数据. - JLA_V2_L_OUT, - JLA_V2_R_OUT, - JLA_V2_LR_MEAN -}; - -enum -{ - JLA_V2_2CH_L_OR_R = 0x80 -}; - -struct audio_decode_para -{ - u32 mode; /*通过codec_config,cmd=SET_DECODE_MODE设置解码输出模式*/ -}; - - -struct audio_set_bitrate -{ - u32 br; -}; - -struct jla_v2_dec_ch_oput_para -{ - u32 mode; - short pL; //mode_3_channel_L_coefficient Q13 8192:50% - short pR; //mode_3_channel_R_coefficient Q13 -}; - - -struct jla_v2_codec_info -{ - u32 sr; ///< sample rate - u32 br; ///< bit rate - u16 fr_idx; ///< 帧长索引. JLA_V2_FRLEN[9 + 4 +1] = {32, 40, 48, 60, 64, 80, 96, 120, 128, 160, 240, 320, 400, 480}; 0~13. - u16 nch; - u16 if_s24; ///< 编码输入数据 或者解码输出数据位宽是否为24比特. -}; ///< enc_para + dec_para - - -struct jla_v2_codec_io -{ - void* priv; - u16(*input_data)(void* priv, u8* buf, u16 len); //bytes - u16(*output_data)(void* priv, u8* buf, u16 len); //bytes /*解码器在struct audio_decode_para参数mode=1时判断是否输出完整*/ -}; - - -struct jla_v2_codec_ops { - u32(*open)(void* work_buf, const struct jla_v2_codec_io* audioIO, void* para); ///<打开解码器 - u32(*run)(void* work_buf, void* scratch); ///<解码主循环(buffer 32bit对齐) - u32(*need_dcbuf_size)(void* para); ///<获取编解码需要的空间work_buf - u32(*need_stbuf_size)(void* para); ///<获取编解码需要的堆栈scratch - u32(*codec_config)(void* work_buf, u32 cmd, void* parm); -}; - - -extern struct jla_v2_codec_ops* get_jla_v2_enc_ops(); -extern struct jla_v2_codec_ops* get_jla_v2_dec_ops(); - - -extern const int JLA_V2_PLC_EN; -extern const int JLA_V2_HW_FFT; -extern const int JLA_V2_ENCODE_I24bit_ENABLE; -extern const int JLA_V2_DECODE_O24bit_ENABLE; -extern const int JLA_V2_PLC_FADE_OUT_START_POINT; -extern const int JLA_V2_PLC_FADE_OUT_POINTS; -extern const int JLA_V2_PLC_FADE_IN_POINTS; -extern const unsigned short JLA_V2_FRAMELEN_MASK; - - - -#endif \ No newline at end of file diff --git a/local_plugins/device_jieli/ios/Frameworks/JLAV2Lib.framework/JLAV2Lib b/local_plugins/device_jieli/ios/Frameworks/JLAV2Lib.framework/JLAV2Lib deleted file mode 100644 index 917688ff3..000000000 Binary files a/local_plugins/device_jieli/ios/Frameworks/JLAV2Lib.framework/JLAV2Lib and /dev/null differ diff --git a/local_plugins/device_jieli/ios/Frameworks/JLAV2Lib.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/Frameworks/JLAV2Lib.framework/Modules/module.modulemap deleted file mode 100644 index 62072c3f3..000000000 --- a/local_plugins/device_jieli/ios/Frameworks/JLAV2Lib.framework/Modules/module.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module JLAV2Lib { - umbrella header "JLAV2Lib.h" - - export * - module * { export * } -} diff --git a/local_plugins/device_jieli/ios/device_jieli.podspec b/local_plugins/device_jieli/ios/device_jieli.podspec deleted file mode 100644 index beb8246e1..000000000 --- a/local_plugins/device_jieli/ios/device_jieli.podspec +++ /dev/null @@ -1,41 +0,0 @@ -Pod::Spec.new do |s| - s.name = 'device_jieli' - s.version = '0.0.1' - s.summary = 'JieLi Home SDK Flutter plugin (iOS).' - s.description = <<-DESC -JieLi (杰理) device plugin — iOS counterpart of the Android implementation under -android/src/main/kotlin/com/jielihome/jielihome. - DESC - s.homepage = 'https://example.com' - s.license = { :type => 'MIT', :file => '../LICENSE' } - s.author = { 'AI Agent' => 'liwei1dao@gmail.com' } - s.source = { :path => '.' } - s.source_files = 'Classes/**/*.{h,m,swift}' - s.dependency 'Flutter' - s.dependency 'device_plugin_interface' - s.platform = :ios, '12.0' - s.swift_version = '5.0' - - # `ota` plugin 已废弃,OTA 全部走本插件。Vendor 完整的 JieLi SDK - # framework 集合(来自 JIELI-Release 同一套,版本一致,避免 ABI 漂移)。 - # JLAudioUnitKit 不在此列表 —— `ble_service` 的 SPM binary target 已提供, - # 同名 vendor 会触发 Xcode "Multiple commands produce ..." 错误。 - s.vendored_frameworks = [ - 'Frameworks/JL_BLEKit.xcframework', - 'Frameworks/JL_OTALib.xcframework', - 'Frameworks/JL_AdvParse.xcframework', - 'Frameworks/JL_HashPair.xcframework', - 'Frameworks/JLLogHelper.xcframework', - 'Frameworks/JLAV2Lib.framework' - ] - - s.frameworks = 'CoreBluetooth', 'AVFoundation', 'UIKit', 'Foundation' - - s.pod_target_xcconfig = { - 'DEFINES_MODULE' => 'YES', - 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64', - # JieLi's xcframeworks ship Obj-C headers; expose them to Swift via the - # generated module map. - 'CLANG_ENABLE_MODULES' => 'YES' - } -end diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/Info.plist new file mode 100644 index 000000000..482e8cd53 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/Info.plist @@ -0,0 +1,44 @@ + + + + + AvailableLibraries + + + BinaryPath + JLAudioUnitKit.framework/JLAudioUnitKit + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + JLAudioUnitKit.framework + SupportedArchitectures + + x86_64 + arm64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + BinaryPath + JLAudioUnitKit.framework/JLAudioUnitKit + LibraryIdentifier + ios-arm64 + LibraryPath + JLAudioUnitKit.framework + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLAudioUnitKit.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLAudioUnitKit.h new file mode 100644 index 000000000..e6b3af64f --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLAudioUnitKit.h @@ -0,0 +1,26 @@ +// +// JLAudioUnitKit.h +// JLAudioUnitKit +// +// Created by EzioChan on 2024/11/14. +// + +#import + +//! Project version number for JLAudioUnitKit. +FOUNDATION_EXPORT double JLAudioUnitKitVersionNumber; + +//! Project version string for JLAudioUnitKit. +FOUNDATION_EXPORT const unsigned char JLAudioUnitKitVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + +#import +#import +#import +#import +#import +#import +#import +#import +#import diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLAudioUnitPlayer.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLAudioUnitPlayer.h new file mode 100644 index 000000000..7da831c67 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLAudioUnitPlayer.h @@ -0,0 +1,58 @@ +// +// JLAudioUnitPlayer.h +// JLAudioUnitKit +// +// Created by EzioChan on 2025/4/16. +// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, JLAudioUnitPlayerType) { + JLAudioUnitPlayerTypeFile, // 支持 MP3/WAV/AAC 等文件格式(AVAudioPlayer) + JLAudioUnitPlayerTypePCM // 支持 PCM 流式播放(Audio Queue) +}; + +@class JLAudioUnitPlayer; + +@protocol JLAudioPlayerDelegate +@optional +// 播放进度更新(秒) +- (void)audioPlayer:(JLAudioUnitPlayer *)player didUpdateProgress:(NSTimeInterval)currentTime duration:(NSTimeInterval)duration; +// 播放完成 +- (void)audioPlayerDidFinishPlaying:(JLAudioUnitPlayer *)player; +// 播放错误 +- (void)audioPlayer:(JLAudioUnitPlayer *)player didFailWithError:(NSError *)error; +@end + +@interface JLAudioUnitPlayer : NSObject + +@property (nonatomic, weak) id delegate; +@property (nonatomic, readonly) NSTimeInterval duration; // 总时长(文件模式下有效) +@property (nonatomic, readonly) NSTimeInterval currentTime; +@property (nonatomic, readonly) BOOL isPlaying; + +#pragma mark - 初始化方法 + +/// 文件播放初始化(MP3/WAV/AAC) +- (instancetype)initWithAudioFile:(NSString *)filePath; + +/// PCM 流播放初始化(需指定格式) +- (instancetype)initWithPCMFormat:(AudioStreamBasicDescription)format; + +#pragma mark - 播放控制 +- (void)play; +- (void)pause; +- (void)stop; +- (void)seekToTime:(NSTimeInterval)time; // 仅文件模式有效 + +#pragma mark - PCM 流式输入(仅 PCM 模式有效) +- (void)appendPCMData:(NSData *)pcmData; +- (void)endPCMStream; // 结束 PCM 流输入 + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusDecoder.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusDecoder.h new file mode 100644 index 000000000..711e2e99a --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusDecoder.h @@ -0,0 +1,66 @@ +// +// JLOpusDecoder.h +// JLAudioUnitKit +// +// Created by EzioChan on 2024/11/14. +// Copyright © 2024 ZhuHai JieLi Technology Co.,Ltd. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN +@class JLOpusDecoder; + +/// Opus 解码代理 +@protocol JLOpusDecoderDelegate + +/// Opus 数据解码 +/// - Parameters: +/// - decoder: 解码器 +/// - data: pcm 数据 +/// - error: 错误信息 +-(void)opusDecoder:(JLOpusDecoder *)decoder Data:(NSData* _Nullable)data error:(NSError* _Nullable)error; + +@end + +typedef void(^JLOpusDecoderConvertBlock)(NSString *_Nullable pcmPath,NSError *_Nullable error); + +/// Opus 解码 +@interface JLOpusDecoder : NSObject + +/// 数据格式参数 +@property (nonatomic, strong) JLOpusFormat *opusFormat; + +/// 代理委托 +@property(nonatomic, weak) id delegate; + +- (instancetype)init NS_UNAVAILABLE; + +/// 初始化 +/// - Parameters: +/// - format: 解码格式 +/// - delegate: 代理 +- (instancetype)initDecoder:(JLOpusFormat *)format delegate:(id)delegate; + +/// 重置解码格式 +/// - Parameter format: 解码格式 +-(void)resetOpusFramet:(JLOpusFormat *)format; + +/// 输入 Opus 数据 +/// - Parameter data: Opus 数据 +-(void)opusDecoderInputData:(NSData *)data; + +/// 解码文件 +/// - Parameters: +/// - input: opus 文件 +/// - outPut: 输出路径 +/// - result: 结果回调 +-(void)opusDecodeFile:(NSString *)input outPut:(NSString *_Nullable)outPut Resoult:(JLOpusDecoderConvertBlock _Nullable)result; + +/// 释放 +-(void)opusOnRelease; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusEncodeConfig.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusEncodeConfig.h new file mode 100644 index 000000000..037284e00 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusEncodeConfig.h @@ -0,0 +1,83 @@ +// +// JLOpusEncodeConfig.h +// JLAudioUnitKit +// +// Created by EzioChan on 2025/8/27. +// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, JLOpusEncoderBandwidth) { + ///(4kHz bandpass) + JLOpusEncoderBandwidthNarrowband = 1101, // 对应 OPUS_BANDWIDTH_NARROWBAND + /// (6kHz bandpass) + JLOpusEncoderBandwidthMediumband = 1102, // 对应 OPUS_BANDWIDTH_MEDIUMBAND + /// (8kHz bandpass) + JLOpusEncoderBandwidthWideband = 1103, // 对应 OPUS_BANDWIDTH_WIDEBAND + /// (12kHz bandpass) + JLOpusEncoderBandwidthSuperwideband = 1104,// 对应 OPUS_BANDWIDTH_SUPERWIDEBAND + /// (20kHz bandpass) + JLOpusEncoderBandwidthFullband = 1105 // 对应 OPUS_BANDWIDTH_FULLBAND +}; + +@interface JLOpusEncodeConfig : NSObject + +/// 采样率 +@property (nonatomic, assign) int sampleRate; + +/// 单/双声道 +@property (nonatomic, assign) int channels; + +/// 帧长度 20ms 默认值 +@property (nonatomic, assign) int frameDuration; + +/// 帧长度 +@property (nonatomic, assign) int frameSize; + +/// BitRate +@property (nonatomic, assign) int bitRate; + +/// 可变比特率开关,默认 NO(使用 CBR) +@property (nonatomic, assign) BOOL useVBR; + +/// VBR 限制模式,默认 NO +@property (nonatomic, assign) BOOL constrainedVBR; + +/// 编码复杂度 0~10,默认 5 +@property (nonatomic, assign) int complexity; + +/// 强制输出声道数 -1: 自适应, 1: 单声道, 2: 双声道, 默认 -1 +@property (nonatomic, assign) int forceChannels; + +/// 是否启用 DTX(静音段不发送数据),默认 NO +@property (nonatomic, assign) BOOL useDTX; + +/// 网络丢包百分比,默认 0 +@property (nonatomic, assign) int packetLossPercent; + +/// 最大带宽限制(OPUS_BANDWIDTH_*),默认 FULLBAND +@property (nonatomic, assign) int bandwidth; + +/// PCM 输入有效位深,默认 16 +@property (nonatomic, assign) int lsbDepth; + +/// 专家模式下帧时长(ms),默认与 frameDuration 一致 +@property (nonatomic, assign) int expertFrameDuration; + +/// 是否启用数据头 +/// 默认是启用的 +@property (nonatomic, assign) BOOL hasDataHeader; + +/// 创建默认编码配置 ++ (instancetype)defaultConfig; + +/// 获取默认配置 +/// 杰理的无头配置 ++ (instancetype)defaultJL; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusEncoder.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusEncoder.h new file mode 100644 index 000000000..24886a038 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusEncoder.h @@ -0,0 +1,69 @@ +// +// JLOpusEncoder.h +// JLAudioUnitKit +// +// Created by EzioChan on 2024/11/14. +// Copyright © 2024 ZhuHai JieLi Technology Co.,Ltd. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN +@class JLOpusEncoder; + +/// Opus 编码代理 +@protocol JLOpusEncoderDelegate + +/// PCM 数据编码 +/// - Parameters: +/// - encoder: 解码器 +/// - data: opus 数据 +/// - error: 错误信息 +-(void)opusEncoder:(JLOpusEncoder *)encoder Data:(NSData* _Nullable)data error:(NSError* _Nullable)error; + +@end + +typedef void(^JLOpusEncoderConvertBlock)(NSString *_Nullable pcmPath,NSError *_Nullable error); + +/// Opus 编码 +@interface JLOpusEncoder : NSObject + +/// 音频格式 +@property (nonatomic, strong) JLOpusEncodeConfig *opusFormat; + +/// 代理 +@property (nonatomic, weak) id delegate; + +-(instancetype)init NS_UNAVAILABLE; + +/// 初始化 +/// - Parameters: +/// - config: 音频格式 +/// - delegate: 代理 +-(instancetype)initFormat:(JLOpusEncodeConfig *)format delegate:(id)delegate; + +/// PCM 数据 +/// - Parameter data: PCM 数据 +-(void)opusEncodeData:(NSData *)data; + +/// PCM 文件转换成 Opus 文件(推荐新接口,流式读取,避免一次性加载内存) +/// - Parameters: +/// - pcmPath: PCM 文件存放路径 +/// - output: 文件输出路径(可空,默认 ~/Documents/pcmToOpus.opus) +/// - result: 结果回调,回传输出路径或错误 +-(void)opusEncodeFile:(NSString *)pcmPath output:(NSString *_Nullable)output result:(JLOpusEncoderConvertBlock _Nullable)result; + +/// 兼容旧接口(已废弃:命名拼写问题,且旧实现一次性读入文件) +/// - Parameters: +/// - pcmPath: PCM 文件存放路径 +/// - outPut: 文件输出路径 +/// - result: 结果回调 +-(void)opusEncodeFile:(NSString *)pcmPath outPut:(NSString *_Nullable)outPut Resoult:(JLOpusEncoderConvertBlock _Nullable)result __attribute__((deprecated("Use opusEncodeFile:output:result: instead."))); + +/// 释放 +-(void)opusOnRelease; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusFormat.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusFormat.h new file mode 100644 index 000000000..016548791 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusFormat.h @@ -0,0 +1,59 @@ +// +// JLOpusOptions.h +// JLAudioUnitKit +// +// Created by EzioChan on 2024/11/14. +// Copyright © 2024 ZhuHai JieLi Technology Co.,Ltd. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +#define OPUS_JL_MAX_FRAME_SIZE 48000 * 2 +#define OPUS_JL_MAX_PACKET_SIZE 1500 + + +@interface JLOpusFormat : NSObject + +/// 采样率 +@property (nonatomic, assign) int sampleRate; + +/// 单/双声道 +@property (nonatomic, assign) int channels; + +/// 帧长度 20ms 默认值 +@property (nonatomic, assign) int frameDuration; + +/// BitRate +@property (nonatomic, assign) int bitRate; + +/// 数据帧大小 +@property (nonatomic, assign, readonly) int frameSize; + +/// 数据大小 +@property (nonatomic, assign) int dataSize; + +/// 是否包含数据头部 +@property (nonatomic, assign) BOOL hasDataHeader; + +/// 是否开启slik语音优化 +@property (nonatomic, assign) BOOL usVoipSlik; + + +-(instancetype)init NS_UNAVAILABLE; + +/// 默认配置 +/// sampleRate: 16000 +/// channels: 1 +/// frameDuration: 20 +/// dataSize: 40 +/// frameSize: 320 +/// hasDataHeader: YES +/// bitRate: 16000 +/// usVoipSlik: false ++(JLOpusFormat*)defaultFormats; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusToOgg.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusToOgg.h new file mode 100644 index 000000000..27b919bde --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusToOgg.h @@ -0,0 +1,67 @@ +// +// JLOpusToOgg.h +// JLAudioUnitKit +// +// Created by EzioChan on 2025/5/16. +// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef void(^JLOpusToOggConvertBlock)(NSData *_Nullable oggData, BOOL isLast, NSError *_Nullable error); + +/// 将Opus数据转换为Ogg数据 +@interface JLOpusToOgg : NSObject + +/// 转码内容的回调 +@property(nonatomic,strong)JLOpusToOggConvertBlock _Nullable convertBlock; + +/// 帧长 +/// - Parameter frameLen: 帧长 +/// 默认值 40 +- (instancetype)initWithFrameLength:(uint32_t)frameLen; + +/// 开始流 +-(void)startStream; + +/// 添加Opus数据 +/// - Parameter opusData: 符合杰理的无头裸 opus 数据 +- (void)appendOpusData:(NSData *)opusData; + +/// 关闭流 +- (void)closeStream; + + +/// 将Opus数据转换为Ogg数据 +/// opus 必须为 16000 +/// opus 必须是 单声道 +/// - Parameters: +/// - opusData: 符合杰理的无头裸 opus 数据 +/// - frameLen: 帧长 公版默认帧长是 40 +/// - duration: 转换后的时长 +/// - error: 错误 ++ (NSData *_Nullable)convertOpusDataToOgg:(NSData *)opusData + frameLen:(uint32_t)frameLen + duration:(double *)duration + error:(NSError *__autoreleasing _Nullable *)error ; + +/// 将Opus文件转换为Ogg文件 +/// 符合杰理的无头裸 opus 文件 +/// opus 必须为 16000 +/// opus 必须是 单声道 +/// - Parameters: +/// - opusFilePath: opus文件路径 +/// - oggFilePath: ogg文件路径 +/// - frameLen: 帧长 公版默认帧长是 40 +/// - duration: 转换后的时长 ++(void)convertOpusFileToOgg:(NSString *)opusFilePath + oggFilePath:(NSString *)oggFilePath + frameLen:(uint32_t)frameLen + duration:(double *)duration; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLPcmToWav.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLPcmToWav.h new file mode 100644 index 000000000..b32ecef2d --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLPcmToWav.h @@ -0,0 +1,55 @@ +// +// JLPcmToWav.h +// JLAudioUnitKit +// +// Created by EzioChan on 2025/4/15. +// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// PCM 转 WAV +@interface JLPcmToWav : NSObject + +#pragma mark - 流式编码接口 + +/// 初始化流式编码器 +/// @param outputPath 输出文件路径 +/// @param sampleRate 采样率(如 16000) +/// @param numChannels 声道数(1 或 2) +/// @param bitsPerSample 位深(16) +- (instancetype)initWithOutputPath:(NSString *)outputPath + sampleRate:(uint32_t)sampleRate + numChannels:(uint16_t)numChannels + bitsPerSample:(uint16_t)bitsPerSample; + +/// 追加 PCM 数据(需确保数据格式与初始化参数一致) +/// @param pcmData PCM 数据块 +/// @param error 错误信息 +- (BOOL)appendPCMData:(NSData *)pcmData error:(NSError **)error; + +/// 完成编码并关闭文件(必须调用以更新 WAV 头) +/// @param error 错误信息 +- (BOOL)finishWithError:(NSError **)error; + +#pragma mark - 一次性编码接口(便捷方法) + +/// 将 PCM 数据一次性转为 WAV 文件 +/// @param pcmData PCM 数据块 +/// @param outputPath 输出文件路径 +/// @param sampleRate 采样率(如 16000) +/// @param numChannels 声道数(1 或 2) +/// @param bitsPerSample 位深(16) +/// @param error 错误信息 ++ (BOOL)convertPCMData:(NSData *)pcmData + toWAVFile:(NSString *)outputPath + sampleRate:(uint32_t)sampleRate + numChannels:(uint16_t)numChannels + bitsPerSample:(uint16_t)bitsPerSample + error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLPcmToWtg.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLPcmToWtg.h new file mode 100644 index 000000000..231b3e837 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLPcmToWtg.h @@ -0,0 +1,51 @@ +// +// JLPcmToWtg.h +// JLAudioUnitKit +// +// Created by EzioChan on 2025/1/21. +// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface JLPcm2WtgModel : NSObject + +/// 音频文件 +@property (nonatomic, copy) NSString *pcmPath; + +/// 输出文件 +@property (nonatomic, copy) NSString *wtgPath; + +@end + + +/// JLPcmToWtgProtocol +@protocol JLPcmToWtgDelegate + +/// 音频转码完成 +/// - Parameter model: 音频转码模型 +- (void)convertPcmToWtgDone:(JLPcm2WtgModel *)model; + +@end + +/// 音频转码 +/// PCM 转 WTG +@interface JLPcmToWtg : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/// 音频转码初始化 +/// - Parameter delegate: 音频转码代理 +- (instancetype)initWithDelegate:(id)delegate; + +/// 音频转码 +/// 当前接口模型只限制 PCM 文件格式为:8k 16bit +/// 输入的pcm文件(16bit,小端,8k采样率),wtg_filename:输出的wtg文件 +/// - Parameter model: 音频转码模型 +-(void)convertPcmToWtg:(JLPcm2WtgModel *)model; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLSpeexDecoder.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLSpeexDecoder.h new file mode 100644 index 000000000..ad223d087 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLSpeexDecoder.h @@ -0,0 +1,54 @@ +// +// JLSpeexUnit.h +// JLAudioUnitKit +// +// Created by EzioChan on 2024/11/14. +// + +#import + +NS_ASSUME_NONNULL_BEGIN +@class JLSpeexDecoder; + +@protocol JLSpeexDelegate + +/// Speex 数据解码 +/// - Parameters: +/// - decoder: 解码器 +/// - data: PCM 数据 +/// - error: 错误信息 +- (void)speexDecoder:(JLSpeexDecoder *)decoder Data:(NSData* _Nullable)data error:(NSError* _Nullable)error; + +@end + +typedef void(^JLSpeexConvertBlock)(NSString *_Nullable pcmPath,NSError *_Nullable error); + +/// Speex 数据解码 +@interface JLSpeexDecoder : NSObject + +-(instancetype)init NS_UNAVAILABLE; + +/// 初始化 +/// - Parameter delegate: 委托 +-(instancetype)initWithDelegate:(id)delegate; + +/// 委托 +@property (nonatomic, weak) id delegate; + +/// 输入 Speex 数据 +/// - Parameter data: Speex 数据 +-(void)speexInputData:(NSData*)data; + +/// 将 Speex 转换为 PCM +/// - Parameters: +/// - filePath: Speex 路径 +/// - opPath: 输出路径 +/// - result: 结果回调 +-(void)speexConvertToPcm:(NSString *)filePath outPutFilePath:(NSString * _Nullable)opPath Result:(JLSpeexConvertBlock)result; + +/// 释放对象 +-(void)speexOnRelease; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Info.plist new file mode 100644 index 000000000..2651c6526 Binary files /dev/null and b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Info.plist differ diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/JLAudioUnitKit b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/JLAudioUnitKit new file mode 100755 index 000000000..871fc91fe Binary files /dev/null and b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/JLAudioUnitKit differ diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Modules/module.modulemap new file mode 100644 index 000000000..5e1ae1d3d --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module JLAudioUnitKit { + umbrella header "JLAudioUnitKit.h" + export * + + module * { export * } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/readme.txt b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/readme.txt new file mode 100644 index 000000000..6789fe6e1 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/readme.txt @@ -0,0 +1,52 @@ +DESCRIPTION +----------- +This package includes the files needed to build the fixed point implementation +of the ITU-T G.729 Appendix IV. + +DIFFERENCES WITH G.729B +----------------------- +The ANSI-C source code simulating the bit-exact, fixed point simulation +software of enhanced VAD is integrated into the G.729B source code. +The modification to the G.729B source code updated files of coder.c, +cod_ld8k.c, bits.c, dec_sid.c, dtx.c, qsidgain.c, qsidlsf.c, tab_dtx.c, +tab_dtx.h, ld8k.h, deleted vad.c/h and integrated the enhanced VAD functions +into the G.729B project. The modification also replaced the original basic +operators used in the original G.729B source code by the basic operators of +ITU-T Software Tool Library STL2005. + +COMPILATION +----------- +For UNIX systems the following makefiles are provided + + coder.mak + decoder.mak + +Edit the makefiles coder.mak and decoder.mak to set the proper options +for your system. +The command to compile and link all code on a UNIX system is + + make -f coder.mak + make -f decoder.mak + +For other platforms, the *.mak files can be used to work out the +compilation procedures. + +USAGE +----- +The command line instruction for the encoder is as follows: + coder inputfile bitstreamfile dtx_option mode x +where: + inputfile : 8 kHz sampled data file containing 16 bit linear PCM signal + bitstreamfile: binary file containing bitstream + dtx_option : = 1 : DTX enabled 0 : DTX disabled + mode X : Run encoder with enhanced VAD in operating point X, + X=0 or 1 or 2 representing balanced or quality-preferred + or bandwidth saving operating point, respectively + +The command line for the decoder is as follows: + decoder bitstreamfile outputfile +where: + bitstreamfile: binary file containing bitstream + outputfile : 8 kHz sampled data file containing 16 bit linear PCM signal + + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLAudioUnitKit.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLAudioUnitKit.h new file mode 100644 index 000000000..e6b3af64f --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLAudioUnitKit.h @@ -0,0 +1,26 @@ +// +// JLAudioUnitKit.h +// JLAudioUnitKit +// +// Created by EzioChan on 2024/11/14. +// + +#import + +//! Project version number for JLAudioUnitKit. +FOUNDATION_EXPORT double JLAudioUnitKitVersionNumber; + +//! Project version string for JLAudioUnitKit. +FOUNDATION_EXPORT const unsigned char JLAudioUnitKitVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + +#import +#import +#import +#import +#import +#import +#import +#import +#import diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLAudioUnitPlayer.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLAudioUnitPlayer.h new file mode 100644 index 000000000..7da831c67 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLAudioUnitPlayer.h @@ -0,0 +1,58 @@ +// +// JLAudioUnitPlayer.h +// JLAudioUnitKit +// +// Created by EzioChan on 2025/4/16. +// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, JLAudioUnitPlayerType) { + JLAudioUnitPlayerTypeFile, // 支持 MP3/WAV/AAC 等文件格式(AVAudioPlayer) + JLAudioUnitPlayerTypePCM // 支持 PCM 流式播放(Audio Queue) +}; + +@class JLAudioUnitPlayer; + +@protocol JLAudioPlayerDelegate +@optional +// 播放进度更新(秒) +- (void)audioPlayer:(JLAudioUnitPlayer *)player didUpdateProgress:(NSTimeInterval)currentTime duration:(NSTimeInterval)duration; +// 播放完成 +- (void)audioPlayerDidFinishPlaying:(JLAudioUnitPlayer *)player; +// 播放错误 +- (void)audioPlayer:(JLAudioUnitPlayer *)player didFailWithError:(NSError *)error; +@end + +@interface JLAudioUnitPlayer : NSObject + +@property (nonatomic, weak) id delegate; +@property (nonatomic, readonly) NSTimeInterval duration; // 总时长(文件模式下有效) +@property (nonatomic, readonly) NSTimeInterval currentTime; +@property (nonatomic, readonly) BOOL isPlaying; + +#pragma mark - 初始化方法 + +/// 文件播放初始化(MP3/WAV/AAC) +- (instancetype)initWithAudioFile:(NSString *)filePath; + +/// PCM 流播放初始化(需指定格式) +- (instancetype)initWithPCMFormat:(AudioStreamBasicDescription)format; + +#pragma mark - 播放控制 +- (void)play; +- (void)pause; +- (void)stop; +- (void)seekToTime:(NSTimeInterval)time; // 仅文件模式有效 + +#pragma mark - PCM 流式输入(仅 PCM 模式有效) +- (void)appendPCMData:(NSData *)pcmData; +- (void)endPCMStream; // 结束 PCM 流输入 + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusDecoder.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusDecoder.h new file mode 100644 index 000000000..711e2e99a --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusDecoder.h @@ -0,0 +1,66 @@ +// +// JLOpusDecoder.h +// JLAudioUnitKit +// +// Created by EzioChan on 2024/11/14. +// Copyright © 2024 ZhuHai JieLi Technology Co.,Ltd. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN +@class JLOpusDecoder; + +/// Opus 解码代理 +@protocol JLOpusDecoderDelegate + +/// Opus 数据解码 +/// - Parameters: +/// - decoder: 解码器 +/// - data: pcm 数据 +/// - error: 错误信息 +-(void)opusDecoder:(JLOpusDecoder *)decoder Data:(NSData* _Nullable)data error:(NSError* _Nullable)error; + +@end + +typedef void(^JLOpusDecoderConvertBlock)(NSString *_Nullable pcmPath,NSError *_Nullable error); + +/// Opus 解码 +@interface JLOpusDecoder : NSObject + +/// 数据格式参数 +@property (nonatomic, strong) JLOpusFormat *opusFormat; + +/// 代理委托 +@property(nonatomic, weak) id delegate; + +- (instancetype)init NS_UNAVAILABLE; + +/// 初始化 +/// - Parameters: +/// - format: 解码格式 +/// - delegate: 代理 +- (instancetype)initDecoder:(JLOpusFormat *)format delegate:(id)delegate; + +/// 重置解码格式 +/// - Parameter format: 解码格式 +-(void)resetOpusFramet:(JLOpusFormat *)format; + +/// 输入 Opus 数据 +/// - Parameter data: Opus 数据 +-(void)opusDecoderInputData:(NSData *)data; + +/// 解码文件 +/// - Parameters: +/// - input: opus 文件 +/// - outPut: 输出路径 +/// - result: 结果回调 +-(void)opusDecodeFile:(NSString *)input outPut:(NSString *_Nullable)outPut Resoult:(JLOpusDecoderConvertBlock _Nullable)result; + +/// 释放 +-(void)opusOnRelease; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusEncodeConfig.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusEncodeConfig.h new file mode 100644 index 000000000..037284e00 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusEncodeConfig.h @@ -0,0 +1,83 @@ +// +// JLOpusEncodeConfig.h +// JLAudioUnitKit +// +// Created by EzioChan on 2025/8/27. +// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, JLOpusEncoderBandwidth) { + ///(4kHz bandpass) + JLOpusEncoderBandwidthNarrowband = 1101, // 对应 OPUS_BANDWIDTH_NARROWBAND + /// (6kHz bandpass) + JLOpusEncoderBandwidthMediumband = 1102, // 对应 OPUS_BANDWIDTH_MEDIUMBAND + /// (8kHz bandpass) + JLOpusEncoderBandwidthWideband = 1103, // 对应 OPUS_BANDWIDTH_WIDEBAND + /// (12kHz bandpass) + JLOpusEncoderBandwidthSuperwideband = 1104,// 对应 OPUS_BANDWIDTH_SUPERWIDEBAND + /// (20kHz bandpass) + JLOpusEncoderBandwidthFullband = 1105 // 对应 OPUS_BANDWIDTH_FULLBAND +}; + +@interface JLOpusEncodeConfig : NSObject + +/// 采样率 +@property (nonatomic, assign) int sampleRate; + +/// 单/双声道 +@property (nonatomic, assign) int channels; + +/// 帧长度 20ms 默认值 +@property (nonatomic, assign) int frameDuration; + +/// 帧长度 +@property (nonatomic, assign) int frameSize; + +/// BitRate +@property (nonatomic, assign) int bitRate; + +/// 可变比特率开关,默认 NO(使用 CBR) +@property (nonatomic, assign) BOOL useVBR; + +/// VBR 限制模式,默认 NO +@property (nonatomic, assign) BOOL constrainedVBR; + +/// 编码复杂度 0~10,默认 5 +@property (nonatomic, assign) int complexity; + +/// 强制输出声道数 -1: 自适应, 1: 单声道, 2: 双声道, 默认 -1 +@property (nonatomic, assign) int forceChannels; + +/// 是否启用 DTX(静音段不发送数据),默认 NO +@property (nonatomic, assign) BOOL useDTX; + +/// 网络丢包百分比,默认 0 +@property (nonatomic, assign) int packetLossPercent; + +/// 最大带宽限制(OPUS_BANDWIDTH_*),默认 FULLBAND +@property (nonatomic, assign) int bandwidth; + +/// PCM 输入有效位深,默认 16 +@property (nonatomic, assign) int lsbDepth; + +/// 专家模式下帧时长(ms),默认与 frameDuration 一致 +@property (nonatomic, assign) int expertFrameDuration; + +/// 是否启用数据头 +/// 默认是启用的 +@property (nonatomic, assign) BOOL hasDataHeader; + +/// 创建默认编码配置 ++ (instancetype)defaultConfig; + +/// 获取默认配置 +/// 杰理的无头配置 ++ (instancetype)defaultJL; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusEncoder.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusEncoder.h new file mode 100644 index 000000000..24886a038 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusEncoder.h @@ -0,0 +1,69 @@ +// +// JLOpusEncoder.h +// JLAudioUnitKit +// +// Created by EzioChan on 2024/11/14. +// Copyright © 2024 ZhuHai JieLi Technology Co.,Ltd. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN +@class JLOpusEncoder; + +/// Opus 编码代理 +@protocol JLOpusEncoderDelegate + +/// PCM 数据编码 +/// - Parameters: +/// - encoder: 解码器 +/// - data: opus 数据 +/// - error: 错误信息 +-(void)opusEncoder:(JLOpusEncoder *)encoder Data:(NSData* _Nullable)data error:(NSError* _Nullable)error; + +@end + +typedef void(^JLOpusEncoderConvertBlock)(NSString *_Nullable pcmPath,NSError *_Nullable error); + +/// Opus 编码 +@interface JLOpusEncoder : NSObject + +/// 音频格式 +@property (nonatomic, strong) JLOpusEncodeConfig *opusFormat; + +/// 代理 +@property (nonatomic, weak) id delegate; + +-(instancetype)init NS_UNAVAILABLE; + +/// 初始化 +/// - Parameters: +/// - config: 音频格式 +/// - delegate: 代理 +-(instancetype)initFormat:(JLOpusEncodeConfig *)format delegate:(id)delegate; + +/// PCM 数据 +/// - Parameter data: PCM 数据 +-(void)opusEncodeData:(NSData *)data; + +/// PCM 文件转换成 Opus 文件(推荐新接口,流式读取,避免一次性加载内存) +/// - Parameters: +/// - pcmPath: PCM 文件存放路径 +/// - output: 文件输出路径(可空,默认 ~/Documents/pcmToOpus.opus) +/// - result: 结果回调,回传输出路径或错误 +-(void)opusEncodeFile:(NSString *)pcmPath output:(NSString *_Nullable)output result:(JLOpusEncoderConvertBlock _Nullable)result; + +/// 兼容旧接口(已废弃:命名拼写问题,且旧实现一次性读入文件) +/// - Parameters: +/// - pcmPath: PCM 文件存放路径 +/// - outPut: 文件输出路径 +/// - result: 结果回调 +-(void)opusEncodeFile:(NSString *)pcmPath outPut:(NSString *_Nullable)outPut Resoult:(JLOpusEncoderConvertBlock _Nullable)result __attribute__((deprecated("Use opusEncodeFile:output:result: instead."))); + +/// 释放 +-(void)opusOnRelease; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusFormat.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusFormat.h new file mode 100644 index 000000000..016548791 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusFormat.h @@ -0,0 +1,59 @@ +// +// JLOpusOptions.h +// JLAudioUnitKit +// +// Created by EzioChan on 2024/11/14. +// Copyright © 2024 ZhuHai JieLi Technology Co.,Ltd. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +#define OPUS_JL_MAX_FRAME_SIZE 48000 * 2 +#define OPUS_JL_MAX_PACKET_SIZE 1500 + + +@interface JLOpusFormat : NSObject + +/// 采样率 +@property (nonatomic, assign) int sampleRate; + +/// 单/双声道 +@property (nonatomic, assign) int channels; + +/// 帧长度 20ms 默认值 +@property (nonatomic, assign) int frameDuration; + +/// BitRate +@property (nonatomic, assign) int bitRate; + +/// 数据帧大小 +@property (nonatomic, assign, readonly) int frameSize; + +/// 数据大小 +@property (nonatomic, assign) int dataSize; + +/// 是否包含数据头部 +@property (nonatomic, assign) BOOL hasDataHeader; + +/// 是否开启slik语音优化 +@property (nonatomic, assign) BOOL usVoipSlik; + + +-(instancetype)init NS_UNAVAILABLE; + +/// 默认配置 +/// sampleRate: 16000 +/// channels: 1 +/// frameDuration: 20 +/// dataSize: 40 +/// frameSize: 320 +/// hasDataHeader: YES +/// bitRate: 16000 +/// usVoipSlik: false ++(JLOpusFormat*)defaultFormats; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusToOgg.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusToOgg.h new file mode 100644 index 000000000..27b919bde --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusToOgg.h @@ -0,0 +1,67 @@ +// +// JLOpusToOgg.h +// JLAudioUnitKit +// +// Created by EzioChan on 2025/5/16. +// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef void(^JLOpusToOggConvertBlock)(NSData *_Nullable oggData, BOOL isLast, NSError *_Nullable error); + +/// 将Opus数据转换为Ogg数据 +@interface JLOpusToOgg : NSObject + +/// 转码内容的回调 +@property(nonatomic,strong)JLOpusToOggConvertBlock _Nullable convertBlock; + +/// 帧长 +/// - Parameter frameLen: 帧长 +/// 默认值 40 +- (instancetype)initWithFrameLength:(uint32_t)frameLen; + +/// 开始流 +-(void)startStream; + +/// 添加Opus数据 +/// - Parameter opusData: 符合杰理的无头裸 opus 数据 +- (void)appendOpusData:(NSData *)opusData; + +/// 关闭流 +- (void)closeStream; + + +/// 将Opus数据转换为Ogg数据 +/// opus 必须为 16000 +/// opus 必须是 单声道 +/// - Parameters: +/// - opusData: 符合杰理的无头裸 opus 数据 +/// - frameLen: 帧长 公版默认帧长是 40 +/// - duration: 转换后的时长 +/// - error: 错误 ++ (NSData *_Nullable)convertOpusDataToOgg:(NSData *)opusData + frameLen:(uint32_t)frameLen + duration:(double *)duration + error:(NSError *__autoreleasing _Nullable *)error ; + +/// 将Opus文件转换为Ogg文件 +/// 符合杰理的无头裸 opus 文件 +/// opus 必须为 16000 +/// opus 必须是 单声道 +/// - Parameters: +/// - opusFilePath: opus文件路径 +/// - oggFilePath: ogg文件路径 +/// - frameLen: 帧长 公版默认帧长是 40 +/// - duration: 转换后的时长 ++(void)convertOpusFileToOgg:(NSString *)opusFilePath + oggFilePath:(NSString *)oggFilePath + frameLen:(uint32_t)frameLen + duration:(double *)duration; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLPcmToWav.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLPcmToWav.h new file mode 100644 index 000000000..b32ecef2d --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLPcmToWav.h @@ -0,0 +1,55 @@ +// +// JLPcmToWav.h +// JLAudioUnitKit +// +// Created by EzioChan on 2025/4/15. +// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// PCM 转 WAV +@interface JLPcmToWav : NSObject + +#pragma mark - 流式编码接口 + +/// 初始化流式编码器 +/// @param outputPath 输出文件路径 +/// @param sampleRate 采样率(如 16000) +/// @param numChannels 声道数(1 或 2) +/// @param bitsPerSample 位深(16) +- (instancetype)initWithOutputPath:(NSString *)outputPath + sampleRate:(uint32_t)sampleRate + numChannels:(uint16_t)numChannels + bitsPerSample:(uint16_t)bitsPerSample; + +/// 追加 PCM 数据(需确保数据格式与初始化参数一致) +/// @param pcmData PCM 数据块 +/// @param error 错误信息 +- (BOOL)appendPCMData:(NSData *)pcmData error:(NSError **)error; + +/// 完成编码并关闭文件(必须调用以更新 WAV 头) +/// @param error 错误信息 +- (BOOL)finishWithError:(NSError **)error; + +#pragma mark - 一次性编码接口(便捷方法) + +/// 将 PCM 数据一次性转为 WAV 文件 +/// @param pcmData PCM 数据块 +/// @param outputPath 输出文件路径 +/// @param sampleRate 采样率(如 16000) +/// @param numChannels 声道数(1 或 2) +/// @param bitsPerSample 位深(16) +/// @param error 错误信息 ++ (BOOL)convertPCMData:(NSData *)pcmData + toWAVFile:(NSString *)outputPath + sampleRate:(uint32_t)sampleRate + numChannels:(uint16_t)numChannels + bitsPerSample:(uint16_t)bitsPerSample + error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLPcmToWtg.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLPcmToWtg.h new file mode 100644 index 000000000..231b3e837 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLPcmToWtg.h @@ -0,0 +1,51 @@ +// +// JLPcmToWtg.h +// JLAudioUnitKit +// +// Created by EzioChan on 2025/1/21. +// Copyright © 2025 ZhuHai JieLi Technology Co.,Ltd. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface JLPcm2WtgModel : NSObject + +/// 音频文件 +@property (nonatomic, copy) NSString *pcmPath; + +/// 输出文件 +@property (nonatomic, copy) NSString *wtgPath; + +@end + + +/// JLPcmToWtgProtocol +@protocol JLPcmToWtgDelegate + +/// 音频转码完成 +/// - Parameter model: 音频转码模型 +- (void)convertPcmToWtgDone:(JLPcm2WtgModel *)model; + +@end + +/// 音频转码 +/// PCM 转 WTG +@interface JLPcmToWtg : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/// 音频转码初始化 +/// - Parameter delegate: 音频转码代理 +- (instancetype)initWithDelegate:(id)delegate; + +/// 音频转码 +/// 当前接口模型只限制 PCM 文件格式为:8k 16bit +/// 输入的pcm文件(16bit,小端,8k采样率),wtg_filename:输出的wtg文件 +/// - Parameter model: 音频转码模型 +-(void)convertPcmToWtg:(JLPcm2WtgModel *)model; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLSpeexDecoder.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLSpeexDecoder.h new file mode 100644 index 000000000..ad223d087 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLSpeexDecoder.h @@ -0,0 +1,54 @@ +// +// JLSpeexUnit.h +// JLAudioUnitKit +// +// Created by EzioChan on 2024/11/14. +// + +#import + +NS_ASSUME_NONNULL_BEGIN +@class JLSpeexDecoder; + +@protocol JLSpeexDelegate + +/// Speex 数据解码 +/// - Parameters: +/// - decoder: 解码器 +/// - data: PCM 数据 +/// - error: 错误信息 +- (void)speexDecoder:(JLSpeexDecoder *)decoder Data:(NSData* _Nullable)data error:(NSError* _Nullable)error; + +@end + +typedef void(^JLSpeexConvertBlock)(NSString *_Nullable pcmPath,NSError *_Nullable error); + +/// Speex 数据解码 +@interface JLSpeexDecoder : NSObject + +-(instancetype)init NS_UNAVAILABLE; + +/// 初始化 +/// - Parameter delegate: 委托 +-(instancetype)initWithDelegate:(id)delegate; + +/// 委托 +@property (nonatomic, weak) id delegate; + +/// 输入 Speex 数据 +/// - Parameter data: Speex 数据 +-(void)speexInputData:(NSData*)data; + +/// 将 Speex 转换为 PCM +/// - Parameters: +/// - filePath: Speex 路径 +/// - opPath: 输出路径 +/// - result: 结果回调 +-(void)speexConvertToPcm:(NSString *)filePath outPutFilePath:(NSString * _Nullable)opPath Result:(JLSpeexConvertBlock)result; + +/// 释放对象 +-(void)speexOnRelease; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/Frameworks/JLAV2Lib.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Info.plist similarity index 54% rename from local_plugins/device_jieli/ios/Frameworks/JLAV2Lib.framework/Info.plist rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Info.plist index ccf3f1eb5..832461042 100644 Binary files a/local_plugins/device_jieli/ios/Frameworks/JLAV2Lib.framework/Info.plist and b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Info.plist differ diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/JLAudioUnitKit b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/JLAudioUnitKit new file mode 100755 index 000000000..98f3b2b9e Binary files /dev/null and b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/JLAudioUnitKit differ diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Modules/module.modulemap new file mode 100644 index 000000000..5e1ae1d3d --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module JLAudioUnitKit { + umbrella header "JLAudioUnitKit.h" + export * + + module * { export * } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/_CodeSignature/CodeResources b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/_CodeSignature/CodeResources new file mode 100644 index 000000000..dab32655b --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/_CodeSignature/CodeResources @@ -0,0 +1,234 @@ + + + + + files + + Headers/JLAudioUnitKit.h + + hE2nr+4q2/NdSgBaayJc7ZoOXb0= + + Headers/JLAudioUnitPlayer.h + + QPMLV/XA8hjk39d+vT+S57t0fDU= + + Headers/JLOpusDecoder.h + + K1bYcaJ67prImNlGuldy8csTRHc= + + Headers/JLOpusEncodeConfig.h + + NArAew5QxnSJGsD0tRzQ2V7bHrY= + + Headers/JLOpusEncoder.h + + 7DYUlvXrp1QC9l2XZ5tNHtsmsBQ= + + Headers/JLOpusFormat.h + + Du1x3VNk87NstmspRk7ElijuZrk= + + Headers/JLOpusToOgg.h + + fSJT6LcyylJHTHX1wsN3JHG2FxU= + + Headers/JLPcmToWav.h + + /BbZmqmWpWVBusXqjnzbjaBNCNg= + + Headers/JLPcmToWtg.h + + zoOdOFpIulND0F1OrfIXR1H0kh0= + + Headers/JLSpeexDecoder.h + + 5ecBKtD3BDGccLiKpTs0fS0CSgs= + + Info.plist + + pXqOzYCqPnMLY0nonWZTcbfnlhU= + + Modules/module.modulemap + + 2vYf0m+hGTOTapX6sliv/F1E1VI= + + readme.txt + + wijZX0Dr2OlODWS6XYbpeKabb7g= + + + files2 + + Headers/JLAudioUnitKit.h + + hash2 + + J5pq6Ed3fR+cQ7CGPXDoPl7BZzix7Cjf5yJZFRRvxqs= + + + Headers/JLAudioUnitPlayer.h + + hash2 + + qBz0OA82HUH4kBtO8xiUw7Q4ohtiIvCSYfCqax5xjJY= + + + Headers/JLOpusDecoder.h + + hash2 + + cnAj6Nt6vgRLoEH6QY3Q+gYpjcxGPN1OLLZbCgdLymI= + + + Headers/JLOpusEncodeConfig.h + + hash2 + + qrPpsrXaNCvyxf0/gUZtvveL+ywsfgBUrMPFibjneOo= + + + Headers/JLOpusEncoder.h + + hash2 + + YoHsvoGH2VEjvEIzEa7t+cAnzf8kHEU3dJTd03ABr1w= + + + Headers/JLOpusFormat.h + + hash2 + + Nm6vM9/EbFzyIWbsysu6EOO2S+vZVeplcop4NvoJaMA= + + + Headers/JLOpusToOgg.h + + hash2 + + dNSvJJnED/6E1p7TYZ4zBzUnLDVsZ8NovuNYjge6T/w= + + + Headers/JLPcmToWav.h + + hash2 + + l/SEQhSFh7ctq/pBV+Z7X+Z0/gz30eCw3s31otjMPho= + + + Headers/JLPcmToWtg.h + + hash2 + + MwtbX3Sa0nytA+m11x+1rDvP4WvJnZLljshuT5B3WD0= + + + Headers/JLSpeexDecoder.h + + hash2 + + pV0X3xn07Ps7wE+0EfL9Yu/08tp8Ug8s14iDigRU46o= + + + Modules/module.modulemap + + hash2 + + akFCUM59aSAaRM/ADWQ138YT9GD3oAjcaTvTUo24Ml8= + + + readme.txt + + hash2 + + +2GG9ORrf2yJnBe2joKWtyBZL9p2sCH8EXqK1GIgW0E= + + + + rules + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/readme.txt b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/readme.txt new file mode 100644 index 000000000..6789fe6e1 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/readme.txt @@ -0,0 +1,52 @@ +DESCRIPTION +----------- +This package includes the files needed to build the fixed point implementation +of the ITU-T G.729 Appendix IV. + +DIFFERENCES WITH G.729B +----------------------- +The ANSI-C source code simulating the bit-exact, fixed point simulation +software of enhanced VAD is integrated into the G.729B source code. +The modification to the G.729B source code updated files of coder.c, +cod_ld8k.c, bits.c, dec_sid.c, dtx.c, qsidgain.c, qsidlsf.c, tab_dtx.c, +tab_dtx.h, ld8k.h, deleted vad.c/h and integrated the enhanced VAD functions +into the G.729B project. The modification also replaced the original basic +operators used in the original G.729B source code by the basic operators of +ITU-T Software Tool Library STL2005. + +COMPILATION +----------- +For UNIX systems the following makefiles are provided + + coder.mak + decoder.mak + +Edit the makefiles coder.mak and decoder.mak to set the proper options +for your system. +The command to compile and link all code on a UNIX system is + + make -f coder.mak + make -f decoder.mak + +For other platforms, the *.mak files can be used to work out the +compilation procedures. + +USAGE +----- +The command line instruction for the encoder is as follows: + coder inputfile bitstreamfile dtx_option mode x +where: + inputfile : 8 kHz sampled data file containing 16 bit linear PCM signal + bitstreamfile: binary file containing bitstream + dtx_option : = 1 : DTX enabled 0 : DTX disabled + mode X : Run encoder with enhanced VAD in operating point X, + X=0 or 1 or 2 representing balanced or quality-preferred + or bandwidth saving operating point, respectively + +The command line for the decoder is as follows: + decoder bitstreamfile outputfile +where: + bitstreamfile: binary file containing bitstream + outputfile : 8 kHz sampled data file containing 16 bit linear PCM signal + + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/Info.plist new file mode 100644 index 000000000..db1458689 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/Info.plist @@ -0,0 +1,44 @@ + + + + + AvailableLibraries + + + BinaryPath + JLBmpConvertKit.framework/JLBmpConvertKit + LibraryIdentifier + ios-arm64 + LibraryPath + JLBmpConvertKit.framework + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + BinaryPath + JLBmpConvertKit.framework/JLBmpConvertKit + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + JLBmpConvertKit.framework + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLBmpConvert.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLBmpConvert.h new file mode 100644 index 000000000..1d20bffaa --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLBmpConvert.h @@ -0,0 +1,95 @@ +// +// JLBmpCovert.h +// JLBmpCovertKit +// +// Created by EzioChan on 2024/9/9. +// Copyright © 2024 www.zh-jieli.com. All rights reserved. +// + +#import +#import +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST +#import +#elif TARGET_OS_OSX +#import +#endif + + +NS_ASSUME_NONNULL_BEGIN +@class JLImageConvertResult; + +typedef void(^JLBmpCovertCallBack)(NSString *inFilePath,NSString *__nullable outFilePath,NSError *__nullable error) __deprecated_msg("This interface is deprecated. Please use convert:inFilePath:outFilePath: or convert:Image: instead."); + +typedef void(^JLBmpCovertCallBack2)(NSData *__nullable outFileData,NSError *__nullable error) __deprecated_msg("This interface is deprecated. Please use convert:inFilePath:outFilePath: or convert:Image: instead."); + + +/// 图像转换 +@interface JLBmpConvert : NSObject +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + +/// 图片转换 +/// - Parameters: +/// - type: 转换类型 +/// - inFilePath: 输入文件 +/// - outFilePath: 输出文件 +/// - completion: 回调 ++(void)covert:(JLBmpConvertType)type + inFilePath:(NSString *_Nonnull)inFilePath + outFilePath:(NSString * __nullable)outFilePath + completion:(JLBmpCovertCallBack _Nonnull)completion __deprecated_msg("This interface is deprecated. Please use convert:inFilePath:outFilePath: or convert:Image: instead."); + +/// 图片转换 +/// - Parameters: +/// - type: 转换类型 +/// - inImage: 输入图片 +/// - completion: 回调 ++(void)covert:(JLBmpConvertType)type + Image:(UIImage *)inImage + completion:(JLBmpCovertCallBack2 _Nonnull)completion __deprecated_msg("This interface is deprecated. Please use convert:inFilePath:outFilePath: or convert:Image: instead."); + +/// 图片转换 +/// - Parameters: +/// - option: 类型 +/// 当前仅仅 707N 系列芯片可指定图像格式 +/// - inFilePath: 输入文件 +/// - outFilePath: 输出文件 +/// 文件路径转换入口 ++ (JLImageConvertResult *)convert:(JLBmpConvertOption *)option + inFilePath:(NSString *)inFilePath + outFilePath:(NSString * __nullable)outFilePath; + +/// UIImage 转换 +/// 当前仅仅 707N 系列芯片可指定图像格式 +/// - Parameters: +/// - option: 类型 +/// - imgData: 图片的原始数据 ++(JLImageConvertResult *)convert:(JLBmpConvertOption *)option ImageData:(NSData *)imgData; + + +/// UIImage 转换成 BGRA +/// - Parameter image: 图片 ++(NSData*)convertImageToBGRABytes:(UIImage *)image; + +/// UIImage 转换成 RGBA +/// - Parameter image: 图片 ++(NSData*)convertImageToRGBABytes:(UIImage *)image; + + +/// 图片缩放 +/// - Parameters: +/// - image: 图片 +/// - newSize: 缩放大小 +/// - Returns: 缩放后的图片 ++(NSData *)resizeImage:(UIImage*)image andResizeTo:(CGSize)newSize; + +#elif TARGET_OS_OSX +/// 图片转换 +/// - Parameters: +/// - option: 转换类型 +/// - inImage: 输入图片 ++(JLImageConvertResult *)convert:(JLBmpConvertOption *)option Image:(NSImage *)image; + +#endif +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLBmpConvertKit.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLBmpConvertKit.h new file mode 100644 index 000000000..9ff73bf13 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLBmpConvertKit.h @@ -0,0 +1,24 @@ +// +// JLBmpConvertKit.h +// JLBmpConvertKit +// +// Created by EzioChan on 2024/9/9. +// Copyright © 2024 www.zh-jieli.com. All rights reserved. +// + +#import + +//! Project version number for JLBmpConvertKit. +FOUNDATION_EXPORT double JLBmpConvertKitVersionNumber; + +//! Project version string for JLBmpConvertKit. +FOUNDATION_EXPORT const unsigned char JLBmpConvertKitVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + +#import +#import +#import +#import + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLBmpConvertOption.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLBmpConvertOption.h new file mode 100644 index 000000000..41e05510c --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLBmpConvertOption.h @@ -0,0 +1,89 @@ +// +// JLBmpConvertOption.h +// JLBmpConvertKit +// +// Created by EzioChan on 2025/5/23. +// Copyright © 2025 www.zh-jieli.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// 图像转换类型 +typedef NS_ENUM(NSUInteger, JLBmpConvertType) { + /// 695N 图像转换 RGB 转换成 695N 芯片对应的图像资源 + /// 对应原 BR23 转换算法 + JLBmpConvertType695N_RBG = 0, + /// 701N 图像转换 RGB 转换成 701N 芯片对应的图像资源 + /// 对应原 BR28 转换的算法 + JLBmpConvertType701N_RBG = 1, + /// 701N 图像转换 ARGB 转换成 701N 芯片对应的图像资源 + /// 对应原 BR28_ARBG 转换的算法 + JLBmpConvertType701N_ARBG = 2, + /// 701N 图像转换 RGB 转换成 701N 芯片对应的图像资源(不打包封装) + /// 对应原 BR28_RGB_NO_PACK 转换的算法(不打包封装) + JLBmpConvertType701N_RBG_NO_PACK = 3, + /// 701N 图像转换 ARGB 转换成 701N 芯片对应的图像资源(不打包封装) + /// 对应原 BR28_ARGB_NO_PACK 转换的算法(不打包封装) + JLBmpConvertType701N_ARGB_NO_PACK = 4, + /// 707N 图像转换 RGB 转换成 707N 芯片对应的图像资源 + /// 对应 BR35 转换的算法 + JLBmpConvertType707N_RBG = 5, + /// 707N 图像转换 ARGB 转换成 707N 芯片对应的图像资源 + /// 对应 BR35_ARGB 转换的算法 + JLBmpConvertType707N_ARGB = 6, + /// 707N 图像转换 RGB 转换成 707N 芯片对应的图像资源(不打包封装) + /// 对应 BR35_RGB_NO_PACK 转换的算法(不打包封装) + JLBmpConvertType707N_ARGB_NO_PACK = 7, + /// 707N 图像转换 RGB 转换成 707N 芯片对应的图像资源(不打包封装) + /// 对应 BR35_RGB_NO_PACK 转换的算法(不打包封装) + JLBmpConvertType707N_RBG_NO_PACK = 8, + /// 701N 图像转换 JPEG + /// 仅仅支持 JPEG 类型 + JLBmpConvertType701N_JPEG = 9 +}; + +/// 图像格式 +/// 仅(707N 系列芯片支持) +typedef NS_ENUM(NSUInteger, JLBmpPixelformat) { + /// ARBG8888/RBG888 格式 + JLBmpPixelformat_888, + /// ARBG8565/RBG565 格式 + JLBmpPixelformat_565, + /// 自动选择最小的格式 + JLBmpPixelformat_Auto, +}; + +/// LVGL 打包格式 +typedef NS_ENUM(NSUInteger, JLBmpPacketFormat) { + JLBmpPacketFormatNone = 0, + JLBmpPacketFormatJLUI = 1, + JLBmpPacketFormatLVGL = 2 +}; + +/// 图像转换选项 +@interface JLBmpConvertOption : NSObject + +/// 图像转换类型 +@property (nonatomic, assign) JLBmpConvertType convertType; + +/// 图像格式 +/// 默认为 auto +/// 仅(707N 系列芯片支持) +@property (nonatomic, assign) JLBmpPixelformat pixelformat; + +/// LVGL 打包格式 +/// 默认为 JLUI +/// 仅(707N 系列芯片支持) +@property (nonatomic, assign) JLBmpPacketFormat packetFormat; + +/// 是否转换成 BGRA +/// 当使用的是 JL 的 UI 框架时需要使用 +/// 使用其他第三方框架时,需要根据固件端框架进行调整 +/// default YES +@property (nonatomic, assign) BOOL convertToBGRA; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLGifBin.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLGifBin.h new file mode 100644 index 000000000..5b7cdc2d7 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLGifBin.h @@ -0,0 +1,77 @@ +// +// JLGifBin.h +// JLGifLib +// +// Created by EzioChan on 2024/1/18. +// Copyright © 2024 www.zh-jieli.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// Gif 转 Bin 支持的芯片类型 +typedef NS_ENUM(NSUInteger, JLGIFBinChipType) { + /// 701N + JLGIFBinChipJL_701N, + /// 707N + JLGIFBinChipJL_707N, +}; + +///packet 是否打包 +typedef NS_ENUM(NSInteger, JLGIFBinPackageType) { + /// 不做其他打包使用源数据,适用 LVGL 的公版UI框架 + JLGIFBinPackageTypeLVGL = 0, + /// 杰理打包格式,适用 JLVGL 的杰理定制版 UI 框架 + JLGIFBinPackageTypeJLVGL = 1, +}; + +typedef void(^JLGif2BinBlock)(int code,NSData *_Nullable binData); + +/// Gif 转 Bin +@interface JLGifBin : NSObject + +/// 打印当前SDK的版本 ++(void)sdkVersion; + +/// 根据 Gif 创建 Bin +/// - Parameters: +/// - gifData: Gif 图片数据 +/// - level: 等级 +/// 1: 低码率 +/// 2: 中码率 +/// 3: 高码率 +/// - block: 回调内容 ++(void)makeDataToBin:(NSData *)gifData Level:(int)level Result:(JLGif2BinBlock)block __attribute__((deprecated("Use makeDataToBin:Level:ChipType:Result: instead."))); + + +/// 根据 Gif 创建 Bin +/// - Parameters: +/// - gifData: Gif 图片数据 +/// - level: 等级 +/// 1: 低码率 +/// 2: 中码率 (仅 JLGIFBinChipJL_701N 支持) +/// 3: 高码率 (仅 JLGIFBinChipJL_701N 支持) +/// - chip: 芯片类型 +/// - block: 回调内容 ++(void)makeDataToBin:(NSData *)gifData Level:(int)level ChipType:(JLGIFBinChipType)chip Result:(JLGif2BinBlock)block __attribute__((deprecated("Use makeDataToBin:Level:ChipType:PackageType:Result: instead."))); + + +/// 根据 Gif 创建 Bin +/// - Parameters: +/// - gifData: Gif 图片数据 +/// - level: 等级 +/// 1: 低码率 +/// 2: 中码率 (仅 JLGIFBinChipJL_701N 支持) +/// 3: 高码率 (仅 JLGIFBinChipJL_701N 支持) +/// - chip: 芯片类型 +/// - packageType: 打包类型 +/// 1: JLUI 杰理 UI 格式 +/// 0: 无 不打包 +/// - block: 回调内容 ++(void)makeDataToBin:(NSData *)gifData Level:(int)level ChipType:(JLGIFBinChipType)chip PackageType:(JLGIFBinPackageType)packageType Result:(JLGif2BinBlock)block; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLImageConvertResult.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLImageConvertResult.h new file mode 100644 index 000000000..0180e5784 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLImageConvertResult.h @@ -0,0 +1,34 @@ +// +// JLImageConvertResult.h +// JLBmpConvertKit +// +// Created by EzioChan on 2025/3/6. +// Copyright © 2025 www.zh-jieli.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// 图像转换结果 +@interface JLImageConvertResult : NSObject +/// 结果码 +@property (nonatomic, assign) int result; +/// 数据大小 +@property (nonatomic, assign) int buf_size; +/// 图像格式 +@property (nonatomic, assign) int pixel_format; +/// 压缩模式 +@property (nonatomic, assign) int compress_mode; + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST +/// 输出文件路径 +@property (nullable, nonatomic, copy) NSString *outFilePath; +#endif + +/// 输出文件内容 +@property (nullable, nonatomic, copy) NSData *outFileData; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Info.plist new file mode 100644 index 000000000..1c45c385b Binary files /dev/null and b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Info.plist differ diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/JLBmpConvertKit b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/JLBmpConvertKit new file mode 100755 index 000000000..bdd0bf9f0 Binary files /dev/null and b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/JLBmpConvertKit differ diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Modules/module.modulemap new file mode 100644 index 000000000..b3d18e590 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module JLBmpConvertKit { + umbrella header "JLBmpConvertKit.h" + export * + + module * { export * } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLBmpConvert.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLBmpConvert.h new file mode 100644 index 000000000..1d20bffaa --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLBmpConvert.h @@ -0,0 +1,95 @@ +// +// JLBmpCovert.h +// JLBmpCovertKit +// +// Created by EzioChan on 2024/9/9. +// Copyright © 2024 www.zh-jieli.com. All rights reserved. +// + +#import +#import +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST +#import +#elif TARGET_OS_OSX +#import +#endif + + +NS_ASSUME_NONNULL_BEGIN +@class JLImageConvertResult; + +typedef void(^JLBmpCovertCallBack)(NSString *inFilePath,NSString *__nullable outFilePath,NSError *__nullable error) __deprecated_msg("This interface is deprecated. Please use convert:inFilePath:outFilePath: or convert:Image: instead."); + +typedef void(^JLBmpCovertCallBack2)(NSData *__nullable outFileData,NSError *__nullable error) __deprecated_msg("This interface is deprecated. Please use convert:inFilePath:outFilePath: or convert:Image: instead."); + + +/// 图像转换 +@interface JLBmpConvert : NSObject +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + +/// 图片转换 +/// - Parameters: +/// - type: 转换类型 +/// - inFilePath: 输入文件 +/// - outFilePath: 输出文件 +/// - completion: 回调 ++(void)covert:(JLBmpConvertType)type + inFilePath:(NSString *_Nonnull)inFilePath + outFilePath:(NSString * __nullable)outFilePath + completion:(JLBmpCovertCallBack _Nonnull)completion __deprecated_msg("This interface is deprecated. Please use convert:inFilePath:outFilePath: or convert:Image: instead."); + +/// 图片转换 +/// - Parameters: +/// - type: 转换类型 +/// - inImage: 输入图片 +/// - completion: 回调 ++(void)covert:(JLBmpConvertType)type + Image:(UIImage *)inImage + completion:(JLBmpCovertCallBack2 _Nonnull)completion __deprecated_msg("This interface is deprecated. Please use convert:inFilePath:outFilePath: or convert:Image: instead."); + +/// 图片转换 +/// - Parameters: +/// - option: 类型 +/// 当前仅仅 707N 系列芯片可指定图像格式 +/// - inFilePath: 输入文件 +/// - outFilePath: 输出文件 +/// 文件路径转换入口 ++ (JLImageConvertResult *)convert:(JLBmpConvertOption *)option + inFilePath:(NSString *)inFilePath + outFilePath:(NSString * __nullable)outFilePath; + +/// UIImage 转换 +/// 当前仅仅 707N 系列芯片可指定图像格式 +/// - Parameters: +/// - option: 类型 +/// - imgData: 图片的原始数据 ++(JLImageConvertResult *)convert:(JLBmpConvertOption *)option ImageData:(NSData *)imgData; + + +/// UIImage 转换成 BGRA +/// - Parameter image: 图片 ++(NSData*)convertImageToBGRABytes:(UIImage *)image; + +/// UIImage 转换成 RGBA +/// - Parameter image: 图片 ++(NSData*)convertImageToRGBABytes:(UIImage *)image; + + +/// 图片缩放 +/// - Parameters: +/// - image: 图片 +/// - newSize: 缩放大小 +/// - Returns: 缩放后的图片 ++(NSData *)resizeImage:(UIImage*)image andResizeTo:(CGSize)newSize; + +#elif TARGET_OS_OSX +/// 图片转换 +/// - Parameters: +/// - option: 转换类型 +/// - inImage: 输入图片 ++(JLImageConvertResult *)convert:(JLBmpConvertOption *)option Image:(NSImage *)image; + +#endif +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLBmpConvertKit.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLBmpConvertKit.h new file mode 100644 index 000000000..9ff73bf13 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLBmpConvertKit.h @@ -0,0 +1,24 @@ +// +// JLBmpConvertKit.h +// JLBmpConvertKit +// +// Created by EzioChan on 2024/9/9. +// Copyright © 2024 www.zh-jieli.com. All rights reserved. +// + +#import + +//! Project version number for JLBmpConvertKit. +FOUNDATION_EXPORT double JLBmpConvertKitVersionNumber; + +//! Project version string for JLBmpConvertKit. +FOUNDATION_EXPORT const unsigned char JLBmpConvertKitVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + +#import +#import +#import +#import + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLBmpConvertOption.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLBmpConvertOption.h new file mode 100644 index 000000000..41e05510c --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLBmpConvertOption.h @@ -0,0 +1,89 @@ +// +// JLBmpConvertOption.h +// JLBmpConvertKit +// +// Created by EzioChan on 2025/5/23. +// Copyright © 2025 www.zh-jieli.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// 图像转换类型 +typedef NS_ENUM(NSUInteger, JLBmpConvertType) { + /// 695N 图像转换 RGB 转换成 695N 芯片对应的图像资源 + /// 对应原 BR23 转换算法 + JLBmpConvertType695N_RBG = 0, + /// 701N 图像转换 RGB 转换成 701N 芯片对应的图像资源 + /// 对应原 BR28 转换的算法 + JLBmpConvertType701N_RBG = 1, + /// 701N 图像转换 ARGB 转换成 701N 芯片对应的图像资源 + /// 对应原 BR28_ARBG 转换的算法 + JLBmpConvertType701N_ARBG = 2, + /// 701N 图像转换 RGB 转换成 701N 芯片对应的图像资源(不打包封装) + /// 对应原 BR28_RGB_NO_PACK 转换的算法(不打包封装) + JLBmpConvertType701N_RBG_NO_PACK = 3, + /// 701N 图像转换 ARGB 转换成 701N 芯片对应的图像资源(不打包封装) + /// 对应原 BR28_ARGB_NO_PACK 转换的算法(不打包封装) + JLBmpConvertType701N_ARGB_NO_PACK = 4, + /// 707N 图像转换 RGB 转换成 707N 芯片对应的图像资源 + /// 对应 BR35 转换的算法 + JLBmpConvertType707N_RBG = 5, + /// 707N 图像转换 ARGB 转换成 707N 芯片对应的图像资源 + /// 对应 BR35_ARGB 转换的算法 + JLBmpConvertType707N_ARGB = 6, + /// 707N 图像转换 RGB 转换成 707N 芯片对应的图像资源(不打包封装) + /// 对应 BR35_RGB_NO_PACK 转换的算法(不打包封装) + JLBmpConvertType707N_ARGB_NO_PACK = 7, + /// 707N 图像转换 RGB 转换成 707N 芯片对应的图像资源(不打包封装) + /// 对应 BR35_RGB_NO_PACK 转换的算法(不打包封装) + JLBmpConvertType707N_RBG_NO_PACK = 8, + /// 701N 图像转换 JPEG + /// 仅仅支持 JPEG 类型 + JLBmpConvertType701N_JPEG = 9 +}; + +/// 图像格式 +/// 仅(707N 系列芯片支持) +typedef NS_ENUM(NSUInteger, JLBmpPixelformat) { + /// ARBG8888/RBG888 格式 + JLBmpPixelformat_888, + /// ARBG8565/RBG565 格式 + JLBmpPixelformat_565, + /// 自动选择最小的格式 + JLBmpPixelformat_Auto, +}; + +/// LVGL 打包格式 +typedef NS_ENUM(NSUInteger, JLBmpPacketFormat) { + JLBmpPacketFormatNone = 0, + JLBmpPacketFormatJLUI = 1, + JLBmpPacketFormatLVGL = 2 +}; + +/// 图像转换选项 +@interface JLBmpConvertOption : NSObject + +/// 图像转换类型 +@property (nonatomic, assign) JLBmpConvertType convertType; + +/// 图像格式 +/// 默认为 auto +/// 仅(707N 系列芯片支持) +@property (nonatomic, assign) JLBmpPixelformat pixelformat; + +/// LVGL 打包格式 +/// 默认为 JLUI +/// 仅(707N 系列芯片支持) +@property (nonatomic, assign) JLBmpPacketFormat packetFormat; + +/// 是否转换成 BGRA +/// 当使用的是 JL 的 UI 框架时需要使用 +/// 使用其他第三方框架时,需要根据固件端框架进行调整 +/// default YES +@property (nonatomic, assign) BOOL convertToBGRA; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLGifBin.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLGifBin.h new file mode 100644 index 000000000..5b7cdc2d7 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLGifBin.h @@ -0,0 +1,77 @@ +// +// JLGifBin.h +// JLGifLib +// +// Created by EzioChan on 2024/1/18. +// Copyright © 2024 www.zh-jieli.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// Gif 转 Bin 支持的芯片类型 +typedef NS_ENUM(NSUInteger, JLGIFBinChipType) { + /// 701N + JLGIFBinChipJL_701N, + /// 707N + JLGIFBinChipJL_707N, +}; + +///packet 是否打包 +typedef NS_ENUM(NSInteger, JLGIFBinPackageType) { + /// 不做其他打包使用源数据,适用 LVGL 的公版UI框架 + JLGIFBinPackageTypeLVGL = 0, + /// 杰理打包格式,适用 JLVGL 的杰理定制版 UI 框架 + JLGIFBinPackageTypeJLVGL = 1, +}; + +typedef void(^JLGif2BinBlock)(int code,NSData *_Nullable binData); + +/// Gif 转 Bin +@interface JLGifBin : NSObject + +/// 打印当前SDK的版本 ++(void)sdkVersion; + +/// 根据 Gif 创建 Bin +/// - Parameters: +/// - gifData: Gif 图片数据 +/// - level: 等级 +/// 1: 低码率 +/// 2: 中码率 +/// 3: 高码率 +/// - block: 回调内容 ++(void)makeDataToBin:(NSData *)gifData Level:(int)level Result:(JLGif2BinBlock)block __attribute__((deprecated("Use makeDataToBin:Level:ChipType:Result: instead."))); + + +/// 根据 Gif 创建 Bin +/// - Parameters: +/// - gifData: Gif 图片数据 +/// - level: 等级 +/// 1: 低码率 +/// 2: 中码率 (仅 JLGIFBinChipJL_701N 支持) +/// 3: 高码率 (仅 JLGIFBinChipJL_701N 支持) +/// - chip: 芯片类型 +/// - block: 回调内容 ++(void)makeDataToBin:(NSData *)gifData Level:(int)level ChipType:(JLGIFBinChipType)chip Result:(JLGif2BinBlock)block __attribute__((deprecated("Use makeDataToBin:Level:ChipType:PackageType:Result: instead."))); + + +/// 根据 Gif 创建 Bin +/// - Parameters: +/// - gifData: Gif 图片数据 +/// - level: 等级 +/// 1: 低码率 +/// 2: 中码率 (仅 JLGIFBinChipJL_701N 支持) +/// 3: 高码率 (仅 JLGIFBinChipJL_701N 支持) +/// - chip: 芯片类型 +/// - packageType: 打包类型 +/// 1: JLUI 杰理 UI 格式 +/// 0: 无 不打包 +/// - block: 回调内容 ++(void)makeDataToBin:(NSData *)gifData Level:(int)level ChipType:(JLGIFBinChipType)chip PackageType:(JLGIFBinPackageType)packageType Result:(JLGif2BinBlock)block; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLImageConvertResult.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLImageConvertResult.h new file mode 100644 index 000000000..0180e5784 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLImageConvertResult.h @@ -0,0 +1,34 @@ +// +// JLImageConvertResult.h +// JLBmpConvertKit +// +// Created by EzioChan on 2025/3/6. +// Copyright © 2025 www.zh-jieli.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// 图像转换结果 +@interface JLImageConvertResult : NSObject +/// 结果码 +@property (nonatomic, assign) int result; +/// 数据大小 +@property (nonatomic, assign) int buf_size; +/// 图像格式 +@property (nonatomic, assign) int pixel_format; +/// 压缩模式 +@property (nonatomic, assign) int compress_mode; + +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST +/// 输出文件路径 +@property (nullable, nonatomic, copy) NSString *outFilePath; +#endif + +/// 输出文件内容 +@property (nullable, nonatomic, copy) NSData *outFileData; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Info.plist new file mode 100644 index 000000000..4409cd0e0 Binary files /dev/null and b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Info.plist differ diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/JLBmpConvertKit b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/JLBmpConvertKit new file mode 100755 index 000000000..e58f37985 Binary files /dev/null and b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/JLBmpConvertKit differ diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Modules/module.modulemap new file mode 100644 index 000000000..b3d18e590 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module JLBmpConvertKit { + umbrella header "JLBmpConvertKit.h" + export * + + module * { export * } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/_CodeSignature/CodeResources b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/_CodeSignature/CodeResources new file mode 100644 index 000000000..7b50e6cc4 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/_CodeSignature/CodeResources @@ -0,0 +1,192 @@ + + + + + files + + Headers/JLBmpConvert.h + + 5urDCSjpqHBbBhVbnkpZDUAqfUQ= + + Headers/JLBmpConvertKit.h + + rJo680NKOdjmobcOdHzU3oQ05lQ= + + Headers/JLBmpConvertOption.h + + bPMnU/cb1ZNEvx+6wLH+tNggsWM= + + Headers/JLGifBin.h + + +UKivjyP0JG0+mVZPKUAWwhParQ= + + Headers/JLImageConvertResult.h + + oTrRd0HS6yuPr1JxLpx1Qgr6sKw= + + Info.plist + + mH2wyDJH099CSL7ndYKtKlBiJ3M= + + Modules/module.modulemap + + VG59AHi+XKWb6IYCdGbZKHPgBbA= + + + files2 + + Headers/JLBmpConvert.h + + hash + + 5urDCSjpqHBbBhVbnkpZDUAqfUQ= + + hash2 + + Ps103art4k9gGQecTWk7WrWV5o2nY38Acp7pydnv0oc= + + + Headers/JLBmpConvertKit.h + + hash + + rJo680NKOdjmobcOdHzU3oQ05lQ= + + hash2 + + 9kChXiw3D4JFaLjgBlaq+/2FazTsVCbA3kfmrYQ+9/A= + + + Headers/JLBmpConvertOption.h + + hash + + bPMnU/cb1ZNEvx+6wLH+tNggsWM= + + hash2 + + 4rgpTV2uvjOrNb0Bx7HOxoqBCtJjHaA7CcNBjIImXzY= + + + Headers/JLGifBin.h + + hash + + +UKivjyP0JG0+mVZPKUAWwhParQ= + + hash2 + + A8LhdAy5NiJx3J5nKUE53rzYmIKX6Cp9oTpGfJJk+tQ= + + + Headers/JLImageConvertResult.h + + hash + + oTrRd0HS6yuPr1JxLpx1Qgr6sKw= + + hash2 + + vacF3oh76vh4n8lafP3hAi/fYXjU4YoqA6CbjC05MYw= + + + Modules/module.modulemap + + hash + + VG59AHi+XKWb6IYCdGbZKHPgBbA= + + hash2 + + nU2jpsOghgGuDPwpIFZ+hGnZ5Xj24fG1pjw1H8vpwEs= + + + + rules + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/Info.plist new file mode 100644 index 000000000..18c1d0ce0 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/Info.plist @@ -0,0 +1,44 @@ + + + + + AvailableLibraries + + + BinaryPath + JLDialUnit.framework/JLDialUnit + LibraryIdentifier + ios-arm64 + LibraryPath + JLDialUnit.framework + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + BinaryPath + JLDialUnit.framework/JLDialUnit + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + JLDialUnit.framework + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/DialManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/DialManager.h new file mode 100644 index 000000000..869a29e09 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/DialManager.h @@ -0,0 +1,127 @@ +// +// DialManager.h +// JLDialUnit +// +// Created by 杰理科技 on 2021/7/20. +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + + + +typedef NS_ENUM(NSInteger, DialOperateType) { + DialOperateTypeNoSpace = 0, //空间不足 + DialOperateTypeDoing = 1, //正在操作 + DialOperateTypeFail = 2, //操作失败 + DialOperateTypeSuccess = 3, //操作成功 + DialOperateTypeUnnecessary = 4, //无需重复打开文件系统 + DialOperateTypeResetFial = 5, //重置文件系统失败 + DialOperateTypeNormal = 6, //文件系统正常 + DialOperateTypeCmdFail = 7, //流程命令执行失败 +}; +typedef NS_ENUM(NSInteger, DialUpdateResult) { + DialUpdateResultFinished = 0, //更新资源完成 + DialUpdateResultNewest = 1, //资源已最新 + DialUpdateResultInvalid = 2, //资源无效 + DialUpdateResultEmpty = 3, //资源为空 + DialUpdateResultReplace = 4, //资源替换 + DialUpdateResultAdd = 5, //资源新增 + DialUpdateResultNoSpace = 6, //空间不足 + DialUpdateResultZipError = 7, //ZIP资源文件错误 + DialUpdateResultCompareFail = 8, //表盘资源对比失败 + DialUpdateResultUpdateUfw = 9, //更新固件 ufw +}; +typedef void(^DialOperateBK)(DialOperateType type, float progress); +typedef void(^DialListBK)(DialOperateType type, NSArray* __nullable array); +typedef void(^DialUpdateBK)(DialUpdateResult updateResult, + NSArray* __nullable array, + NSString * _Nullable filePath, + NSInteger index ,float progress); + +typedef void(^FatfsFreeBlock)(uint32_t freeSize); + +/// 表盘管理类 +@interface DialManager : NSObject + +#pragma mark - 连接成功后,必须调用一次! ++(void)openDialFileSystemWithCmdManager:(JL_ManagerM *)manager withResult:(DialOperateBK)result; + +#pragma mark - 重置表盘系统 ++(void)resetDialFileSystemWithCmdManager:(JL_ManagerM *)manager withResult:(DialOperateBK)result; + +#pragma mark - 查询文件 +/// 查询文件 +/// @param result 操作回调 ++(void)listFile:(DialListBK __nullable)result; + +#pragma mark - 添加文件 +/// 添加文件 +/// @param file 文件名需要加斜杠,类似@“/WACTH1”。 +/// @param content 文件数据 +/// @param result 操作回调 ++(void)addFile:(NSString*)file + Content:(NSData*)content + Result:(DialOperateBK)result; + +#pragma mark - 删除文件 +/// 删除文件(第一种) +/// @param file 文件名需要加斜杠,类似@“/WACTH1”。 +/// @param result 操作回调 ++(void)deleteFile:(NSString*)file + Result:(DialOperateBK)result; + +/// 删除文件(第二种) +/// @param fileModel JLModel_File类型 +/// @param result 操作回调 ++(void)deleteDialResourceWithFileModel:(JLModel_File*)fileModel Result:(DialOperateBK)result; + +#pragma mark - 替换文件 +/// 替换文件 +/// @param file 文件名需要加斜杠,类似@“/WACTH1”。 +/// @param content 文件数据 +/// @param result 操作回调 ++(void)repaceFile:(NSString*)file + Content:(NSData*)content + Result:(DialOperateBK)result; + +#pragma mark - 格式化外部Flash +/// 格式化外部Flash操作 +/// @param handle 设备句柄 +/// @param result 操作回调 ++(void)formatFlash:(NSString*)handle Result:(DialOperateBK)result; + +#pragma mark - 更新设备的表盘资源(异步调用) +/// 更新设备的表盘资源(异步调用) +/// @param path 资源文件 +/// @param array 表盘列表(当前) +/// @param result 更新结果 ++(void)updateResourcePath:(NSString*)path + List:(NSArray*)array + Result:(DialUpdateBK)result; + +/// 获取 fatfs 系统的剩余空间(仅仅fatfs 系统可用) +/// 需要异步执行 ++(uint32_t)getFatfsFree; + +/// 获取 fatfs 系统的剩余空间(仅仅fatfs 系统可用) +/// @param block 回调空间 ++(void)getFatfsFree:(FatfsFreeBlock) block; + +/// 获取某文件的文件大小 +/// @param fileName 文件名 ++(uint32_t)getSizeOfFileName:(NSString*)fileName; + + +/// 记录文件大小(私有接口) +/// @param size 文件大小 +/// @param fileName 文件名 ++(void)setFileSize:(uint32_t)size FileName:(NSString*)fileName; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/FatfsObject.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/FatfsObject.h new file mode 100644 index 000000000..c6fe7e0d1 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/FatfsObject.h @@ -0,0 +1,92 @@ +// +// FatfsObject.h +// JLDialUnit +// +// Created by 杰理科技 on 2021/7/20. +// + +#import +#import + + + +NS_ASSUME_NONNULL_BEGIN + +@interface FatfsObject : NSObject + +#pragma mark ---> FATFS API +/// 输入命令中心类,在JL_Entity内。 +/// @param manager 命令中心类 ++(void)makeCmdManager:(JL_ManagerM*)manager; + +/// 返回 当前 JL_ManagerM ++(JL_ManagerM*)outputCmdManager; + +/// 设置外挂Flash的大小,FATFS的大小。 +/// @param flashSize Flash大小 +/// @param fatsSize FATFS的大小 ++(BOOL)makeFlashSize:(uint32_t)flashSize + FatsSize:(uint32_t)fatsSize; + +/// 获取Flash上的文件 +/// @param path 文件名数组,其中"JL",“FONT”不能操作。 ++(NSArray*)makeListPath:(NSString*)path; + +typedef void(^FatfsCreateFile_BK)(float progress); +/// 新增文件 +/// @param path “/文件名” +/// @param data 文件数据内容 +/// @param result 进度 ++(BOOL)makeCreateFile:(NSString*)path + Content:(NSData* __nullable)data + Result:(FatfsCreateFile_BK __nullable)result; + +typedef void(^FatfsReplaceFile_BK)(float progress); +/// 替换文件 +/// @param path “/文件名” +/// @param data 文件数据内容 ++(BOOL)makeReplaceFile:(NSString*)path + Content:(NSData* __nullable)data + Result:(FatfsReplaceFile_BK __nullable)result; + +/// 删除文件 +/// @param path “/文件名” ++(BOOL)makeRemoveFile:(NSString*)path; + +/*--------------------------------------------------------------------------------- + FatSize -- FAT文件系统认为自己的大小 + 因为小机的FAT项按4K对齐了大小,所以实际上会认为FAT系统占用的空间比FLASH实际大小还要大 + 用 f_getfree 获取的是 FAT文件系统认为自己剩余的空间的簇个数,其中有一部分是在Flash上实际不存在的 + 所以需要减掉这部分 + 计算方式如下: + FatUsed = FatSize - FreeSize + FlashRemainSize = FlashSize - FatUsed + ----------------------------------------------------------------------------------*/ +/// 获取FATFS系统剩余空间,“FreeSize”。 ++(uint32_t)makeGetFree; + +/// 查看watch本间版本 +/// @param path 文件路径 ++(NSString*)makeVersionWatch:(NSString*)path; + +/// 获取文件前面512个字节 +/// @param path 文件路径 ++(NSData *)makeFileHead:(NSString*)path; + +/// 读文件 +/// @param path 文件路径 ++(NSData*)makeFileContent:(NSString*)path; + +/// 解压资源文件 +/// @param path 文件路径 +/// @param aimDirection 存放的路径 ++(NSArray*)unzipFileAtPath:(NSString *)path toDestination:(NSString *)aimDirection; + +/// 重置外部flash ++(BOOL)makeResetFlash; + + +@end + +NS_ASSUME_NONNULL_END + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/JLDialSourceModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/JLDialSourceModel.h new file mode 100644 index 000000000..e32e99dad --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/JLDialSourceModel.h @@ -0,0 +1,27 @@ +// +// JLDialSourceModel.h +// JLDialUnit +// +// Created by EzioChan on 2025/3/19. +// Copyright © 2025 www.zh-jieli.com. All rights reserved. +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface JLDialSourceModel : JLModel_File + +/// 文件大小 +@property (nonatomic, assign)int size; + +/// CRC +@property (nonatomic, assign)uint16_t crc; + ++(JLDialSourceModel *)createModel:(JLModel_File *)model; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/JLDialUnit.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/JLDialUnit.h new file mode 100644 index 000000000..07e55445b --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/JLDialUnit.h @@ -0,0 +1,30 @@ +// +// JLDialUnit.h +// JLDialUnit +// +// Created by EzioChan on 2023/2/28. +// Copyright © 2023 www.zh-jieli.com. All rights reserved. +// + +#import + +//! Project version number for JLDialUnit. +FOUNDATION_EXPORT double JLDialUnitVersionNumber; + +//! Project version string for JLDialUnit. +FOUNDATION_EXPORT const unsigned char JLDialUnitVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/JLDialUnitMgr.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/JLDialUnitMgr.h new file mode 100644 index 000000000..88ce0d6f0 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/JLDialUnitMgr.h @@ -0,0 +1,115 @@ +// +// JLDialUnitMgr.h +// JLDialUnit +// +// Created by EzioChan on 2025/3/18. +// Copyright © 2025 www.zh-jieli.com. All rights reserved. +// + +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN +@class JLDialSourceModel; +@interface JLDialUnitMgr : NSObject + +/// 设备文件列表 +@property (nonatomic,strong)NSMutableArray *fileArray; + +/// 当前 Flash 操控句柄 +@property (nonatomic, strong) JLFlashOpMgr *currentFlaghOpMgr; + +/// 当前使用的表盘文件 +@property (nonatomic,strong)JLDialSourceModel *currentFileSource; + +/// 当前使用的表盘背景 +@property (nonatomic,strong)JLDialSourceModel *_Nullable currentBackground; + +/// 初始化 +/// - Parameters: +/// - mgr: 蓝牙管理对象 +/// - completion: 回调 +-(instancetype)initWithManager:(JL_ManagerM*)mgr completion:(void (^)(NSError * _Nullable))completion; + +/// 获取文件列表 +/// - Parameters: +/// - cardType: 设备存储的类型 +/// - count: 请求的数目 +/// - completion: 回调 +/// 返回时,是返回累计数目 +-(void)getFileList:(JL_CardType)cardType count:(NSInteger)count completion:(void (^)(NSArray * _Nullable,NSError * _Nullable))completion; + +/// 清空文件列表 +/// - Parameter cardType: 设备存储的类型 +-(void)cleanFileList; + +/// 更新文件列表信息 +/// - Parameters: +/// - fileSources: NSArray +/// - completion: 回调 +-(void)updateFileSources:(NSArray *)fileSources completion:(void (^)(NSArray * _Nullable,NSError * _Nullable))completion; + +/// 更新文件到设备 +/// - Parameters: +/// - fileHandleType: 设备目标存储句柄 +/// - fileData: 文件数据 +/// - filePath: 文件存储路径 +/// - resultCompletion: 回调 +/// state: 0:成功 1:传输中 -1:失败 +/// progress: 传输进度 +/// error: 错误 +-(void)updateFileToDevice:(JL_FileHandleType)fileHandleType Data:(NSData *)fileData FilePath:(NSString *)filePath completion:(void (^)(int,double,NSError * _Nullable))resultCompletion; + +/// 删除文件 +/// - Parameters: +/// - filePath: 文件路径 +/// - resultCompletion: 回调 +-(void)deleteFile:(NSString *)filePath getFileList:(BOOL)isGet Completion:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion; + +/// 删除多个文件 +/// - Parameters: +/// - filePathList: 文件路径 +/// - resultCompletion: 回调 +-(void)deleteFiles:(NSArray *) filePathList Completion:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion; + +/// 获取剩余空间 +/// - Parameter completion: 回调 +-(void)getRemainingSpace:(void(^)(int freeSize, NSError * _Nullable error))completion; + +/// 获取剩余空间 V1 +/// - Parameter completion: 回调 +-(void)getFlashRemainingSpace:(void(^)(int freeSize, NSError * _Nullable error))completion; + +//MARK: 表盘操作 + +/// 获取当前正在使用的表盘 +/// - Parameter completion: 回调 +-(void)dialGetCurrent:(void (^)(JLDialSourceModel * _Nullable))completion; + +/// 设置当前表盘 +/// - Parameters: +/// - model: 表盘 +/// - resultCompletion: 回调 +-(void)dialSetCurrent:(JLDialSourceModel *)model Completion:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion; + +/// 激活自定义表盘背景 +/// - Parameters: +/// - model: 表盘背景对象 +/// - resultCompletion: 回调 +-(void)dialActiveCustomBackground:(JLDialSourceModel *)model Completion:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion; + +/// 重置自定义表盘背景 +/// - Parameter resultCompletion: 回调 +-(void)dialResetCustomBackground:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion; + +/// 获取表盘背景 +/// - Parameters: +/// - path: 路径 诸如“/WATCH” +/// - resultCompletion: 回调 +-(void)dialGetDial:(NSString *)path BackgroundInfo:(void (^)(JLDialSourceModel * _Nullable model, NSError * _Nullable error))resultCompletion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/JLSourceUpdate.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/JLSourceUpdate.h new file mode 100644 index 000000000..4c52a6d64 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/JLSourceUpdate.h @@ -0,0 +1,94 @@ +// +// JLSourceUpdate.h +// JLDialUnit +// +// Created by EzioChan on 2025/3/18. +// Copyright © 2025 www.zh-jieli.com. All rights reserved. +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN +@class JLDialUnitMgr; + +typedef NS_ENUM(NSInteger, JLSourceUpdateType) { + /// 更新资源完成 + JLSourceUpdateTypeFinished = 0, + /// 资源已最新 + JLSourceUpdateTypeNewest = 1, + /// 资源无效 + JLSourceUpdateTypeInvalid = 2, + /// 资源为空 + JLSourceUpdateTypeEmpty = 3, + /// 资源更新错误 + JLSourceUpdateTypeSrcError = 4, + /// 资源更新中 + JLSourceUpdateTypeUpdateing = 5, + /// 空间不足 + JLSourceUpdateTypeNoSpace = 6, + /// ZIP 资源文件错误 + JLSourceUpdateTypeZipError = 7, + /// 表盘资源对比失败 + JLSourceUpdateTypeCompareFail = 8, + /// 更新固件 ufw + JLSourceUpdateTypeUpdateUfw = 9, + /// 不支持 + JLSourceUpdateTypeUpdateNotSupport = 10, +}; + + +@interface JLSourceInfo : NSObject + +@property(nonatomic,strong)NSString *filePath; +@property(nonatomic,strong,readonly)NSData * _Nullable fileData; +@property(nonatomic,strong,readonly)NSString *fileName; +@property(nonatomic,assign)NSInteger index; + +@end + +@protocol JLSourceUpdateDelegate + +- (void)updateProgress:(double)progress type:(JLSourceUpdateType)type source:(JLSourceInfo *_Nullable)source error:(NSError* _Nullable)error; + +- (void)updateUfwFirst:(NSString *)filePath ThenUpdateSrc:(NSArray *) filePaths; + +@end + +/// 资源更新 +@interface JLSourceUpdate : NSObject + +@property(nonatomic,weak)id delegate; + +/// 初始化 +/// - Parameters: +/// - mgr: 蓝牙管理对象 +/// - fileType: 文件句柄 +/// - dialMgr: JLDialUnitMgr +/// - delegate: 代理 +-(instancetype)initWithManager:(JL_ManagerM*)mgr HandleType:(JL_FileHandleType) fileType DialMgr:(JLDialUnitMgr *)dialMgr delegate:(id)delegate; + + +/// 更新资源 +/// - Parameters: +/// - path: zip包路径 +/// - mgr: JLDialUnitMgr +-(void)updateSources:(NSString*)path DialUnitMgr:(JLDialUnitMgr *)mgr; + + +/// 更新资源 AC707N 手表特殊升级流程 +/// - Parameters: +/// - path: zip 包路径 +/// - mgr: JLDialUnitMgr +-(void)updateSourcesV2:(NSString*)path DialUnitMgr:(JLDialUnitMgr *)mgr; + + +/// 更新资源任务 +/// @param targetUpdateList 需要更新的文件列表 +/// @param updateResult 更新结果 +-(void)updateAciton:(NSArray *)targetUpdateList Result:(void (^)(BOOL isFinish, NSError * _Nullable))updateResult; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZZArchive.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZZArchive.h new file mode 100644 index 000000000..a37e0a7e7 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZZArchive.h @@ -0,0 +1,131 @@ +// +// ZZArchive.h +// ZipZap +// +// Created by Glen Low on 25/09/12. +// Copyright (c) 2012, Pixelglow Software. All rights reserved. +// + +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +#import +#import "ZZConstants.h" + +@class ZZArchiveEntry; + +NS_ASSUME_NONNULL_BEGIN + +/** + * The ZZArchive class represents a zip file for reading only. + */ +@interface ZZArchive : NSObject + +/** + * The URL representing this archive. + */ +@property (readonly, nullable, nonatomic) NSURL* URL; + +/** + * The uninterpreted contents of this archive. + */ +@property (readonly, nonatomic) NSData* contents; + +/** + * The array of ZZArchiveEntry entries within this archive. + */ +@property (readonly, nonatomic) NSArray* entries; + +/** + * Creates a new archive with the zip file at the given file URL. + * + * The archive will use UTF-8 encoding for reading entry file names and comments, and will not create the file if it is missing. + * + * @param URL The file URL of the zip file. + * @param error The error information when an error occurs. Pass in nil if you do not want error information. + * @return The initialized archive. Returns nil if the archive cannot be initialized. + */ ++ (nullable instancetype)archiveWithURL:(NSURL*)URL + error:(out NSError**)error; + +/** + * Creates a new archive with the raw zip file data given. + * + * The archive will use UTF-8 encoding for reading entry file names and comments, and will not create the data if it is missing. + * + * @param data The raw data of the zip file. + * @param error The error information when an error occurs. Pass in nil if you do not want error information. + * @return The initialized archive. Returns nil if the archive cannot be initialized. + */ ++ (nullable instancetype)archiveWithData:(NSData*)data + error:(out NSError**)error; + +/** + * This initializer should never be used. + */ +- (instancetype)init NS_UNAVAILABLE; + +/** + * Initializes a new archive with the zip file at the given file URL. + * + * @param URL The file URL of the zip file. + * @param options The options to consider when opening the zip file. + * @param error The error information when an error occurs. Pass in nil if you do not want error information. + * @return The initialized archive. Returns nil if the archive cannot be initialized. + */ +- (nullable instancetype)initWithURL:(NSURL*)URL + options:(nullable NSDictionary*)options + error:(out NSError**)error; + +/** + * Initializes a new archive with the raw zip file data given. + * + * @param data The raw data of the zip file + * @param options The options to consider when opening the zip file. + * @param error The error information when an error occurs. Pass in nil if you do not want error information. + * @return The initialized archive. Returns nil if the archive cannot be initialized. + */ +- (nullable instancetype)initWithData:(NSData*)data + options:(nullable NSDictionary*)options + error:(out NSError**)error; + +/** + * Updates the entries and writes them to the source. + * + * If the write fails and the entries are completely new, the existing zip file will be untouched. + * + * If the write fails and the entries contain some or all existing entries, the zip file may be corrupted. + * In this case, the error information will report the ZZReplaceWriteErrorCode error code. + * + * @param newEntries The entries to update to, may contain some or all existing entries. + * @param error The error information when an error occurs. Pass in nil if you do not want error information. + * @return Whether the update was successful or not. + * + */ +- (BOOL)updateEntries:(NSArray*)newEntries + error:(out NSError**)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZZArchiveEntry.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZZArchiveEntry.h new file mode 100644 index 000000000..2fa9b0836 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZZArchiveEntry.h @@ -0,0 +1,239 @@ +// +// ZZArchiveEntry.h +// ZipZap +// +// Created by Glen Low on 25/09/12. +// Copyright (c) 2012, Pixelglow Software. All rights reserved. +// + +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#include +#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +#include +#endif + +#import + +@protocol ZZArchiveEntryWriter; + +NS_ASSUME_NONNULL_BEGIN + +/** + * The ZZArchiveEntry class represents an entry in the ZZArchive instance. + */ +@interface ZZArchiveEntry : NSObject + +/** + * Whether the entry is compressed. + */ +@property (readonly, nonatomic) BOOL compressed; + +/** + * Whether the entry is encrypted. + */ +@property (readonly, nonatomic) BOOL encrypted; + +/** + * The last modified date and time of the entry. The time value is only accurate to 2 seconds. + */ +@property (readonly, nonatomic) NSDate* lastModified; + +/** + * The CRC32 code of the entry file: 0 for new entries. + */ +@property (readonly, nonatomic) NSUInteger crc32; + +/** + * The compressed size of the entry file: 0 for new entries. + */ +@property (readonly, nonatomic) NSUInteger compressedSize; + +/** + * The uncompressed size of the entry file: 0 for new entries. + */ +@property (readonly, nonatomic) NSUInteger uncompressedSize; + +/** + * The UNIX file mode for the entry: 0 for new or non-UNIX entries. This includes the file type bits. + */ +@property (readonly, nonatomic) mode_t fileMode; + +/** + * The file name of the entry. Encoding is either CP-437 or UTF-8, depending on the EFS flag of the general purpose bit flags. + */ +@property (readonly, nonatomic) NSString* fileName; + +/** + * The file name of the entry as array of bytes + */ +@property (readonly, nonatomic) NSData* rawFileName; + +/** + * The encoding of filename and comment fields, according to the EFS flag of the general purpose bit flags. + */ +@property (readonly, nonatomic) NSStringEncoding encoding; + +/** + * Creates a new file entry from a streaming callback. + * + * @param fileName The file name for the entry. + * @param compress Whether to compress the entry. + * @param streamBlock The callback to write the entry's data to the stream. Returns whether the write should be considered successful. + * @return The created entry. + */ ++ (instancetype)archiveEntryWithFileName:(NSString*)fileName + compress:(BOOL)compress + streamBlock:(BOOL(^)(NSOutputStream* stream, NSError** error))streamBlock; + +/** + * Creates a new file entry from a data callback. + * + * @param fileName The file name for the entry. + * @param compress Whether to compress the entry. + * @param dataBlock The callback to return the entry's data. Returns nil if the write should be considered unsuccessful. + * @return The created entry. + */ ++ (instancetype)archiveEntryWithFileName:(NSString*)fileName + compress:(BOOL)compress + dataBlock:(NSData* _Nullable(^)(NSError** error))dataBlock; + +/** + * Creates a new file entry from a data-consuming callback. + * + * @param fileName The file name for the entry. + * @param compress Whether to compress the entry. + * @param dataConsumerBlock The callback to put the entry's data into the data consumer. Returns whether the write should be considered successful. + * @return The created entry. + */ ++ (instancetype)archiveEntryWithFileName:(NSString*)fileName + compress:(BOOL)compress + dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer, NSError** error))dataConsumerBlock; + +/** + * Creates a new directory entry. + * + * @param directoryName The directory name for the entry. + * @return The created entry. + */ ++ (instancetype)archiveEntryWithDirectoryName:(NSString*)directoryName; + +/** + * Creates a new entry. + * + * The archive entry will choose the first non-nil dataBlock, streamBlock or dataConsumerBlock to supply its data. + * + * @param fileName The file name for the entry. + * @param fileMode The UNIX file mode for the entry. This includes the file type bits. + * @param lastModified The last modified date and time for the entry. The time value is only accurate to 2 seconds. + * @param compressionLevel The compression level for the entry: 0 for stored, -1 for default deflate, 1 - 9 for custom deflate levels. + * @param dataBlock The callback that returns the entry file data. Returns nil if the write should be considered unsuccessful. + * @param streamBlock The callback that writes the entry file to the stream. Returns whether the write should be considered successful. + * @param dataConsumerBlock The callback that writes the entry file to the data consumer. Returns whether the write should be considered successful. + * @return The created entry. + */ ++ (instancetype)archiveEntryWithFileName:(NSString*)fileName + fileMode:(mode_t)fileMode + lastModified:(NSDate*)lastModified + compressionLevel:(NSInteger)compressionLevel + dataBlock:(nullable NSData* _Nullable(^)(NSError** error))dataBlock + streamBlock:(nullable BOOL(^)(NSOutputStream* stream, NSError** error))streamBlock + dataConsumerBlock:(nullable BOOL(^)(CGDataConsumerRef dataConsumer, NSError** error))dataConsumerBlock; + +/** + * Checks whether the entry file is consistent. + * + * Checks whether the local file entry is consistent with the central file entry and also that + * the recorded and actual checksums of the data agree. + * + * @param error The error information when an error occurs. Pass in nil if you do not want error information. + * @return Whether entry file is consistent or not. + */ +- (BOOL)check:(out NSError**)error; + +/** + * The file name of the entry with the given encoding. + * + * @param encoding The encoding for the filename. + */ +- (NSString*)fileNameWithEncoding:(NSStringEncoding)encoding; + +/** + * Creates a stream to represent the entry file. + * + * @param error A pointer to a variable that will contain the error if any. + * @return The new stream: nil for new entries. + */ +- (nullable NSInputStream*)newStreamWithError:(NSError**)error; + +/** + * Creates a stream to represent the entry file. + * + * @param password The password to be used for decryption. + * @param error A pointer to a variable that will contain the error if any. + * @return The new stream: nil for new entries. + */ +- (nullable NSInputStream*)newStreamWithPassword:(nullable NSString*)password error:(NSError**)error; + +/** + * Creates data to represent the entry file. + * + * @param error A pointer to a variable that will contain the error if any. + * @return The new data: nil for new entries. + */ +- (nullable NSData*)newDataWithError:(NSError**)error; + +/** + * Creates data to represent the entry file. + * + * @param password The password to be used for decryption. + * @param error A pointer to a variable that will contain the error if any. + * @return The new data: nil for new entries. + */ +- (nullable NSData*)newDataWithPassword:(nullable NSString*)password error:(NSError**)error; + +/** + * Creates a data provider to represent the entry file. + * + * @param error A pointer to a variable that will contain the error if any. + * @return The new data provider: nil for new entries. + */ +- (nullable CGDataProviderRef)newDataProviderWithError:(NSError**)error CF_RETURNS_RETAINED; + +/** + * Creates a data provider to represent the entry file. + * + * @param password The password to be used for decryption. + * @param error A pointer to a variable that will contain the error if any. + * @return The new data provider: nil for new entries. + */ +- (nullable CGDataProviderRef)newDataProviderWithPassword:(nullable NSString*)password error:(NSError**)error CF_RETURNS_RETAINED; + +- (id)newWriterCanSkipLocalFile:(BOOL)canSkipLocalFile; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZZConstants.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZZConstants.h new file mode 100644 index 000000000..698841852 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZZConstants.h @@ -0,0 +1,79 @@ +// +// ZZConstants.h +// ZipZap +// +// Created by Daniel Cohen Gindi on 29/12/13. +// Copyright (c) 2013, Pixelglow Software. All rights reserved. +// + +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +#import + +typedef NS_ENUM(NSInteger, ZZEncryptionMode) +{ + /** + * No encryption. + */ + ZZEncryptionModeNone, + + /** + * Standard PKZIP encryption. + */ + ZZEncryptionModeStandard, + + /** + * Strong PKZIP encryption. Currently not supported. + */ + ZZEncryptionModeStrong, + + /** + * WinZip encryption using AES. + */ + ZZEncryptionModeWinZipAES +}; + +typedef NS_ENUM(uint8_t, ZZAESEncryptionStrength) +{ + /** + * Use 128-bit AES for encryption. + */ + ZZAESEncryptionStrength128 = 0x01, + + /** + * Use 192-bit AES for encryption. + */ + ZZAESEncryptionStrength192 = 0x02, + + /** + * Use 256-bit AES for encryption. + */ + ZZAESEncryptionStrength256 = 0x03 +}; + +/** + * An NSNumber object that determines whether to create the archive file if it is missing. Creation occurs during -[ZZArchive updateEntries:error:]. Default is @NO. + */ +extern NSString* const ZZOpenOptionsCreateIfMissingKey; diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZZError.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZZError.h new file mode 100644 index 000000000..a307763cf --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZZError.h @@ -0,0 +1,136 @@ +// +// ZZError.h +// ZipZap +// +// Created by Glen Low on 25/01/13. +// Copyright (c) 2013, Pixelglow Software. All rights reserved. +// + +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +#import + +/** + * The domain of ZipZap errors. + */ +extern NSString* const ZZErrorDomain; + +/** + * The index of the erroneous entry. + */ +extern NSString* const ZZEntryIndexKey; + +typedef NS_ENUM(NSInteger, ZZErrorCode) +{ + /** + * Cannot open an archive for reading. + */ + ZZOpenReadErrorCode, + + /** + * Cannot read the end of central directory. + */ + ZZEndOfCentralDirectoryReadErrorCode, + + /** + * Cannot read a central file header. + */ + ZZCentralFileHeaderReadErrorCode, + + /** + * Cannot read a local file. + */ + ZZLocalFileReadErrorCode, + + /** + * Cannot open an archive for writing. + */ + ZZOpenWriteErrorCode, + + /** + * Cannot write a local file. + */ + ZZLocalFileWriteErrorCode, + + /** + * Cannot write a central file header. + */ + ZZCentralFileHeaderWriteErrorCode, + + /** + * Cannot write the end of central directory. + */ + ZZEndOfCentralDirectoryWriteErrorCode, + + /** + * Cannot replace the zip file after writing. + */ + ZZReplaceWriteErrorCode, + + /** + * The compression used is currently unsupported. + */ + ZZUnsupportedCompressionMethod, + + /** + * The encryption used is currently unsupported. + */ + ZZUnsupportedEncryptionMethod, + + /** + * An invalid CRC checksum has been encountered. + */ + ZZInvalidCRChecksum, + + /** + * The wrong key was passed in. + */ + ZZWrongPassword, + + /** + * The data, stream or data consumer block failed but did not set the error. + * This will be set on the underlying error of the local file write. + */ + ZZBlockFailedWithoutError +}; + +static inline BOOL ZZRaiseErrorNo(NSError** error, ZZErrorCode errorCode, NSDictionary* userInfo) +{ + if (error) + *error = [NSError errorWithDomain:ZZErrorDomain + code:errorCode + userInfo:userInfo]; + return NO; +} + +static inline id ZZRaiseErrorNil(NSError** error, ZZErrorCode errorCode, NSDictionary* userInfo) +{ + if (error) + *error = [NSError errorWithDomain:ZZErrorDomain + code:errorCode + userInfo:userInfo]; + return nil; +} + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZipZap.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZipZap.h new file mode 100644 index 000000000..068fc16a0 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZipZap.h @@ -0,0 +1,45 @@ +// +// ZipZap.h +// ZipZap +// +// Created by Glen Low on 31/10/12. +// Copyright (c) 2012, Pixelglow Software. All rights reserved. +// + +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +#import + +#import "ZZArchive.h" +#import "ZZArchiveEntry.h" +#import "ZZConstants.h" +#import "ZZError.h" + +//! Project version number for ZipZap. +FOUNDATION_EXPORT double ZipZapVersionNumber; + +//! Project version string for ZipZap. +FOUNDATION_EXPORT const unsigned char ZipZapVersionString[]; + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Info.plist new file mode 100644 index 000000000..effe2c50a Binary files /dev/null and b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Info.plist differ diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/JLDialUnit b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/JLDialUnit new file mode 100755 index 000000000..12ada05cd Binary files /dev/null and b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/JLDialUnit differ diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Modules/module.modulemap new file mode 100644 index 000000000..873f1cbf9 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module JLDialUnit { + umbrella header "JLDialUnit.h" + export * + + module * { export * } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/DialManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/DialManager.h new file mode 100644 index 000000000..869a29e09 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/DialManager.h @@ -0,0 +1,127 @@ +// +// DialManager.h +// JLDialUnit +// +// Created by 杰理科技 on 2021/7/20. +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + + + +typedef NS_ENUM(NSInteger, DialOperateType) { + DialOperateTypeNoSpace = 0, //空间不足 + DialOperateTypeDoing = 1, //正在操作 + DialOperateTypeFail = 2, //操作失败 + DialOperateTypeSuccess = 3, //操作成功 + DialOperateTypeUnnecessary = 4, //无需重复打开文件系统 + DialOperateTypeResetFial = 5, //重置文件系统失败 + DialOperateTypeNormal = 6, //文件系统正常 + DialOperateTypeCmdFail = 7, //流程命令执行失败 +}; +typedef NS_ENUM(NSInteger, DialUpdateResult) { + DialUpdateResultFinished = 0, //更新资源完成 + DialUpdateResultNewest = 1, //资源已最新 + DialUpdateResultInvalid = 2, //资源无效 + DialUpdateResultEmpty = 3, //资源为空 + DialUpdateResultReplace = 4, //资源替换 + DialUpdateResultAdd = 5, //资源新增 + DialUpdateResultNoSpace = 6, //空间不足 + DialUpdateResultZipError = 7, //ZIP资源文件错误 + DialUpdateResultCompareFail = 8, //表盘资源对比失败 + DialUpdateResultUpdateUfw = 9, //更新固件 ufw +}; +typedef void(^DialOperateBK)(DialOperateType type, float progress); +typedef void(^DialListBK)(DialOperateType type, NSArray* __nullable array); +typedef void(^DialUpdateBK)(DialUpdateResult updateResult, + NSArray* __nullable array, + NSString * _Nullable filePath, + NSInteger index ,float progress); + +typedef void(^FatfsFreeBlock)(uint32_t freeSize); + +/// 表盘管理类 +@interface DialManager : NSObject + +#pragma mark - 连接成功后,必须调用一次! ++(void)openDialFileSystemWithCmdManager:(JL_ManagerM *)manager withResult:(DialOperateBK)result; + +#pragma mark - 重置表盘系统 ++(void)resetDialFileSystemWithCmdManager:(JL_ManagerM *)manager withResult:(DialOperateBK)result; + +#pragma mark - 查询文件 +/// 查询文件 +/// @param result 操作回调 ++(void)listFile:(DialListBK __nullable)result; + +#pragma mark - 添加文件 +/// 添加文件 +/// @param file 文件名需要加斜杠,类似@“/WACTH1”。 +/// @param content 文件数据 +/// @param result 操作回调 ++(void)addFile:(NSString*)file + Content:(NSData*)content + Result:(DialOperateBK)result; + +#pragma mark - 删除文件 +/// 删除文件(第一种) +/// @param file 文件名需要加斜杠,类似@“/WACTH1”。 +/// @param result 操作回调 ++(void)deleteFile:(NSString*)file + Result:(DialOperateBK)result; + +/// 删除文件(第二种) +/// @param fileModel JLModel_File类型 +/// @param result 操作回调 ++(void)deleteDialResourceWithFileModel:(JLModel_File*)fileModel Result:(DialOperateBK)result; + +#pragma mark - 替换文件 +/// 替换文件 +/// @param file 文件名需要加斜杠,类似@“/WACTH1”。 +/// @param content 文件数据 +/// @param result 操作回调 ++(void)repaceFile:(NSString*)file + Content:(NSData*)content + Result:(DialOperateBK)result; + +#pragma mark - 格式化外部Flash +/// 格式化外部Flash操作 +/// @param handle 设备句柄 +/// @param result 操作回调 ++(void)formatFlash:(NSString*)handle Result:(DialOperateBK)result; + +#pragma mark - 更新设备的表盘资源(异步调用) +/// 更新设备的表盘资源(异步调用) +/// @param path 资源文件 +/// @param array 表盘列表(当前) +/// @param result 更新结果 ++(void)updateResourcePath:(NSString*)path + List:(NSArray*)array + Result:(DialUpdateBK)result; + +/// 获取 fatfs 系统的剩余空间(仅仅fatfs 系统可用) +/// 需要异步执行 ++(uint32_t)getFatfsFree; + +/// 获取 fatfs 系统的剩余空间(仅仅fatfs 系统可用) +/// @param block 回调空间 ++(void)getFatfsFree:(FatfsFreeBlock) block; + +/// 获取某文件的文件大小 +/// @param fileName 文件名 ++(uint32_t)getSizeOfFileName:(NSString*)fileName; + + +/// 记录文件大小(私有接口) +/// @param size 文件大小 +/// @param fileName 文件名 ++(void)setFileSize:(uint32_t)size FileName:(NSString*)fileName; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/FatfsObject.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/FatfsObject.h new file mode 100644 index 000000000..c6fe7e0d1 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/FatfsObject.h @@ -0,0 +1,92 @@ +// +// FatfsObject.h +// JLDialUnit +// +// Created by 杰理科技 on 2021/7/20. +// + +#import +#import + + + +NS_ASSUME_NONNULL_BEGIN + +@interface FatfsObject : NSObject + +#pragma mark ---> FATFS API +/// 输入命令中心类,在JL_Entity内。 +/// @param manager 命令中心类 ++(void)makeCmdManager:(JL_ManagerM*)manager; + +/// 返回 当前 JL_ManagerM ++(JL_ManagerM*)outputCmdManager; + +/// 设置外挂Flash的大小,FATFS的大小。 +/// @param flashSize Flash大小 +/// @param fatsSize FATFS的大小 ++(BOOL)makeFlashSize:(uint32_t)flashSize + FatsSize:(uint32_t)fatsSize; + +/// 获取Flash上的文件 +/// @param path 文件名数组,其中"JL",“FONT”不能操作。 ++(NSArray*)makeListPath:(NSString*)path; + +typedef void(^FatfsCreateFile_BK)(float progress); +/// 新增文件 +/// @param path “/文件名” +/// @param data 文件数据内容 +/// @param result 进度 ++(BOOL)makeCreateFile:(NSString*)path + Content:(NSData* __nullable)data + Result:(FatfsCreateFile_BK __nullable)result; + +typedef void(^FatfsReplaceFile_BK)(float progress); +/// 替换文件 +/// @param path “/文件名” +/// @param data 文件数据内容 ++(BOOL)makeReplaceFile:(NSString*)path + Content:(NSData* __nullable)data + Result:(FatfsReplaceFile_BK __nullable)result; + +/// 删除文件 +/// @param path “/文件名” ++(BOOL)makeRemoveFile:(NSString*)path; + +/*--------------------------------------------------------------------------------- + FatSize -- FAT文件系统认为自己的大小 + 因为小机的FAT项按4K对齐了大小,所以实际上会认为FAT系统占用的空间比FLASH实际大小还要大 + 用 f_getfree 获取的是 FAT文件系统认为自己剩余的空间的簇个数,其中有一部分是在Flash上实际不存在的 + 所以需要减掉这部分 + 计算方式如下: + FatUsed = FatSize - FreeSize + FlashRemainSize = FlashSize - FatUsed + ----------------------------------------------------------------------------------*/ +/// 获取FATFS系统剩余空间,“FreeSize”。 ++(uint32_t)makeGetFree; + +/// 查看watch本间版本 +/// @param path 文件路径 ++(NSString*)makeVersionWatch:(NSString*)path; + +/// 获取文件前面512个字节 +/// @param path 文件路径 ++(NSData *)makeFileHead:(NSString*)path; + +/// 读文件 +/// @param path 文件路径 ++(NSData*)makeFileContent:(NSString*)path; + +/// 解压资源文件 +/// @param path 文件路径 +/// @param aimDirection 存放的路径 ++(NSArray*)unzipFileAtPath:(NSString *)path toDestination:(NSString *)aimDirection; + +/// 重置外部flash ++(BOOL)makeResetFlash; + + +@end + +NS_ASSUME_NONNULL_END + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/JLDialSourceModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/JLDialSourceModel.h new file mode 100644 index 000000000..e32e99dad --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/JLDialSourceModel.h @@ -0,0 +1,27 @@ +// +// JLDialSourceModel.h +// JLDialUnit +// +// Created by EzioChan on 2025/3/19. +// Copyright © 2025 www.zh-jieli.com. All rights reserved. +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface JLDialSourceModel : JLModel_File + +/// 文件大小 +@property (nonatomic, assign)int size; + +/// CRC +@property (nonatomic, assign)uint16_t crc; + ++(JLDialSourceModel *)createModel:(JLModel_File *)model; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/JLDialUnit.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/JLDialUnit.h new file mode 100644 index 000000000..07e55445b --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/JLDialUnit.h @@ -0,0 +1,30 @@ +// +// JLDialUnit.h +// JLDialUnit +// +// Created by EzioChan on 2023/2/28. +// Copyright © 2023 www.zh-jieli.com. All rights reserved. +// + +#import + +//! Project version number for JLDialUnit. +FOUNDATION_EXPORT double JLDialUnitVersionNumber; + +//! Project version string for JLDialUnit. +FOUNDATION_EXPORT const unsigned char JLDialUnitVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/JLDialUnitMgr.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/JLDialUnitMgr.h new file mode 100644 index 000000000..88ce0d6f0 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/JLDialUnitMgr.h @@ -0,0 +1,115 @@ +// +// JLDialUnitMgr.h +// JLDialUnit +// +// Created by EzioChan on 2025/3/18. +// Copyright © 2025 www.zh-jieli.com. All rights reserved. +// + +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN +@class JLDialSourceModel; +@interface JLDialUnitMgr : NSObject + +/// 设备文件列表 +@property (nonatomic,strong)NSMutableArray *fileArray; + +/// 当前 Flash 操控句柄 +@property (nonatomic, strong) JLFlashOpMgr *currentFlaghOpMgr; + +/// 当前使用的表盘文件 +@property (nonatomic,strong)JLDialSourceModel *currentFileSource; + +/// 当前使用的表盘背景 +@property (nonatomic,strong)JLDialSourceModel *_Nullable currentBackground; + +/// 初始化 +/// - Parameters: +/// - mgr: 蓝牙管理对象 +/// - completion: 回调 +-(instancetype)initWithManager:(JL_ManagerM*)mgr completion:(void (^)(NSError * _Nullable))completion; + +/// 获取文件列表 +/// - Parameters: +/// - cardType: 设备存储的类型 +/// - count: 请求的数目 +/// - completion: 回调 +/// 返回时,是返回累计数目 +-(void)getFileList:(JL_CardType)cardType count:(NSInteger)count completion:(void (^)(NSArray * _Nullable,NSError * _Nullable))completion; + +/// 清空文件列表 +/// - Parameter cardType: 设备存储的类型 +-(void)cleanFileList; + +/// 更新文件列表信息 +/// - Parameters: +/// - fileSources: NSArray +/// - completion: 回调 +-(void)updateFileSources:(NSArray *)fileSources completion:(void (^)(NSArray * _Nullable,NSError * _Nullable))completion; + +/// 更新文件到设备 +/// - Parameters: +/// - fileHandleType: 设备目标存储句柄 +/// - fileData: 文件数据 +/// - filePath: 文件存储路径 +/// - resultCompletion: 回调 +/// state: 0:成功 1:传输中 -1:失败 +/// progress: 传输进度 +/// error: 错误 +-(void)updateFileToDevice:(JL_FileHandleType)fileHandleType Data:(NSData *)fileData FilePath:(NSString *)filePath completion:(void (^)(int,double,NSError * _Nullable))resultCompletion; + +/// 删除文件 +/// - Parameters: +/// - filePath: 文件路径 +/// - resultCompletion: 回调 +-(void)deleteFile:(NSString *)filePath getFileList:(BOOL)isGet Completion:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion; + +/// 删除多个文件 +/// - Parameters: +/// - filePathList: 文件路径 +/// - resultCompletion: 回调 +-(void)deleteFiles:(NSArray *) filePathList Completion:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion; + +/// 获取剩余空间 +/// - Parameter completion: 回调 +-(void)getRemainingSpace:(void(^)(int freeSize, NSError * _Nullable error))completion; + +/// 获取剩余空间 V1 +/// - Parameter completion: 回调 +-(void)getFlashRemainingSpace:(void(^)(int freeSize, NSError * _Nullable error))completion; + +//MARK: 表盘操作 + +/// 获取当前正在使用的表盘 +/// - Parameter completion: 回调 +-(void)dialGetCurrent:(void (^)(JLDialSourceModel * _Nullable))completion; + +/// 设置当前表盘 +/// - Parameters: +/// - model: 表盘 +/// - resultCompletion: 回调 +-(void)dialSetCurrent:(JLDialSourceModel *)model Completion:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion; + +/// 激活自定义表盘背景 +/// - Parameters: +/// - model: 表盘背景对象 +/// - resultCompletion: 回调 +-(void)dialActiveCustomBackground:(JLDialSourceModel *)model Completion:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion; + +/// 重置自定义表盘背景 +/// - Parameter resultCompletion: 回调 +-(void)dialResetCustomBackground:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion; + +/// 获取表盘背景 +/// - Parameters: +/// - path: 路径 诸如“/WATCH” +/// - resultCompletion: 回调 +-(void)dialGetDial:(NSString *)path BackgroundInfo:(void (^)(JLDialSourceModel * _Nullable model, NSError * _Nullable error))resultCompletion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/JLSourceUpdate.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/JLSourceUpdate.h new file mode 100644 index 000000000..4c52a6d64 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/JLSourceUpdate.h @@ -0,0 +1,94 @@ +// +// JLSourceUpdate.h +// JLDialUnit +// +// Created by EzioChan on 2025/3/18. +// Copyright © 2025 www.zh-jieli.com. All rights reserved. +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN +@class JLDialUnitMgr; + +typedef NS_ENUM(NSInteger, JLSourceUpdateType) { + /// 更新资源完成 + JLSourceUpdateTypeFinished = 0, + /// 资源已最新 + JLSourceUpdateTypeNewest = 1, + /// 资源无效 + JLSourceUpdateTypeInvalid = 2, + /// 资源为空 + JLSourceUpdateTypeEmpty = 3, + /// 资源更新错误 + JLSourceUpdateTypeSrcError = 4, + /// 资源更新中 + JLSourceUpdateTypeUpdateing = 5, + /// 空间不足 + JLSourceUpdateTypeNoSpace = 6, + /// ZIP 资源文件错误 + JLSourceUpdateTypeZipError = 7, + /// 表盘资源对比失败 + JLSourceUpdateTypeCompareFail = 8, + /// 更新固件 ufw + JLSourceUpdateTypeUpdateUfw = 9, + /// 不支持 + JLSourceUpdateTypeUpdateNotSupport = 10, +}; + + +@interface JLSourceInfo : NSObject + +@property(nonatomic,strong)NSString *filePath; +@property(nonatomic,strong,readonly)NSData * _Nullable fileData; +@property(nonatomic,strong,readonly)NSString *fileName; +@property(nonatomic,assign)NSInteger index; + +@end + +@protocol JLSourceUpdateDelegate + +- (void)updateProgress:(double)progress type:(JLSourceUpdateType)type source:(JLSourceInfo *_Nullable)source error:(NSError* _Nullable)error; + +- (void)updateUfwFirst:(NSString *)filePath ThenUpdateSrc:(NSArray *) filePaths; + +@end + +/// 资源更新 +@interface JLSourceUpdate : NSObject + +@property(nonatomic,weak)id delegate; + +/// 初始化 +/// - Parameters: +/// - mgr: 蓝牙管理对象 +/// - fileType: 文件句柄 +/// - dialMgr: JLDialUnitMgr +/// - delegate: 代理 +-(instancetype)initWithManager:(JL_ManagerM*)mgr HandleType:(JL_FileHandleType) fileType DialMgr:(JLDialUnitMgr *)dialMgr delegate:(id)delegate; + + +/// 更新资源 +/// - Parameters: +/// - path: zip包路径 +/// - mgr: JLDialUnitMgr +-(void)updateSources:(NSString*)path DialUnitMgr:(JLDialUnitMgr *)mgr; + + +/// 更新资源 AC707N 手表特殊升级流程 +/// - Parameters: +/// - path: zip 包路径 +/// - mgr: JLDialUnitMgr +-(void)updateSourcesV2:(NSString*)path DialUnitMgr:(JLDialUnitMgr *)mgr; + + +/// 更新资源任务 +/// @param targetUpdateList 需要更新的文件列表 +/// @param updateResult 更新结果 +-(void)updateAciton:(NSArray *)targetUpdateList Result:(void (^)(BOOL isFinish, NSError * _Nullable))updateResult; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/ZZArchive.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/ZZArchive.h new file mode 100644 index 000000000..a37e0a7e7 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/ZZArchive.h @@ -0,0 +1,131 @@ +// +// ZZArchive.h +// ZipZap +// +// Created by Glen Low on 25/09/12. +// Copyright (c) 2012, Pixelglow Software. All rights reserved. +// + +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +#import +#import "ZZConstants.h" + +@class ZZArchiveEntry; + +NS_ASSUME_NONNULL_BEGIN + +/** + * The ZZArchive class represents a zip file for reading only. + */ +@interface ZZArchive : NSObject + +/** + * The URL representing this archive. + */ +@property (readonly, nullable, nonatomic) NSURL* URL; + +/** + * The uninterpreted contents of this archive. + */ +@property (readonly, nonatomic) NSData* contents; + +/** + * The array of ZZArchiveEntry entries within this archive. + */ +@property (readonly, nonatomic) NSArray* entries; + +/** + * Creates a new archive with the zip file at the given file URL. + * + * The archive will use UTF-8 encoding for reading entry file names and comments, and will not create the file if it is missing. + * + * @param URL The file URL of the zip file. + * @param error The error information when an error occurs. Pass in nil if you do not want error information. + * @return The initialized archive. Returns nil if the archive cannot be initialized. + */ ++ (nullable instancetype)archiveWithURL:(NSURL*)URL + error:(out NSError**)error; + +/** + * Creates a new archive with the raw zip file data given. + * + * The archive will use UTF-8 encoding for reading entry file names and comments, and will not create the data if it is missing. + * + * @param data The raw data of the zip file. + * @param error The error information when an error occurs. Pass in nil if you do not want error information. + * @return The initialized archive. Returns nil if the archive cannot be initialized. + */ ++ (nullable instancetype)archiveWithData:(NSData*)data + error:(out NSError**)error; + +/** + * This initializer should never be used. + */ +- (instancetype)init NS_UNAVAILABLE; + +/** + * Initializes a new archive with the zip file at the given file URL. + * + * @param URL The file URL of the zip file. + * @param options The options to consider when opening the zip file. + * @param error The error information when an error occurs. Pass in nil if you do not want error information. + * @return The initialized archive. Returns nil if the archive cannot be initialized. + */ +- (nullable instancetype)initWithURL:(NSURL*)URL + options:(nullable NSDictionary*)options + error:(out NSError**)error; + +/** + * Initializes a new archive with the raw zip file data given. + * + * @param data The raw data of the zip file + * @param options The options to consider when opening the zip file. + * @param error The error information when an error occurs. Pass in nil if you do not want error information. + * @return The initialized archive. Returns nil if the archive cannot be initialized. + */ +- (nullable instancetype)initWithData:(NSData*)data + options:(nullable NSDictionary*)options + error:(out NSError**)error; + +/** + * Updates the entries and writes them to the source. + * + * If the write fails and the entries are completely new, the existing zip file will be untouched. + * + * If the write fails and the entries contain some or all existing entries, the zip file may be corrupted. + * In this case, the error information will report the ZZReplaceWriteErrorCode error code. + * + * @param newEntries The entries to update to, may contain some or all existing entries. + * @param error The error information when an error occurs. Pass in nil if you do not want error information. + * @return Whether the update was successful or not. + * + */ +- (BOOL)updateEntries:(NSArray*)newEntries + error:(out NSError**)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/ZZArchiveEntry.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/ZZArchiveEntry.h new file mode 100644 index 000000000..2fa9b0836 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/ZZArchiveEntry.h @@ -0,0 +1,239 @@ +// +// ZZArchiveEntry.h +// ZipZap +// +// Created by Glen Low on 25/09/12. +// Copyright (c) 2012, Pixelglow Software. All rights reserved. +// + +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#include +#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +#include +#endif + +#import + +@protocol ZZArchiveEntryWriter; + +NS_ASSUME_NONNULL_BEGIN + +/** + * The ZZArchiveEntry class represents an entry in the ZZArchive instance. + */ +@interface ZZArchiveEntry : NSObject + +/** + * Whether the entry is compressed. + */ +@property (readonly, nonatomic) BOOL compressed; + +/** + * Whether the entry is encrypted. + */ +@property (readonly, nonatomic) BOOL encrypted; + +/** + * The last modified date and time of the entry. The time value is only accurate to 2 seconds. + */ +@property (readonly, nonatomic) NSDate* lastModified; + +/** + * The CRC32 code of the entry file: 0 for new entries. + */ +@property (readonly, nonatomic) NSUInteger crc32; + +/** + * The compressed size of the entry file: 0 for new entries. + */ +@property (readonly, nonatomic) NSUInteger compressedSize; + +/** + * The uncompressed size of the entry file: 0 for new entries. + */ +@property (readonly, nonatomic) NSUInteger uncompressedSize; + +/** + * The UNIX file mode for the entry: 0 for new or non-UNIX entries. This includes the file type bits. + */ +@property (readonly, nonatomic) mode_t fileMode; + +/** + * The file name of the entry. Encoding is either CP-437 or UTF-8, depending on the EFS flag of the general purpose bit flags. + */ +@property (readonly, nonatomic) NSString* fileName; + +/** + * The file name of the entry as array of bytes + */ +@property (readonly, nonatomic) NSData* rawFileName; + +/** + * The encoding of filename and comment fields, according to the EFS flag of the general purpose bit flags. + */ +@property (readonly, nonatomic) NSStringEncoding encoding; + +/** + * Creates a new file entry from a streaming callback. + * + * @param fileName The file name for the entry. + * @param compress Whether to compress the entry. + * @param streamBlock The callback to write the entry's data to the stream. Returns whether the write should be considered successful. + * @return The created entry. + */ ++ (instancetype)archiveEntryWithFileName:(NSString*)fileName + compress:(BOOL)compress + streamBlock:(BOOL(^)(NSOutputStream* stream, NSError** error))streamBlock; + +/** + * Creates a new file entry from a data callback. + * + * @param fileName The file name for the entry. + * @param compress Whether to compress the entry. + * @param dataBlock The callback to return the entry's data. Returns nil if the write should be considered unsuccessful. + * @return The created entry. + */ ++ (instancetype)archiveEntryWithFileName:(NSString*)fileName + compress:(BOOL)compress + dataBlock:(NSData* _Nullable(^)(NSError** error))dataBlock; + +/** + * Creates a new file entry from a data-consuming callback. + * + * @param fileName The file name for the entry. + * @param compress Whether to compress the entry. + * @param dataConsumerBlock The callback to put the entry's data into the data consumer. Returns whether the write should be considered successful. + * @return The created entry. + */ ++ (instancetype)archiveEntryWithFileName:(NSString*)fileName + compress:(BOOL)compress + dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer, NSError** error))dataConsumerBlock; + +/** + * Creates a new directory entry. + * + * @param directoryName The directory name for the entry. + * @return The created entry. + */ ++ (instancetype)archiveEntryWithDirectoryName:(NSString*)directoryName; + +/** + * Creates a new entry. + * + * The archive entry will choose the first non-nil dataBlock, streamBlock or dataConsumerBlock to supply its data. + * + * @param fileName The file name for the entry. + * @param fileMode The UNIX file mode for the entry. This includes the file type bits. + * @param lastModified The last modified date and time for the entry. The time value is only accurate to 2 seconds. + * @param compressionLevel The compression level for the entry: 0 for stored, -1 for default deflate, 1 - 9 for custom deflate levels. + * @param dataBlock The callback that returns the entry file data. Returns nil if the write should be considered unsuccessful. + * @param streamBlock The callback that writes the entry file to the stream. Returns whether the write should be considered successful. + * @param dataConsumerBlock The callback that writes the entry file to the data consumer. Returns whether the write should be considered successful. + * @return The created entry. + */ ++ (instancetype)archiveEntryWithFileName:(NSString*)fileName + fileMode:(mode_t)fileMode + lastModified:(NSDate*)lastModified + compressionLevel:(NSInteger)compressionLevel + dataBlock:(nullable NSData* _Nullable(^)(NSError** error))dataBlock + streamBlock:(nullable BOOL(^)(NSOutputStream* stream, NSError** error))streamBlock + dataConsumerBlock:(nullable BOOL(^)(CGDataConsumerRef dataConsumer, NSError** error))dataConsumerBlock; + +/** + * Checks whether the entry file is consistent. + * + * Checks whether the local file entry is consistent with the central file entry and also that + * the recorded and actual checksums of the data agree. + * + * @param error The error information when an error occurs. Pass in nil if you do not want error information. + * @return Whether entry file is consistent or not. + */ +- (BOOL)check:(out NSError**)error; + +/** + * The file name of the entry with the given encoding. + * + * @param encoding The encoding for the filename. + */ +- (NSString*)fileNameWithEncoding:(NSStringEncoding)encoding; + +/** + * Creates a stream to represent the entry file. + * + * @param error A pointer to a variable that will contain the error if any. + * @return The new stream: nil for new entries. + */ +- (nullable NSInputStream*)newStreamWithError:(NSError**)error; + +/** + * Creates a stream to represent the entry file. + * + * @param password The password to be used for decryption. + * @param error A pointer to a variable that will contain the error if any. + * @return The new stream: nil for new entries. + */ +- (nullable NSInputStream*)newStreamWithPassword:(nullable NSString*)password error:(NSError**)error; + +/** + * Creates data to represent the entry file. + * + * @param error A pointer to a variable that will contain the error if any. + * @return The new data: nil for new entries. + */ +- (nullable NSData*)newDataWithError:(NSError**)error; + +/** + * Creates data to represent the entry file. + * + * @param password The password to be used for decryption. + * @param error A pointer to a variable that will contain the error if any. + * @return The new data: nil for new entries. + */ +- (nullable NSData*)newDataWithPassword:(nullable NSString*)password error:(NSError**)error; + +/** + * Creates a data provider to represent the entry file. + * + * @param error A pointer to a variable that will contain the error if any. + * @return The new data provider: nil for new entries. + */ +- (nullable CGDataProviderRef)newDataProviderWithError:(NSError**)error CF_RETURNS_RETAINED; + +/** + * Creates a data provider to represent the entry file. + * + * @param password The password to be used for decryption. + * @param error A pointer to a variable that will contain the error if any. + * @return The new data provider: nil for new entries. + */ +- (nullable CGDataProviderRef)newDataProviderWithPassword:(nullable NSString*)password error:(NSError**)error CF_RETURNS_RETAINED; + +- (id)newWriterCanSkipLocalFile:(BOOL)canSkipLocalFile; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/ZZConstants.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/ZZConstants.h new file mode 100644 index 000000000..698841852 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/ZZConstants.h @@ -0,0 +1,79 @@ +// +// ZZConstants.h +// ZipZap +// +// Created by Daniel Cohen Gindi on 29/12/13. +// Copyright (c) 2013, Pixelglow Software. All rights reserved. +// + +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +#import + +typedef NS_ENUM(NSInteger, ZZEncryptionMode) +{ + /** + * No encryption. + */ + ZZEncryptionModeNone, + + /** + * Standard PKZIP encryption. + */ + ZZEncryptionModeStandard, + + /** + * Strong PKZIP encryption. Currently not supported. + */ + ZZEncryptionModeStrong, + + /** + * WinZip encryption using AES. + */ + ZZEncryptionModeWinZipAES +}; + +typedef NS_ENUM(uint8_t, ZZAESEncryptionStrength) +{ + /** + * Use 128-bit AES for encryption. + */ + ZZAESEncryptionStrength128 = 0x01, + + /** + * Use 192-bit AES for encryption. + */ + ZZAESEncryptionStrength192 = 0x02, + + /** + * Use 256-bit AES for encryption. + */ + ZZAESEncryptionStrength256 = 0x03 +}; + +/** + * An NSNumber object that determines whether to create the archive file if it is missing. Creation occurs during -[ZZArchive updateEntries:error:]. Default is @NO. + */ +extern NSString* const ZZOpenOptionsCreateIfMissingKey; diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/ZZError.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/ZZError.h new file mode 100644 index 000000000..a307763cf --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/ZZError.h @@ -0,0 +1,136 @@ +// +// ZZError.h +// ZipZap +// +// Created by Glen Low on 25/01/13. +// Copyright (c) 2013, Pixelglow Software. All rights reserved. +// + +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +#import + +/** + * The domain of ZipZap errors. + */ +extern NSString* const ZZErrorDomain; + +/** + * The index of the erroneous entry. + */ +extern NSString* const ZZEntryIndexKey; + +typedef NS_ENUM(NSInteger, ZZErrorCode) +{ + /** + * Cannot open an archive for reading. + */ + ZZOpenReadErrorCode, + + /** + * Cannot read the end of central directory. + */ + ZZEndOfCentralDirectoryReadErrorCode, + + /** + * Cannot read a central file header. + */ + ZZCentralFileHeaderReadErrorCode, + + /** + * Cannot read a local file. + */ + ZZLocalFileReadErrorCode, + + /** + * Cannot open an archive for writing. + */ + ZZOpenWriteErrorCode, + + /** + * Cannot write a local file. + */ + ZZLocalFileWriteErrorCode, + + /** + * Cannot write a central file header. + */ + ZZCentralFileHeaderWriteErrorCode, + + /** + * Cannot write the end of central directory. + */ + ZZEndOfCentralDirectoryWriteErrorCode, + + /** + * Cannot replace the zip file after writing. + */ + ZZReplaceWriteErrorCode, + + /** + * The compression used is currently unsupported. + */ + ZZUnsupportedCompressionMethod, + + /** + * The encryption used is currently unsupported. + */ + ZZUnsupportedEncryptionMethod, + + /** + * An invalid CRC checksum has been encountered. + */ + ZZInvalidCRChecksum, + + /** + * The wrong key was passed in. + */ + ZZWrongPassword, + + /** + * The data, stream or data consumer block failed but did not set the error. + * This will be set on the underlying error of the local file write. + */ + ZZBlockFailedWithoutError +}; + +static inline BOOL ZZRaiseErrorNo(NSError** error, ZZErrorCode errorCode, NSDictionary* userInfo) +{ + if (error) + *error = [NSError errorWithDomain:ZZErrorDomain + code:errorCode + userInfo:userInfo]; + return NO; +} + +static inline id ZZRaiseErrorNil(NSError** error, ZZErrorCode errorCode, NSDictionary* userInfo) +{ + if (error) + *error = [NSError errorWithDomain:ZZErrorDomain + code:errorCode + userInfo:userInfo]; + return nil; +} + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/ZipZap.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/ZipZap.h new file mode 100644 index 000000000..068fc16a0 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/ZipZap.h @@ -0,0 +1,45 @@ +// +// ZipZap.h +// ZipZap +// +// Created by Glen Low on 31/10/12. +// Copyright (c) 2012, Pixelglow Software. All rights reserved. +// + +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +#import + +#import "ZZArchive.h" +#import "ZZArchiveEntry.h" +#import "ZZConstants.h" +#import "ZZError.h" + +//! Project version number for ZipZap. +FOUNDATION_EXPORT double ZipZapVersionNumber; + +//! Project version string for ZipZap. +FOUNDATION_EXPORT const unsigned char ZipZapVersionString[]; + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Info.plist new file mode 100644 index 000000000..83e1ad523 Binary files /dev/null and b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Info.plist differ diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/JLDialUnit b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/JLDialUnit new file mode 100755 index 000000000..3bff2f486 Binary files /dev/null and b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/JLDialUnit differ diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Modules/module.modulemap new file mode 100644 index 000000000..873f1cbf9 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module JLDialUnit { + umbrella header "JLDialUnit.h" + export * + + module * { export * } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/_CodeSignature/CodeResources b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/_CodeSignature/CodeResources new file mode 100644 index 000000000..84c4f3a0e --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/_CodeSignature/CodeResources @@ -0,0 +1,282 @@ + + + + + files + + Headers/DialManager.h + + Mb2jV1CnOQ8OFCZUBx0Z3/6vUx4= + + Headers/FatfsObject.h + + kduOIsvKG9oOwJR5cZFVR744ZP0= + + Headers/JLDialSourceModel.h + + eIkUWVijyig9VgWIRPImfF9A2DM= + + Headers/JLDialUnit.h + + lDdkEIa40U/+f8NpGLO6SJHK/ZI= + + Headers/JLDialUnitMgr.h + + l6ivZmKQhFyNKu5Wy1Wp2IHCH3w= + + Headers/JLSourceUpdate.h + + gFCkm0vIO2ZW1DtpnDS6m1MI+yQ= + + Headers/ZZArchive.h + + fiHROZsQUJUlj+6a/iwZVg85QzM= + + Headers/ZZArchiveEntry.h + + a1D8K7lXbAyn/Q+LjNUtzBndzb8= + + Headers/ZZConstants.h + + k+fTTarqbjiDQ4X2wXehYKxH38Y= + + Headers/ZZError.h + + 29Dme+O1rSf3n+RCHQLmE7mPdoo= + + Headers/ZipZap.h + + RaXhaS0pUtZHPeOSQhDq2kmw38A= + + Info.plist + + S01R8qKENWyeFMKwZu8s9ypBNCQ= + + Modules/module.modulemap + + X9AQ9UbZdsaQnmASvtGbwRKrbwY= + + + files2 + + Headers/DialManager.h + + hash + + Mb2jV1CnOQ8OFCZUBx0Z3/6vUx4= + + hash2 + + DWCDneg/KpcPLEMS9W0V8GS8NAJRJQ3xU2vQ9wDNKGI= + + + Headers/FatfsObject.h + + hash + + kduOIsvKG9oOwJR5cZFVR744ZP0= + + hash2 + + tbux7Dz4wdT+oqHrgs9sZiQYnpSqKu9X0iKE/ZuhcNA= + + + Headers/JLDialSourceModel.h + + hash + + eIkUWVijyig9VgWIRPImfF9A2DM= + + hash2 + + OcwV19AMXbyLPjp7/nLR+O/wnumr3eppJtpyJ7JzVrw= + + + Headers/JLDialUnit.h + + hash + + lDdkEIa40U/+f8NpGLO6SJHK/ZI= + + hash2 + + H3VhMgaLiZwlRDQxjabXw2Jsw7KjIKg2P6SjQ69ztiA= + + + Headers/JLDialUnitMgr.h + + hash + + l6ivZmKQhFyNKu5Wy1Wp2IHCH3w= + + hash2 + + vCyZvP2a+LBqY0HXMyIKvdxpUrVJr9F9kknNsg5pzWY= + + + Headers/JLSourceUpdate.h + + hash + + gFCkm0vIO2ZW1DtpnDS6m1MI+yQ= + + hash2 + + cJV9DTlK3k4aIGNCEn3CpdQiCT5stcM6cNdC95AC39I= + + + Headers/ZZArchive.h + + hash + + fiHROZsQUJUlj+6a/iwZVg85QzM= + + hash2 + + Y2wNoNW2qcR6Kb51+8YoEZekVt5J8PMGJpfOcEfRPfI= + + + Headers/ZZArchiveEntry.h + + hash + + a1D8K7lXbAyn/Q+LjNUtzBndzb8= + + hash2 + + 7uQNNgDpWK2PbPtCFX+Y8mDCRo7c85NIpL8y58KonFM= + + + Headers/ZZConstants.h + + hash + + k+fTTarqbjiDQ4X2wXehYKxH38Y= + + hash2 + + 5gRNhwXKHN2OLt5jDVuXbYLLumdcEkNfenslGce1/Ec= + + + Headers/ZZError.h + + hash + + 29Dme+O1rSf3n+RCHQLmE7mPdoo= + + hash2 + + KpD5n8wFzDzn8wwMg6E7r3zYIOdZcWozb3hstntbEN0= + + + Headers/ZipZap.h + + hash + + RaXhaS0pUtZHPeOSQhDq2kmw38A= + + hash2 + + SfFE2DDepKHCHaP6yMkWN0t7/aZ2OOq5tWABp9ZQ958= + + + Modules/module.modulemap + + hash + + X9AQ9UbZdsaQnmASvtGbwRKrbwY= + + hash2 + + XvC1qrDmDLbId8N8voUZDEjYELULS2EUMos/IW53IuU= + + + + rules + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/Info.plist similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/Info.plist rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/Info.plist diff --git a/local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/Headers/JLLogHelper.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/Headers/JLLogHelper.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/Headers/JLLogHelper.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/Headers/JLLogHelper.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/Headers/JLLogManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/Headers/JLLogManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/Headers/JLLogManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/Headers/JLLogManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/Info.plist similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/Info.plist rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/Info.plist diff --git a/local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/JLLogHelper b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/JLLogHelper similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/JLLogHelper rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/JLLogHelper diff --git a/local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/Modules/module.modulemap similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/Modules/module.modulemap rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64/JLLogHelper.framework/Modules/module.modulemap diff --git a/local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/Headers/JLLogHelper.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/Headers/JLLogHelper.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/Headers/JLLogHelper.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/Headers/JLLogHelper.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/Headers/JLLogManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/Headers/JLLogManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/Headers/JLLogManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/Headers/JLLogManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/Info.plist similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/Info.plist rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/Info.plist diff --git a/local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/JLLogHelper b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/JLLogHelper similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/JLLogHelper rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/JLLogHelper diff --git a/local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/Modules/module.modulemap similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/Modules/module.modulemap rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/Modules/module.modulemap diff --git a/local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/_CodeSignature/CodeResources b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/_CodeSignature/CodeResources similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/_CodeSignature/CodeResources rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JLLogHelper.xcframework/ios-arm64_x86_64-simulator/JLLogHelper.framework/_CodeSignature/CodeResources diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/Info.plist new file mode 100644 index 000000000..6a1da49d2 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/Info.plist @@ -0,0 +1,44 @@ + + + + + AvailableLibraries + + + BinaryPath + JLPackageResKit.framework/JLPackageResKit + LibraryIdentifier + ios-arm64 + LibraryPath + JLPackageResKit.framework + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + BinaryPath + JLPackageResKit.framework/JLPackageResKit + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + JLPackageResKit.framework + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLPackageResKit.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLPackageResKit.h new file mode 100644 index 000000000..b414dcf73 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLPackageResKit.h @@ -0,0 +1,23 @@ +// +// JLPackageResKit.h +// JLPackageResKit +// +// Created by EzioChan on 2025/4/28. +// Copyright © 2025 www.zh-jieli.com. All rights reserved. +// + +#import + +//! Project version number for JLPackageResKit. +FOUNDATION_EXPORT double JLPackageResKitVersionNumber; + +//! Project version string for JLPackageResKit. +FOUNDATION_EXPORT const unsigned char JLPackageResKitVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import +#import +#import +#import +#import +#import + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLPackageSourceMgr.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLPackageSourceMgr.h new file mode 100644 index 000000000..3405e1d47 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLPackageSourceMgr.h @@ -0,0 +1,34 @@ +// +// JLPackageSourceMgr.h +// JLPackageResKit +// +// Created by EzioChan on 2025/4/28. +// Copyright © 2025 www.zh-jieli.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// 信息 +@interface JLPackageBaseInfo : NSObject + +/// 文件内容 +@property(nonatomic,strong)NSData *contentData; + +/// 文件名称 +@property(nonatomic,strong)NSString *fileName; + +@end + +/// 资源打包类 +@interface JLPackageSourceMgr : NSObject + +/// 打包多个文件成 xxx.package 数据 +/// - Parameters: +/// - infos: 文件信息 ++(NSData *)makePks:(NSArray*)infos; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLPcmToWts.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLPcmToWts.h new file mode 100644 index 000000000..1e1ba31e8 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLPcmToWts.h @@ -0,0 +1,34 @@ +// +// JLPcmToWts.h +// JLWtsToCfgLib +// +// Created by EzioChan on 2024/1/18. +// Copyright © 2024 www.zh-jieli.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef void(^wtsResultBlock)(BOOL isSuccess,NSData *_Nullable wtsData,NSString *filePath); + +/// PCM 转换 WTS +@interface JLPcmToWts : NSObject + +/// 单例 ++(instancetype)share; + +/// PCM 文件编码 WTS 文件 +/// - Parameters: +/// - speechInFileName: PCM 文件路径 +/// - bitOutFileName: WTS 文件路径 +/// - targetRate: wts 文件的目标码率 +/// - sr_in: pcm 文件的采样率 +/// - vadthr: vad 阈值 +/// - usesavemodef: 0:位流优先 1:质量优先 +/// - block: 数据回调 +-(void)pcmToWts:(NSString *)speechInFileName bitOutFileName:(NSString *)bitOutFileName targetRate:(int)targetRate sr_in:(int)sr_in vadthr:(float)vadthr usesavemodef:(int)usesavemodef Result:(wtsResultBlock)block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLToneCfgModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLToneCfgModel.h new file mode 100644 index 000000000..63569672d --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLToneCfgModel.h @@ -0,0 +1,23 @@ +// +// JLToneCfgModel.h +// JLWtsToCfgLib +// +// Created by EzioChan on 2024/1/22. +// Copyright © 2024 www.zh-jieli.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface JLToneCfgModel : NSObject + +/// 提示音文件名称 +@property(nonatomic, copy) NSString *fileName; + +/// 提示音文件内容 +@property(nonatomic, copy) NSData *data; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLVoicePackageManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLVoicePackageManager.h new file mode 100644 index 000000000..4bac0482a --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLVoicePackageManager.h @@ -0,0 +1,36 @@ +// +// JLVoicePackageManager.h +// JL_BLEKit +// +// Created by EzioChan on 2024/1/16. +// Copyright © 2024 www.zh-jieli.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN +@class JLToneCfgModel; +@class JLVoiceReplaceInfo; + + + +/// 提示音替换类 +@interface JLVoicePackageManager : NSObject + +/// 打包多个 .wts 文件成 tone.cfg 数据 +/// - Parameters: +/// - paths: 文件存放路径 +/// - names: 文件在设备端使用时的名称 +/// - info: 设备限制的信息 ++(NSData *)makePks:(NSArray *)paths FileNames:(NSArray *)names Info:(JLVoiceReplaceInfo*)info; + +/// 解包 tone.cfg 数据 +/// - Parameter data: tone.cfg 数据 ++(NSArray *)parsePks:(NSData *)data; + +/// 当前 SDK 版本 ++(NSString *)getVersion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLVoiceReplaceInfo.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLVoiceReplaceInfo.h new file mode 100644 index 000000000..7866a9bcf --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Headers/JLVoiceReplaceInfo.h @@ -0,0 +1,54 @@ +// +// JLVoiceReplaceInfo.h +// JLWtsToCfgLib +// +// Created by EzioChan on 2024/1/23. +// Copyright © 2024 www.zh-jieli.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// 提示音频信息 +@interface JLTipsVoiceInfo : NSObject + +/// 索引号 +@property(nonatomic,assign)uint8_t index; + +/// 偏移地址 +@property(nonatomic,assign)uint32_t offset; + +/// 文件大小 +@property(nonatomic,assign)uint32_t length; + +/// 文件名称 +@property(nonatomic,strong)NSString *fileName; + +/// 昵称名称 +@property(nonatomic,strong)NSString *nickName; +@end + +/// 提示音信息 +@interface JLVoiceReplaceInfo : NSObject + +/// 版本 +@property(nonatomic,assign)uint8_t version; + +/// 预留区域 +@property(nonatomic,assign)uint32_t blockSize; + +/// 最大音频数目 +@property(nonatomic,assign)uint8_t maxNum; + +/// 文件名称 +@property(nonatomic,strong)NSString *fileName; + +/// 文件列表细节 +@property(nonatomic,strong)NSArray *infoArray; + +-(instancetype)init:(NSData *)data; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Info.plist new file mode 100644 index 000000000..63c31bd3c Binary files /dev/null and b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Info.plist differ diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/JLPackageResKit b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/JLPackageResKit new file mode 100755 index 000000000..fc3c89793 Binary files /dev/null and b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/JLPackageResKit differ diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Modules/module.modulemap new file mode 100644 index 000000000..ce93f978e --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64/JLPackageResKit.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module JLPackageResKit { + umbrella header "JLPackageResKit.h" + export * + + module * { export * } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLPackageResKit.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLPackageResKit.h new file mode 100644 index 000000000..b414dcf73 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLPackageResKit.h @@ -0,0 +1,23 @@ +// +// JLPackageResKit.h +// JLPackageResKit +// +// Created by EzioChan on 2025/4/28. +// Copyright © 2025 www.zh-jieli.com. All rights reserved. +// + +#import + +//! Project version number for JLPackageResKit. +FOUNDATION_EXPORT double JLPackageResKitVersionNumber; + +//! Project version string for JLPackageResKit. +FOUNDATION_EXPORT const unsigned char JLPackageResKitVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import +#import +#import +#import +#import +#import + diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLPackageSourceMgr.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLPackageSourceMgr.h new file mode 100644 index 000000000..3405e1d47 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLPackageSourceMgr.h @@ -0,0 +1,34 @@ +// +// JLPackageSourceMgr.h +// JLPackageResKit +// +// Created by EzioChan on 2025/4/28. +// Copyright © 2025 www.zh-jieli.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// 信息 +@interface JLPackageBaseInfo : NSObject + +/// 文件内容 +@property(nonatomic,strong)NSData *contentData; + +/// 文件名称 +@property(nonatomic,strong)NSString *fileName; + +@end + +/// 资源打包类 +@interface JLPackageSourceMgr : NSObject + +/// 打包多个文件成 xxx.package 数据 +/// - Parameters: +/// - infos: 文件信息 ++(NSData *)makePks:(NSArray*)infos; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLPcmToWts.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLPcmToWts.h new file mode 100644 index 000000000..1e1ba31e8 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLPcmToWts.h @@ -0,0 +1,34 @@ +// +// JLPcmToWts.h +// JLWtsToCfgLib +// +// Created by EzioChan on 2024/1/18. +// Copyright © 2024 www.zh-jieli.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef void(^wtsResultBlock)(BOOL isSuccess,NSData *_Nullable wtsData,NSString *filePath); + +/// PCM 转换 WTS +@interface JLPcmToWts : NSObject + +/// 单例 ++(instancetype)share; + +/// PCM 文件编码 WTS 文件 +/// - Parameters: +/// - speechInFileName: PCM 文件路径 +/// - bitOutFileName: WTS 文件路径 +/// - targetRate: wts 文件的目标码率 +/// - sr_in: pcm 文件的采样率 +/// - vadthr: vad 阈值 +/// - usesavemodef: 0:位流优先 1:质量优先 +/// - block: 数据回调 +-(void)pcmToWts:(NSString *)speechInFileName bitOutFileName:(NSString *)bitOutFileName targetRate:(int)targetRate sr_in:(int)sr_in vadthr:(float)vadthr usesavemodef:(int)usesavemodef Result:(wtsResultBlock)block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLToneCfgModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLToneCfgModel.h new file mode 100644 index 000000000..63569672d --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLToneCfgModel.h @@ -0,0 +1,23 @@ +// +// JLToneCfgModel.h +// JLWtsToCfgLib +// +// Created by EzioChan on 2024/1/22. +// Copyright © 2024 www.zh-jieli.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface JLToneCfgModel : NSObject + +/// 提示音文件名称 +@property(nonatomic, copy) NSString *fileName; + +/// 提示音文件内容 +@property(nonatomic, copy) NSData *data; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLVoicePackageManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLVoicePackageManager.h new file mode 100644 index 000000000..4bac0482a --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLVoicePackageManager.h @@ -0,0 +1,36 @@ +// +// JLVoicePackageManager.h +// JL_BLEKit +// +// Created by EzioChan on 2024/1/16. +// Copyright © 2024 www.zh-jieli.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN +@class JLToneCfgModel; +@class JLVoiceReplaceInfo; + + + +/// 提示音替换类 +@interface JLVoicePackageManager : NSObject + +/// 打包多个 .wts 文件成 tone.cfg 数据 +/// - Parameters: +/// - paths: 文件存放路径 +/// - names: 文件在设备端使用时的名称 +/// - info: 设备限制的信息 ++(NSData *)makePks:(NSArray *)paths FileNames:(NSArray *)names Info:(JLVoiceReplaceInfo*)info; + +/// 解包 tone.cfg 数据 +/// - Parameter data: tone.cfg 数据 ++(NSArray *)parsePks:(NSData *)data; + +/// 当前 SDK 版本 ++(NSString *)getVersion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLVoiceReplaceInfo.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLVoiceReplaceInfo.h new file mode 100644 index 000000000..7866a9bcf --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Headers/JLVoiceReplaceInfo.h @@ -0,0 +1,54 @@ +// +// JLVoiceReplaceInfo.h +// JLWtsToCfgLib +// +// Created by EzioChan on 2024/1/23. +// Copyright © 2024 www.zh-jieli.com. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// 提示音频信息 +@interface JLTipsVoiceInfo : NSObject + +/// 索引号 +@property(nonatomic,assign)uint8_t index; + +/// 偏移地址 +@property(nonatomic,assign)uint32_t offset; + +/// 文件大小 +@property(nonatomic,assign)uint32_t length; + +/// 文件名称 +@property(nonatomic,strong)NSString *fileName; + +/// 昵称名称 +@property(nonatomic,strong)NSString *nickName; +@end + +/// 提示音信息 +@interface JLVoiceReplaceInfo : NSObject + +/// 版本 +@property(nonatomic,assign)uint8_t version; + +/// 预留区域 +@property(nonatomic,assign)uint32_t blockSize; + +/// 最大音频数目 +@property(nonatomic,assign)uint8_t maxNum; + +/// 文件名称 +@property(nonatomic,strong)NSString *fileName; + +/// 文件列表细节 +@property(nonatomic,strong)NSArray *infoArray; + +-(instancetype)init:(NSData *)data; + +@end + +NS_ASSUME_NONNULL_END diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Info.plist new file mode 100644 index 000000000..e72e0f50b Binary files /dev/null and b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Info.plist differ diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/JLPackageResKit b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/JLPackageResKit new file mode 100755 index 000000000..996ef7cc4 Binary files /dev/null and b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/JLPackageResKit differ diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Modules/module.modulemap new file mode 100644 index 000000000..ce93f978e --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module JLPackageResKit { + umbrella header "JLPackageResKit.h" + export * + + module * { export * } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/_CodeSignature/CodeResources b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/_CodeSignature/CodeResources new file mode 100644 index 000000000..c9aa7c031 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JLPackageResKit.xcframework/ios-arm64_x86_64-simulator/JLPackageResKit.framework/_CodeSignature/CodeResources @@ -0,0 +1,207 @@ + + + + + files + + Headers/JLPackageResKit.h + + Zn+ogIz2zOnYs97Pie6cIg0gX5s= + + Headers/JLPackageSourceMgr.h + + olIJ5vwreBtqSMoN5b4a4RRcUyI= + + Headers/JLPcmToWts.h + + sLvB3Fo/zOnvwkFlAIBmE93XWg4= + + Headers/JLToneCfgModel.h + + K6S94ANWmClkwIx8dpcsqPIQlD4= + + Headers/JLVoicePackageManager.h + + ZtAJcNPi5eVQpZEHCfwjVLot738= + + Headers/JLVoiceReplaceInfo.h + + 1emvE9YE7Ia6O/z3mnV5RZ6UpI8= + + Info.plist + + yXrlu2gHIrhBkMciWNVOUwLLmlU= + + Modules/module.modulemap + + TrKfgSNU2x6QJTo3He1Horza5No= + + + files2 + + Headers/JLPackageResKit.h + + hash + + Zn+ogIz2zOnYs97Pie6cIg0gX5s= + + hash2 + + f8vCb+BR9ICqRELJes+w7AW/6Jh1sjjQI7rIQALdStE= + + + Headers/JLPackageSourceMgr.h + + hash + + olIJ5vwreBtqSMoN5b4a4RRcUyI= + + hash2 + + fsZVvqbG86eqRGOnIE/0ZmZuDQ70TY4mirIQQkhBKuI= + + + Headers/JLPcmToWts.h + + hash + + sLvB3Fo/zOnvwkFlAIBmE93XWg4= + + hash2 + + GFVQ2EhEki3UZiQ+QUO76lInl9a68ww/0vB9KG6WIgI= + + + Headers/JLToneCfgModel.h + + hash + + K6S94ANWmClkwIx8dpcsqPIQlD4= + + hash2 + + D7E+i4SccjZT20HMTBlAl9/w9WXKQRUEJ6nF/bj+ve4= + + + Headers/JLVoicePackageManager.h + + hash + + ZtAJcNPi5eVQpZEHCfwjVLot738= + + hash2 + + asbhQigHJseKba2YZMOVUzmQHLdFkaTqUyqBZw0uyU0= + + + Headers/JLVoiceReplaceInfo.h + + hash + + 1emvE9YE7Ia6O/z3mnV5RZ6UpI8= + + hash2 + + /Fs5x+A6tm3gTNBeg/uBESk8MmF9u3OSLfui/uZLc0E= + + + Modules/module.modulemap + + hash + + TrKfgSNU2x6QJTo3He1Horza5No= + + hash2 + + 4PLXenv/w9G5pReCdRzQ2n5+LDT59uMegJDgTHA/c/Q= + + + + rules + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/Info.plist similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/Info.plist rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/Info.plist diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLAdvParse.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLAdvParse.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLAdvParse.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLAdvParse.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLChargingBoxAdv.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLChargingBoxAdv.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLChargingBoxAdv.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLChargingBoxAdv.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLDevicesAdv.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLDevicesAdv.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLDevicesAdv.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLDevicesAdv.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLEarphoneAdv.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLEarphoneAdv.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLEarphoneAdv.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLEarphoneAdv.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLOtaAdv.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLOtaAdv.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLOtaAdv.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLOtaAdv.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLSoundBoxAdv.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLSoundBoxAdv.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLSoundBoxAdv.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLSoundBoxAdv.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLSoundCardAdv.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLSoundCardAdv.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLSoundCardAdv.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLSoundCardAdv.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLTwsAdv.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLTwsAdv.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLTwsAdv.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLTwsAdv.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLWatchAdv.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLWatchAdv.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLWatchAdv.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JLWatchAdv.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JL_AdvParse.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JL_AdvParse.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JL_AdvParse.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Headers/JL_AdvParse.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Info.plist similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Info.plist rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Info.plist diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/JL_AdvParse b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/JL_AdvParse similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/JL_AdvParse rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/JL_AdvParse diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Modules/module.modulemap similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Modules/module.modulemap rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64/JL_AdvParse.framework/Modules/module.modulemap diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLAdvParse.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLAdvParse.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLAdvParse.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLAdvParse.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLChargingBoxAdv.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLChargingBoxAdv.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLChargingBoxAdv.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLChargingBoxAdv.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLDevicesAdv.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLDevicesAdv.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLDevicesAdv.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLDevicesAdv.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLEarphoneAdv.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLEarphoneAdv.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLEarphoneAdv.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLEarphoneAdv.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLOtaAdv.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLOtaAdv.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLOtaAdv.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLOtaAdv.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLSoundBoxAdv.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLSoundBoxAdv.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLSoundBoxAdv.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLSoundBoxAdv.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLSoundCardAdv.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLSoundCardAdv.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLSoundCardAdv.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLSoundCardAdv.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLTwsAdv.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLTwsAdv.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLTwsAdv.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLTwsAdv.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLWatchAdv.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLWatchAdv.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLWatchAdv.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JLWatchAdv.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JL_AdvParse.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JL_AdvParse.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JL_AdvParse.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Headers/JL_AdvParse.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Info.plist similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Info.plist rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Info.plist diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/JL_AdvParse b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/JL_AdvParse similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/JL_AdvParse rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/JL_AdvParse diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Modules/module.modulemap similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Modules/module.modulemap rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/Modules/module.modulemap diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/_CodeSignature/CodeResources b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/_CodeSignature/CodeResources similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/_CodeSignature/CodeResources rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_AdvParse.xcframework/ios-arm64_x86_64-simulator/JL_AdvParse.framework/_CodeSignature/CodeResources diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/Info.plist similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/Info.plist rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/Info.plist diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/ECBDTManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/ECBDTManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/ECBDTManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/ECBDTManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/ECBDTObjc.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/ECBDTObjc.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/ECBDTObjc.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/ECBDTObjc.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/ECOneToMorePtl.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/ECOneToMorePtl.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/ECOneToMorePtl.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/ECOneToMorePtl.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/ECThreadHelper.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/ECThreadHelper.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/ECThreadHelper.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/ECThreadHelper.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL4GUpgradeManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL4GUpgradeManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL4GUpgradeManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL4GUpgradeManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAIDialManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAIDialManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAIDialManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAIDialManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAiManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAiManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAiManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAiManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAuracastDevStateModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAuracastDevStateModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAuracastDevStateModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAuracastDevStateModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAuracastLancerManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAuracastLancerManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAuracastLancerManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAuracastLancerManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAuracastLancerSettingMode.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAuracastLancerSettingMode.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAuracastLancerSettingMode.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAuracastLancerSettingMode.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAuracastManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAuracastManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAuracastManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAuracastManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAutoConfigAnc.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAutoConfigAnc.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAutoConfigAnc.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAutoConfigAnc.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAutoPressureModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAutoPressureModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAutoPressureModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLAutoPressureModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLBroadcastDataModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLBroadcastDataModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLBroadcastDataModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLBroadcastDataModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLCmdBasic.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLCmdBasic.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLCmdBasic.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLCmdBasic.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLConsequentHeartRateModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLConsequentHeartRateModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLConsequentHeartRateModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLConsequentHeartRateModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDevAudioAIttsHandler.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDevAudioAIttsHandler.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDevAudioAIttsHandler.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDevAudioAIttsHandler.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDevAudioManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDevAudioManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDevAudioManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDevAudioManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDevPlayerCtrl.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDevPlayerCtrl.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDevPlayerCtrl.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDevPlayerCtrl.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfig.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfig.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfig.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfig.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfigDongle.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfigDongle.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfigDongle.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfigDongle.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfigFuncModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfigFuncModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfigFuncModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfigFuncModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfigSoundBox.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfigSoundBox.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfigSoundBox.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfigSoundBox.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfigTws.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfigTws.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfigTws.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDeviceConfigTws.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDhaFitting.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDhaFitting.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDhaFitting.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDhaFitting.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDialInfoExtentManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDialInfoExtentManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDialInfoExtentManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDialInfoExtentManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDialInfoExtentedModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDialInfoExtentedModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDialInfoExtentedModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDialInfoExtentedModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDisconnectRemindModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDisconnectRemindModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDisconnectRemindModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLDisconnectRemindModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLEcTimerHelper.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLEcTimerHelper.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLEcTimerHelper.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLEcTimerHelper.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLEmergencyContactModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLEmergencyContactModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLEmergencyContactModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLEmergencyContactModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLExerciseHeartRateRemindModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLExerciseHeartRateRemindModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLExerciseHeartRateRemindModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLExerciseHeartRateRemindModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLFallDetectionModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLFallDetectionModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLFallDetectionModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLFallDetectionModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLFlashOpMgr.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLFlashOpMgr.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLFlashOpMgr.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLFlashOpMgr.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLHttpHelper.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLHttpHelper.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLHttpHelper.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLHttpHelper.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelCardInfo.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelCardInfo.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelCardInfo.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelCardInfo.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelDevFunc.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelDevFunc.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelDevFunc.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelDevFunc.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelNoiseDetection.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelNoiseDetection.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelNoiseDetection.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelNoiseDetection.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelPCServer.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelPCServer.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelPCServer.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelPCServer.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelSceneNoiseReduction.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelSceneNoiseReduction.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelSceneNoiseReduction.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelSceneNoiseReduction.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelSmartPickFree.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelSmartPickFree.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelSmartPickFree.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelSmartPickFree.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelSpdif.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelSpdif.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelSpdif.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelSpdif.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelVocalBoost.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelVocalBoost.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelVocalBoost.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModelVocalBoost.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_ANC.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_ANC.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_ANC.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_ANC.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_AlarmSetting.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_AlarmSetting.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_AlarmSetting.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_AlarmSetting.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_BT.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_BT.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_BT.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_BT.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_Device.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_Device.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_Device.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_Device.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_EQ.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_EQ.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_EQ.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_EQ.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_FM.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_FM.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_FM.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_FM.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_File.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_File.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_File.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_File.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_Flash.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_Flash.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_Flash.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_Flash.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_Headset.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_Headset.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_Headset.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_Headset.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_RTC.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_RTC.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_RTC.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_RTC.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_Ring.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_Ring.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_Ring.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_Ring.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_SPEEX.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_SPEEX.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_SPEEX.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_SPEEX.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_SmallFile.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_SmallFile.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_SmallFile.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLModel_SmallFile.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLOxygenSturationRemindModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLOxygenSturationRemindModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLOxygenSturationRemindModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLOxygenSturationRemindModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLPersonInfoModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLPersonInfoModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLPersonInfoModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLPersonInfoModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLPublicSetting.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLPublicSetting.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLPublicSetting.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLPublicSetting.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLPublicSettingBlocks.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLPublicSettingBlocks.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLPublicSettingBlocks.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLPublicSettingBlocks.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSedentaryRmdModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSedentaryRmdModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSedentaryRmdModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSedentaryRmdModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSensorFuncModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSensorFuncModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSensorFuncModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSensorFuncModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSleepMonitorModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSleepMonitorModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSleepMonitorModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSleepMonitorModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSpdifPCManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSpdifPCManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSpdifPCManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSpdifPCManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSportDataModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSportDataModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSportDataModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLSportDataModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTaskChain.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTaskChain.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTaskChain.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTaskChain.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTipsSoundReplaceMgr.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTipsSoundReplaceMgr.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTipsSoundReplaceMgr.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTipsSoundReplaceMgr.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTranslateAudio.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTranslateAudio.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTranslateAudio.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTranslateAudio.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTranslateSet.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTranslateSet.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTranslateSet.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTranslateSet.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTranslateSetMode.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTranslateSetMode.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTranslateSetMode.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTranslateSetMode.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTranslationManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTranslationManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTranslationManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTranslationManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTwsHealthConfig.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTwsHealthConfig.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTwsHealthConfig.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTwsHealthConfig.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTwsHealthHeartRateModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTwsHealthHeartRateModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTwsHealthHeartRateModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTwsHealthHeartRateModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTwsHealthManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTwsHealthManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTwsHealthManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTwsHealthManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTwsSupportFuncs.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTwsSupportFuncs.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTwsSupportFuncs.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLTwsSupportFuncs.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWatchEnum.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWatchEnum.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWatchEnum.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWatchEnum.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSync.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSync.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSync.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSync.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSyncCustom.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSyncCustom.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSyncCustom.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSyncCustom.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSyncFinishModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSyncFinishModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSyncFinishModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSyncFinishModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSyncInfoModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSyncInfoModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSyncInfoModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSyncInfoModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSyncRealTimeModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSyncRealTimeModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSyncRealTimeModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearSyncRealTimeModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearable.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearable.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearable.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWearable.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWristLiftDetectionModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWristLiftDetectionModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWristLiftDetectionModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLWristLiftDetectionModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_AlarmClockManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_AlarmClockManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_AlarmClockManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_AlarmClockManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_Assist.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_Assist.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_Assist.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_Assist.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BLEAction.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BLEAction.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BLEAction.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BLEAction.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BLEKit.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BLEKit.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BLEKit.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BLEKit.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BLEMultiple.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BLEMultiple.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BLEMultiple.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BLEMultiple.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BatchManger.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BatchManger.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BatchManger.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BatchManger.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BigDataManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BigDataManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BigDataManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BigDataManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BinChargeManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BinChargeManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BinChargeManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_BinChargeManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_CallManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_CallManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_CallManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_CallManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_CustomManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_CustomManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_CustomManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_CustomManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_DeviceLogs.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_DeviceLogs.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_DeviceLogs.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_DeviceLogs.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_EntityM.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_EntityM.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_EntityM.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_EntityM.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FileManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FileManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FileManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FileManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FindDeviceManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FindDeviceManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FindDeviceManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FindDeviceManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FlashOperateManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FlashOperateManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FlashOperateManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FlashOperateManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FmManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FmManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FmManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FmManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FunctionBaseManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FunctionBaseManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FunctionBaseManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_FunctionBaseManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_Handle.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_Handle.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_Handle.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_Handle.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_LRCManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_LRCManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_LRCManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_LRCManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_LightManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_LightManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_LightManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_LightManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_MSG_Func.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_MSG_Func.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_MSG_Func.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_MSG_Func.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_MSG_Weather.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_MSG_Weather.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_MSG_Weather.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_MSG_Weather.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_ManagerM.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_ManagerM.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_ManagerM.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_ManagerM.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_MusicControlManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_MusicControlManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_MusicControlManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_MusicControlManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_NFC.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_NFC.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_NFC.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_NFC.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_OpCode.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_OpCode.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_OpCode.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_OpCode.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_RCSP.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_RCSP.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_RCSP.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_RCSP.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_AirPressure.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_AirPressure.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_AirPressure.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_AirPressure.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_Altitude.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_Altitude.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_Altitude.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_Altitude.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_Header.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_Header.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_Header.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_Header.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_HeartRate.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_HeartRate.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_HeartRate.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_HeartRate.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_MaxOxg.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_MaxOxg.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_MaxOxg.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_MaxOxg.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_MoveSteps.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_MoveSteps.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_MoveSteps.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_MoveSteps.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_OxSaturation.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_OxSaturation.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_OxSaturation.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_OxSaturation.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_RecTime.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_RecTime.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_RecTime.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_RecTime.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_SportMessage.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_SportMessage.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_SportMessage.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_SportMessage.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_Stress.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_Stress.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_Stress.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_Stress.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_TrainLoad.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_TrainLoad.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_TrainLoad.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SDM_TrainLoad.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SmallFileManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SmallFileManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SmallFileManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SmallFileManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SoundCardManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SoundCardManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SoundCardManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SoundCardManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SpeechAIttsHandler.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SpeechAIttsHandler.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SpeechAIttsHandler.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SpeechAIttsHandler.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SpeexManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SpeexManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SpeexManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SpeexManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SystemEQ.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SystemEQ.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SystemEQ.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SystemEQ.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SystemTime.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SystemTime.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SystemTime.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SystemTime.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SystemVolume.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SystemVolume.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SystemVolume.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_SystemVolume.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_Tools.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_Tools.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_Tools.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_Tools.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_TwsManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_TwsManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_TwsManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_TwsManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_TypeEnum.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_TypeEnum.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_TypeEnum.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_TypeEnum.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_WatchProtocol.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_WatchProtocol.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_WatchProtocol.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_WatchProtocol.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_WatchSyncProtocol.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_WatchSyncProtocol.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_WatchSyncProtocol.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_WatchSyncProtocol.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_vad.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_vad.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_vad.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JL_vad.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLeSimManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLeSimManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLeSimManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLeSimManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLwSettingModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLwSettingModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLwSettingModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/JLwSettingModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/NSData+ToUnit.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/NSData+ToUnit.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/NSData+ToUnit.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/NSData+ToUnit.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/NSObject+JLTools.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/NSObject+JLTools.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/NSObject+JLTools.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/NSObject+JLTools.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/NfcModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/NfcModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/NfcModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/NfcModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/RTC_RingInfo.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/RTC_RingInfo.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/RTC_RingInfo.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Headers/RTC_RingInfo.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Info.plist similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Info.plist rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Info.plist diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/JL_BLEKit b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/JL_BLEKit similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/JL_BLEKit rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/JL_BLEKit diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Modules/module.modulemap similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Modules/module.modulemap rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64/JL_BLEKit.framework/Modules/module.modulemap diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/ECBDTManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/ECBDTManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/ECBDTManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/ECBDTManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/ECBDTObjc.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/ECBDTObjc.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/ECBDTObjc.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/ECBDTObjc.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/ECOneToMorePtl.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/ECOneToMorePtl.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/ECOneToMorePtl.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/ECOneToMorePtl.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/ECThreadHelper.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/ECThreadHelper.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/ECThreadHelper.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/ECThreadHelper.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL4GUpgradeManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL4GUpgradeManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL4GUpgradeManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL4GUpgradeManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAIDialManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAIDialManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAIDialManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAIDialManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAiManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAiManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAiManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAiManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAuracastDevStateModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAuracastDevStateModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAuracastDevStateModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAuracastDevStateModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAuracastLancerManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAuracastLancerManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAuracastLancerManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAuracastLancerManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAuracastLancerSettingMode.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAuracastLancerSettingMode.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAuracastLancerSettingMode.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAuracastLancerSettingMode.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAuracastManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAuracastManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAuracastManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAuracastManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAutoConfigAnc.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAutoConfigAnc.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAutoConfigAnc.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAutoConfigAnc.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAutoPressureModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAutoPressureModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAutoPressureModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLAutoPressureModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLBroadcastDataModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLBroadcastDataModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLBroadcastDataModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLBroadcastDataModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLCmdBasic.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLCmdBasic.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLCmdBasic.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLCmdBasic.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLConsequentHeartRateModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLConsequentHeartRateModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLConsequentHeartRateModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLConsequentHeartRateModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDevAudioAIttsHandler.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDevAudioAIttsHandler.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDevAudioAIttsHandler.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDevAudioAIttsHandler.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDevAudioManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDevAudioManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDevAudioManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDevAudioManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDevPlayerCtrl.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDevPlayerCtrl.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDevPlayerCtrl.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDevPlayerCtrl.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfig.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfig.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfig.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfig.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfigDongle.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfigDongle.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfigDongle.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfigDongle.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfigFuncModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfigFuncModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfigFuncModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfigFuncModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfigSoundBox.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfigSoundBox.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfigSoundBox.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfigSoundBox.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfigTws.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfigTws.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfigTws.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDeviceConfigTws.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDhaFitting.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDhaFitting.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDhaFitting.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDhaFitting.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDialInfoExtentManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDialInfoExtentManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDialInfoExtentManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDialInfoExtentManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDialInfoExtentedModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDialInfoExtentedModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDialInfoExtentedModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDialInfoExtentedModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDisconnectRemindModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDisconnectRemindModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDisconnectRemindModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLDisconnectRemindModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLEcTimerHelper.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLEcTimerHelper.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLEcTimerHelper.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLEcTimerHelper.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLEmergencyContactModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLEmergencyContactModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLEmergencyContactModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLEmergencyContactModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLExerciseHeartRateRemindModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLExerciseHeartRateRemindModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLExerciseHeartRateRemindModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLExerciseHeartRateRemindModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLFallDetectionModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLFallDetectionModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLFallDetectionModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLFallDetectionModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLFlashOpMgr.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLFlashOpMgr.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLFlashOpMgr.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLFlashOpMgr.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLHttpHelper.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLHttpHelper.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLHttpHelper.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLHttpHelper.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelCardInfo.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelCardInfo.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelCardInfo.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelCardInfo.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelDevFunc.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelDevFunc.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelDevFunc.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelDevFunc.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelNoiseDetection.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelNoiseDetection.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelNoiseDetection.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelNoiseDetection.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelPCServer.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelPCServer.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelPCServer.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelPCServer.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelSceneNoiseReduction.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelSceneNoiseReduction.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelSceneNoiseReduction.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelSceneNoiseReduction.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelSmartPickFree.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelSmartPickFree.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelSmartPickFree.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelSmartPickFree.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelSpdif.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelSpdif.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelSpdif.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelSpdif.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelVocalBoost.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelVocalBoost.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelVocalBoost.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModelVocalBoost.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_ANC.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_ANC.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_ANC.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_ANC.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_AlarmSetting.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_AlarmSetting.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_AlarmSetting.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_AlarmSetting.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_BT.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_BT.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_BT.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_BT.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_Device.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_Device.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_Device.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_Device.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_EQ.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_EQ.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_EQ.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_EQ.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_FM.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_FM.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_FM.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_FM.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_File.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_File.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_File.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_File.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_Flash.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_Flash.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_Flash.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_Flash.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_Headset.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_Headset.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_Headset.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_Headset.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_RTC.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_RTC.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_RTC.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_RTC.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_Ring.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_Ring.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_Ring.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_Ring.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_SPEEX.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_SPEEX.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_SPEEX.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_SPEEX.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_SmallFile.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_SmallFile.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_SmallFile.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLModel_SmallFile.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLOxygenSturationRemindModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLOxygenSturationRemindModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLOxygenSturationRemindModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLOxygenSturationRemindModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLPersonInfoModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLPersonInfoModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLPersonInfoModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLPersonInfoModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLPublicSetting.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLPublicSetting.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLPublicSetting.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLPublicSetting.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLPublicSettingBlocks.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLPublicSettingBlocks.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLPublicSettingBlocks.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLPublicSettingBlocks.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSedentaryRmdModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSedentaryRmdModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSedentaryRmdModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSedentaryRmdModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSensorFuncModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSensorFuncModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSensorFuncModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSensorFuncModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSleepMonitorModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSleepMonitorModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSleepMonitorModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSleepMonitorModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSpdifPCManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSpdifPCManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSpdifPCManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSpdifPCManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSportDataModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSportDataModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSportDataModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLSportDataModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTaskChain.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTaskChain.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTaskChain.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTaskChain.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTipsSoundReplaceMgr.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTipsSoundReplaceMgr.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTipsSoundReplaceMgr.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTipsSoundReplaceMgr.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTranslateAudio.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTranslateAudio.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTranslateAudio.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTranslateAudio.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTranslateSet.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTranslateSet.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTranslateSet.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTranslateSet.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTranslateSetMode.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTranslateSetMode.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTranslateSetMode.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTranslateSetMode.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTranslationManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTranslationManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTranslationManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTranslationManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTwsHealthConfig.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTwsHealthConfig.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTwsHealthConfig.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTwsHealthConfig.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTwsHealthHeartRateModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTwsHealthHeartRateModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTwsHealthHeartRateModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTwsHealthHeartRateModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTwsHealthManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTwsHealthManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTwsHealthManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTwsHealthManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTwsSupportFuncs.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTwsSupportFuncs.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTwsSupportFuncs.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLTwsSupportFuncs.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWatchEnum.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWatchEnum.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWatchEnum.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWatchEnum.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSync.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSync.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSync.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSync.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSyncCustom.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSyncCustom.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSyncCustom.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSyncCustom.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSyncFinishModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSyncFinishModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSyncFinishModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSyncFinishModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSyncInfoModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSyncInfoModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSyncInfoModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSyncInfoModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSyncRealTimeModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSyncRealTimeModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSyncRealTimeModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearSyncRealTimeModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearable.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearable.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearable.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWearable.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWristLiftDetectionModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWristLiftDetectionModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWristLiftDetectionModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLWristLiftDetectionModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_AlarmClockManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_AlarmClockManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_AlarmClockManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_AlarmClockManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_Assist.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_Assist.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_Assist.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_Assist.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BLEAction.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BLEAction.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BLEAction.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BLEAction.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BLEKit.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BLEKit.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BLEKit.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BLEKit.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BLEMultiple.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BLEMultiple.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BLEMultiple.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BLEMultiple.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BatchManger.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BatchManger.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BatchManger.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BatchManger.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BigDataManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BigDataManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BigDataManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BigDataManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BinChargeManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BinChargeManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BinChargeManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_BinChargeManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_CallManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_CallManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_CallManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_CallManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_CustomManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_CustomManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_CustomManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_CustomManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_DeviceLogs.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_DeviceLogs.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_DeviceLogs.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_DeviceLogs.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_EntityM.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_EntityM.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_EntityM.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_EntityM.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FileManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FileManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FileManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FileManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FindDeviceManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FindDeviceManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FindDeviceManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FindDeviceManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FlashOperateManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FlashOperateManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FlashOperateManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FlashOperateManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FmManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FmManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FmManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FmManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FunctionBaseManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FunctionBaseManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FunctionBaseManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_FunctionBaseManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_Handle.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_Handle.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_Handle.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_Handle.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_LRCManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_LRCManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_LRCManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_LRCManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_LightManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_LightManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_LightManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_LightManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_MSG_Func.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_MSG_Func.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_MSG_Func.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_MSG_Func.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_MSG_Weather.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_MSG_Weather.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_MSG_Weather.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_MSG_Weather.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_ManagerM.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_ManagerM.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_ManagerM.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_ManagerM.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_MusicControlManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_MusicControlManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_MusicControlManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_MusicControlManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_NFC.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_NFC.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_NFC.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_NFC.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_OpCode.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_OpCode.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_OpCode.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_OpCode.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_RCSP.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_RCSP.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_RCSP.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_RCSP.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_AirPressure.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_AirPressure.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_AirPressure.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_AirPressure.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_Altitude.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_Altitude.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_Altitude.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_Altitude.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_Header.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_Header.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_Header.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_Header.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_HeartRate.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_HeartRate.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_HeartRate.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_HeartRate.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_MaxOxg.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_MaxOxg.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_MaxOxg.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_MaxOxg.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_MoveSteps.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_MoveSteps.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_MoveSteps.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_MoveSteps.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_OxSaturation.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_OxSaturation.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_OxSaturation.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_OxSaturation.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_RecTime.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_RecTime.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_RecTime.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_RecTime.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_SportMessage.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_SportMessage.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_SportMessage.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_SportMessage.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_Stress.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_Stress.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_Stress.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_Stress.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_TrainLoad.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_TrainLoad.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_TrainLoad.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SDM_TrainLoad.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SmallFileManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SmallFileManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SmallFileManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SmallFileManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SoundCardManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SoundCardManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SoundCardManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SoundCardManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SpeechAIttsHandler.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SpeechAIttsHandler.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SpeechAIttsHandler.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SpeechAIttsHandler.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SpeexManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SpeexManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SpeexManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SpeexManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SystemEQ.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SystemEQ.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SystemEQ.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SystemEQ.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SystemTime.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SystemTime.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SystemTime.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SystemTime.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SystemVolume.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SystemVolume.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SystemVolume.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_SystemVolume.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_Tools.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_Tools.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_Tools.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_Tools.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_TwsManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_TwsManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_TwsManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_TwsManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_TypeEnum.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_TypeEnum.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_TypeEnum.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_TypeEnum.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_WatchProtocol.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_WatchProtocol.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_WatchProtocol.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_WatchProtocol.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_WatchSyncProtocol.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_WatchSyncProtocol.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_WatchSyncProtocol.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_WatchSyncProtocol.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_vad.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_vad.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_vad.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JL_vad.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLeSimManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLeSimManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLeSimManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLeSimManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLwSettingModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLwSettingModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLwSettingModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/JLwSettingModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/NSData+ToUnit.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/NSData+ToUnit.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/NSData+ToUnit.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/NSData+ToUnit.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/NSObject+JLTools.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/NSObject+JLTools.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/NSObject+JLTools.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/NSObject+JLTools.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/NfcModel.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/NfcModel.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/NfcModel.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/NfcModel.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/RTC_RingInfo.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/RTC_RingInfo.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/RTC_RingInfo.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Headers/RTC_RingInfo.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Info.plist similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Info.plist rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Info.plist diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/JL_BLEKit b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/JL_BLEKit similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/JL_BLEKit rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/JL_BLEKit diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Modules/module.modulemap similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Modules/module.modulemap rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/Modules/module.modulemap diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/_CodeSignature/CodeResources b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/_CodeSignature/CodeResources similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/_CodeSignature/CodeResources rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_BLEKit.xcframework/ios-arm64_x86_64-simulator/JL_BLEKit.framework/_CodeSignature/CodeResources diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/Info.plist similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/Info.plist rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/Info.plist diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Headers/BtHash.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Headers/BtHash.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Headers/BtHash.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Headers/BtHash.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Headers/JLHashHandler.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Headers/JLHashHandler.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Headers/JLHashHandler.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Headers/JLHashHandler.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Headers/JL_HashPair.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Headers/JL_HashPair.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Headers/JL_HashPair.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Headers/JL_HashPair.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Headers/JL_ble_pair.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Headers/JL_ble_pair.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Headers/JL_ble_pair.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Headers/JL_ble_pair.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Info.plist similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Info.plist rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Info.plist diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/JL_HashPair b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/JL_HashPair similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/JL_HashPair rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/JL_HashPair diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Modules/module.modulemap similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Modules/module.modulemap rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64/JL_HashPair.framework/Modules/module.modulemap diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Headers/BtHash.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Headers/BtHash.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Headers/BtHash.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Headers/BtHash.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Headers/JLHashHandler.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Headers/JLHashHandler.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Headers/JLHashHandler.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Headers/JLHashHandler.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Headers/JL_HashPair.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Headers/JL_HashPair.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Headers/JL_HashPair.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Headers/JL_HashPair.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Headers/JL_ble_pair.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Headers/JL_ble_pair.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Headers/JL_ble_pair.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Headers/JL_ble_pair.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Info.plist similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Info.plist rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Info.plist diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/JL_HashPair b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/JL_HashPair similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/JL_HashPair rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/JL_HashPair diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Modules/module.modulemap similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Modules/module.modulemap rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/Modules/module.modulemap diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/_CodeSignature/CodeResources b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/_CodeSignature/CodeResources similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/_CodeSignature/CodeResources rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_HashPair.xcframework/ios-arm64_x86_64-simulator/JL_HashPair.framework/_CodeSignature/CodeResources diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/Info.plist similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/Info.plist rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/Info.plist diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JLOTAFile.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JLOTAFile.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JLOTAFile.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JLOTAFile.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JLOtaCustom.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JLOtaCustom.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JLOtaCustom.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JLOtaCustom.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JLOtaReConnectOption.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JLOtaReConnectOption.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JLOtaReConnectOption.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JLOtaReConnectOption.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JL_OTALib.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JL_OTALib.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JL_OTALib.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JL_OTALib.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JL_OTAManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JL_OTAManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JL_OTAManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/JL_OTAManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/jl_ufw.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/jl_ufw.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/jl_ufw.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Headers/jl_ufw.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Info.plist similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Info.plist rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Info.plist diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/JL_OTALib b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/JL_OTALib similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/JL_OTALib rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/JL_OTALib diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Modules/module.modulemap similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Modules/module.modulemap rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64/JL_OTALib.framework/Modules/module.modulemap diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JLOTAFile.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JLOTAFile.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JLOTAFile.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JLOTAFile.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JLOtaCustom.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JLOtaCustom.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JLOtaCustom.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JLOtaCustom.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JLOtaReConnectOption.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JLOtaReConnectOption.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JLOtaReConnectOption.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JLOtaReConnectOption.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JL_OTALib.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JL_OTALib.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JL_OTALib.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JL_OTALib.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JL_OTAManager.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JL_OTAManager.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JL_OTAManager.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/JL_OTAManager.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/jl_ufw.h b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/jl_ufw.h similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/jl_ufw.h rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Headers/jl_ufw.h diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Info.plist b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Info.plist similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Info.plist rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Info.plist diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/JL_OTALib b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/JL_OTALib similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/JL_OTALib rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/JL_OTALib diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Modules/module.modulemap b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Modules/module.modulemap similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Modules/module.modulemap rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/Modules/module.modulemap diff --git a/local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/_CodeSignature/CodeResources b/local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/_CodeSignature/CodeResources similarity index 100% rename from local_plugins/device_jieli/ios/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/_CodeSignature/CodeResources rename to local_plugins/device_jieli/ios/device_jieli/Frameworks/JL_OTALib.xcframework/ios-arm64_x86_64-simulator/JL_OTALib.framework/_CodeSignature/CodeResources diff --git a/local_plugins/device_jieli/ios/device_jieli/Package.swift b/local_plugins/device_jieli/ios/device_jieli/Package.swift new file mode 100644 index 000000000..638a3b420 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Package.swift @@ -0,0 +1,53 @@ +// swift-tools-version: 5.9 +// +// 杰理 RCSP SDK Flutter plugin —— iOS 走 Swift Package Manager。 +// +// 所有杰理官方 xcframework(含 OPUS 编解码用的 JLAudioUnitKit)都作为 +// 本 plugin 的私有 binaryTarget,放在 `Frameworks/` 下。Package 不依赖 +// 任何其他本地 plugin —— 自包含。 + +import PackageDescription + +let package = Package( + name: "device_jieli", + platforms: [ + .iOS("12.0"), + ], + products: [ + // Flutter 约定:library 产品名用 dash,target 名用 underscore + .library(name: "device-jieli", targets: ["device_jieli"]), + ], + dependencies: [], + targets: [ + .target( + name: "device_jieli", + dependencies: [ + "JL_BLEKit", + "JL_OTALib", + "JL_AdvParse", + "JL_HashPair", + "JLLogHelper", + "JLAudioUnitKit", + "JLBmpConvertKit", + "JLDialUnit", + "JLPackageResKit", + ], + path: "Sources/device_jieli", + linkerSettings: [ + .linkedFramework("CoreBluetooth"), + .linkedFramework("AVFoundation"), + .linkedFramework("UIKit"), + .linkedFramework("Foundation"), + ] + ), + .binaryTarget(name: "JL_BLEKit", path: "Frameworks/JL_BLEKit.xcframework"), + .binaryTarget(name: "JL_OTALib", path: "Frameworks/JL_OTALib.xcframework"), + .binaryTarget(name: "JL_AdvParse", path: "Frameworks/JL_AdvParse.xcframework"), + .binaryTarget(name: "JL_HashPair", path: "Frameworks/JL_HashPair.xcframework"), + .binaryTarget(name: "JLLogHelper", path: "Frameworks/JLLogHelper.xcframework"), + .binaryTarget(name: "JLAudioUnitKit", path: "Frameworks/JLAudioUnitKit.xcframework"), + .binaryTarget(name: "JLBmpConvertKit", path: "Frameworks/JLBmpConvertKit.xcframework"), + .binaryTarget(name: "JLDialUnit", path: "Frameworks/JLDialUnit.xcframework"), + .binaryTarget(name: "JLPackageResKit", path: "Frameworks/JLPackageResKit.xcframework"), + ] +) diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/EventDispatcher.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/EventDispatcher.swift new file mode 100644 index 000000000..402c3f049 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/EventDispatcher.swift @@ -0,0 +1,74 @@ +import Flutter +import Foundation + +/// 与 Android 端 `EventDispatcher` 同语义: +/// - 内部维护一个 `FlutterEventSink` 与一组 native 监听器 +/// - `send(_:)` 一次同时分发给两边 +/// - Flutter 端事件强制在 main thread 投递 +public final class EventDispatcher: NSObject, FlutterStreamHandler { + + public typealias NativeListener = (_ payload: [String: Any?]) -> Void + + private let lock = NSLock() + private var sink: FlutterEventSink? + private var nativeListeners: [(UUID, NativeListener)] = [] + + public override init() { super.init() } + + // MARK: - FlutterStreamHandler + + public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { + lock.lock(); defer { lock.unlock() } + sink = events + return nil + } + + public func onCancel(withArguments arguments: Any?) -> FlutterError? { + lock.lock(); defer { lock.unlock() } + sink = nil + return nil + } + + // MARK: - Native subscription + + @discardableResult + public func addNativeListener(_ listener: @escaping NativeListener) -> UUID { + let id = UUID() + lock.lock() + nativeListeners.append((id, listener)) + lock.unlock() + return id + } + + public func removeNativeListener(_ id: UUID) { + lock.lock() + nativeListeners.removeAll { $0.0 == id } + lock.unlock() + } + + // MARK: - Send + + /// 直接外抛事件。同时通知 native 监听器和 Flutter sink。 + /// payload 必须是 plugin-channel 可序列化的形态(基本类型 / Map / List / FlutterStandardTypedData)。 + public func send(_ payload: [String: Any?]) { + // native 监听同步派发(在调用方线程,避免高频音频帧切换 thread 的额外开销) + let listeners: [NativeListener] + let sinkCopy: FlutterEventSink? + lock.lock() + listeners = nativeListeners.map { $0.1 } + sinkCopy = sink + lock.unlock() + + for l in listeners { l(payload) } + + guard let s = sinkCopy else { return } + let cleaned = payload.reduce(into: [String: Any]()) { acc, kv in + if let v = kv.value { acc[kv.key] = v } + } + if Thread.isMainThread { + s(cleaned) + } else { + DispatchQueue.main.async { s(cleaned) } + } + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/JieliHomeServer.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/JieliHomeServer.swift new file mode 100644 index 000000000..0e84e8822 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/JieliHomeServer.swift @@ -0,0 +1,119 @@ +import CoreBluetooth +import Foundation +import JL_BLEKit +import JL_OTALib +import JLLogHelper + +/// 全局单例。与 Android `JieliHomeServer` 对应: +/// - 持有 `JL_BLEMultiple`(搜索 + 连接 + 已连 entity 列表) +/// - 持有 `JL_OTAManager` 引用(OTA 由 SDK 暴露的单例) +/// - 持有 per-device [TranslationSession],**全平台单一翻译入口** +/// - 各 feature 模块通过 [translationSession(for:)] 拿当前会话, +/// 与 Android 通过 `JL_BluetoothManager.connectedDevice + TranslationImpl` 等价。 +public final class JieliHomeServer: NSObject { + + public static let shared = JieliHomeServer() + + public let dispatcher = EventDispatcher() + + public private(set) var initialized: Bool = false + + public let bleMultiple: JL_BLEMultiple = JL_BLEMultiple() + public var otaManager: JL_OTAManager { JL_OTAManager.getOTAManager() } + + // MARK: - Per-device translation sessions + + private var sessions: [String: TranslationSession] = [:] + private let sessionsLock = NSLock() + + /// 取或新建给定设备的翻译会话。会话生命周期与连接绑定(断开时 [removeTranslationSession])。 + public func translationSession(for uuid: String) -> TranslationSession? { + guard let entity = connectedEntity(forUuid: uuid) else { return nil } + let mgr = entity.mCmdManager + sessionsLock.lock(); defer { sessionsLock.unlock() } + if let exist = sessions[uuid] { return exist } + let s = TranslationSession(uuid: uuid, manager: mgr) + sessions[uuid] = s + return s + } + + public func currentTranslationSession() -> TranslationSession? { + guard let entity = currentConnectedEntity() else { return nil } + return translationSession(for: entity.mUUID ?? "") + } + + public func removeTranslationSession(uuid: String) { + sessionsLock.lock(); defer { sessionsLock.unlock() } + sessions.removeValue(forKey: uuid) + } + + // MARK: - Feature modules + + public private(set) lazy var scanFeature: ScanFeature = ScanFeature(server: self) + public private(set) lazy var connectFeature: ConnectFeature = ConnectFeature(server: self) + public private(set) lazy var deviceInfoFeature: DeviceInfoFeature = DeviceInfoFeature(server: self) + public private(set) lazy var customCmdFeature: CustomCmdFeature = CustomCmdFeature(server: self) + public private(set) lazy var translationFeature: TranslationFeature = TranslationFeature(server: self) + public private(set) lazy var speechFeature: SpeechFeature = SpeechFeature(server: self) + public private(set) lazy var assistantBridge: AssistantBridge = AssistantBridge(server: self) + public private(set) lazy var deviceRecordFeature: DeviceRecordFeature = DeviceRecordFeature(server: self) + public private(set) lazy var otaFeature: OtaFeature = OtaFeature(server: self) + + // MARK: - Event forwarder + + private var bluetoothForwarder: BluetoothEventForwarder? + private var customForwarder: CustomEventForwarder? + private var deviceInfoForwarder: DeviceInfoEventForwarder? + + private override init() { super.init() } + + public func initialize(multiDevice: Bool, skipNoNameDev: Bool, enableLog: Bool) { + if initialized { return } + + JLLogManager.clearLog() + JLLogManager.setLog(enableLog, isMore: false, level: .DEBUG) + JLLogManager.log(withTimestamp: true) + JLLogManager.saveLog(asFile: enableLog) + + bleMultiple.ble_FILTER_ENABLE = false + bleMultiple.ble_TIMEOUT = 10 + bleMultiple.authEnable = true + + let btFwd = BluetoothEventForwarder(server: self) + btFwd.attach() + bluetoothForwarder = btFwd + + let customFwd = CustomEventForwarder(server: self) + customFwd.attach() + customForwarder = customFwd + + let infoFwd = DeviceInfoEventForwarder(server: self) + infoFwd.attach() + deviceInfoForwarder = infoFwd + + // 提前实例化 ScanFeature,让它的 CBCentralManager 趁早创建, + // 等用户第一次 startScan 时 state 已经 poweredOn。 + _ = scanFeature + + initialized = true + } + + // MARK: - Helpers + + /// 通过 BLE UUID 找到已搜索 / 已连接到的 entity + public func entity(forUuid uuid: String) -> JL_EntityM? { + let pool: [JL_EntityM] = (bleMultiple.bleConnectedArr as? [JL_EntityM] ?? []) + + (bleMultiple.blePeripheralArr as? [JL_EntityM] ?? []) + return pool.first { $0.mUUID == uuid } + } + + public func connectedEntity(forUuid uuid: String) -> JL_EntityM? { + let pool = bleMultiple.bleConnectedArr as? [JL_EntityM] ?? [] + return pool.first { $0.mUUID == uuid } + } + + public func currentConnectedEntity() -> JL_EntityM? { + let pool = bleMultiple.bleConnectedArr as? [JL_EntityM] ?? [] + return pool.first + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/JielihomePlugin.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/JielihomePlugin.swift new file mode 100644 index 000000000..101aadb72 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/JielihomePlugin.swift @@ -0,0 +1,36 @@ +import Flutter +import UIKit + +@objc public class JielihomePlugin: NSObject, FlutterPlugin { + + private var router: MethodRouter? + + @objc public static func register(with registrar: FlutterPluginRegistrar) { + let methodChannel = FlutterMethodChannel( + name: "device_jieli/method", + binaryMessenger: registrar.messenger() + ) + let eventChannel = FlutterEventChannel( + name: "device_jieli/event", + binaryMessenger: registrar.messenger() + ) + + let server = JieliHomeServer.shared + let plugin = JielihomePlugin() + let router = MethodRouter(server: server) + plugin.router = router + + methodChannel.setMethodCallHandler { [weak router] call, result in + router?.handle(call: call, result: result) + } + eventChannel.setStreamHandler(server.dispatcher) + + registrar.addMethodCallDelegate(plugin, channel: methodChannel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + // 真正的 method 分派在 setMethodCallHandler 闭包里直接走 router; + // 这里仅是 FlutterPlugin 协议要求。 + router?.handle(call: call, result: result) + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/MethodRouter.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/MethodRouter.swift new file mode 100644 index 000000000..8aa684581 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/MethodRouter.swift @@ -0,0 +1,295 @@ +import Flutter +import Foundation + +/// 与 Android 端 `MethodRouter` 同语义:把 Dart MethodChannel 调用映射到 server 内的各 feature 模块。 +/// 任何 throws/exception 统一兜底 `PLUGIN_ERR`,避免 Dart 端拿到悬空 Future。 +public final class MethodRouter { + + private let server: JieliHomeServer + public init(server: JieliHomeServer) { self.server = server } + + public func handle(call: FlutterMethodCall, result: @escaping FlutterResult) { + let args = (call.arguments as? [String: Any]) ?? [:] + do { + try route(method: call.method, args: args, result: result) + } catch { + result(FlutterError(code: "PLUGIN_ERR", message: error.localizedDescription, details: nil)) + } + } + + private func route(method: String, args: [String: Any], result: @escaping FlutterResult) throws { + switch method { + case "getPlatformVersion": + result("iOS \(UIDevice.current.systemVersion)") + + case "initialize": + server.initialize( + multiDevice: args["multiDevice"] as? Bool ?? true, + skipNoNameDev: args["skipNoNameDev"] as? Bool ?? false, + enableLog: args["enableLog"] as? Bool ?? false + ) + result(true) + + // ───── Scan ───── + case "startScan": + let timeoutMs = (args["timeoutMs"] as? NSNumber)?.intValue ?? 30_000 + let nameList = args["nameList"] as? [String] ?? [] + let uuidList = args["uuidList"] as? [String] ?? [] + let skipUnnamed = args["skipUnnamed"] as? Bool ?? true + do { + try server.scanFeature.startScan( + timeoutMs: timeoutMs, + nameList: nameList, + uuidList: uuidList, + skipUnnamed: skipUnnamed + ) + result(true) + } catch { + result(FlutterError(code: "SCAN_FAILED", message: error.localizedDescription, details: nil)) + } + + case "stopScan": + server.scanFeature.stopScan() + result(true) + + case "isScanning": + result(server.scanFeature.isScanning) + + // ───── Connect ───── + case "connect": + guard let address = args["address"] as? String, !address.isEmpty else { + result(FlutterError(code: "BAD_ARG", message: "address required", details: nil)); return + } + server.connectFeature.connect( + bleAddress: address, + edrAddress: args["edrAddr"] as? String, + deviceType: (args["deviceType"] as? NSNumber)?.intValue ?? -1, + connectWay: (args["connectWay"] as? NSNumber)?.intValue ?? 0 + ) { ok, errMsg in + if ok { result(true) } + else { result(FlutterError(code: "CONNECT_FAILED", message: errMsg ?? "connect failed", details: nil)) } + } + + case "disconnect": + guard let address = args["address"] as? String, !address.isEmpty else { + result(FlutterError(code: "BAD_ARG", message: "address required", details: nil)); return + } + server.connectFeature.disconnect(address: address) { ok, errMsg in + if ok { result(true) } + else { result(FlutterError(code: "DISCONNECT_FAILED", message: errMsg ?? "disconnect failed", details: nil)) } + } + + case "isConnected": + guard let address = args["address"] as? String, !address.isEmpty else { + result(FlutterError(code: "BAD_ARG", message: "address required", details: nil)); return + } + result(server.connectFeature.isConnected(address: address)) + + case "connectedDevice": + if let info = server.connectFeature.connectedDeviceInfo() { + result(info) + } else { + result(nil) + } + + // ───── Device info ───── + case "deviceSnapshot": + guard let address = args["address"] as? String, !address.isEmpty else { + result(FlutterError(code: "BAD_ARG", message: "address required", details: nil)); return + } + result(server.deviceInfoFeature.snapshot(address: address)) + + case "queryTargetInfo": + guard let address = args["address"] as? String, !address.isEmpty else { + result(FlutterError(code: "BAD_ARG", message: "address required", details: nil)); return + } + let mask = (args["mask"] as? NSNumber)?.intValue ?? 0x0F + server.deviceInfoFeature.queryTargetInfo(address: address, mask: mask) { info, errCode, errMsg in + if let info = info { result(info) } + else { result(FlutterError(code: "TARGET_INFO_ERR", message: errMsg ?? "query failed", details: errCode)) } + } + + // ───── Custom cmd ───── + case "sendCustomCmd": + guard let address = args["address"] as? String, !address.isEmpty else { + result(FlutterError(code: "BAD_ARG", message: "address required", details: nil)); return + } + guard let opCode = (args["opCode"] as? NSNumber)?.intValue else { + result(FlutterError(code: "BAD_ARG", message: "opCode required", details: nil)); return + } + let payloadBytes: [UInt8] = { + if let arr = args["payload"] as? [Int] { return arr.map { UInt8(truncatingIfNeeded: $0) } } + if let arr = args["payload"] as? [NSNumber] { return arr.map { UInt8(truncatingIfNeeded: $0.intValue) } } + if let typed = args["payload"] as? FlutterStandardTypedData { + return [UInt8](typed.data) + } + return [] + }() + server.customCmdFeature.send(address: address, opCode: opCode, payload: Data(payloadBytes)) { ok, data, errCode, errMsg in + if ok { + result(data.map { [UInt8]($0) } ?? []) + } else { + result(FlutterError(code: "CUSTOM_CMD_ERR", message: errMsg ?? "custom cmd failed", details: errCode)) + } + } + + // ───── Translation ───── + case "startTranslation": + guard let modeId = (args["modeId"] as? NSNumber)?.intValue else { + result(FlutterError(code: "BAD_ARG", message: "modeId required", details: nil)); return + } + let innerArgs = args["args"] as? [String: Any] ?? [:] + print("[JieliRouter] startTranslation modeId=\(modeId) args=\(innerArgs) " + + "assistantRunning=\(server.assistantBridge.isRunning) " + + "deviceRecording=\(server.deviceRecordFeature.isRecording) " + + "translationWorking=\(server.translationFeature.isWorking)") + // 设备录音 / AI 助理 与翻译互斥(同一 JLTranslationManager session) + if server.assistantBridge.isRunning { + print("[JieliRouter] startTranslation: stopping AssistantBridge first") + server.assistantBridge.stop() + } + if server.deviceRecordFeature.isRecording { + print("[JieliRouter] startTranslation: stopping DeviceRecord first") + server.deviceRecordFeature.stop() + } + server.translationFeature.start(modeId: modeId, args: innerArgs) { ok, errMsg in + print("[JieliRouter] startTranslation result modeId=\(modeId) ok=\(ok) err=\(errMsg ?? "nil")") + if ok { result(true) } + else { result(FlutterError(code: "TRANSLATION_ERR", message: errMsg ?? "start failed", details: nil)) } + } + + case "stopTranslation": + server.translationFeature.stop() + result(true) + + case "translationStatus": + result([ + "working": server.translationFeature.isWorking, + "modeId": server.translationFeature.currentModeId as Any, + "inputStreams": server.translationFeature.currentInputStreams, + "outputStreams": server.translationFeature.currentOutputStreams, + ] as [String: Any?]) + + case "feedTranslatedAudio": + guard let streamId = args["streamId"] as? String else { + result(FlutterError(code: "BAD_ARG", message: "streamId required", details: nil)); return + } + guard let pcm = (args["pcm"] as? FlutterStandardTypedData)?.data else { + result(FlutterError(code: "BAD_ARG", message: "pcm required", details: nil)); return + } + let sr = (args["sampleRate"] as? NSNumber)?.intValue ?? 16000 + let ch = (args["channels"] as? NSNumber)?.intValue ?? 1 + let bits = (args["bitsPerSample"] as? NSNumber)?.intValue ?? 16 + let isFinal = args["final"] as? Bool ?? false + let ok = server.translationFeature.feedTranslatedAudio( + streamId: streamId, + pcm: pcm, + sampleRate: sr, channels: ch, bitsPerSample: bits, + isFinal: isFinal + ) + result(ok) + + case "feedTranslationResult": + server.translationFeature.feedTranslationResult( + srcLang: args["srcLang"] as? String, + srcText: args["srcText"] as? String, + destLang: args["destLang"] as? String, + destText: args["destText"] as? String, + requestId: args["requestId"] as? String + ) + result(true) + + case "isSupportCallTranslationWithStereo": + result(server.translationFeature.isSupportCallTranslationWithStereo(address: args["address"] as? String)) + + case "feedAudioFilePcm": + guard let pcm = (args["pcm"] as? FlutterStandardTypedData)?.data else { + result(FlutterError(code: "BAD_ARG", message: "pcm required", details: nil)); return + } + let sr = (args["sampleRate"] as? NSNumber)?.intValue ?? 16000 + result(server.translationFeature.feedAudioFilePcm(pcm: pcm, sampleRate: sr)) + + // ───── Speech assistant (cmd=4/5/210 路径) ───── + case "speechIsRecording": + result(server.speechFeature.isRecording(address: args["address"] as? String)) + + case "speechStart": + server.speechFeature.start( + address: args["address"] as? String, + voiceType: (args["voiceType"] as? NSNumber)?.intValue ?? 2, + sampleRate: (args["sampleRate"] as? NSNumber)?.intValue ?? 16, + vadWay: (args["vadWay"] as? NSNumber)?.intValue ?? 0 + ) { ok, msg in + if ok { result(true) } + else { result(FlutterError(code: "SPEECH_START_ERR", message: msg ?? "speech start failed", details: nil)) } + } + + case "speechStop": + server.speechFeature.stop( + address: args["address"] as? String, + reason: (args["reason"] as? NSNumber)?.intValue ?? 0 + ) { ok, msg in + if ok { result(true) } + else { result(FlutterError(code: "SPEECH_STOP_ERR", message: msg ?? "speech stop failed", details: nil)) } + } + + // ───── AI 助理通路 ───── + case "assistantStart": + if server.translationFeature.isWorking { server.translationFeature.stop() } + if server.deviceRecordFeature.isRecording { server.deviceRecordFeature.stop() } + let ok = server.assistantBridge.start( + address: args["address"] as? String, + sampleRate: (args["sampleRate"] as? NSNumber)?.intValue ?? 16000 + ) + if ok { result(true) } + else { result(FlutterError(code: "ASSISTANT_START_ERR", message: "assistant bridge start failed", details: nil)) } + + case "assistantStop": + server.assistantBridge.stop() + result(true) + + case "assistantIsRunning": + result(server.assistantBridge.isRunning) + + // ───── 设备录音 ───── + case "deviceRecordStart": + let innerArgs = args["args"] as? [String: Any] ?? [:] + if server.translationFeature.isWorking { server.translationFeature.stop() } + if server.assistantBridge.isRunning { server.assistantBridge.stop() } + server.deviceRecordFeature.start(args: innerArgs) { ok, errMsg in + if ok { result(true) } + else { result(FlutterError(code: "DEVICE_RECORD_ERR", message: errMsg ?? "device record start failed", details: nil)) } + } + + case "deviceRecordStop": + server.deviceRecordFeature.stop() + result(true) + + case "deviceRecordStatus": + result(["recording": server.deviceRecordFeature.isRecording]) + + // ───── OTA ───── + case "otaStart": + guard let path = args["firmwareFilePath"] as? String, !path.isEmpty else { + result(FlutterError(code: "BAD_ARG", message: "firmwareFilePath required", details: nil)); return + } + server.otaFeature.start( + address: args["address"] as? String, + firmwareFilePath: path, + blockSize: (args["blockSize"] as? NSNumber)?.intValue ?? 512, + fileFlagBytes: (args["fileFlag"] as? FlutterStandardTypedData)?.data ?? Data() + ) + result(true) + + case "otaCancel": + server.otaFeature.cancel() + result(true) + + case "otaIsRunning": + result(server.otaFeature.isRunning) + + default: + result(FlutterMethodNotImplemented) + } + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/audio/OpusStreamCodec.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/audio/OpusStreamCodec.swift new file mode 100644 index 000000000..8fb19c560 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/audio/OpusStreamCodec.swift @@ -0,0 +1,119 @@ +import Foundation +import JLAudioUnitKit + +/// OPUS 流式解码器 —— 与 Android `OpusStreamDecoder` 同语义。 +/// +/// 输入:从耳机收到的 OPUS 编码帧(一次一帧,最大 ~80B) +/// 输出:16kHz / 给定声道 PCM 流(16-bit signed little-endian, interleaved) +/// +/// **dataSize 是单个 OPUS 帧的实际字节大小**: +/// - 16k / 16bit / mono / 20ms ≈ 40 B +/// - 16k / 16bit / stereo / 20ms ≈ 80 B +/// +/// 这与 Android `OpusStreamDecoder.packetSize` 对齐:传错值会导致 OpusManager 把多帧拼成 +/// 一帧解,丢 80% 数据。 +public final class OpusStreamDecoder { + + private let proxy: OpusDecoderProxy + private let decoder: JLOpusDecoder + + public init( + channels: Int = 1, + dataSize: Int = 40, + sampleRate: Int = 16000, + hasDataHeader: Bool = false, + onPcm: @escaping (Data) -> Void, + onError: @escaping (Int, String?) -> Void = { _, _ in } + ) { + let fmt = JLOpusFormat.defaultFormats() + fmt.sampleRate = Int32(sampleRate) + fmt.channels = Int32(channels) + fmt.frameDuration = 20 + fmt.dataSize = Int32(dataSize) + fmt.hasDataHeader = hasDataHeader + + let proxy = OpusDecoderProxy(onPcm: onPcm, onError: onError) + self.proxy = proxy + self.decoder = JLOpusDecoder(decoder: fmt, delegate: proxy) + } + + public func feed(_ opus: Data) { + decoder.opusDecoderInputData(opus) + } + + public func release() { + decoder.opusOnRelease() + } +} + +/// JLOpusDecoder 的 delegate 是 `weak`,Swift class 直接做 delegate 会被立刻 dealloc。 +/// 我们让 OpusStreamDecoder 强引用这个 proxy(通过 stored property)来保活。 +private final class OpusDecoderProxy: NSObject, JLOpusDecoderDelegate { + private let onPcm: (Data) -> Void + private let onError: (Int, String?) -> Void + + init(onPcm: @escaping (Data) -> Void, onError: @escaping (Int, String?) -> Void) { + self.onPcm = onPcm + self.onError = onError + } + + func opusDecoder(_ decoder: JLOpusDecoder, data: Data?, error: Error?) { + if let err = error as NSError? { + onError(err.code, err.localizedDescription); return + } + if let data = data, !data.isEmpty { onPcm(data) } + } +} + +/// OPUS 流式编码器 —— 与 Android `OpusStreamEncoder` 同语义。 +/// +/// 输入:16k/16bit/mono PCM 帧(20ms 步长 = 640 B) +/// 输出:OPUS 编码帧(默认无头,~40 B) +public final class OpusStreamEncoder { + + private let proxy: OpusEncoderProxy + private let encoder: JLOpusEncoder + + public init( + channels: Int = 1, + sampleRate: Int = 16000, + hasDataHeader: Bool = false, + onOpus: @escaping (Data) -> Void, + onError: @escaping (Int, String?) -> Void = { _, _ in } + ) { + let cfg = JLOpusEncodeConfig.defaultJL() + cfg.sampleRate = Int32(sampleRate) + cfg.channels = Int32(channels) + cfg.frameDuration = 20 + cfg.hasDataHeader = hasDataHeader + + let proxy = OpusEncoderProxy(onOpus: onOpus, onError: onError) + self.proxy = proxy + self.encoder = JLOpusEncoder(format: cfg, delegate: proxy) + } + + public func feed(_ pcm: Data) { + encoder.opusEncode(pcm) + } + + public func release() { + encoder.opusOnRelease() + } +} + +private final class OpusEncoderProxy: NSObject, JLOpusEncoderDelegate { + private let onOpus: (Data) -> Void + private let onError: (Int, String?) -> Void + + init(onOpus: @escaping (Data) -> Void, onError: @escaping (Int, String?) -> Void) { + self.onOpus = onOpus + self.onError = onError + } + + func opusEncoder(_ encoder: JLOpusEncoder, data: Data?, error: Error?) { + if let err = error as NSError? { + onError(err.code, err.localizedDescription); return + } + if let data = data, !data.isEmpty { onOpus(data) } + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/audio/PcmKit.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/audio/PcmKit.swift new file mode 100644 index 000000000..fb87c0a6c --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/audio/PcmKit.swift @@ -0,0 +1,57 @@ +import Foundation + +/// 16-bit signed PCM 工具集 —— 与 Android `audio/PcmKit.kt` 对齐。 +public enum PcmKit { + + /// 把 16-bit signed stereo interleaved PCM (L/R/L/R…) 拆成左右两路 mono。 + /// 输入字节数必须为 4 的整数倍。 + public static func splitStereo16(_ interleaved: Data) -> (left: Data, right: Data) { + let n = interleaved.count + guard n >= 4 else { return (Data(), Data()) } + let frameCount = n / 4 + var left = Data(count: frameCount * 2) + var right = Data(count: frameCount * 2) + interleaved.withUnsafeBytes { (raw: UnsafeRawBufferPointer) in + let p = raw.bindMemory(to: UInt8.self).baseAddress! + left.withUnsafeMutableBytes { (lRaw: UnsafeMutableRawBufferPointer) in + right.withUnsafeMutableBytes { (rRaw: UnsafeMutableRawBufferPointer) in + let lp = lRaw.bindMemory(to: UInt8.self).baseAddress! + let rp = rRaw.bindMemory(to: UInt8.self).baseAddress! + for i in 0.. Data { + let frameCount = min(left.count, right.count) / 2 + var out = Data(count: frameCount * 4) + left.withUnsafeBytes { (lRaw: UnsafeRawBufferPointer) in + right.withUnsafeBytes { (rRaw: UnsafeRawBufferPointer) in + out.withUnsafeMutableBytes { (oRaw: UnsafeMutableRawBufferPointer) in + let lp = lRaw.bindMemory(to: UInt8.self).baseAddress! + let rp = rRaw.bindMemory(to: UInt8.self).baseAddress! + let op = oRaw.bindMemory(to: UInt8.self).baseAddress! + for i in 0..`(翻译流) +/// - `AssistantBridge`(AI 助理:MODE_RECORD + 设备录音) +/// - `DeviceRecordFeature`(设备录音:MODE=6 + stereo + 设备录音) +/// +/// 业务方 enter 时把自己注册为 `currentOwner`,所有 SDK 回调(onReceiveAudioData / +/// onModeChange / onError / onSendAudioQueueOver / isOnCalling)都会路由到它;exit 时 +/// 主动让出所有权。中途被其他业务抢占时,先抢占方需要先调 [TranslationSession.release]。 +public protocol TranslationSessionOwner: AnyObject { + /// 与 Android `BaseTranslationModeHandler.modeId` 等价;用于日志和事件 payload + var sessionModeId: Int { get } + + func onReceiveAudio(_ audio: JLTranslateAudio) + func onModeChange(_ mode: JLTranslateSetMode) + func onError(_ error: NSError) + func onSendAudioQueueOver() + func onCallingChange(_ isCalling: Bool) +} + +public extension TranslationSessionOwner { + func onSendAudioQueueOver() {} + func onCallingChange(_ isCalling: Bool) {} +} + +/// 单台已连接设备的翻译会话。 +/// +/// - 内部独占持有 [JLTranslationManager],对外通过 [acquire] / [release] 协调使用方。 +/// - 自身作为 `JLTranslationManagerDelegate`,把回调原样转发给 `currentOwner`。 +/// 若当前没有 owner,回调被静默丢弃(避免 SDK 在 exitMode 后还推一两帧把状态搅乱)。 +public final class TranslationSession: NSObject, JLTranslationManagerDelegate { + + public let uuid: String + private(set) public weak var manager: JL_ManagerM? + + private var tm: JLTranslationManager? + private weak var currentOwner: TranslationSessionOwner? + private let lock = NSRecursiveLock() + private var rxAudioFrameCount: Int64 = 0 + + init(uuid: String, manager: JL_ManagerM) { + self.uuid = uuid + self.manager = manager + super.init() + } + + public func acquire( + owner: TranslationSessionOwner, + completion: @escaping (_ tm: JLTranslationManager?, _ err: String?) -> Void + ) { + lock.lock() + defer { lock.unlock() } + + print("[JieliSession] acquire uuid=\(uuid) requesterModeId=\(owner.sessionModeId) " + + "currentOwnerModeId=\(currentOwner?.sessionModeId.description ?? "nil") tmExists=\(tm != nil)") + + // 已有 owner:先让它退出(让位)。同一 owner 重复 acquire 直接复用。 + if let existing = currentOwner, existing !== owner { + print("[JieliSession] acquire BUSY existingModeId=\(existing.sessionModeId) requesterModeId=\(owner.sessionModeId)") + completion(nil, "translation session busy (sessionModeId=\(existing.sessionModeId))") + return + } + currentOwner = owner + + if let tm = tm { + print("[JieliSession] acquire OK (reused tm) ownerModeId=\(owner.sessionModeId)") + completion(tm, nil); return + } + guard let manager = manager else { + print("[JieliSession] acquire FAIL manager released") + completion(nil, "manager released") + return + } + print("[JieliSession] creating new JLTranslationManager ...") + let inst = JLTranslationManager(delegate: self, manager: manager) { [weak self] success, err in + guard let self = self else { return } + self.lock.lock(); defer { self.lock.unlock() } + print("[JieliSession] JLTranslationManager init result success=\(success) err=\(err?.localizedDescription ?? "nil")") + if success { + completion(self.tm, nil) + } else { + self.currentOwner = nil + self.tm = nil + completion(nil, err?.localizedDescription ?? "JLTranslationManager init failed") + } + } + self.tm = inst + inst.delegate = self + } + + /// 让出所有权。会调 `trExitMode`,调用方应在 stop 路径里调它。 + public func release(owner: TranslationSessionOwner, completion: ((Bool) -> Void)? = nil) { + lock.lock() + if currentOwner === owner { + currentOwner = nil + } + let tm = self.tm + lock.unlock() + guard let tm = tm else { completion?(true); return } + tm.trExitMode { _, _ in completion?(true) } + } + + /// 当前是否有 owner 在使用 + public var isBusy: Bool { + lock.lock(); defer { lock.unlock() } + return currentOwner != nil + } + + /// 给只想"探测能力"的调用方用:不需要拿所有权 + public func currentManager() -> JLTranslationManager? { + lock.lock(); defer { lock.unlock() } + return tm + } + + // MARK: - JLTranslationManagerDelegate + + public func onInitSuccess(_ uuid: String) { + print("[JieliSession/SDK] onInitSuccess uuid=\(uuid)") + } + + public func onModeChange(_ uuid: String, mode: JLTranslateSetMode) { + print("[JieliSession/SDK] onModeChange uuid=\(uuid) modeType=\(mode.modeType.rawValue) " + + "dataType=\(mode.dataType.rawValue) ch=\(mode.channel) sr=\(mode.sampleRate) " + + "ownerModeId=\(currentOwnerSnapshot()?.sessionModeId.description ?? "nil")") + currentOwnerSnapshot()?.onModeChange(mode) + } + + public func onReceiveAudioData(_ uuid: String, audioData data: JLTranslateAudio) { + rxAudioFrameCount &+= 1 + if rxAudioFrameCount <= 5 || rxAudioFrameCount % 100 == 1 { + print("[JieliSession/SDK] onReceiveAudioData #\(rxAudioFrameCount) uuid=\(uuid) " + + "source=\(data.sourceType.rawValue) audioType=\(data.audioType.rawValue) " + + "len=\(data.data.count) count=\(data.count) " + + "ownerModeId=\(currentOwnerSnapshot()?.sessionModeId.description ?? "nil")") + } + currentOwnerSnapshot()?.onReceiveAudio(data) + } + + public func onError(_ uuid: String, error: Error) { + print("[JieliSession/SDK] onError uuid=\(uuid) err=\(error.localizedDescription)") + currentOwnerSnapshot()?.onError(error as NSError) + } + + public func onSendAudioQueueOver(_ uuid: String) { + print("[JieliSession/SDK] onSendAudioQueueOver uuid=\(uuid)") + currentOwnerSnapshot()?.onSendAudioQueueOver() + } + + public func isOnCalling(_ isCalling: Bool, uuid: String) { + print("[JieliSession/SDK] isOnCalling=\(isCalling) uuid=\(uuid) " + + "ownerModeId=\(currentOwnerSnapshot()?.sessionModeId.description ?? "nil")") + currentOwnerSnapshot()?.onCallingChange(isCalling) + } + + private func currentOwnerSnapshot() -> TranslationSessionOwner? { + lock.lock(); defer { lock.unlock() } + return currentOwner + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/event/BluetoothEventForwarder.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/event/BluetoothEventForwarder.swift new file mode 100644 index 000000000..dc34114c4 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/event/BluetoothEventForwarder.swift @@ -0,0 +1,99 @@ +import CoreBluetooth +import Foundation +import JL_BLEKit + +/// 监听 iOS SDK 的 BLE 状态 / 连接通知,转成 Android 端同形态的事件 payload: +/// - adapterStatus : { enabled, hasBle } ← 由 ScanFeature 内的 CBCentralManager 兜底派发; +/// 这里也监听 SDK 的 kJL_BLE_M_ON / OFF 作为补充 +/// - scanStatus : { ble:true, started } ← ScanFeature 直接派发,这里不参与 +/// - deviceFound : { ...JieliDevice 字段 } ← ScanFeature 直接派发,这里不参与 +/// - connectionState: { address, state } +/// - rcspInit : { address, code } ← iOS SDK 不区分;我们把"connected 后 cmdTargetFeature"成功视为 init OK +/// +/// iOS SDK 通知名常量定义在 `JL_BLEMultiple.h`: +/// - kJL_BLE_M_ON / kJL_BLE_M_OFF +/// - kJL_BLE_M_ENTITY_CONNECTED / kJL_BLE_M_ENTITY_DISCONNECTED +public final class BluetoothEventForwarder: NSObject { + + private weak var server: JieliHomeServer? + + public init(server: JieliHomeServer) { + self.server = server + } + + public func attach() { + let nc = NotificationCenter.default + nc.addObserver(self, selector: #selector(onBleOn(_:)), name: NSNotification.Name(kJL_BLE_M_ON), object: nil) + nc.addObserver(self, selector: #selector(onBleOff(_:)), name: NSNotification.Name(kJL_BLE_M_OFF), object: nil) + nc.addObserver(self, selector: #selector(onConnected(_:)), name: NSNotification.Name(kJL_BLE_M_ENTITY_CONNECTED), object: nil) + nc.addObserver(self, selector: #selector(onDisconnected(_:)), name: NSNotification.Name(kJL_BLE_M_ENTITY_DISCONNECTED), object: nil) + // 注:扫描发现走 ScanFeature 内部的 CBCentralManager,SDK 的 kJL_BLE_M_FOUND + // 通知(只对 Jieli 协议匹配的广播抛)这里不再监听,避免重复 / 路径冲突。 + } + + public func detach() { + NotificationCenter.default.removeObserver(self) + } + + deinit { detach() } + + // MARK: - Notification handlers + + @objc private func onBleOn(_ note: Notification) { + server?.dispatcher.send([ + "type": "adapterStatus", + "enabled": true, + "hasBle": true, + ]) + } + + @objc private func onBleOff(_ note: Notification) { + server?.dispatcher.send([ + "type": "adapterStatus", + "enabled": false, + "hasBle": true, + ]) + } + + @objc private func onConnected(_ note: Notification) { + guard let entity = extractEntity(note) else { return } + let address = entity.mUUID ?? "" + server?.dispatcher.send([ + "type": "connectionState", + "address": address, + "state": 1, // CONNECTION_OK + ]) + // 与 Android RcspInitEvent 等价:iOS SDK 在 connected 时已可发命令, + // 这里直接把 init code=0 发出去,方便 Dart 侧的状态机走通 + server?.dispatcher.send([ + "type": "rcspInit", + "address": address, + "code": 0, + ]) + } + + @objc private func onDisconnected(_ note: Notification) { + // 通知 object 可能是 CBPeripheral 或 JL_EntityM + let address: String = { + if let entity = note.object as? JL_EntityM { return entity.mUUID ?? "" } + if let peripheral = note.object as? CBPeripheral { return peripheral.identifier.uuidString } + return "" + }() + server?.dispatcher.send([ + "type": "connectionState", + "address": address, + "state": 0, // CONNECTION_DISCONNECT + ]) + } + + // MARK: - helpers + + private func extractEntity(_ note: Notification) -> JL_EntityM? { + if let entity = note.object as? JL_EntityM { return entity } + if let cbp = note.object as? CBPeripheral, + let server = server { + return server.entity(forUuid: cbp.identifier.uuidString) + } + return nil + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/event/CustomEventForwarder.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/event/CustomEventForwarder.swift new file mode 100644 index 000000000..c33429e84 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/event/CustomEventForwarder.swift @@ -0,0 +1,55 @@ +import Foundation +import JL_BLEKit + +/// 监听设备主动推送的 Custom Cmd / Custom Response。 +/// +/// 设备 → APP 自定义命令在 iOS SDK 里通过 NSNotification 形式抛出(key +/// `kJL_MANAGER_CUSTOM_DATA` / `kJL_MANAGER_CUSTOM_DATA_RSP`),具体 payload +/// 走 `JL_CustomManager` 的 delegate;这里仅把"设备主动请求"广播事件转发到 Dart 端, +/// payload 用 base64 encode 兼容 Android 端 `ExpandFunctionEvent` 形态。 +public final class CustomEventForwarder: NSObject { + + private weak var server: JieliHomeServer? + + public init(server: JieliHomeServer) { + self.server = server + } + + public func attach() { + NotificationCenter.default.addObserver( + self, + selector: #selector(onCustomData(_:)), + name: NSNotification.Name(kJL_MANAGER_CUSTOM_DATA), + object: nil + ) + } + + public func detach() { + NotificationCenter.default.removeObserver(self) + } + + deinit { detach() } + + @objc private func onCustomData(_ note: Notification) { + guard let info = note.userInfo as? [String: Any] else { return } + let uuid = info[kJL_MANAGER_KEY_UUID] as? String + var opCode: Int = 0 + var base64: String? = nil + + // iOS SDK 把 payload 放在 kJL_MANAGER_KEY_OBJECT 里——历史上有时是 NSData, + // 有时是字典含 opCode + data;两边都兜底处理。 + if let dict = info[kJL_MANAGER_KEY_OBJECT] as? [String: Any] { + if let code = (dict["opCode"] as? NSNumber)?.intValue { opCode = code } + if let data = dict["data"] as? Data { base64 = data.base64EncodedString() } + } else if let data = info[kJL_MANAGER_KEY_OBJECT] as? Data { + base64 = data.base64EncodedString() + } + + server?.dispatcher.send([ + "type": "expandFunction", + "address": uuid as Any, + "opCode": opCode, + "payloadBase64": base64 as Any, + ]) + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/event/DeviceInfoEventForwarder.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/event/DeviceInfoEventForwarder.swift new file mode 100644 index 000000000..7607722bf --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/event/DeviceInfoEventForwarder.swift @@ -0,0 +1,110 @@ +import Foundation +import JL_BLEKit + +/// 设备主动推送的状态事件(电量 / 通话状态 / 系统信息 / 耳机广播)。 +/// +/// 与 Android `DeviceInfoEventForwarder` 对齐,覆盖: +/// - `battery` — 电量变化(来源:`kJL_MANAGER_HEADSET_ADV` 通知里的 POWER_C/L/R, +/// 以及 `kJL_MANAGER_SYSTEM_INFO` 通知里 JLModel_Device.battery 字段) +/// - `phoneCallStatus` — 通话状态(来源:`kJL_MANAGER_CALL_STATUS`) +/// +/// iOS SDK 没有像 Android `OnRcspEventListener.onBatteryChange` 那种细粒度推送,电量更新 +/// 都是间接通过广播 / 系统信息走的。我们订阅两条通知,把每次推上来的最新值统一外抛 `battery`。 +public final class DeviceInfoEventForwarder: NSObject { + + private weak var server: JieliHomeServer? + private var lastBatteryByDevice: [String: Int] = [:] + + public init(server: JieliHomeServer) { + self.server = server + } + + public func attach() { + let nc = NotificationCenter.default + nc.addObserver(self, selector: #selector(onCallStatus(_:)), + name: NSNotification.Name(kJL_MANAGER_CALL_STATUS), object: nil) + nc.addObserver(self, selector: #selector(onHeadsetAdv(_:)), + name: NSNotification.Name(kJL_MANAGER_HEADSET_ADV), object: nil) + nc.addObserver(self, selector: #selector(onSystemInfo(_:)), + name: NSNotification.Name(kJL_MANAGER_SYSTEM_INFO), object: nil) + nc.addObserver(self, selector: #selector(onTargetInfo(_:)), + name: NSNotification.Name(kJL_MANAGER_TARGET_INFO), object: nil) + } + + public func detach() { + NotificationCenter.default.removeObserver(self) + } + + deinit { detach() } + + // MARK: - Notifications → events + + @objc private func onCallStatus(_ note: Notification) { + guard let info = note.userInfo as? [String: Any] else { return } + let uuid = info[kJL_MANAGER_KEY_UUID] as? String + // payload 一般是 status (NSNumber) 或对象,按 SDK 习惯取 intValue + var status: Int = 0 + if let n = info[kJL_MANAGER_KEY_OBJECT] as? NSNumber { + status = n.intValue + } else if let n = info[kJL_MANAGER_KEY_OBJECT] as? Int { + status = n + } else if let dict = info[kJL_MANAGER_KEY_OBJECT] as? [String: Any] { + status = (dict["status"] as? NSNumber)?.intValue ?? 0 + } + server?.dispatcher.send([ + "type": "phoneCallStatus", + "address": uuid as Any, + "status": status, + ]) + } + + @objc private func onHeadsetAdv(_ note: Notification) { + guard let info = note.userInfo as? [String: Any] else { return } + let uuid = info[kJL_MANAGER_KEY_UUID] as? String + guard let raw = info[kJL_MANAGER_KEY_OBJECT] as? [String: Any] else { return } + // 广播里 POWER_C 是充电仓电量,POWER_L/R 是左右耳;Android 端 `battery` 取的是 + // 综合电量。这里优先取仓位(最常用于显示),再回退左/右耳的最小值。 + let pc = (raw["POWER_C"] as? NSNumber)?.intValue + let pl = (raw["POWER_L"] as? NSNumber)?.intValue + let pr = (raw["POWER_R"] as? NSNumber)?.intValue + let level: Int? = pc ?? [pl, pr].compactMap { $0 }.min() + guard let lv = level else { return } + if let id = uuid, lastBatteryByDevice[id] == lv { return } + if let id = uuid { lastBatteryByDevice[id] = lv } + server?.dispatcher.send([ + "type": "battery", + "address": uuid as Any, + "level": lv, + "left": pl as Any, + "right": pr as Any, + "case": pc as Any, + ]) + } + + @objc private func onSystemInfo(_ note: Notification) { + guard let info = note.userInfo as? [String: Any] else { return } + let uuid = info[kJL_MANAGER_KEY_UUID] as? String + // 系统信息推送时拉一次最新 model + guard let id = uuid, let entity = server?.connectedEntity(forUuid: id) else { return } + let model = entity.mCmdManager.getDeviceModel() + let lv = Int(model.battery) + if lastBatteryByDevice[id] == lv { return } + lastBatteryByDevice[id] = lv + server?.dispatcher.send([ + "type": "battery", + "address": id, + "level": lv, + ]) + } + + @objc private func onTargetInfo(_ note: Notification) { + // 设备配对成功后 SDK 会自动拉一次 TargetInfo;与 Android RcspInit 完成时机对齐。 + guard let info = note.userInfo as? [String: Any] else { return } + let uuid = (info[kJL_MANAGER_KEY_UUID] as? String) ?? "" + server?.dispatcher.send([ + "type": "rcspInit", + "address": uuid, + "code": 0, + ]) + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/AssistantBridge.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/AssistantBridge.swift new file mode 100644 index 000000000..289265dce --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/AssistantBridge.swift @@ -0,0 +1,142 @@ +import Flutter +import Foundation +import JL_BLEKit + +/// AI 助理通路(iOS)—— 与 Android `AssistantBridge + JieliAssistantPort` 对齐。 +/// +/// # 通路细节(与 Android 一致) +/// - 进入 `MODE_RECORD(1)` + `recordtype = .byDevice`(等价 STRATEGY_DEVICE_ALWAYS_RECORDING) +/// - 耳机持续推 OPUS 单声道帧;本类解码成 16k/16bit/mono PCM +/// - 上行帧通过 `assistantAudio` EventChannel 推给 Dart +/// - TTS 下行**不走** RCSP(iOS 同样让宿主走 AVAudioEngine + 系统蓝牙路由播放) +/// +/// # 与 [TranslationFeature] 的关系 +/// 两者都走同一个 [TranslationSession],是设备级互斥的。`MethodRouter` 在调 `assistantStart` +/// 前会先 stop 翻译 / 设备录音。 +public final class AssistantBridge: NSObject, TranslationSessionOwner { + + private weak var server: JieliHomeServer? + private var session: TranslationSession? + private var decoder: OpusStreamDecoder? + private(set) public var isRunning: Bool = false + private var sequence: Int64 = 0 + + public var sessionModeId: Int { TranslationModeIds.MODE_RECORD } + + init(server: JieliHomeServer) { + self.server = server + super.init() + } + + @discardableResult + public func start(address: String? = nil, sampleRate: Int = 16000) -> Bool { + guard let server = server, !isRunning else { return false } + + let session = address.flatMap { server.translationSession(for: $0) } + ?? server.currentTranslationSession() + guard let s = session else { + emitError("device.assistant.no_device", "no connected device") + return false + } + self.session = s + + // OPUS 解码器:单帧 40 B / 16k / mono,与 Android JieliAssistantPort packetSize=40 对齐 + decoder = OpusStreamDecoder(channels: 1, dataSize: 40, sampleRate: sampleRate) { [weak self] pcm in + self?.emitAudioFrame(pcm: pcm, sampleRate: sampleRate) + } onError: { [weak self] code, msg in + self?.emitError("device.decoder_failed", "opus: code=\(code) msg=\(msg ?? "")") + } + + s.acquire(owner: self) { [weak self] tm, errMsg in + guard let self = self else { return } + guard let tm = tm else { + self.cleanup() + self.emitError("device.assistant.session_busy", errMsg) + return + } + tm.recordtype = .byDevice + let mode = BaseTranslationModeHandler.buildMode( + modeType: .onlyRecord, audioType: .OPUS, channel: 1, sampleRate: sampleRate + ) + tm.trStartTranslate(mode) { [weak self] status, err in + guard let self = self else { return } + if status == .success || status == .sameMode { + self.isRunning = true + self.server?.dispatcher.send([ + "type": "assistantStart", + "sampleRate": sampleRate, + "tsMs": NSNumber(value: Int64(Date().timeIntervalSince1970 * 1000)), + ]) + } else { + self.cleanup() + self.emitError("device.assistant.enter_failed", err?.localizedDescription ?? "trStartTranslateMode status=\(status.rawValue)") + } + } + } + return true + } + + public func stop() { + guard isRunning else { cleanup(); return } + isRunning = false + let session = self.session + session?.release(owner: self) + cleanup() + server?.dispatcher.send([ + "type": "assistantEnd", + "tsMs": NSNumber(value: Int64(Date().timeIntervalSince1970 * 1000)), + ]) + } + + // MARK: - TranslationSessionOwner + + public func onReceiveAudio(_ audio: JLTranslateAudio) { + guard isRunning else { return } + switch audio.audioType { + case .OPUS: decoder?.feed(audio.data) + case .PCM: emitAudioFrame(pcm: audio.data, sampleRate: 16000) + default: break + } + } + + public func onModeChange(_ mode: JLTranslateSetMode) { + if mode.modeType == .idle && isRunning { + emitError("device.mode_exited", "headset exited MODE_RECORD → MODE_IDLE") + stop() + } + } + + public func onError(_ error: NSError) { + emitError("device.translation_error", "code=\(error.code) msg=\(error.localizedDescription)") + } + + // MARK: - Helpers + + private func emitAudioFrame(pcm: Data, sampleRate: Int) { + sequence &+= 1 + server?.dispatcher.send([ + "type": "assistantAudio", + "encoding": "pcm16", + "sampleRate": sampleRate, + "channels": 1, + "bitsPerSample": 16, + "sequence": NSNumber(value: sequence), + "tsMs": NSNumber(value: Int64(Date().timeIntervalSince1970 * 1000)), + "pcm": FlutterStandardTypedData(bytes: pcm), + ]) + } + + private func emitError(_ code: String, _ message: String?) { + server?.dispatcher.send([ + "type": "assistantError", + "code": code, + "message": message as Any, + ]) + } + + private func cleanup() { + decoder?.release() + decoder = nil + session = nil + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/ConnectFeature.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/ConnectFeature.swift new file mode 100644 index 000000000..2d6d4acf7 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/ConnectFeature.swift @@ -0,0 +1,118 @@ +import Foundation +import JL_BLEKit + +/// 连接 / 断开 / 状态查询 —— 与 Android `ConnectFeature` 对齐。 +/// +/// # 注意 +/// - iOS 上 `address` 是 BLE 设备的 CoreBluetooth identifier (UUID 字符串), +/// 不是 BLE MAC(iOS 系统对 BLE MAC 全程匿名)。Android 端 `address` 是 MAC。 +/// - 配对成功后**主动调一次 `cmdTargetFeatureResult`**,以拿到电量 / 版本 / vid-pid 等 +/// 基础信息——这一步与 Android `RCSPController.init` 后由 SDK 自动拉取的行为对齐。 +/// 完成后向 Dart 推 `rcspInit`,让上层确定 RCSP 已 ready。 +public final class ConnectFeature { + + private weak var server: JieliHomeServer? + + init(server: JieliHomeServer) { self.server = server } + + public func connect( + bleAddress: String, + edrAddress: String?, + deviceType: Int, + connectWay: Int, + completion: @escaping (_ ok: Bool, _ errMsg: String?) -> Void + ) { + guard let server = server else { completion(false, "server uninitialized"); return } + let bleMultiple = server.bleMultiple + + let entity: JL_EntityM? = server.entity(forUuid: bleAddress) + ?? bleMultiple.makeEntity(withUUID: bleAddress) + guard let target = entity else { + completion(false, "entity not found for uuid=\(bleAddress)") + return + } + + server.dispatcher.send([ + "type": "connectionState", + "address": bleAddress, + "state": 2, // CONNECTION_CONNECTING + ]) + + bleMultiple.connectEntity(target) { [weak self] status in + guard let self = self else { return } + switch status { + case .paired, .connectRepeat: + // 1) 触发一次 RCSP TargetFeature 拉取,等价于 Android 端 SDK 内部 + // 在 RCSPController.init 成功后自动做的事。 + target.mCmdManager.cmdTargetFeatureResult { [weak self] state, _, _ in + guard let self = self else { return } + let ok = state == .success + // 2) 拉取 COMMON 字段(电量 / 版本号等)填充 model 缓存, + // 供 deviceSnapshot() 读取。 + if ok { + target.mCmdManager.cmdGetSystemInfo(.COMMON) { _, _, _ in } + } + self.server?.dispatcher.send([ + "type": "rcspInit", + "address": target.mUUID ?? bleAddress, + "code": ok ? 0 : 1, + ]) + completion(ok, ok ? nil : "rcsp init failed") + } + case .connecting: + break + case .bleOFF: + completion(false, "ble off") + case .connectFail: + completion(false, "connect fail") + case .connectTimeout: + completion(false, "connect timeout") + case .connectRefuse: + completion(false, "connect refuse") + case .pairFail: + completion(false, "pair fail") + case .pairTimeout: + completion(false, "pair timeout") + case .masterChanging: + break + case .disconnectOk: + completion(false, "disconnected") + case .null: + completion(false, "entity null") + @unknown default: + completion(false, "unknown status \(status.rawValue)") + } + } + } + + public func disconnect(address: String, completion: @escaping (_ ok: Bool, _ errMsg: String?) -> Void) { + guard let server = server else { completion(false, "server uninitialized"); return } + guard let entity = server.connectedEntity(forUuid: address) else { + completion(false, "not connected: \(address)") + return + } + // 断开前先收尾翻译 / 助理 / 设备录音,避免脏状态 + server.translationFeature.stop() + server.assistantBridge.stop() + server.deviceRecordFeature.stop() + + server.bleMultiple.disconnectEntity(entity) { _ in + // 让翻译会话也一起释放 + server.removeTranslationSession(uuid: address) + completion(true, nil) + } + } + + public func isConnected(address: String) -> Bool { + guard let server = server else { return false } + return server.connectedEntity(forUuid: address) != nil + } + + public func connectedDeviceInfo() -> [String: Any?]? { + guard let entity = server?.currentConnectedEntity() else { return nil } + return [ + "address": entity.mUUID ?? "", + "name": entity.mPeripheral.name ?? "", + ] + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/CustomCmdFeature.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/CustomCmdFeature.swift new file mode 100644 index 000000000..dddac8dd1 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/CustomCmdFeature.swift @@ -0,0 +1,39 @@ +import Foundation +import JL_BLEKit + +/// 厂商扩展指令通道(与 Android `CustomCmdFeature` 对齐)。 +/// +/// iOS SDK 通过每个设备 `JL_ManagerM` 上的 `mCustomManager` 暴露 `cmdCustomData:`。 +/// 设备主动推的数据由 [CustomEventForwarder] 监听通知转发。 +public final class CustomCmdFeature { + + private weak var server: JieliHomeServer? + init(server: JieliHomeServer) { self.server = server } + + public func send( + address: String, + opCode: Int, + payload: Data, + completion: @escaping (_ ok: Bool, _ response: Data?, _ errCode: Int?, _ errMsg: String?) -> Void + ) { + guard let entity = server?.connectedEntity(forUuid: address) else { + completion(false, nil, -1, "remote device not found"); return + } + let custom = entity.mCmdManager.mCustomManager + + // iOS SDK 没有原生 opCode 字段——厂商扩展指令 0xF0 的 sub-op 习惯放在 payload 第一字节, + // 这里把 Dart 端传入的 opCode 拼到 payload 头部,保持与 Android 端 SDK 的等价语义。 + var bytes = [UInt8]() + bytes.append(UInt8(truncatingIfNeeded: opCode)) + bytes.append(contentsOf: [UInt8](payload)) + let outData = Data(bytes) + + custom.cmdCustomData(outData, isNeedResponse: true) { status, _, replyData in + if status == .success { + completion(true, replyData, nil, nil) + } else { + completion(false, nil, Int(status.rawValue), "cmd status=\(status.rawValue)") + } + } + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/DeviceInfoFeature.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/DeviceInfoFeature.swift new file mode 100644 index 000000000..020d46bb4 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/DeviceInfoFeature.swift @@ -0,0 +1,54 @@ +import Foundation +import JL_BLEKit + +/// 查询设备静态信息(电量 / 版本 / VID-PID 等)。 +/// +/// - `snapshot(address:)` 直接读 iOS SDK 缓存(连接成功后 BleManager 已经 +/// 触发过 `cmdTargetFeatureResult`)。 +/// - `queryTargetInfo(address:mask:)` 主动再向设备拉一次最新值。 +public final class DeviceInfoFeature { + + private weak var server: JieliHomeServer? + init(server: JieliHomeServer) { self.server = server } + + public func snapshot(address: String) -> [String: Any?]? { + guard let entity = server?.connectedEntity(forUuid: address) else { return nil } + let m = entity.mCmdManager.getDeviceModel() + let pidvidParts = (m.pidvid ?? "").split(separator: "-") + let vid: String? = pidvidParts.count >= 1 ? String(pidvidParts[0]) : nil + let pid: String? = pidvidParts.count >= 2 ? String(pidvidParts[1]) : nil + return [ + "address": entity.mUUID ?? address, + "battery": Int(m.battery), + "volume": Int(m.currentVol), + "maxVolume": Int(m.maxVol), + "versionCode": nil, // iOS SDK 只暴露 versionFirmware(字符串),与 Android versionCode 含义不同 + "versionName": m.versionFirmware ?? "", + "name": entity.mPeripheral.name ?? "", + "vid": vid, + "pid": pid, + "uid": m.license ?? "", + "edrAddr": entity.mEdr ?? m.btAddr ?? "", + ] + } + + public func queryTargetInfo( + address: String, + mask: Int, + completion: @escaping (_ info: [String: Any?]?, _ errCode: Int?, _ errMsg: String?) -> Void + ) { + guard let entity = server?.connectedEntity(forUuid: address) else { + completion(nil, -1, "remote device not found"); return + } + let cmd = entity.mCmdManager + // iOS SDK 的 mask 走"选择性获取"接口 + cmd.cmdGetSystemInfo(.COMMON, selectionBit: UInt32(mask)) { [weak self] status, _, _ in + guard let self = self else { return } + if status == .success { + completion(self.snapshot(address: address), nil, nil) + } else { + completion(nil, Int(status.rawValue), "cmd status=\(status.rawValue)") + } + } + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/DeviceRecordFeature.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/DeviceRecordFeature.swift new file mode 100644 index 000000000..b3422020a --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/DeviceRecordFeature.swift @@ -0,0 +1,156 @@ +import Flutter +import Foundation +import JL_BLEKit + +/// 设备录音通路(iOS)—— 与 Android `DeviceRecordFeature + JieliDeviceRecordPort` 对齐。 +/// +/// # 通路细节(与 Android 一致) +/// - 进入 `MODE_CALL_TRANSLATION_WITH_STEREO(6)` + `recordtype = .byDevice` + `channel = 2` +/// - 耳机一路推 SOURCE_E_SCO_MIX 立体声 OPUS(L=本端 / R=对端) +/// - 内部用 `OpusStreamDecoder(channels=2, dataSize=80)` 解码成 16k/16bit/stereo 交织 PCM +/// - 通过 `deviceRecordAudio` EventChannel 外抛(streamId = "in.stereo") +/// +/// # 真机注意 +/// 与 Android 同样:mode=6 依赖真实通话事件(SCO_MIX),无通话时固件可能不上推 → 零帧。 +public final class DeviceRecordFeature: NSObject, TranslationSessionOwner { + + private weak var server: JieliHomeServer? + private var session: TranslationSession? + private var decoder: OpusStreamDecoder? + private(set) public var isRecording: Bool = false + private var deviceAddress: String? + private var sampleRate = 16000 + + public var sessionModeId: Int { TranslationModeIds.MODE_CALL_TRANSLATION_WITH_STEREO } + + init(server: JieliHomeServer) { + self.server = server + super.init() + } + + public func start(args: [String: Any], completion: @escaping (Bool, String?) -> Void) { + guard let server = server, !isRecording else { + completion(false, "already recording"); return + } + let addressArg = args["address"] as? String + let sr = (args["sampleRate"] as? NSNumber)?.intValue ?? 16000 + + let session = addressArg.flatMap { server.translationSession(for: $0) } + ?? server.currentTranslationSession() + guard let s = session else { + emitError("device.record.no_device", code: -1, message: "no connected device") + completion(false, "no connected device"); return + } + self.session = s + self.sampleRate = sr + self.deviceAddress = addressArg ?? server.currentConnectedEntity()?.mUUID + + // 立体声 OPUS 解码:单帧 80 B / 16k / stereo + decoder = OpusStreamDecoder(channels: 2, dataSize: 80, sampleRate: sr) { [weak self] stereoPcm in + self?.emitStereoFrame(stereoPcm) + } onError: { [weak self] code, msg in + self?.emitError("device.record.decode_failed", code: code, message: "opus stereo: \(msg ?? "")") + } + + s.acquire(owner: self) { [weak self] tm, errMsg in + guard let self = self else { return } + guard let tm = tm else { + self.cleanup() + self.emitError("device.record.session_busy", code: -2, message: errMsg) + completion(false, errMsg) + return + } + tm.recordtype = .byDevice + let mode = BaseTranslationModeHandler.buildMode( + modeType: .callTranslateStereo, audioType: .OPUS, channel: 2, sampleRate: sr + ) + tm.trStartTranslate(mode) { [weak self] status, err in + guard let self = self else { return } + if status == .success || status == .sameMode { + self.isRecording = true + self.server?.dispatcher.send([ + "type": "deviceRecordStart", + "address": self.deviceAddress as Any, + "sampleRate": sr, + "tsMs": NSNumber(value: Int64(Date().timeIntervalSince1970 * 1000)), + ]) + completion(true, nil) + } else { + self.cleanup() + self.emitError("device.record.enter_failed", code: Int(status.rawValue), + message: err?.localizedDescription ?? "trStartTranslateMode status=\(status.rawValue)") + completion(false, "tm status=\(status.rawValue)") + } + } + } + } + + public func stop() { + guard isRecording else { cleanup(); return } + isRecording = false + session?.release(owner: self) + let addr = deviceAddress + cleanup() + server?.dispatcher.send([ + "type": "deviceRecordStop", + "address": addr as Any, + "tsMs": NSNumber(value: Int64(Date().timeIntervalSince1970 * 1000)), + ]) + } + + // MARK: - TranslationSessionOwner + + public func onReceiveAudio(_ audio: JLTranslateAudio) { + guard isRecording else { return } + // mode=6 时 SDK 把上行帧 source 标成 .typeESCOMax (SOURCE_E_SCO_MIX);其他 source 忽略 + guard audio.sourceType == .typeESCOMax else { return } + switch audio.audioType { + case .OPUS: decoder?.feed(audio.data) + case .PCM: emitStereoFrame(audio.data) + default: break + } + } + + public func onModeChange(_ mode: JLTranslateSetMode) { + if mode.modeType == .idle && isRecording { + emitError("device.record.mode_exited", code: -1, + message: "headset exited MODE=6 → MODE_IDLE") + stop() + } + } + + public func onError(_ error: NSError) { + emitError("device.record.translation_error", code: error.code, message: error.localizedDescription) + } + + // MARK: - Helpers + + private func emitStereoFrame(_ pcm: Data) { + server?.dispatcher.send([ + "type": "deviceRecordAudio", + "address": deviceAddress as Any, + "streamId": "in.stereo", + "sampleRate": sampleRate, + "channels": 2, + "bitsPerSample": 16, + "tsMs": NSNumber(value: Int64(Date().timeIntervalSince1970 * 1000)), + "pcm": FlutterStandardTypedData(bytes: pcm), + ]) + } + + private func emitError(_ code: String, code intCode: Int, message: String?) { + server?.dispatcher.send([ + "type": "deviceRecordError", + "address": deviceAddress as Any, + "code": intCode, + "message": message as Any, + "errorCode": code, + ]) + } + + private func cleanup() { + decoder?.release() + decoder = nil + session = nil + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/OtaFeature.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/OtaFeature.swift new file mode 100644 index 000000000..da7a2f353 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/OtaFeature.swift @@ -0,0 +1,127 @@ +import Foundation +import JL_BLEKit +import JL_OTALib + +/// OTA 升级(iOS)—— 与 iOS demo `OTAViewController` 对齐。 +/// +/// 走 per-device 的 `JL_OTAManager.cmdUpgrade(_:option:result:)`: +/// - `mOTAManager` 挂在 `JL_ManagerM` 上,由 SDK 在连接成功后自动初始化 +/// - 我们直接读取固件文件字节,调 `cmdUpgrade(data, option: nil, result: ...)` +/// - 取消用 `cmdOTACancelResult()`(无参,demo 形态) +public final class OtaFeature { + + private weak var server: JieliHomeServer? + public private(set) var isRunning: Bool = false + + private var currentFileSize: Int64 = 0 + private var currentAddress: String? + private weak var currentOtaManager: JL_OTAManager? + + init(server: JieliHomeServer) { self.server = server } + + public func start( + address: String?, + firmwareFilePath: String, + blockSize: Int, + fileFlagBytes: Data + ) { + guard let server = server else { + emitError(-1, "server uninitialized"); return + } + guard !isRunning else { emitError(-2, "ota already running"); return } + + let entity: JL_EntityM? = { + if let addr = address { return server.connectedEntity(forUuid: addr) } + return server.currentConnectedEntity() + }() + guard let target = entity else { + emitError(-3, "no connected device"); return + } + let otaManager = target.mCmdManager.mOTAManager + guard let firmwareData = try? Data(contentsOf: URL(fileURLWithPath: firmwareFilePath)) else { + emitError(-4, "firmware file invalid: \(firmwareFilePath)"); return + } + if firmwareData.isEmpty { emitError(-5, "firmware file is empty"); return } + currentFileSize = Int64(firmwareData.count) + + currentAddress = target.mUUID + currentOtaManager = otaManager + isRunning = true + emitState("INQUIRING", sent: 0, total: currentFileSize) + + otaManager.cmdUpgrade(firmwareData, option: nil) { [weak self] result, progress in + self?.dispatchOtaCallback(result: result, progress: progress) + } + } + + public func cancel() { + guard isRunning else { return } + // demo 形态:无参取消 + currentOtaManager?.cmdOTACancelResult() + isRunning = false + emitState("CANCELLED", sent: -1, total: currentFileSize) + } + + // MARK: - SDK callback dispatch + + private func dispatchOtaCallback(result: JL_OTAResult, progress: Float) { + let total = currentFileSize + let sent = Int64((Float(total) * max(0, min(1.0, progress))).rounded()) + + switch result { + case .success: + isRunning = false + emitState("DONE", sent: total, total: total) + case .fail, .commandFail, .seekFail, .infoFail, .lowPower, .enterFail, + .failVerification, .failCompletely, .failKey, .failErrorFile, .failUboot, + .failLenght, .failFlash, .failCmdTimeout, .failSameVersion, + .failTWSDisconnect, .failNotInBin, .disconnect, .failSameSN, .unknown: + isRunning = false + emitError(Int(result.rawValue), "ota failed result=\(result.rawValue)") + emitState("FAILED", sent: -1, total: total) + case .upgrading, .preparing, .prepared: + emitState("TRANSFERRING", sent: sent, total: total) + case .reconnect, .reconnectWithMacAddr, .reconnectUpdateSource: + emitState("ENTERING", sent: sent, total: total) + case .reboot: + emitState("REBOOTING", sent: total, total: total) + case .cancel: + isRunning = false + emitState("CANCELLED", sent: -1, total: total) + case .statusIsUpdating, .failedConnectMore, .dataIsNull: + isRunning = false + emitError(Int(result.rawValue), "ota cannot start: \(result.rawValue)") + emitState("FAILED", sent: -1, total: total) + @unknown default: + // 默认作进度 + emitState("TRANSFERRING", sent: sent, total: total) + } + } + + // MARK: - Event emission + + private func emitState(_ state: String, sent: Int64, total: Int64) { + let percent: Int = { + if total > 0 && sent >= 0 && sent <= total { + return Int((Double(sent) * 100.0 / Double(total)).rounded()) + } + return -1 + }() + server?.dispatcher.send([ + "type": "otaState", + "state": state, + "sent": NSNumber(value: sent), + "total": NSNumber(value: total), + "percent": percent, + "tsMs": NSNumber(value: Int64(Date().timeIntervalSince1970 * 1000)), + ]) + } + + private func emitError(_ code: Int, _ msg: String?) { + server?.dispatcher.send([ + "type": "otaError", + "code": code, + "message": msg as Any, + ]) + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/ScanFeature.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/ScanFeature.swift new file mode 100644 index 000000000..0ee4fc299 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/ScanFeature.swift @@ -0,0 +1,240 @@ +import CoreBluetooth +import Foundation +import JL_BLEKit + +/// BLE 扫描入口(iOS)—— 与 Android `feature/ScanFeature.kt` 对齐: +/// +/// 1. **不**走 Jieli SDK 的 `JL_BLEMultiple.scanStart()`(其内部只对 Jieli 协议 +/// 匹配的广播抛 `kJL_BLE_M_FOUND`,且不支持按 16-bit UUID 过滤)。改用 +/// `CBCentralManager` 原生扫描。 +/// 2. UUID 白名单走 **OS 级过滤**:把 `CBUUID` 列表传给 +/// `scanForPeripherals(withServices:)`,对应 Android 端 +/// `ScanFilter.Builder().setServiceUuid(...)`。空列表 = 不过滤。 +/// 3. 软过滤只剩 `nameList`(精确忽略大小写)+ `skipUnnamed`,对应 Android +/// `handleScanResult` 中的 in-code 过滤。 +/// 4. 不在 native 层做 dedupe —— 每条 ADV 都派发 `deviceFound`, +/// 对应 Android `CALLBACK_TYPE_ALL_MATCHES`(由 Dart 侧 `scannedDevices` +/// 自行去重)。 +/// +/// 上报字段与 Android schema 1:1: +/// ``` +/// { +/// type: "deviceFound", +/// name, address, edrAddr, deviceType, connectWay, rssi, +/// rawAdv, // ⚠️ iOS 仅 manufacturer hex —— 见下 +/// advRecords: [{len, type, data}], // 重建一份伪 AD record,方便 Dart UI +/// advFlags, // iOS CoreBluetooth 不暴露 → null +/// manufacturerCompanyId, manufacturerData, +/// serviceUuids: [String] +/// } +/// ``` +/// +/// `rawAdv` 在 iOS 上是 **manufacturer data 完整 hex**(不是整段 scan record)— +/// CoreBluetooth 不暴露原始 scan record 字节。Dart 侧 [JieliAdvParser] 已经按 +/// `Platform.isIOS` 给了不同的字节偏移:iOS 从 company-id 开头算起,所以喂 +/// manufacturer 完整 hex 即可让绑定解析跑通。 +/// +/// `address` 在 iOS 上是 `CBPeripheral.identifier.uuidString`(iOS 系统对 BLE +/// MAC 全程匿名)。`ConnectFeature` 那一侧用同样的字符串走 +/// `bleMultiple.makeEntity(withUUID:)` 回查。 +public final class ScanFeature: NSObject, CBCentralManagerDelegate { + + private weak var server: JieliHomeServer? + + /// 扫描专用 central(与 SDK 内部的 `JL_BLEMultiple` central 解耦)。 + private var centralManager: CBCentralManager? + + public private(set) var isScanning: Bool = false + + private var nameFilter: [String] = [] + private var skipUnnamed: Bool = true + + private var stopTimer: DispatchSourceTimer? + + init(server: JieliHomeServer) { + self.server = server + super.init() + // 立刻起 central —— didUpdateState 会把 adapterStatus 推上去; + // 同时也让 BLE state 在第一次 startScan 前就 poweredOn。 + self.centralManager = CBCentralManager(delegate: self, queue: .main) + } + + public func startScan( + timeoutMs: Int, + nameList: [String], + uuidList: [String], + skipUnnamed: Bool + ) throws { + guard let central = centralManager else { + throw NSError(domain: "jieli.scan", code: -1, + userInfo: [NSLocalizedDescriptionKey: "BLE central unavailable"]) + } + // 已在扫描:先停旧的(替换扫描参数),对齐 Android `if (scanning) doStop(scanner)` + if isScanning { doStopScan(silent: true) } + + nameFilter = nameList.filter { !$0.isEmpty } + self.skipUnnamed = skipUnnamed + let serviceFilters: [CBUUID] = uuidList.compactMap { Self.parseUuid($0) } + + guard central.state == .poweredOn else { + throw NSError(domain: "jieli.scan", code: -2, + userInfo: [NSLocalizedDescriptionKey: + "BLE adapter not powered on (state=\(central.state.rawValue))"]) + } + + // OS 级 UUID 过滤;空 → nil(扫所有广播)。 + let services: [CBUUID]? = serviceFilters.isEmpty ? nil : serviceFilters + // allowDuplicates=true:每条 ADV 都回调,对应 Android CALLBACK_TYPE_ALL_MATCHES。 + let options: [String: Any] = [ + CBCentralManagerScanOptionAllowDuplicatesKey: true + ] + central.scanForPeripherals(withServices: services, options: options) + isScanning = true + NSLog("%@", "[JieliScan] startScan timeoutMs=\(timeoutMs) filters=\(serviceFilters.count) nameList=\(nameFilter)") + server?.dispatcher.send([ + "type": "scanStatus", + "ble": true, + "started": true, + ]) + + stopTimer?.cancel() + if timeoutMs > 0 { + let t = DispatchSource.makeTimerSource(queue: .main) + t.schedule(deadline: .now() + .milliseconds(timeoutMs)) + t.setEventHandler { [weak self] in + NSLog("%@", "[JieliScan] scan timeout reached, stopping") + self?.stopScan() + } + t.resume() + stopTimer = t + } + } + + public func stopScan() { + doStopScan(silent: false) + } + + private func doStopScan(silent: Bool) { + stopTimer?.cancel() + stopTimer = nil + if let central = centralManager, central.state == .poweredOn { + central.stopScan() + } + let wasScanning = isScanning + isScanning = false + nameFilter = [] + if wasScanning && !silent { + server?.dispatcher.send([ + "type": "scanStatus", + "ble": true, + "started": false, + ]) + } + } + + // MARK: - CBCentralManagerDelegate + + public func centralManagerDidUpdateState(_ central: CBCentralManager) { + let on = central.state == .poweredOn + server?.dispatcher.send([ + "type": "adapterStatus", + "enabled": on, + "hasBle": true, + ]) + if !on && isScanning { doStopScan(silent: false) } + } + + public func centralManager( + _ central: CBCentralManager, + didDiscover peripheral: CBPeripheral, + advertisementData: [String: Any], + rssi RSSI: NSNumber + ) { + let localName = advertisementData[CBAdvertisementDataLocalNameKey] as? String + let name = peripheral.name ?? localName + + // === 软过滤:与 Android handleScanResult 顺序对齐 === + // (1) skipUnnamed + if skipUnnamed && (name?.isEmpty ?? true) { return } + // (2) name 白名单(忽略大小写) + if !nameFilter.isEmpty { + guard let n = name, + nameFilter.contains(where: { $0.caseInsensitiveCompare(n) == .orderedSame }) + else { return } + } + // UUID 过滤在 OS 级已完成,这里不再重复。 + + // === 解析广播 === + let manufacturerData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data + let advServiceUuids = (advertisementData[CBAdvertisementDataServiceUUIDsKey] as? [CBUUID]) ?? [] + + // rawAdv:iOS 没有原始 scan record 字节 —— 见类注释。 + let rawAdvHex: String? = manufacturerData? + .map { String(format: "%02X", $0) }.joined() + + var mfrCompanyId: Int? = nil + var mfrPayloadHex: String? = nil + if let m = manufacturerData, m.count >= 2 { + // companyId 在 ADV 里是小端,对齐 Android `(it[1] shl 8) or it[0]` + mfrCompanyId = Int(m[0]) | (Int(m[1]) << 8) + if m.count > 2 { + mfrPayloadHex = m[2.. CBUUID? { + let s = raw.trimmingCharacters(in: .whitespacesAndNewlines) + guard !s.isEmpty else { return nil } + let len = s.count + // 显式校验长度,避免 `CBUUID(string:)` 对非法输入抛 Obj-C 异常。 + guard len == 4 || len == 8 || len == 32 || len == 36 else { + NSLog("%@", "[JieliScan] invalid UUID format: \(raw)") + return nil + } + return CBUUID(string: s) + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/SpeechFeature.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/SpeechFeature.swift new file mode 100644 index 000000000..f91473957 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/SpeechFeature.swift @@ -0,0 +1,62 @@ +import Foundation +import JL_BLEKit + +/// 语音助手通路(cmd=4 / 5 / 210)。 +/// +/// 与 Android `SpeechFeature` 对齐。 +/// +/// # iOS 实现现状 +/// iOS 杰理 SDK 的语音助手能力主要由 `JL_SpeechAIttsHandler` / `JLDevAudioAIttsHandler` +/// 提供,**它们的稳定上行帧路径与 Android 实测一致:依赖耳机端按键唤醒**——直接 APP +/// 端调 startRecord 通常只能拿到 START 回调,WORKING 帧不到货(详见 plugin +/// JIELI_SDK_COMMANDS.md "通路 A")。 +/// +/// 因此当前实现只做"按钮触发的开始/停止 + 事件桥",不去强行拉数据帧;当耳机端触发 +/// 唤醒时,事件由 SDK 通知层进入,由 [TranslationFeature] 或 [AssistantBridge] 等 +/// 其他通路接管。 +public final class SpeechFeature { + + private weak var server: JieliHomeServer? + public private(set) var isRecordingFlag: Bool = false + + init(server: JieliHomeServer) { self.server = server } + + public func isRecording(address: String?) -> Bool { isRecordingFlag } + + public func start( + address: String?, + voiceType: Int, + sampleRate: Int, + vadWay: Int, + completion: @escaping (_ ok: Bool, _ msg: String?) -> Void + ) { + // iOS SDK 在 4.2 beta 包里没有把 cmd=4/5 的 startRecord 暴露成公开 Swift API; + // 暂时把"开始"事件直接派发出去保持状态机一致,真正上行帧依赖耳机硬件唤醒。 + isRecordingFlag = true + server?.dispatcher.send([ + "type": "speechStart", + "address": address as Any, + "voiceType": voiceType, + "sampleRate": sampleRate * 1000, // Android 端语义:上报已转换成 Hz + "vadWay": vadWay, + "tsMs": NSNumber(value: Int64(Date().timeIntervalSince1970 * 1000)), + ]) + completion(true, nil) + } + + public func stop( + address: String?, + reason: Int, + completion: @escaping (_ ok: Bool, _ msg: String?) -> Void + ) { + isRecordingFlag = false + server?.dispatcher.send([ + "type": "speechEnd", + "address": address as Any, + "reason": reason, + "message": "stop", + "tsMs": NSNumber(value: Int64(Date().timeIntervalSince1970 * 1000)), + ]) + completion(true, nil) + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/TranslationFeature.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/TranslationFeature.swift new file mode 100644 index 000000000..fd9aedf0e --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/TranslationFeature.swift @@ -0,0 +1,94 @@ +import Foundation +import JL_BLEKit + +/// 翻译特性入口(iOS)—— 6-mode 分发器,对齐 Android `TranslationFeature` 的 method/event 表面。 +/// +/// # 与 Android 行为差异(按 iOS demo 实测能力定) +/// - **不做 mode=3 → mode=6 自动升级**:iOS demo 由用户手动二选一(callTranslate / callTranslateStereo), +/// 我们也沿用这个语义。Dart 端按需直接选 modeId。 +/// - 6 个 mode handler 共享同一个 [TranslationSession](同一台设备一个 `JLTranslationManager`), +/// 切换 mode 时先 stop 旧的、再 start 新的。 +public final class TranslationFeature { + + private weak var server: JieliHomeServer? + + private let handlers: [Int: BaseTranslationModeHandler] + private var current: BaseTranslationModeHandler? + + init(server: JieliHomeServer) { + self.server = server + self.handlers = [ + TranslationModeIds.MODE_RECORD: RecordModeHandler(server: server), + TranslationModeIds.MODE_RECORDING_TRANSLATION: RecordingTranslationModeHandler(server: server), + TranslationModeIds.MODE_CALL_TRANSLATION: CallTranslationModeHandler(server: server), + TranslationModeIds.MODE_CALL_TRANSLATION_WITH_STEREO: StereoCallTranslationModeHandler(server: server), + TranslationModeIds.MODE_AUDIO_TRANSLATION: AudioTranslationModeHandler(server: server), + TranslationModeIds.MODE_FACE_TO_FACE_TRANSLATION: FaceToFaceTranslationModeHandler(server: server), + ] + } + + public var isWorking: Bool { current?.isWorking == true } + public var currentModeId: Int? { current?.modeId } + public var currentInputStreams: [String] { current?.inputStreams ?? [] } + public var currentOutputStreams: [String] { current?.outputStreams ?? [] } + + public func start(modeId: Int, args: [String: Any], completion: @escaping (Bool, String?) -> Void) { + guard let handler = handlers[modeId] else { + completion(false, "unknown modeId=\(modeId)"); return + } + if let cur = current, cur.isWorking, cur !== handler { cur.stop() } + current = handler + handler.start(args: args, completion: completion) + } + + public func stop() { + current?.stop() + current = nil + } + + public func feedTranslatedAudio( + streamId: String, pcm: Data, + sampleRate: Int, channels: Int, bitsPerSample: Int, + isFinal: Bool + ) -> Bool { + guard let h = current, h.isWorking else { return false } + if !h.outputStreams.contains(streamId) { return false } + return h.onTranslatedAudio( + streamId: streamId, pcm: pcm, + format: AudioFormat(sampleRate: sampleRate, channels: channels, bitsPerSample: bitsPerSample), + isFinal: isFinal + ) + } + + public func feedTranslationResult( + srcLang: String?, srcText: String?, + destLang: String?, destText: String?, + requestId: String? + ) { + let mid = current?.modeId ?? 0 + server?.dispatcher.send([ + "type": "translationResult", + "modeId": mid, + "srcLang": srcLang as Any, + "srcText": srcText as Any, + "destLang": destLang as Any, + "destText": destText as Any, + "requestId": requestId as Any, + ]) + } + + /// 探测设备是否支持立体声通话翻译模式(mode=6)。 + /// iOS SDK 没有显式的 `isSupportCallTranslationWithStereo` API;demo 用 + /// `trIsPlayWithA2dp() == true && trIsSupportTranslate() == true` 综合判定。 + public func isSupportCallTranslationWithStereo(address: String?) -> Bool { + guard let server = server else { return false } + let session = address.flatMap { server.translationSession(for: $0) } ?? server.currentTranslationSession() + guard let tm = session?.currentManager() else { return false } + return tm.trIsSupportTranslate() && tm.trIsPlayWithA2dp() + } + + public func feedAudioFilePcm(pcm: Data, sampleRate: Int) -> Bool { + guard let h = current as? AudioTranslationModeHandler else { return false } + return h.feedFilePcm(pcm, sampleRate: sampleRate) + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/TranslationConstants.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/TranslationConstants.swift new file mode 100644 index 000000000..83e5c59ea --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/TranslationConstants.swift @@ -0,0 +1,37 @@ +import Foundation + +/// 翻译流逻辑 ID。Dart `TranslationStreams` / Android `TranslationStreams` 一一对应。 +public enum TranslationStreams { + public static let IN_MIC = "in.mic" + public static let IN_UPLINK = "in.uplink" // 通话上行:本机用户 + public static let IN_DOWNLINK = "in.downlink" // 通话下行:对端 + public static let IN_AUDIO_FILE = "in.audioFile" + + public static let OUT_SPEAKER = "out.speaker" + public static let OUT_UPLINK = "out.uplink" + public static let OUT_DOWNLINK = "out.downlink" + public static let OUT_LOCAL_PLAYBACK = "out.localPlayback" +} + +/// 与 SDK `JLTranslateSetModeType` 完全一致;和 Dart `TranslationModeIds` 对齐。 +public enum TranslationModeIds { + public static let MODE_IDLE = 0 + public static let MODE_RECORD = 1 + public static let MODE_RECORDING_TRANSLATION = 2 + public static let MODE_CALL_TRANSLATION = 3 + public static let MODE_AUDIO_TRANSLATION = 4 + public static let MODE_FACE_TO_FACE_TRANSLATION = 5 + public static let MODE_CALL_TRANSLATION_WITH_STEREO = 6 +} + +/// 一帧 PCM 音频规格 +public struct AudioFormat { + public let sampleRate: Int + public let channels: Int + public let bitsPerSample: Int + public init(sampleRate: Int = 16000, channels: Int = 1, bitsPerSample: Int = 16) { + self.sampleRate = sampleRate + self.channels = channels + self.bitsPerSample = bitsPerSample + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/TranslationModeHandler.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/TranslationModeHandler.swift new file mode 100644 index 000000000..dfa11ff9d --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/TranslationModeHandler.swift @@ -0,0 +1,154 @@ +import Flutter +import Foundation +import JL_BLEKit + +/// 翻译模式处理器协议。与 Android `TranslationModeHandler` 对齐: +/// - 声明自己消费哪些「输入流」、产出哪些「输出流」 +/// - `start` 进入翻译模式,启动上下行通路 +/// - `stop` 退出模式,释放编解码器 +/// - `onTranslatedAudio` 接外部 TTS PCM,按输出流路由 +public protocol TranslationModeHandler: TranslationSessionOwner { + var modeId: Int { get } + var inputStreams: [String] { get } + var outputStreams: [String] { get } + var isWorking: Bool { get } + func start(args: [String: Any], completion: @escaping (_ ok: Bool, _ errMsg: String?) -> Void) + func stop() + func onTranslatedAudio(streamId: String, pcm: Data, format: AudioFormat, isFinal: Bool) -> Bool +} + +/// 公共基类:维护 `working` 标志,提供 pushFrame / emitLog / emitError 等工具。 +public class BaseTranslationModeHandler: NSObject, TranslationModeHandler { + + public let modeId: Int + public let inputStreams: [String] + public let outputStreams: [String] + + weak var server: JieliHomeServer? + var session: TranslationSession? + @objc dynamic var working: Bool = false + + public var isWorking: Bool { working } + + public var sessionModeId: Int { modeId } + + private var seq: Int64 = 0 + + public init(server: JieliHomeServer, modeId: Int, inputs: [String], outputs: [String]) { + self.server = server + self.modeId = modeId + self.inputStreams = inputs + self.outputStreams = outputs + super.init() + } + + // 子类需要重写 + public func start(args: [String: Any], completion: @escaping (Bool, String?) -> Void) { + completion(false, "unimplemented") + } + public func stop() { working = false } + public func onTranslatedAudio(streamId: String, pcm: Data, format: AudioFormat, isFinal: Bool) -> Bool { false } + + // TranslationSessionOwner + public func onReceiveAudio(_ audio: JLTranslateAudio) {} + public func onModeChange(_ mode: JLTranslateSetMode) { + // 设备退出回到 IDLE 时收尾 + if mode.modeType == .idle && working { + emitError(-1, "device exited mode → IDLE") + stop() + } + } + public func onError(_ error: NSError) { emitError(error.code, error.localizedDescription) } + + // MARK: - Helpers + + func pushFrame( + streamId: String, + pcm: Data, + format: AudioFormat = AudioFormat(), + isFinal: Bool = false, + encoding: String = "pcm16" + ) { + seq &+= 1 + server?.dispatcher.send([ + "type": "translationAudio", + "modeId": modeId, + "streamId": streamId, + "sampleRate": format.sampleRate, + "channels": format.channels, + "bitsPerSample": format.bitsPerSample, + "encoding": encoding, + "seq": NSNumber(value: seq), + "tsMs": NSNumber(value: Int64(Date().timeIntervalSince1970 * 1000)), + "final": isFinal, + "pcm": FlutterStandardTypedData(bytes: pcm), + ]) + } + + func emitLog(_ content: String) { + server?.dispatcher.send([ + "type": "translationLog", + "modeId": modeId, + "content": content, + ]) + } + + func emitError(_ code: Int, _ message: String?) { + server?.dispatcher.send([ + "type": "translationError", + "modeId": modeId, + "code": code, + "message": message as Any, + ]) + } + + // MARK: - Mode helpers + + /// 解析常用枚举参数(audioType / strategy) + static func parseAudioType(_ raw: Any?) -> JL_SpeakDataType? { + if let s = raw as? String { + switch s.lowercased() { + case "opus": return .OPUS + case "pcm": return .PCM + case "speex": return .SPEEX + case "jla_v2", "jlav2": return .JLA_V2 + case "msbc", "m_sbc": return .MSBC + default: break + } + } + if let n = (raw as? NSNumber)?.uint8Value { + return JL_SpeakDataType(rawValue: n) + } + return nil + } + + /// 解析录音策略(与 Android STRATEGY_DEVICE_ALWAYS_RECORDING / CUSTOM 对齐) + static func parseRecordType(_ raw: Any?, default def: JLTranslateRecordType) -> JLTranslateRecordType { + if let s = raw as? String { + switch s.lowercased() { + case "phone", "custom": return .byPhone + case "device", "always": return .byDevice + default: break + } + } + if let n = (raw as? NSNumber)?.intValue { + return JLTranslateRecordType(rawValue: n) ?? def + } + return def + } + + /// 构造 SDK 的 JLTranslateSetMode + static func buildMode( + modeType: JLTranslateSetModeType, + audioType: JL_SpeakDataType, + channel: Int, + sampleRate: Int + ) -> JLTranslateSetMode { + let m = JLTranslateSetMode() + m.modeType = modeType + m.dataType = audioType + m.channel = channel + m.sampleRate = sampleRate + return m + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/mode/AudioAndFaceModeHandlers.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/mode/AudioAndFaceModeHandlers.swift new file mode 100644 index 000000000..a3833e4a3 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/mode/AudioAndFaceModeHandlers.swift @@ -0,0 +1,150 @@ +import Foundation +import JL_BLEKit + +/// MODE_AUDIO_TRANSLATION(mode=4)—— 把外部音频文件 PCM 灌入翻译通路。 +public final class AudioTranslationModeHandler: BaseTranslationModeHandler { + + private var sampleRate = 16000 + + public init(server: JieliHomeServer) { + super.init( + server: server, + modeId: TranslationModeIds.MODE_AUDIO_TRANSLATION, + inputs: [TranslationStreams.IN_AUDIO_FILE], + outputs: [TranslationStreams.OUT_LOCAL_PLAYBACK] + ) + } + + public override func start(args: [String: Any], completion: @escaping (Bool, String?) -> Void) { + guard let server = server else { completion(false, "server uninitialized"); return } + if working { completion(true, nil); return } + let address = args["address"] as? String + let session = address.flatMap { server.translationSession(for: $0) } ?? server.currentTranslationSession() + guard let s = session else { + emitError(-200, "no connected device") + completion(false, "no connected device"); return + } + let sr = (args["sampleRate"] as? NSNumber)?.intValue ?? 16000 + sampleRate = sr + self.session = s + + s.acquire(owner: self) { [weak self] tm, errMsg in + guard let self = self else { return } + guard let tm = tm else { + self.emitError(-201, errMsg) + completion(false, errMsg); return + } + tm.recordtype = .byPhone + let mode = Self.buildMode(modeType: .audioTranslate, audioType: .PCM, channel: 1, sampleRate: sr) + tm.trStartTranslate(mode) { [weak self] status, err in + guard let self = self else { return } + if status == .success || status == .sameMode { + self.working = true + completion(true, nil) + } else { + self.emitError(Int(status.rawValue), err?.localizedDescription) + self.stop() + completion(false, "tm status=\(status.rawValue)") + } + } + } + } + + public override func stop() { + guard let s = session, working else { working = false; return } + working = false + s.release(owner: self) + emitLog("AudioTranslation stop") + } + + /// 与 Android `AudioTranslationModeHandler.feedFilePcm` 对齐:通过 `trWriteAudio`+ + /// `sourceType = .typeFile` 把外部解出的 PCM 灌入翻译通路。 + public func feedFilePcm(_ pcm: Data, sampleRate: Int) -> Bool { + guard working, let tm = session?.currentManager() else { return false } + let audio = JLTranslateAudio() + audio.sourceType = .typeFile + audio.audioType = .PCM + audio.data = pcm + audio.len = Int32(pcm.count) + audio.count = 1 + tm.trWriteAudioV2(audio, translate: pcm) + return true + } + + public override func onReceiveAudio(_ audio: JLTranslateAudio) { + guard working else { return } + // 音频翻译模式 SDK 把译后 PCM 反推到本地播放:作为 `out.localPlayback` 输出 + if audio.audioType == .PCM { + pushFrame(streamId: TranslationStreams.OUT_LOCAL_PLAYBACK, pcm: audio.data, + format: AudioFormat(sampleRate: sampleRate, channels: 1, bitsPerSample: 16)) + } + } +} + +/// MODE_FACE_TO_FACE_TRANSLATION(mode=5)—— 面对面翻译,双麦阵列。 +public final class FaceToFaceTranslationModeHandler: BaseTranslationModeHandler { + + public init(server: JieliHomeServer) { + super.init( + server: server, + modeId: TranslationModeIds.MODE_FACE_TO_FACE_TRANSLATION, + inputs: [TranslationStreams.IN_MIC], + outputs: [TranslationStreams.OUT_LOCAL_PLAYBACK] + ) + } + + public override func start(args: [String: Any], completion: @escaping (Bool, String?) -> Void) { + guard let server = server else { completion(false, "server uninitialized"); return } + if working { completion(true, nil); return } + let address = args["address"] as? String + let session = address.flatMap { server.translationSession(for: $0) } ?? server.currentTranslationSession() + guard let s = session else { + emitError(-200, "no connected device") + completion(false, "no connected device"); return + } + let sr = (args["sampleRate"] as? NSNumber)?.intValue ?? 16000 + self.session = s + + s.acquire(owner: self) { [weak self] tm, errMsg in + guard let self = self else { return } + guard let tm = tm else { + self.emitError(-201, errMsg) + completion(false, errMsg); return + } + tm.recordtype = .byDevice + let mode = Self.buildMode(modeType: .faceToFaceTranslate, audioType: .OPUS, channel: 1, sampleRate: sr) + tm.trStartTranslate(mode) { [weak self] status, err in + guard let self = self else { return } + if status == .success || status == .sameMode { + self.working = true + completion(true, nil) + } else { + self.emitError(Int(status.rawValue), err?.localizedDescription) + self.stop() + completion(false, "tm status=\(status.rawValue)") + } + } + } + } + + public override func stop() { + guard let s = session, working else { working = false; return } + working = false + s.release(owner: self) + emitLog("FaceToFaceTranslation stop") + } + + public override func onReceiveAudio(_ audio: JLTranslateAudio) { + guard working else { return } + let encoding: String + switch audio.audioType { + case .PCM: encoding = "pcm16" + case .OPUS: encoding = "opus" + case .SPEEX: encoding = "speex" + case .JLA_V2: encoding = "jla_v2" + case .MSBC: encoding = "msbc" + default: encoding = "unknown" + } + pushFrame(streamId: TranslationStreams.IN_MIC, pcm: audio.data, encoding: encoding) + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/mode/CallTranslationModeHandler.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/mode/CallTranslationModeHandler.swift new file mode 100644 index 000000000..3d7fffa79 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/mode/CallTranslationModeHandler.swift @@ -0,0 +1,255 @@ +import Foundation +import JL_BLEKit + +/// MODE_CALL_TRANSLATION(mode=3)—— 通话翻译,eSCO 上下行各一路单声道。 +/// +/// 与 Android `CallTranslationModeHandler` 对齐: +/// - 输入:`in.uplink`(本机用户麦克风) + `in.downlink`(对端通话音) +/// - 输出:`out.uplink`(译文给对端) + `out.downlink`(译文给本机) +/// - 默认 OPUS 编码、`byDevice` 录音策略(设备始终录音、APP 不开手机麦) +/// +/// 上行:耳机推 OPUS → `OpusStreamDecoder` 解码 → 按 sourceType 分流 (`in.uplink` / `in.downlink`) +/// 下行:Dart 喂 PCM → `OpusStreamEncoder` 编码 → `JLTranslationManager.trWriteAudio` 回耳机 +public final class CallTranslationModeHandler: BaseTranslationModeHandler { + + private var decoder: OpusStreamDecoder? + private var encoderUplink: OpusStreamEncoder? + private var encoderDownlink: OpusStreamEncoder? + private var pendingAudioSource: JLTranslateAudioSourceType = .typeESCOUp + private var sampleRate = 16000 + private var audioType: JL_SpeakDataType = .OPUS + private var rxFrameCount: Int64 = 0 + private var rxUplinkBytes: Int64 = 0 + private var rxDownlinkBytes: Int64 = 0 + private var heartbeatTimer: DispatchSourceTimer? + private let heartbeatQueue = DispatchQueue(label: "calltrans.heartbeat", qos: .utility) + + public init(server: JieliHomeServer) { + super.init( + server: server, + modeId: TranslationModeIds.MODE_CALL_TRANSLATION, + inputs: [TranslationStreams.IN_UPLINK, TranslationStreams.IN_DOWNLINK], + outputs: [TranslationStreams.OUT_UPLINK, TranslationStreams.OUT_DOWNLINK] + ) + } + + public override func start(args: [String: Any], completion: @escaping (Bool, String?) -> Void) { + print("[CallTrans] start ENTRY args=\(args)") + guard let server = server else { + print("[CallTrans] start FAIL server uninitialized") + completion(false, "server uninitialized"); return + } + if working { + print("[CallTrans] start already working, returning ok") + completion(true, nil); return + } + + let address = args["address"] as? String + let session: TranslationSession? = { + if let addr = address { return server.translationSession(for: addr) } + return server.currentTranslationSession() + }() + guard let s = session else { + print("[CallTrans] start FAIL no connected device") + emitError(-200, "no connected device; pass args.address or connect first") + completion(false, "no connected device"); return + } + print("[CallTrans] start session resolved uuid=\(s.uuid) isBusy=\(s.isBusy)") + + let sr = (args["sampleRate"] as? NSNumber)?.intValue ?? 16000 + let aType = Self.parseAudioType(args["audioType"]) ?? .OPUS + let recordType = Self.parseRecordType(args["strategy"], default: .byDevice) + print("[CallTrans] start params sr=\(sr) audioType=\(aType.rawValue) recordType=\(recordType.rawValue)") + + sampleRate = sr + audioType = aType + self.session = s + + // 解码 / 编码器(仅 OPUS 模式需要) + if aType == .OPUS { + decoder = OpusStreamDecoder(channels: 1, dataSize: 40, sampleRate: sr) { [weak self] pcm in + guard let self = self else { return } + let stream = self.pendingAudioSource == .typeESCODown ? + TranslationStreams.IN_DOWNLINK : TranslationStreams.IN_UPLINK + self.pushFrame(streamId: stream, pcm: pcm, format: AudioFormat(sampleRate: sr, channels: 1, bitsPerSample: 16)) + } onError: { [weak self] code, msg in + self?.emitError(code, "opus decode: \(msg ?? "")") + } + encoderUplink = OpusStreamEncoder(channels: 1, sampleRate: sr) { [weak self] opus in + self?.writeBackAudio(opus, source: .typeESCOUp) + } onError: { [weak self] code, msg in + self?.emitError(code, "opus encode uplink: \(msg ?? "")") + } + encoderDownlink = OpusStreamEncoder(channels: 1, sampleRate: sr) { [weak self] opus in + self?.writeBackAudio(opus, source: .typeESCODown) + } onError: { [weak self] code, msg in + self?.emitError(code, "opus encode downlink: \(msg ?? "")") + } + } + + s.acquire(owner: self) { [weak self] tm, errMsg in + guard let self = self else { return } + guard let tm = tm else { + print("[CallTrans] acquire FAIL err=\(errMsg ?? "nil")") + self.emitError(-201, errMsg) + completion(false, errMsg); return + } + print("[CallTrans] acquire OK, applying recordtype=\(recordType.rawValue) " + + "isSupportTranslate=\(tm.trIsSupportTranslate()) isPlayWithA2dp=\(tm.trIsPlayWithA2dp()) " + + "isWorking=\(tm.trIsWorking()) isCalling=\(tm.isCalling)") + tm.recordtype = recordType + let mode = Self.buildMode(modeType: .callTranslate, audioType: aType, channel: 1, sampleRate: sr) + self.emitLog("CallTranslation start audioType=\(aType.rawValue) sr=\(sr) recordType=\(recordType.rawValue)") + print("[CallTrans] calling trStartTranslateMode mode=callTranslate(0x03) dataType=\(aType.rawValue) ch=1 sr=\(sr)") + tm.trStartTranslate(mode) { [weak self] status, err in + guard let self = self else { return } + print("[CallTrans] trStartTranslateMode completion status=\(status.rawValue) err=\(err?.localizedDescription ?? "nil")") + if status == .success || status == .sameMode { + self.working = true + NSLog("[CallTrans] ✅ 通话翻译启动成功 (status=\(status.rawValue) mode=3 sr=\(sr) audioType=\(aType.rawValue) recordType=\(recordType.rawValue)) — 等待耳机上行音频...") + self.startHeartbeat() + completion(true, nil) + } else { + NSLog("[CallTrans] ❌ 通话翻译启动失败 status=\(status.rawValue) err=\(err?.localizedDescription ?? "nil")") + self.emitError(Int(status.rawValue), err?.localizedDescription ?? "trStartTranslateMode failed") + self.stop() + completion(false, "tm status=\(status.rawValue)") + } + } + } + } + + public override func stop() { + stopHeartbeat() + guard let s = session, working else { working = false; return } + working = false + s.release(owner: self) + decoder?.release(); decoder = nil + encoderUplink?.release(); encoderUplink = nil + encoderDownlink?.release(); encoderDownlink = nil + NSLog("[CallTrans] 🛑 CallTranslation stop (totalRxFrames=\(rxFrameCount) uplinkBytes=\(rxUplinkBytes) downlinkBytes=\(rxDownlinkBytes))") + emitLog("CallTranslation stop") + } + + // MARK: - Heartbeat (每 2 秒打一次"是否收到上行音频"心跳) + + private func startHeartbeat() { + stopHeartbeat() + rxFrameCount = 0 + rxUplinkBytes = 0 + rxDownlinkBytes = 0 + let t = DispatchSource.makeTimerSource(queue: heartbeatQueue) + t.schedule(deadline: .now() + 2, repeating: 2.0) + t.setEventHandler { [weak self] in + guard let self = self, self.working else { return } + if self.rxFrameCount == 0 { + NSLog("[CallTrans/Heartbeat] ⚠️ 通话翻译运行中,但耳机上行音频 0 帧 — SDK 尚未回调 onReceiveAudioData。请确认设备是否处于真实通话/录音状态。") + } else { + NSLog("[CallTrans/Heartbeat] ✓ rx=\(self.rxFrameCount) 帧 uplinkBytes=\(self.rxUplinkBytes) downlinkBytes=\(self.rxDownlinkBytes)") + } + } + heartbeatTimer = t + t.resume() + } + + private func stopHeartbeat() { + heartbeatTimer?.cancel() + heartbeatTimer = nil + } + + // MARK: - Audio upstream (SDK → APP) + + public override func onReceiveAudio(_ audio: JLTranslateAudio) { + rxFrameCount &+= 1 + // 诊断日志:前 5 帧每帧都打,之后每 100 帧打一次。同时打到 NSLog 和 emitLog。 + if rxFrameCount <= 5 || rxFrameCount % 100 == 1 { + let msg = "[CallTrans/onRx] #\(rxFrameCount) working=\(working) " + + "source=\(audio.sourceType.rawValue) audioType=\(audio.audioType.rawValue) " + + "len=\(audio.data.count) count=\(audio.count)" + NSLog("%@", msg) + emitLog(msg) + } + guard working else { return } + let stream: String + switch audio.sourceType { + case .typeESCOUp, .typeDeviceMic, .typePhoneMic: + stream = TranslationStreams.IN_UPLINK + rxUplinkBytes &+= Int64(audio.data.count) + case .typeESCODown: + stream = TranslationStreams.IN_DOWNLINK + rxDownlinkBytes &+= Int64(audio.data.count) + default: + if rxFrameCount <= 5 || rxFrameCount % 100 == 1 { + let msg = "[CallTrans/onRx] drop unhandled sourceType=\(audio.sourceType.rawValue)" + NSLog("%@", msg) + emitLog(msg) + } + return + } + pendingAudioSource = audio.sourceType + + switch audio.audioType { + case .PCM: + pushFrame(streamId: stream, pcm: audio.data, + format: AudioFormat(sampleRate: sampleRate, channels: 1, bitsPerSample: 16)) + case .OPUS: + decoder?.feed(audio.data) + default: + // Speex / JLA_V2 / MSBC 等先原样透传,并标注 encoding + let encoding: String = { + switch audio.audioType { + case .SPEEX: return "speex" + case .JLA_V2: return "jla_v2" + case .MSBC: return "msbc" + default: return "unknown" + } + }() + pushFrame(streamId: stream, pcm: audio.data, + format: AudioFormat(sampleRate: sampleRate, channels: 1, bitsPerSample: 16), + encoding: encoding) + } + } + + // MARK: - TTS downstream (APP → SDK) + + public override func onTranslatedAudio(streamId: String, pcm: Data, format: AudioFormat, isFinal: Bool) -> Bool { + guard working else { return false } + switch streamId { + case TranslationStreams.OUT_UPLINK: + return feed(pcm: pcm, encoderTo: encoderUplink, source: .typeESCOUp) + case TranslationStreams.OUT_DOWNLINK: + return feed(pcm: pcm, encoderTo: encoderDownlink, source: .typeESCODown) + default: + return false + } + } + + private func feed(pcm: Data, encoderTo encoder: OpusStreamEncoder?, source: JLTranslateAudioSourceType) -> Bool { + if audioType == .OPUS { + // 拆 640 字节一帧(16k/16bit/mono/20ms)后送编码器 + let frameBytes = 640 + var ofs = 0 + while ofs < pcm.count { + let len = min(frameBytes, pcm.count - ofs) + encoder?.feed(pcm.subdata(in: ofs..<(ofs + len))) + ofs += len + } + return true + } else { + // 直接 PCM 回写 + writeBackAudio(pcm, source: source, audioType: .PCM) + return true + } + } + + private func writeBackAudio(_ data: Data, source: JLTranslateAudioSourceType, audioType: JL_SpeakDataType? = nil) { + guard let tm = session?.currentManager() else { return } + let audio = JLTranslateAudio() + audio.sourceType = source + audio.audioType = audioType ?? self.audioType + audio.data = data + audio.len = Int32(data.count) + audio.count = 1 + tm.trWriteAudioV2(audio, translate: data) + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/mode/RecordModeHandler.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/mode/RecordModeHandler.swift new file mode 100644 index 000000000..5e6670c7a --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/mode/RecordModeHandler.swift @@ -0,0 +1,158 @@ +import Foundation +import JL_BLEKit + +/// MODE_RECORD(mode=1)—— 单向录音翻译。 +/// 与 Android `RecordModeHandler` 对齐: +/// - 通过 SDK 进入 RECORD 状态机;耳机 LED / 提示音随之切换 +/// - 走 `recordtype=.byPhone` 语义(STRATEGY_CUSTOM_RECORDING):APP 端自己采手机麦 +/// - iOS 端 APP 录音用 AVAudioEngine 由宿主自定(Dart 侧已经在管音频采集);本 handler 只做 +/// 翻译模式的状态机切换 + 接耳机上推帧(如果固件配成 byDevice 也支持) +public final class RecordModeHandler: BaseTranslationModeHandler { + + public init(server: JieliHomeServer) { + super.init( + server: server, + modeId: TranslationModeIds.MODE_RECORD, + inputs: [TranslationStreams.IN_MIC], + outputs: [TranslationStreams.OUT_SPEAKER] + ) + } + + public override func start(args: [String: Any], completion: @escaping (Bool, String?) -> Void) { + guard let server = server else { completion(false, "server uninitialized"); return } + if working { completion(true, nil); return } + let address = args["address"] as? String + let session = address.flatMap { server.translationSession(for: $0) } ?? server.currentTranslationSession() + guard let s = session else { + emitError(-200, "no connected device") + completion(false, "no connected device"); return + } + let sr = (args["sampleRate"] as? NSNumber)?.intValue ?? 16000 + let aType = Self.parseAudioType(args["audioType"]) ?? .OPUS + let recordType = Self.parseRecordType(args["strategy"], default: .byPhone) + self.session = s + + s.acquire(owner: self) { [weak self] tm, errMsg in + guard let self = self else { return } + guard let tm = tm else { + self.emitError(-201, errMsg) + completion(false, errMsg); return + } + tm.recordtype = recordType + let mode = Self.buildMode(modeType: .onlyRecord, audioType: aType, channel: 1, sampleRate: sr) + self.emitLog("Record start audioType=\(aType.rawValue) sr=\(sr) recordType=\(recordType.rawValue)") + tm.trStartTranslate(mode) { [weak self] status, err in + guard let self = self else { return } + if status == .success || status == .sameMode { + self.working = true + completion(true, nil) + } else { + self.emitError(Int(status.rawValue), err?.localizedDescription ?? "trStartTranslateMode failed") + self.stop() + completion(false, "tm status=\(status.rawValue)") + } + } + } + } + + public override func stop() { + guard let s = session, working else { working = false; return } + working = false + s.release(owner: self) + emitLog("Record stop") + } + + public override func onReceiveAudio(_ audio: JLTranslateAudio) { + // STRATEGY_CUSTOM_RECORDING 语义下耳机不上推;如果固件配成 byDevice 也直接外抛上行帧 + guard working else { return } + switch audio.audioType { + case .PCM: + pushFrame(streamId: TranslationStreams.IN_MIC, pcm: audio.data) + default: + // 其他编码(OPUS 等)原样透传 + encoding 标注,避免内嵌解码器误用 + let encoding: String = { + switch audio.audioType { + case .OPUS: return "opus" + case .SPEEX: return "speex" + case .JLA_V2: return "jla_v2" + case .MSBC: return "msbc" + default: return "unknown" + } + }() + pushFrame(streamId: TranslationStreams.IN_MIC, pcm: audio.data, encoding: encoding) + } + } + + public override func onTranslatedAudio(streamId: String, pcm: Data, format: AudioFormat, isFinal: Bool) -> Bool { + // 与 Android 一致:RECORD 模式下耳机不参与回送 TTS;宿主自己用音频播放器播 + if streamId == TranslationStreams.OUT_SPEAKER { + emitLog("recv tts pcm=\(pcm.count)B final=\(isFinal) (host-side playback)") + return true + } + return false + } +} + +/// MODE_RECORDING_TRANSLATION(mode=2)—— 流式录音翻译。与 RecordModeHandler 仅 modeType 不同。 +public final class RecordingTranslationModeHandler: BaseTranslationModeHandler { + + public init(server: JieliHomeServer) { + super.init( + server: server, + modeId: TranslationModeIds.MODE_RECORDING_TRANSLATION, + inputs: [TranslationStreams.IN_MIC], + outputs: [TranslationStreams.OUT_SPEAKER] + ) + } + + public override func start(args: [String: Any], completion: @escaping (Bool, String?) -> Void) { + guard let server = server else { completion(false, "server uninitialized"); return } + if working { completion(true, nil); return } + let address = args["address"] as? String + let session = address.flatMap { server.translationSession(for: $0) } ?? server.currentTranslationSession() + guard let s = session else { + emitError(-200, "no connected device") + completion(false, "no connected device"); return + } + let sr = (args["sampleRate"] as? NSNumber)?.intValue ?? 16000 + let aType = Self.parseAudioType(args["audioType"]) ?? .OPUS + let recordType = Self.parseRecordType(args["strategy"], default: .byPhone) + self.session = s + + s.acquire(owner: self) { [weak self] tm, errMsg in + guard let self = self else { return } + guard let tm = tm else { + self.emitError(-201, errMsg) + completion(false, errMsg); return + } + tm.recordtype = recordType + let mode = Self.buildMode(modeType: .recordTranslate, audioType: aType, channel: 1, sampleRate: sr) + self.emitLog("RecordingTranslation start audioType=\(aType.rawValue) sr=\(sr) recordType=\(recordType.rawValue)") + tm.trStartTranslate(mode) { [weak self] status, err in + guard let self = self else { return } + if status == .success || status == .sameMode { + self.working = true + completion(true, nil) + } else { + self.emitError(Int(status.rawValue), err?.localizedDescription ?? "trStartTranslateMode failed") + self.stop() + completion(false, "tm status=\(status.rawValue)") + } + } + } + } + + public override func stop() { + guard let s = session, working else { working = false; return } + working = false + s.release(owner: self) + emitLog("RecordingTranslation stop") + } + + public override func onReceiveAudio(_ audio: JLTranslateAudio) { + guard working else { return } + if audio.audioType == .PCM { + pushFrame(streamId: TranslationStreams.IN_MIC, pcm: audio.data) + } + } +} diff --git a/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/mode/StereoCallTranslationModeHandler.swift b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/mode/StereoCallTranslationModeHandler.swift new file mode 100644 index 000000000..d2712eff2 --- /dev/null +++ b/local_plugins/device_jieli/ios/device_jieli/Sources/device_jieli/feature/translation/mode/StereoCallTranslationModeHandler.swift @@ -0,0 +1,140 @@ +import Foundation +import JL_BLEKit + +/// MODE_CALL_TRANSLATION_WITH_STEREO(mode=6)—— 立体声通话翻译。 +/// +/// 与 Android `StereoCallTranslationModeHandler` 对齐: +/// - 耳机一路推 SOURCE_E_SCO_MIX 立体声 OPUS:L=本端 uplink, R=对端 downlink +/// - 软件分声道后等价于 [CallTranslationModeHandler] +public final class StereoCallTranslationModeHandler: BaseTranslationModeHandler { + + private var decoder: OpusStreamDecoder? + private var encoderUplink: OpusStreamEncoder? + private var encoderDownlink: OpusStreamEncoder? + private var sampleRate = 16000 + private var audioType: JL_SpeakDataType = .OPUS + + public init(server: JieliHomeServer) { + super.init( + server: server, + modeId: TranslationModeIds.MODE_CALL_TRANSLATION_WITH_STEREO, + inputs: [TranslationStreams.IN_UPLINK, TranslationStreams.IN_DOWNLINK], + outputs: [TranslationStreams.OUT_UPLINK, TranslationStreams.OUT_DOWNLINK] + ) + } + + public override func start(args: [String: Any], completion: @escaping (Bool, String?) -> Void) { + guard let server = server else { completion(false, "server uninitialized"); return } + if working { completion(true, nil); return } + let address = args["address"] as? String + let session: TranslationSession? = address.flatMap { server.translationSession(for: $0) } ?? server.currentTranslationSession() + guard let s = session else { + emitError(-200, "no connected device") + completion(false, "no connected device"); return + } + let sr = (args["sampleRate"] as? NSNumber)?.intValue ?? 16000 + let aType = Self.parseAudioType(args["audioType"]) ?? .OPUS + let recordType = Self.parseRecordType(args["strategy"], default: .byDevice) + sampleRate = sr + audioType = aType + self.session = s + + if aType == .OPUS { + decoder = OpusStreamDecoder(channels: 2, dataSize: 80, sampleRate: sr) { [weak self] stereoPcm in + guard let self = self else { return } + let (l, r) = PcmKit.splitStereo16(stereoPcm) + let fmt = AudioFormat(sampleRate: sr, channels: 1, bitsPerSample: 16) + self.pushFrame(streamId: TranslationStreams.IN_UPLINK, pcm: l, format: fmt) + self.pushFrame(streamId: TranslationStreams.IN_DOWNLINK, pcm: r, format: fmt) + } onError: { [weak self] code, msg in + self?.emitError(code, "opus stereo decode: \(msg ?? "")") + } + encoderUplink = OpusStreamEncoder(channels: 1, sampleRate: sr) { [weak self] opus in + self?.writeBack(opus, source: .typeESCOUp) + } + encoderDownlink = OpusStreamEncoder(channels: 1, sampleRate: sr) { [weak self] opus in + self?.writeBack(opus, source: .typeESCODown) + } + } + + s.acquire(owner: self) { [weak self] tm, errMsg in + guard let self = self else { return } + guard let tm = tm else { + self.emitError(-201, errMsg) + completion(false, errMsg); return + } + tm.recordtype = recordType + let mode = Self.buildMode(modeType: .callTranslateStereo, audioType: aType, channel: 2, sampleRate: sr) + self.emitLog("StereoCallTranslation start audioType=\(aType.rawValue) sr=\(sr) recordType=\(recordType.rawValue)") + tm.trStartTranslate(mode) { [weak self] status, err in + guard let self = self else { return } + if status == .success || status == .sameMode { + self.working = true + completion(true, nil) + } else { + self.emitError(Int(status.rawValue), err?.localizedDescription ?? "trStartTranslateMode failed") + self.stop() + completion(false, "tm status=\(status.rawValue)") + } + } + } + } + + public override func stop() { + guard let s = session, working else { working = false; return } + working = false + s.release(owner: self) + decoder?.release(); decoder = nil + encoderUplink?.release(); encoderUplink = nil + encoderDownlink?.release(); encoderDownlink = nil + emitLog("StereoCallTranslation stop") + } + + public override func onReceiveAudio(_ audio: JLTranslateAudio) { + guard working else { return } + guard audio.sourceType == .typeESCOMax else { return } // SOURCE_E_SCO_MIX + switch audio.audioType { + case .OPUS: decoder?.feed(audio.data) + case .PCM: + let (l, r) = PcmKit.splitStereo16(audio.data) + let fmt = AudioFormat(sampleRate: sampleRate, channels: 1, bitsPerSample: 16) + pushFrame(streamId: TranslationStreams.IN_UPLINK, pcm: l, format: fmt) + pushFrame(streamId: TranslationStreams.IN_DOWNLINK, pcm: r, format: fmt) + default: break + } + } + + public override func onTranslatedAudio(streamId: String, pcm: Data, format: AudioFormat, isFinal: Bool) -> Bool { + guard working else { return false } + let encoder: OpusStreamEncoder? + let source: JLTranslateAudioSourceType + switch streamId { + case TranslationStreams.OUT_UPLINK: encoder = encoderUplink; source = .typeESCOUp + case TranslationStreams.OUT_DOWNLINK: encoder = encoderDownlink; source = .typeESCODown + default: return false + } + if audioType == .OPUS { + let frameBytes = 640 + var ofs = 0 + while ofs < pcm.count { + let len = min(frameBytes, pcm.count - ofs) + encoder?.feed(pcm.subdata(in: ofs..<(ofs + len))) + ofs += len + } + } else { + writeBack(pcm, source: source, audioType: .PCM) + } + return true + } + + private func writeBack(_ data: Data, source: JLTranslateAudioSourceType, audioType: JL_SpeakDataType? = nil) { + guard let tm = session?.currentManager() else { return } + let audio = JLTranslateAudio() + audio.sourceType = source + audio.audioType = audioType ?? self.audioType + audio.data = data + audio.len = Int32(data.count) + audio.count = 1 + tm.trWriteAudioV2(audio, translate: data) + } +} diff --git a/local_plugins/device_jieli/pubspec.yaml b/local_plugins/device_jieli/pubspec.yaml index 5065b209a..b82ad95f5 100644 --- a/local_plugins/device_jieli/pubspec.yaml +++ b/local_plugins/device_jieli/pubspec.yaml @@ -42,6 +42,7 @@ flutter: pluginClass: JielihomePlugin ios: pluginClass: JielihomePlugin + sharedDarwinSource: false # To add assets to your plugin package, add an assets section, like this: # assets: diff --git a/pubspec.yaml b/pubspec.yaml index d233090cc..f6d326383 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -97,8 +97,10 @@ dependencies: path: local_plugins/open_ai_service chat_storage: path: local_plugins/chat_storage - spotify_sdk: - path: local_plugins/spotify_sdk + # 暂时关闭 —— spotify_sdk 引入 SpotifyiOS framework,没用上反而拖累 simulator + # 构建(缺 arm64-simulator slice)。需要时取消下面两行注释。 + # spotify_sdk: + # path: local_plugins/spotify_sdk device_jieli: path: local_plugins/device_jieli classic_bluetooth: