Browse Source

Merge branch 'liwei_jielisdk_ios' of https://github.com/deepcloud2048/deep_voice into liwei_jielisdk

weicu
liwei1dao 5 months ago
parent
commit
117278e6a8
  1. 39
      ios/Podfile
  2. 6
      ios/Runner.xcodeproj/project.pbxproj
  3. 57
      lib/data/services/ble_manager.dart
  4. 2
      lib/data/services/spotify_service.dart
  5. 6
      lib/modules/agent/controllers/agent_controller.dart
  6. 6
      lib/modules/devices/controllers/devices_controller.dart
  7. 108
      lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart
  8. 8
      lib/modules/translation/controllers/translation_controller.dart
  9. 2
      local_plugins/agent_service/ios/agent_service/Package.swift
  10. 35
      local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServicePlugin.swift
  11. 174
      local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift
  12. 25
      local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureTtsHelper.swift
  13. 64
      local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/DoubaoE2ETranslateHelper.swift
  14. 48
      local_plugins/azure_speech/ios/azure_speech/Sources/tools/SimpleAudioReceiver.swift
  15. 11
      local_plugins/ble_service/ios/ble_service/Package.swift
  16. 214
      local_plugins/ble_service/ios/ble_service/Sources/ble_service/SwiftOpusAudioProcessor.swift
  17. 6
      local_plugins/chat_api/ios/chat_api/Package.swift
  18. 84
      local_plugins/device_jieli/CHANGELOG.md
  19. 47
      local_plugins/device_jieli/README.md
  20. 44
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/Info.plist
  21. 26
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLAudioUnitKit.h
  22. 58
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLAudioUnitPlayer.h
  23. 66
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusDecoder.h
  24. 83
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusEncodeConfig.h
  25. 69
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusEncoder.h
  26. 59
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusFormat.h
  27. 67
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLOpusToOgg.h
  28. 55
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLPcmToWav.h
  29. 51
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLPcmToWtg.h
  30. 54
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Headers/JLSpeexDecoder.h
  31. BIN
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Info.plist
  32. BIN
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/JLAudioUnitKit
  33. 6
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Modules/module.modulemap
  34. 52
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/readme.txt
  35. 26
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLAudioUnitKit.h
  36. 58
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLAudioUnitPlayer.h
  37. 66
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusDecoder.h
  38. 83
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusEncodeConfig.h
  39. 69
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusEncoder.h
  40. 59
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusFormat.h
  41. 67
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLOpusToOgg.h
  42. 55
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLPcmToWav.h
  43. 51
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLPcmToWtg.h
  44. 54
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Headers/JLSpeexDecoder.h
  45. BIN
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Info.plist
  46. BIN
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/JLAudioUnitKit
  47. 6
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Modules/module.modulemap
  48. 234
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/_CodeSignature/CodeResources
  49. 52
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/readme.txt
  50. 59
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/Info.plist
  51. 95
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLBmpConvert.h
  52. 24
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLBmpConvertKit.h
  53. 89
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLBmpConvertOption.h
  54. 77
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLGifBin.h
  55. 34
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Headers/JLImageConvertResult.h
  56. BIN
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Info.plist
  57. BIN
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/JLBmpConvertKit
  58. 6
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Modules/module.modulemap
  59. 95
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLBmpConvert.h
  60. 24
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLBmpConvertKit.h
  61. 89
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLBmpConvertOption.h
  62. 77
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLGifBin.h
  63. 34
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Headers/JLImageConvertResult.h
  64. BIN
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Info.plist
  65. BIN
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/JLBmpConvertKit
  66. 6
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Modules/module.modulemap
  67. 192
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/_CodeSignature/CodeResources
  68. 1
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Headers
  69. 1
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/JLBmpConvertKit
  70. 1
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Modules
  71. 1
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Resources
  72. 95
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/A/Headers/JLBmpConvert.h
  73. 24
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/A/Headers/JLBmpConvertKit.h
  74. 89
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/A/Headers/JLBmpConvertOption.h
  75. 77
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/A/Headers/JLGifBin.h
  76. 34
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/A/Headers/JLImageConvertResult.h
  77. BIN
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/A/JLBmpConvertKit
  78. 6
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/A/Modules/module.modulemap
  79. 48
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/A/Resources/Info.plist
  80. 1
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/Current
  81. 44
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/Info.plist
  82. 127
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/DialManager.h
  83. 92
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/FatfsObject.h
  84. 27
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/JLDialSourceModel.h
  85. 30
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/JLDialUnit.h
  86. 115
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/JLDialUnitMgr.h
  87. 94
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/JLSourceUpdate.h
  88. 131
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZZArchive.h
  89. 239
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZZArchiveEntry.h
  90. 79
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZZConstants.h
  91. 136
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZZError.h
  92. 45
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Headers/ZipZap.h
  93. BIN
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Info.plist
  94. BIN
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/JLDialUnit
  95. 6
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Modules/module.modulemap
  96. 127
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/DialManager.h
  97. 92
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/FatfsObject.h
  98. 27
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/JLDialSourceModel.h
  99. 30
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/JLDialUnit.h
  100. 115
      local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64_x86_64-simulator/JLDialUnit.framework/Headers/JLDialUnitMgr.h

39
ios/Podfile

@ -40,6 +40,45 @@ target 'Runner' do
end
post_install do |installer|
# ---- Auto patch: bump FlutterGeneratedPluginSwiftPackage iOS platform ----
# Flutter 生成的中介包总是写死 .iOS("12.0"),与 Podfile 中的
# platform :ios, '16.0' 不一致,会报
# "requires minimum platform version 15.0/16.0 ... but this target supports 12.0"。
# 这里自动同步为 16.0。
generated_pkg = File.expand_path(
'Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage/Package.swift', __dir__)
if File.exist?(generated_pkg)
content = File.read(generated_pkg)
new_content = content.gsub(/\.iOS\("\d+\.\d+"\)/, '.iOS("16.0")')
if new_content != content
File.write(generated_pkg, new_content)
puts '[Podfile patch] Bumped FlutterGeneratedPluginSwiftPackage iOS platform to 16.0'
end
end
# ---- Auto patch: comment out illegal `<netinet6/in6.h>` imports in all Pods sources ----
# 新版 Xcode 模块校验禁止跨模块引用私有头 `<netinet6/in6.h>`
# `<netinet/in.h>` 已包含全部 IPv6 定义,注释掉即可。
pods_dir = File.expand_path('Pods', __dir__)
if Dir.exist?(pods_dir)
patched_files = []
Dir.glob(File.join(pods_dir, '**', '*.{m,mm,h,hpp,c,cpp}')).each do |file|
content = File.read(file)
new_content = content.gsub(
/^[ \t]*#\s*(?:import|include)\s*<netinet6\/in6\.h>.*$/,
'// \0 // patched: private header, covered by <netinet/in.h>'
)
if new_content != content
File.write(file, new_content)
patched_files << file.sub(pods_dir + '/', '')
end
end
unless patched_files.empty?
puts "[Podfile patch] Commented out <netinet6/in6.h> imports in #{patched_files.size} file(s):"
patched_files.each { |f| puts " - #{f}" }
end
end
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)

6
ios/Runner.xcodeproj/project.pbxproj

@ -251,7 +251,7 @@
);
mainGroup = 97C146E51CF9000F007C117D;
packageReferences = (
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */,
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
);
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
@ -506,7 +506,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 537SFY28XS;
DEVELOPMENT_TEAM = K39FB49XKY;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 16.6;
@ -781,7 +781,7 @@
/* End XCConfigurationList section */
/* Begin XCLocalSwiftPackageReference section */
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = {
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
isa = XCLocalSwiftPackageReference;
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
};

57
lib/data/services/ble_manager.dart

@ -223,20 +223,28 @@ class BleManager extends GetxService {
bool isFinal = false,
}) async {
// 段尾帧(pcm 为空 + isFinal=true)必须放行,让 native 触发立即整段下发;
// 仅对"普通空帧(非段尾)"短路。
// 仅对“普通空帧(非段尾)”短路。
if (pcm.isEmpty && !isFinal) return true;
final upper = leg.toUpperCase();
final streamId = (upper == 'A' || upper == 'UPLINK')
? 'out.uplink'
: 'out.downlink';
final isFlush = isFinal;
try {
return await Jielihome.instance.feedTranslatedAudio(
final ok = await Jielihome.instance.feedTranslatedAudio(
streamId: streamId,
pcm: pcm,
sampleRate: sampleRate,
channels: channels,
isFinal: isFinal,
);
if (isFlush || !ok) {
// 只在段尾 flush 或 native 返回 false 时打,避免刷屏。
Logger.info(
'[BleManager] feedAstTtsPcm leg=$leg streamId=$streamId pcm=${pcm.length}B isFinal=$isFinal nativeOk=$ok',
);
}
return ok;
} catch (e) {
Logger.w('BleManager', 'feedAstTtsPcm leg=$leg failed: $e');
return false;
@ -265,12 +273,14 @@ class BleManager extends GetxService {
_eventSub?.cancel();
_eventSub = Jielihome.instance.events.listen(
(event) {
// 总开关日志:每一个事件都打一次,便于诊断 native → dart 通道是否畅通。
// 通话翻译 PCM 上推 (TranslationAudioEvent) 是高频事件(20ms 一帧),
// 日志量大且无诊断价值,单独跳过;其它事件保留 runtimeType 打印。
if (event is! TranslationAudioEvent) {
print('[BLE] [EVT] ${event.runtimeType}');
// TranslationAudioEvent 频率极高(10ms/帧),最先拦截并单独走节流日志处理器,
// 避免进入下面的 print / if‑链路经引发刷屏。
if (event is TranslationAudioEvent) {
_handleTranslationAudio(event);
return;
}
// 总开关日志:每一个事件都打一次,便于诊断 native → dart 通道是否畅通
print('[BLE] [EVT] ${event.runtimeType}');
if (event is AdapterStatusEvent) {
print('[BLE] [EVT] 适配器状态 enabled=${event.enabled} hasBle=${event.hasBle}');
} else if (event is ScanStatusEvent) {
@ -313,8 +323,11 @@ class BleManager extends GetxService {
_isCall.value = event.status != 0;
} else if (event is ExpandFunctionEvent) {
_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}');
}
@ -326,11 +339,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 位)
@ -339,11 +358,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})');
}
}
@ -493,16 +514,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);
}
@ -765,15 +799,16 @@ class BleManager extends GetxService {
}
Future<bool> 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;
}

2
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 {
}
}
}
*/

6
lib/modules/agent/controllers/agent_controller.dart

@ -1755,11 +1755,7 @@ class AgentController extends GetxController with WidgetsBindingObserver {
return;
}
// 必须在 Android 上且已连接杰理耳机
if (!Platform.isAndroid) {
Get.snackbar('tips'.tr, '该功能仅安卓端支持');
return;
}
// 必须已连接杰理耳机;iOS / Android 都走 device_jieli 的 AI 助理通路
final BleManager bleManager = Get.find<BleManager>();
if (!bleManager.isConnected) {
Get.snackbar('tips'.tr, '请先连接耳机');

6
lib/modules/devices/controllers/devices_controller.dart

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'package:device_jieli/device_jieli.dart';
import 'package:flutter/material.dart';
@ -310,6 +311,11 @@ class DevicesController extends GetxController {
}
Future<bool> _ensureBluetoothPermission() async {
// iOS: CoreBluetooth 首次扫描时由系统自动弹出蓝牙权限对话框
// (依赖 Info.plist 的 NSBluetoothAlwaysUsageDescription),
// permission_handler 的 bluetoothScan/bluetoothConnect 是 Android 专属,
// 在 iOS 上调用永远返回 denied,会误拦截扫描。
if (Platform.isIOS) return true;
try {
final results = await [
Permission.bluetoothScan,

108
lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart

@ -1,9 +1,11 @@
import 'dart:async';
import 'package:classic_bluetooth/classic_bluetooth.dart';
import 'package:device_jieli/device_jieli.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
// import 'package:logger/web.dart';
import 'package:get_storage/get_storage.dart';
import 'package:ota/ota.dart';
import '../../../data/services/ble_manager.dart';
import 'ota_core_contropller.dart';
import 'ota_download_contropller.dart';
@ -18,7 +20,8 @@ class OtaUpgradeController extends GetxController {
final _downloadService = OtaDownloadService();
final _uiService = OtaUpgradePopUp();
final _bleManager = Get.find<BleManager>();
final _jlota = Ota.instance;
final _jlota = Jielihome.instance;
StreamSubscription<JieliEvent>? _otaEventSub;
// 状态管理
final RxString errorMessage = ''.obs;
@ -52,7 +55,9 @@ class OtaUpgradeController extends GetxController {
@override
void onClose() {
_jlota.onOtaClose();
_jlota.otaCancel();
_otaEventSub?.cancel();
_otaEventSub = null;
_bleManager.setEnterOta(false);
isUpgrade.value = false;
super.onClose();
@ -160,7 +165,8 @@ class OtaUpgradeController extends GetxController {
try {
await _bleManager.setEnterOta(true);
await Future.delayed(const Duration(seconds: 2));
_jlota.init();
// device_jieli 已经在全局 InitialBinding 里 initialize 过;
// 这里无需重复 init —— 直接订阅 OTA 事件流即可。
isDownloading.value = true;
showDetailedStatus.value = true;
@ -194,13 +200,13 @@ class OtaUpgradeController extends GetxController {
detailedStatus.value = 'otaPreparingConnection'.tr;
await Future.delayed(const Duration(milliseconds: 500));
detailedStatus.value = 'otaConnectingDevice'.tr;
_jlota.startUpgrading(address: _address, filePath: filePath);
_jlota.otaStart(address: _address, firmwareFilePath: filePath);
}
/// 停止升级
/// 关闭OTA连接
void stopUpgrading() {
_jlota.onOtaClose();
_jlota.otaCancel();
}
/// 重试连接
@ -218,44 +224,70 @@ class OtaUpgradeController extends GetxController {
}
/// 监听OTA事件
/// 设置OTA事件流的监听器
/// 订阅 device_jieli 的统一事件流,过滤 OTA 相关事件并转发到分发器。
void _listenToOtaEvents() {
_jlota.eventStream.listen((event) {
_handleOtaEvent(event);
});
_otaEventSub?.cancel();
_otaEventSub = _jlota.events.listen(_handleOtaEvent);
}
/// 处理OTA事件
/// [event] OTA事件对象
/// 根据不同的事件类型执行相应的处理逻辑
void _handleOtaEvent(dynamic event) {
switch (event.event) {
case 'onProgress':
if (event.type != null && event.progress != null) {
progress.value = event.progress!;
type.value = event.type!;
/// 处理 device_jieli 抛出的 OTA / 连接事件。
/// - [OtaStateEvent]: 进度 / 状态机推进
/// - [OtaErrorEvent]: 升级失败
/// - [ConnectionStateEvent]: 设备连接 / 断开(OTA 期间用于"中断"指示)
void _handleOtaEvent(JieliEvent event) {
if (event is OtaStateEvent) {
switch (event.state) {
case OtaState.entering:
case OtaState.inquiring:
case OtaState.notifyingSize:
type.value = OtaCoreService.START_UPGRADING;
isUpgrade.value = true;
detailedStatus.value = 'otaUpgradeStarted'.tr;
break;
case OtaState.transferring:
if (event.percent >= 0) {
// device_jieli 发的是 0..100 整数;UI 这边按 0..1 浮点用。
progress.value = event.percent / 100.0;
type.value = OtaCoreService.ONUPGRADE;
isUpgrade.value = true;
detailedStatus.value = 'otaUpgradeProgressWithPercent'
.trParams({'percent': event.percent.toStringAsFixed(1)});
}
break;
case OtaState.verifying:
detailedStatus.value = 'otaUpgradeStarted'.tr;
break;
case OtaState.rebooting:
// 设备重启等待,仍在升级中
isUpgrade.value = true;
detailedStatus.value =
'otaUpgradeProgressWithPercent'.trParams({'percent': (event.progress! * 100).toStringAsFixed(1)});
}
break;
case 'onStartOTA':
type.value = OtaCoreService.START_UPGRADING;
isUpgrade.value = true;
detailedStatus.value = 'otaUpgradeStarted'.tr;
break;
case 'onStopOTA':
_handleUpgradeSuccess();
break;
case 'onError':
_handleUpgradeError(event.code);
break;
case 'NOCONNECTION':
break;
case OtaState.done:
_handleUpgradeSuccess();
break;
case OtaState.failed:
_handleUpgradeError(-1);
break;
case OtaState.cancelled:
isUpgrade.value = false;
showDetailedStatus.value = false;
break;
case OtaState.idle:
break;
}
return;
}
if (event is OtaErrorEvent) {
_handleUpgradeError(event.code);
return;
}
if (event is ConnectionStateEvent) {
if (event.state == ConnectionStateEvent.connectionDisconnect &&
isUpgrade.value) {
_handleConnectionLost();
break;
case 'CONNECTION_SUCCESS':
} else if (event.state == ConnectionStateEvent.connectionOk &&
isUpgrade.value) {
_handleConnectionSuccess();
break;
}
}
}

8
lib/modules/translation/controllers/translation_controller.dart

@ -1318,8 +1318,16 @@ class TranslationController extends GetxController with WidgetsBindingObserver {
},
onError: (e) => Logger.error('[CALL-BRIDGE] in.downlink stream error: $e'),
);
// 诊断:计数 + isFinal 必打,用于定位 “TTS 到底有没有被转发到 native”。
int forwardCnt = 0;
_jieliCallAstTtsSub = _astService.astTtsFrames.listen(
(frame) {
forwardCnt++;
if (frame.isFinal || forwardCnt == 1 || forwardCnt % 50 == 0) {
Logger.info(
'[CALL-BRIDGE] forward TTS leg=${frame.leg} pcm=${frame.pcm.length}B isFinal=${frame.isFinal} (#$forwardCnt)',
);
}
bleManager.feedAstTtsPcm(
leg: frame.leg,
pcm: frame.pcm,

2
local_plugins/agent_service/ios/agent_service/Package.swift

@ -16,7 +16,7 @@ let package = Package(
.package(path: "../../../chat_storage/ios/chat_storage"),
.package(path: "../../../location_service/ios/location_service"),
.package(path: "../../../music_service/ios/music_service"),
.package(path: "../../../music_service/ios/user_portrait"),
.package(path: "../../../user_portrait/ios/user_portrait"),
.package(path: "../../../qq_music/ios/qq_music"),
],
targets: [

35
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":
// 防抖:短时间重复调用直接忽略

174
local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift

@ -59,6 +59,9 @@ import ble_service
case tts
case audioData
case ast
// 通话翻译下行:A/B 两路 TTS PCM 以 {leg, pcm, final} 抩回 Dart,
// 由 translation_controller 转发给 device_jieli 回灌耳机。
case astTts
}
/**
* 通道配置结构体
@ -93,6 +96,11 @@ import ble_service
private var astChannel: FlutterMethodChannel?
private var astEventChannel: FlutterEventChannel?
internal var astEventSink: FlutterEventSink?
// 通话翻译 TTS 回灬事件通道(与 Android 的 ast_tts_events 对齐)
private var astTtsEventChannel: FlutterEventChannel?
internal var astTtsEventSink: FlutterEventSink?
// 通话翻译 TTS 发送计数,节流日志
private var astTtsEmitCount: Int64 = 0
// 双向 AST 服务:A(己方)和 B(对方),与 Android 对齐
private let azureAstHelperA = IntegratedSpeechTranslationService()
private let azureAstHelperB = IntegratedSpeechTranslationService()
@ -128,6 +136,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 +157,68 @@ import ble_service
return Data(count: data.count)
}
/// 把杰理 SCO 上行 PCM(己方麦克风)按当前 provider 推到 A 路 AST。
fileprivate func pushAstAudioToA(_ data: Data) {
astAudioPushCountA &+= 1
if astAudioPushCountA % 100 == 1 {
let rms = Self.rmsOfPCM16LE(data)
let head = Self.hexPreview(data, 16)
os_log("pushAstAudioToA: provider=%{public}@ size=%d count=%lld rms=%.1f head=%{public}@",
log: ctLog, type: .info, currentAstProvider, data.count, astAudioPushCountA, rms, head)
}
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 {
let rms = Self.rmsOfPCM16LE(data)
let head = Self.hexPreview(data, 16)
os_log("pushAstAudioToB: provider=%{public}@ size=%d count=%lld rms=%.1f head=%{public}@",
log: ctLog, type: .info, currentAstProvider, data.count, astAudioPushCountB, rms, head)
}
let payload = gateAgainstBPlayback(data)
switch currentAstProvider {
case "volcano":
_ = doubaoAstHelperB.pushAudioData(payload)
case "alibaba":
_ = bailianAstHelperB.pushAudioData(payload)
default:
azureAstHelperB.pushAudioData(audioData: payload)
}
}
/// 计算 PCM16LE 的 RMS 能量(静音检测用)。返回 0 表示全 0 样本。
private static func rmsOfPCM16LE(_ data: Data) -> Double {
guard data.count >= 2 else { return 0 }
let count = data.count / 2
var sumSq: Double = 0
data.withUnsafeBytes { (raw: UnsafeRawBufferPointer) in
let p = raw.bindMemory(to: Int16.self)
for i in 0..<count {
let s = Double(p[i])
sumSq += s * s
}
}
return (sumSq / Double(count)).squareRoot()
}
/// 取前 N 字节 hex 预览(诊断是否全 0)。
private static func hexPreview(_ data: Data, _ n: Int) -> String {
let take = min(n, data.count)
if take == 0 { return "<empty>" }
return data.prefix(take).map { String(format: "%02x", $0) }.joined()
}
// 音频数据相关
private var audioDataEventChannel: FlutterEventChannel?
internal var audioDataEventSink: FlutterEventSink?
@ -200,6 +274,9 @@ import ble_service
case .ast:
self.astChannel = methodChannel
self.astEventChannel = eventChannel
case .astTts:
// 不走 setupChannel 路径(在 register 里单独注册),这里仅为了详尽性。
break
}
}
/**
@ -224,6 +301,10 @@ private func sendEvent(_ event: [String: Any], type: EventType) {
case .ast:
eventSink = astEventSink
logMessage = "无法发送AST事件:事件通道未准备好"
case .astTts:
// .astTts 不走 sendEvent 通用路径(单独走 pushAstTtsFrame),这里仅为了详尽性。
eventSink = astTtsEventSink
logMessage = "无法发送AST-TTS事件:事件通道未准备好"
}
guard eventSink != nil else {
@ -273,6 +354,11 @@ private func sendEvent(_ event: [String: Any], type: EventType) {
audioDataEventChannel.setStreamHandler(BaseEventStreamHandler(instance: instance, eventType: .audioData))
instance.audioDataEventChannel = audioDataEventChannel
// 通话翻译 TTS 回灬事件通道(只有事件通道,与 Android 对齐)
let astTtsEventChannel = FlutterEventChannel(name: "azure_speech/ast_tts_events", binaryMessenger: registrar.messenger())
astTtsEventChannel.setStreamHandler(BaseEventStreamHandler(instance: instance, eventType: .astTts))
instance.astTtsEventChannel = astTtsEventChannel
// 设置讯飞原生日志回调,通过 EventChannel 回传 Dart
instance.azureAsrHelper.nativeLogCallback = { [weak instance] level, message in
instance?.sendAsrEvent(["type": "nativeLog", "level": level, "message": message])
@ -875,6 +961,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()
@ -1083,6 +1194,36 @@ private func sendAudioDataEvent(_ event: [String: Any]) {
}
}
/// 通话翻译下行TTS PCM抩回 Dart,由 translation_controller 转发给 device_jieli 回灬耳机。
/// - Parameters:
/// - leg: "A" → 对端听(SCO_UP_LINK);"B" → 本机听(SCO_DOWN_LINK)
/// - pcm: 16bit、单声道、16kHz PCM;段尾帧允许传空
/// - isFinal: 是否是该段 utterance 的段尾(与 Android 合约一致,native 依靠 isFinal=true 触发整段下发)
internal func pushAstTtsFrame(leg: String, pcm: Data, isFinal: Bool) {
guard let sink = astTtsEventSink else {
// 调试:报警一次,避免刷屏
astTtsEmitCount &+= 1
if astTtsEmitCount <= 5 || astTtsEmitCount % 200 == 0 {
NSLog("[AST-TTS] DROPPED leg=%@ pcm=%dB isFinal=%@ (no sink yet, emitCount=%lld)",
leg, pcm.count, isFinal ? "true" : "false", astTtsEmitCount)
}
return
}
astTtsEmitCount &+= 1
if isFinal || astTtsEmitCount <= 3 || astTtsEmitCount % 100 == 0 {
NSLog("[AST-TTS] emit leg=%@ pcm=%dB isFinal=%@ (#%lld)",
leg, pcm.count, isFinal ? "true" : "false", astTtsEmitCount)
}
let payload: [String: Any] = [
"leg": leg,
"pcm": FlutterStandardTypedData(bytes: pcm),
"final": isFinal,
]
DispatchQueue.main.async {
sink(payload)
}
}
/// 发送AST事件到Flutter层
internal func sendAstEvent(_ event: [String: Any]) {
let eventType = event["type"] as? String ?? "unknown"
@ -1444,6 +1585,9 @@ private class BaseEventStreamHandler: NSObject, FlutterStreamHandler {
print("音频数据事件通道已准备好")
case .ast:
instance?.astEventSink = events
case .astTts:
instance?.astTtsEventSink = events
print("[AST-TTS] event channel ready (azure_speech/ast_tts_events)")
}
return nil
}
@ -1459,6 +1603,9 @@ private class BaseEventStreamHandler: NSObject, FlutterStreamHandler {
print("音频数据事件通道已取消")
case .ast:
instance?.astEventSink = nil
case .astTts:
instance?.astTtsEventSink = nil
print("[AST-TTS] event channel cancelled")
}
return nil
}
@ -1545,11 +1692,14 @@ private class DoubaoCallbackProxy: DoubaoE2ETranslateHelper.Callback {
func onPartialAudio(sessionId: String, data: Data) {
if data.isEmpty { return }
// 新路径:同时抩到 ast_tts_events sink,让通话翻译返回耳机走 device_jieli。
// isFinal=false,native 会累积,等 onSessionFinished 返回后一次性下发。
plugin?.pushAstTtsFrame(leg: serviceId, pcm: data, isFinal: false)
if serviceId == "A" {
// A 路:翻译后的音频通过 BLE 写到耳机外放给对方
// A 路:翻译后的音频通过 BLE 写到耳机外放给对方(遗留,已 deprecated noop)
BleService.shared.writeExternalAudioData(data: data)
} else {
// B 路:对方的翻译音频,本地 PCM 播放器直接播
// B 路:对方的翻译音频,本地 PCM 播放器直接播(遗留)
plugin?.pushCallBPcm(data)
}
}
@ -1559,6 +1709,9 @@ private class DoubaoCallbackProxy: DoubaoE2ETranslateHelper.Callback {
let original = plugin?.doubaoFinalSourceTextCache.removeValue(forKey: key) ?? ""
os_log("[DoubaoCallback-%{public}@] onSessionFinished finalText=%{public}@ original=%{public}@ audioLen=%d",
log: ctLog, type: .info, serviceId, finalText, original, finalAudio.count)
// 段尾合约(与 Android markEnd 对齐):**不**重复写 finalAudio,只 emit 空 PCM + isFinal=true。
// partial 阶段已累积到 native,这里只是告诉 device_jieli “这段结束了,可以整段下发”。
plugin?.pushAstTtsFrame(leg: serviceId, pcm: Data(), isFinal: true)
plugin?.sendAstEvent([
"type": "translated",
"serviceId": serviceId,
@ -1573,6 +1726,8 @@ private class DoubaoCallbackProxy: DoubaoE2ETranslateHelper.Callback {
func onSessionError(sessionId: String, code: Int, message: String) {
os_log("[DoubaoCallback-%{public}@] onSessionError code=%d msg=%{public}@",
log: ctLog, type: .error, serviceId, code, message)
// 错误也要 emit isFinal=true,避免 native 累积 buffer 残留(与 Android markEnd 对齐)。
plugin?.pushAstTtsFrame(leg: serviceId, pcm: Data(), isFinal: true)
plugin?.sendAstEvent([
"type": "error",
"serviceId": serviceId,
@ -1583,6 +1738,15 @@ private class DoubaoCallbackProxy: DoubaoE2ETranslateHelper.Callback {
])
}
/// 句级 TTS 结束。豆包会在每说完一句后发 ttssentenceEnd(351),不是会话级 sessionFinished(152)。
/// 这里 emit 空 PCM + isFinal=true,使 device_jieli 端把累积的整段 PCM 立即编码下发给 SDK。
/// 不走 onSessionFinished 的理由:那一条会触发 WS cancel + 会话结束事件,与句级语义不符。
func onSentenceEnd(sessionId: String) {
os_log("[DoubaoCallback-%{public}@] onSentenceEnd sessionId=%{public}@",
log: ctLog, type: .info, serviceId, sessionId)
plugin?.pushAstTtsFrame(leg: serviceId, pcm: Data(), isFinal: true)
}
func onFinalTranslatedText(sessionId: String, finalText: String) {
let key = "\(serviceId):\(sessionId)"
let original = plugin?.doubaoFinalSourceTextCache.removeValue(forKey: key) ?? ""
@ -1678,6 +1842,8 @@ private class AliyunCallbackProxy: AliyunBailianE2EHelper.Callback {
func onPartialAudio(sessionId: String, data: Data) {
if data.isEmpty { return }
// 新路径:同时抩到 ast_tts_events sink,走 device_jieli 回灬耳机。
plugin?.pushAstTtsFrame(leg: serviceId, pcm: data, isFinal: false)
if serviceId == "A" {
BleService.shared.writeExternalAudioData(data: data)
} else {
@ -1690,6 +1856,8 @@ private class AliyunCallbackProxy: AliyunBailianE2EHelper.Callback {
let original = sourceTextCache.removeValue(forKey: key) ?? ""
os_log("[AliyunCallback-%{public}@] onSessionFinished finalText=%{public}@ original=%{public}@ audioLen=%d",
log: ctLog, type: .info, serviceId, finalText, original, finalAudio.count)
// 段尾合约(与 Android markEnd 对齐):**不**重复写 finalAudio,只 emit 空 PCM + isFinal=true。
plugin?.pushAstTtsFrame(leg: serviceId, pcm: Data(), isFinal: true)
plugin?.sendAstEvent([
"type": "translated",
"serviceId": serviceId,
@ -1704,6 +1872,8 @@ private class AliyunCallbackProxy: AliyunBailianE2EHelper.Callback {
func onSessionError(sessionId: String, code: Int, message: String) {
os_log("[AliyunCallback-%{public}@] onSessionError code=%d msg=%{public}@",
log: ctLog, type: .error, serviceId, code, message)
// 错误也要 emit isFinal=true,避免 native 累积 buffer 残留(与 Android markEnd 对齐)。
plugin?.pushAstTtsFrame(leg: serviceId, pcm: Data(), isFinal: true)
plugin?.sendAstEvent([
"type": "error",
"serviceId": serviceId,

25
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 {

64
local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/DoubaoE2ETranslateHelper.swift

@ -22,6 +22,10 @@ class DoubaoE2ETranslateHelper: NSObject, URLSessionWebSocketDelegate {
func onPartialAudio(sessionId: String, data: Data)
func onSessionFinished(sessionId: String, finalText: String, finalAudio: Data)
func onSessionError(sessionId: String, code: Int, message: String)
/// 句级结束:豆包发的是 `ttssentenceEnd (351)`,不是会话级 `sessionFinished (152)`。
/// 用来告诉调用方 “这一句 TTS PCM 发完了,应立即把 native 端累积的段所有 PCM 制作为一整段下发”。
/// 与 Android `markEnd` 合约对齐。
func onSentenceEnd(sessionId: String)
}
/**
@ -75,6 +79,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 +211,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
}
@ -538,6 +572,21 @@ class DoubaoE2ETranslateHelper: NSObject, URLSessionWebSocketDelegate {
return
}
// 句级 TTS 结束:豆包每说完一句发一次 ttssentenceEnd(351),
// 此时应立即触发 isFinal=true,让 device_jieli 把累积的 PCM 一次性下发给 SDK。
// 与会话级 sessionFinished(152) 的关键区别:不关闭 WS,不清空 recvText/recvAudio、
// 后续的 startNewSubSession 由 translationSubtitleEnd 控制。
if event == .ttssentenceEnd {
os_log("TTSSentenceEnd: emit sentence isFinal", log: log, type: .info)
// 先 flush audioChunkBuffer 里不足 1280 倍数的尾料,避免最后几十字节永远满不下发。
if !audioChunkBuffer.isEmpty {
callback?.onPartialAudio(sessionId: sessionId, data: audioChunkBuffer)
audioChunkBuffer = Data()
}
callback?.onSentenceEnd(sessionId: sessionId)
return
}
if !audioData.isEmpty {
recvAudio.append(audioData)
// 极高频(音频帧),取消 info 日志
@ -609,6 +658,15 @@ class DoubaoE2ETranslateHelper: NSObject, URLSessionWebSocketDelegate {
webSocket?.cancel(with: .normalClosure, reason: "Completed".data(using: .utf8))
return
}
// JSON 兜底:句级结束也走 onSentenceEnd。
if event == "TTSSentenceEnd" {
if !audioChunkBuffer.isEmpty {
callback?.onPartialAudio(sessionId: sessionId, data: audioChunkBuffer)
audioChunkBuffer = Data()
}
callback?.onSentenceEnd(sessionId: sessionId)
return
}
if let audioB64 = (obj["data"] as? String), let audioData = Data(base64Encoded: audioB64) {
recvAudio.append(audioData)
processAudioChunk(incoming: audioData, multiple: 1280)

48
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)

11
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"
)
]
)

214
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..<self.chunkSize)
self.pcmDataQueue.removeSubrange(0..<self.chunkSize)
} else if self.pcmDataQueue.count > 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)
}
}
}

6
local_plugins/chat_api/ios/chat_api/Package.swift

@ -14,7 +14,9 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/MacPaw/OpenAI.git", exact: "0.4.3"),
.package(url: "https://github.com/modelcontextprotocol/swift-sdk.git", from: "0.9.0"),
// 改用本地副本:上游 swift-sdk 0.9.0 的 Package.swift 声明 swift-tools-version:6.0
// 会在 Xcode 16 上把 sending 警告升级为错误。本地副本将其降为 5.10 规避。
.package(path: "../../../swift_sdk_local"),
.package(path: "../../../location_service/ios/location_service"),
.package(path: "../../../music_service/ios/music_service"),
.package(path: "../../../qq_music/ios/qq_music"),
@ -25,7 +27,7 @@ let package = Package(
.target(
name: "chat_api",
dependencies: [
.product(name: "MCP", package: "swift-sdk"),
.product(name: "MCP", package: "swift_sdk_local"),
.product(name: "OpenAI", package: "OpenAI"),
.product(name: "music-service", package: "music_service"),
.product(name: "qq-music", package: "qq_music"),

84
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.

47
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
<key>NSBluetoothAlwaysUsageDescription</key>
<string>用于连接杰理蓝牙耳机</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>用于连接杰理蓝牙耳机</string>
<key>NSMicrophoneUsageDescription</key>
<string>用于翻译 / 语音助手录音</string>
```
## 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).

44
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/Info.plist

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>BinaryPath</key>
<string>JLAudioUnitKit.framework/JLAudioUnitKit</string>
<key>LibraryIdentifier</key>
<string>ios-arm64_x86_64-simulator</string>
<key>LibraryPath</key>
<string>JLAudioUnitKit.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>x86_64</string>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
<dict>
<key>BinaryPath</key>
<string>JLAudioUnitKit.framework/JLAudioUnitKit</string>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>JLAudioUnitKit.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>

26
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 <Foundation/Foundation.h>
//! 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 <JLAudioUnitKit/PublicHeader.h>
#import <JLAudioUnitKit/JLSpeexDecoder.h>
#import <JLAudioUnitKit/JLOpusDecoder.h>
#import <JLAudioUnitKit/JLOpusEncoder.h>
#import <JLAudioUnitKit/JLOpusFormat.h>
#import <JLAudioUnitKit/JLOpusEncodeConfig.h>
#import <JLAudioUnitKit/JLPcmToWtg.h>
#import <JLAudioUnitKit/JLPcmToWav.h>
#import <JLAudioUnitKit/JLAudioUnitPlayer.h>
#import <JLAudioUnitKit/JLOpusToOgg.h>

58
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 <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, JLAudioUnitPlayerType) {
JLAudioUnitPlayerTypeFile, // 支持 MP3/WAV/AAC 等文件格式(AVAudioPlayer)
JLAudioUnitPlayerTypePCM // 支持 PCM 流式播放(Audio Queue)
};
@class JLAudioUnitPlayer;
@protocol JLAudioPlayerDelegate <NSObject>
@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<JLAudioPlayerDelegate> 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

66
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 <Foundation/Foundation.h>
#import <JLAudioUnitKit/JLOpusFormat.h>
NS_ASSUME_NONNULL_BEGIN
@class JLOpusDecoder;
/// Opus 解码代理
@protocol JLOpusDecoderDelegate <NSObject>
/// 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<JLOpusDecoderDelegate> delegate;
- (instancetype)init NS_UNAVAILABLE;
/// 初始化
/// - Parameters:
/// - format: 解码格式
/// - delegate: 代理
- (instancetype)initDecoder:(JLOpusFormat *)format delegate:(id<JLOpusDecoderDelegate>)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

83
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 <Foundation/Foundation.h>
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

69
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 <Foundation/Foundation.h>
#import <JLAudioUnitKit/JLOpusEncodeConfig.h>
NS_ASSUME_NONNULL_BEGIN
@class JLOpusEncoder;
/// Opus 编码代理
@protocol JLOpusEncoderDelegate <NSObject>
/// 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<JLOpusEncoderDelegate> delegate;
-(instancetype)init NS_UNAVAILABLE;
/// 初始化
/// - Parameters:
/// - config: 音频格式
/// - delegate: 代理
-(instancetype)initFormat:(JLOpusEncodeConfig *)format delegate:(id<JLOpusEncoderDelegate>)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

59
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 <Foundation/Foundation.h>
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

67
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 <Foundation/Foundation.h>
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

55
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 <Foundation/Foundation.h>
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

51
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 <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface JLPcm2WtgModel : NSObject <NSCopying>
/// 音频文件
@property (nonatomic, copy) NSString *pcmPath;
/// 输出文件
@property (nonatomic, copy) NSString *wtgPath;
@end
/// JLPcmToWtgProtocol
@protocol JLPcmToWtgDelegate <NSObject>
/// 音频转码完成
/// - Parameter model: 音频转码模型
- (void)convertPcmToWtgDone:(JLPcm2WtgModel *)model;
@end
/// 音频转码
/// PCM 转 WTG
@interface JLPcmToWtg : NSObject
- (instancetype)init NS_UNAVAILABLE;
/// 音频转码初始化
/// - Parameter delegate: 音频转码代理
- (instancetype)initWithDelegate:(id<JLPcmToWtgDelegate>)delegate;
/// 音频转码
/// 当前接口模型只限制 PCM 文件格式为:8k 16bit
/// 输入的pcm文件(16bit,小端,8k采样率),wtg_filename:输出的wtg文件
/// - Parameter model: 音频转码模型
-(void)convertPcmToWtg:(JLPcm2WtgModel *)model;
@end
NS_ASSUME_NONNULL_END

54
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 <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class JLSpeexDecoder;
@protocol JLSpeexDelegate <NSObject>
/// 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<JLSpeexDelegate>)delegate;
/// 委托
@property (nonatomic, weak) id<JLSpeexDelegate> 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

BIN
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/Info.plist

Binary file not shown.

BIN
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64/JLAudioUnitKit.framework/JLAudioUnitKit

Binary file not shown.

6
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 * }
}

52
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

26
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 <Foundation/Foundation.h>
//! 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 <JLAudioUnitKit/PublicHeader.h>
#import <JLAudioUnitKit/JLSpeexDecoder.h>
#import <JLAudioUnitKit/JLOpusDecoder.h>
#import <JLAudioUnitKit/JLOpusEncoder.h>
#import <JLAudioUnitKit/JLOpusFormat.h>
#import <JLAudioUnitKit/JLOpusEncodeConfig.h>
#import <JLAudioUnitKit/JLPcmToWtg.h>
#import <JLAudioUnitKit/JLPcmToWav.h>
#import <JLAudioUnitKit/JLAudioUnitPlayer.h>
#import <JLAudioUnitKit/JLOpusToOgg.h>

58
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 <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, JLAudioUnitPlayerType) {
JLAudioUnitPlayerTypeFile, // 支持 MP3/WAV/AAC 等文件格式(AVAudioPlayer)
JLAudioUnitPlayerTypePCM // 支持 PCM 流式播放(Audio Queue)
};
@class JLAudioUnitPlayer;
@protocol JLAudioPlayerDelegate <NSObject>
@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<JLAudioPlayerDelegate> 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

66
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 <Foundation/Foundation.h>
#import <JLAudioUnitKit/JLOpusFormat.h>
NS_ASSUME_NONNULL_BEGIN
@class JLOpusDecoder;
/// Opus 解码代理
@protocol JLOpusDecoderDelegate <NSObject>
/// 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<JLOpusDecoderDelegate> delegate;
- (instancetype)init NS_UNAVAILABLE;
/// 初始化
/// - Parameters:
/// - format: 解码格式
/// - delegate: 代理
- (instancetype)initDecoder:(JLOpusFormat *)format delegate:(id<JLOpusDecoderDelegate>)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

83
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 <Foundation/Foundation.h>
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

69
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 <Foundation/Foundation.h>
#import <JLAudioUnitKit/JLOpusEncodeConfig.h>
NS_ASSUME_NONNULL_BEGIN
@class JLOpusEncoder;
/// Opus 编码代理
@protocol JLOpusEncoderDelegate <NSObject>
/// 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<JLOpusEncoderDelegate> delegate;
-(instancetype)init NS_UNAVAILABLE;
/// 初始化
/// - Parameters:
/// - config: 音频格式
/// - delegate: 代理
-(instancetype)initFormat:(JLOpusEncodeConfig *)format delegate:(id<JLOpusEncoderDelegate>)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

59
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 <Foundation/Foundation.h>
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

67
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 <Foundation/Foundation.h>
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

55
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 <Foundation/Foundation.h>
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

51
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 <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface JLPcm2WtgModel : NSObject <NSCopying>
/// 音频文件
@property (nonatomic, copy) NSString *pcmPath;
/// 输出文件
@property (nonatomic, copy) NSString *wtgPath;
@end
/// JLPcmToWtgProtocol
@protocol JLPcmToWtgDelegate <NSObject>
/// 音频转码完成
/// - Parameter model: 音频转码模型
- (void)convertPcmToWtgDone:(JLPcm2WtgModel *)model;
@end
/// 音频转码
/// PCM 转 WTG
@interface JLPcmToWtg : NSObject
- (instancetype)init NS_UNAVAILABLE;
/// 音频转码初始化
/// - Parameter delegate: 音频转码代理
- (instancetype)initWithDelegate:(id<JLPcmToWtgDelegate>)delegate;
/// 音频转码
/// 当前接口模型只限制 PCM 文件格式为:8k 16bit
/// 输入的pcm文件(16bit,小端,8k采样率),wtg_filename:输出的wtg文件
/// - Parameter model: 音频转码模型
-(void)convertPcmToWtg:(JLPcm2WtgModel *)model;
@end
NS_ASSUME_NONNULL_END

54
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 <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class JLSpeexDecoder;
@protocol JLSpeexDelegate <NSObject>
/// 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<JLSpeexDelegate>)delegate;
/// 委托
@property (nonatomic, weak) id<JLSpeexDelegate> 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

BIN
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/Info.plist

Binary file not shown.

BIN
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/JLAudioUnitKit

Binary file not shown.

6
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 * }
}

234
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLAudioUnitKit.xcframework/ios-arm64_x86_64-simulator/JLAudioUnitKit.framework/_CodeSignature/CodeResources

@ -0,0 +1,234 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Headers/JLAudioUnitKit.h</key>
<data>
hE2nr+4q2/NdSgBaayJc7ZoOXb0=
</data>
<key>Headers/JLAudioUnitPlayer.h</key>
<data>
QPMLV/XA8hjk39d+vT+S57t0fDU=
</data>
<key>Headers/JLOpusDecoder.h</key>
<data>
K1bYcaJ67prImNlGuldy8csTRHc=
</data>
<key>Headers/JLOpusEncodeConfig.h</key>
<data>
NArAew5QxnSJGsD0tRzQ2V7bHrY=
</data>
<key>Headers/JLOpusEncoder.h</key>
<data>
7DYUlvXrp1QC9l2XZ5tNHtsmsBQ=
</data>
<key>Headers/JLOpusFormat.h</key>
<data>
Du1x3VNk87NstmspRk7ElijuZrk=
</data>
<key>Headers/JLOpusToOgg.h</key>
<data>
fSJT6LcyylJHTHX1wsN3JHG2FxU=
</data>
<key>Headers/JLPcmToWav.h</key>
<data>
/BbZmqmWpWVBusXqjnzbjaBNCNg=
</data>
<key>Headers/JLPcmToWtg.h</key>
<data>
zoOdOFpIulND0F1OrfIXR1H0kh0=
</data>
<key>Headers/JLSpeexDecoder.h</key>
<data>
5ecBKtD3BDGccLiKpTs0fS0CSgs=
</data>
<key>Info.plist</key>
<data>
pXqOzYCqPnMLY0nonWZTcbfnlhU=
</data>
<key>Modules/module.modulemap</key>
<data>
2vYf0m+hGTOTapX6sliv/F1E1VI=
</data>
<key>readme.txt</key>
<data>
wijZX0Dr2OlODWS6XYbpeKabb7g=
</data>
</dict>
<key>files2</key>
<dict>
<key>Headers/JLAudioUnitKit.h</key>
<dict>
<key>hash2</key>
<data>
J5pq6Ed3fR+cQ7CGPXDoPl7BZzix7Cjf5yJZFRRvxqs=
</data>
</dict>
<key>Headers/JLAudioUnitPlayer.h</key>
<dict>
<key>hash2</key>
<data>
qBz0OA82HUH4kBtO8xiUw7Q4ohtiIvCSYfCqax5xjJY=
</data>
</dict>
<key>Headers/JLOpusDecoder.h</key>
<dict>
<key>hash2</key>
<data>
cnAj6Nt6vgRLoEH6QY3Q+gYpjcxGPN1OLLZbCgdLymI=
</data>
</dict>
<key>Headers/JLOpusEncodeConfig.h</key>
<dict>
<key>hash2</key>
<data>
qrPpsrXaNCvyxf0/gUZtvveL+ywsfgBUrMPFibjneOo=
</data>
</dict>
<key>Headers/JLOpusEncoder.h</key>
<dict>
<key>hash2</key>
<data>
YoHsvoGH2VEjvEIzEa7t+cAnzf8kHEU3dJTd03ABr1w=
</data>
</dict>
<key>Headers/JLOpusFormat.h</key>
<dict>
<key>hash2</key>
<data>
Nm6vM9/EbFzyIWbsysu6EOO2S+vZVeplcop4NvoJaMA=
</data>
</dict>
<key>Headers/JLOpusToOgg.h</key>
<dict>
<key>hash2</key>
<data>
dNSvJJnED/6E1p7TYZ4zBzUnLDVsZ8NovuNYjge6T/w=
</data>
</dict>
<key>Headers/JLPcmToWav.h</key>
<dict>
<key>hash2</key>
<data>
l/SEQhSFh7ctq/pBV+Z7X+Z0/gz30eCw3s31otjMPho=
</data>
</dict>
<key>Headers/JLPcmToWtg.h</key>
<dict>
<key>hash2</key>
<data>
MwtbX3Sa0nytA+m11x+1rDvP4WvJnZLljshuT5B3WD0=
</data>
</dict>
<key>Headers/JLSpeexDecoder.h</key>
<dict>
<key>hash2</key>
<data>
pV0X3xn07Ps7wE+0EfL9Yu/08tp8Ug8s14iDigRU46o=
</data>
</dict>
<key>Modules/module.modulemap</key>
<dict>
<key>hash2</key>
<data>
akFCUM59aSAaRM/ADWQ138YT9GD3oAjcaTvTUo24Ml8=
</data>
</dict>
<key>readme.txt</key>
<dict>
<key>hash2</key>
<data>
+2GG9ORrf2yJnBe2joKWtyBZL9p2sCH8EXqK1GIgW0E=
</data>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^.*</key>
<true/>
<key>^.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^.*</key>
<true/>
<key>^.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>

52
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

59
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/Info.plist

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>BinaryPath</key>
<string>JLBmpConvertKit.framework/JLBmpConvertKit</string>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>JLBmpConvertKit.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
<dict>
<key>BinaryPath</key>
<string>JLBmpConvertKit.framework/Versions/A/JLBmpConvertKit</string>
<key>LibraryIdentifier</key>
<string>macos-arm64_x86_64</string>
<key>LibraryPath</key>
<string>JLBmpConvertKit.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
<string>x86_64</string>
</array>
<key>SupportedPlatform</key>
<string>macos</string>
</dict>
<dict>
<key>BinaryPath</key>
<string>JLBmpConvertKit.framework/JLBmpConvertKit</string>
<key>LibraryIdentifier</key>
<string>ios-arm64_x86_64-simulator</string>
<key>LibraryPath</key>
<string>JLBmpConvertKit.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
<string>x86_64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>

95
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 <Foundation/Foundation.h>
#import <JLBmpConvertKit/JLBmpConvertKit.h>
#if TARGET_OS_IOS || TARGET_OS_MACCATALYST
#import <UIKit/UIKit.h>
#elif TARGET_OS_OSX
#import <AppKit/AppKit.h>
#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

24
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 <Foundation/Foundation.h>
//! 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 <JLBmpConvertKit/PublicHeader.h>
#import <JLBmpConvertKit/JLBmpConvertOption.h>
#import <JLBmpConvertKit/JLBmpConvert.h>
#import <JLBmpConvertKit/JLGifBin.h>
#import <JLBmpConvertKit/JLImageConvertResult.h>

89
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 <Foundation/Foundation.h>
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

77
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 <Foundation/Foundation.h>
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

34
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 <Foundation/Foundation.h>
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

BIN
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/Info.plist

Binary file not shown.

BIN
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64/JLBmpConvertKit.framework/JLBmpConvertKit

Binary file not shown.

6
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 * }
}

95
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 <Foundation/Foundation.h>
#import <JLBmpConvertKit/JLBmpConvertKit.h>
#if TARGET_OS_IOS || TARGET_OS_MACCATALYST
#import <UIKit/UIKit.h>
#elif TARGET_OS_OSX
#import <AppKit/AppKit.h>
#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

24
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 <Foundation/Foundation.h>
//! 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 <JLBmpConvertKit/PublicHeader.h>
#import <JLBmpConvertKit/JLBmpConvertOption.h>
#import <JLBmpConvertKit/JLBmpConvert.h>
#import <JLBmpConvertKit/JLGifBin.h>
#import <JLBmpConvertKit/JLImageConvertResult.h>

89
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 <Foundation/Foundation.h>
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

77
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 <Foundation/Foundation.h>
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

34
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 <Foundation/Foundation.h>
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

BIN
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/Info.plist

Binary file not shown.

BIN
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/JLBmpConvertKit

Binary file not shown.

6
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 * }
}

192
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/ios-arm64_x86_64-simulator/JLBmpConvertKit.framework/_CodeSignature/CodeResources

@ -0,0 +1,192 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Headers/JLBmpConvert.h</key>
<data>
5urDCSjpqHBbBhVbnkpZDUAqfUQ=
</data>
<key>Headers/JLBmpConvertKit.h</key>
<data>
rJo680NKOdjmobcOdHzU3oQ05lQ=
</data>
<key>Headers/JLBmpConvertOption.h</key>
<data>
bPMnU/cb1ZNEvx+6wLH+tNggsWM=
</data>
<key>Headers/JLGifBin.h</key>
<data>
+UKivjyP0JG0+mVZPKUAWwhParQ=
</data>
<key>Headers/JLImageConvertResult.h</key>
<data>
oTrRd0HS6yuPr1JxLpx1Qgr6sKw=
</data>
<key>Info.plist</key>
<data>
yF0Q5ofGeBGl7TtNvUrICUVzXe8=
</data>
<key>Modules/module.modulemap</key>
<data>
VG59AHi+XKWb6IYCdGbZKHPgBbA=
</data>
</dict>
<key>files2</key>
<dict>
<key>Headers/JLBmpConvert.h</key>
<dict>
<key>hash</key>
<data>
5urDCSjpqHBbBhVbnkpZDUAqfUQ=
</data>
<key>hash2</key>
<data>
Ps103art4k9gGQecTWk7WrWV5o2nY38Acp7pydnv0oc=
</data>
</dict>
<key>Headers/JLBmpConvertKit.h</key>
<dict>
<key>hash</key>
<data>
rJo680NKOdjmobcOdHzU3oQ05lQ=
</data>
<key>hash2</key>
<data>
9kChXiw3D4JFaLjgBlaq+/2FazTsVCbA3kfmrYQ+9/A=
</data>
</dict>
<key>Headers/JLBmpConvertOption.h</key>
<dict>
<key>hash</key>
<data>
bPMnU/cb1ZNEvx+6wLH+tNggsWM=
</data>
<key>hash2</key>
<data>
4rgpTV2uvjOrNb0Bx7HOxoqBCtJjHaA7CcNBjIImXzY=
</data>
</dict>
<key>Headers/JLGifBin.h</key>
<dict>
<key>hash</key>
<data>
+UKivjyP0JG0+mVZPKUAWwhParQ=
</data>
<key>hash2</key>
<data>
A8LhdAy5NiJx3J5nKUE53rzYmIKX6Cp9oTpGfJJk+tQ=
</data>
</dict>
<key>Headers/JLImageConvertResult.h</key>
<dict>
<key>hash</key>
<data>
oTrRd0HS6yuPr1JxLpx1Qgr6sKw=
</data>
<key>hash2</key>
<data>
vacF3oh76vh4n8lafP3hAi/fYXjU4YoqA6CbjC05MYw=
</data>
</dict>
<key>Modules/module.modulemap</key>
<dict>
<key>hash</key>
<data>
VG59AHi+XKWb6IYCdGbZKHPgBbA=
</data>
<key>hash2</key>
<data>
nU2jpsOghgGuDPwpIFZ+hGnZ5Xj24fG1pjw1H8vpwEs=
</data>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^.*</key>
<true/>
<key>^.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^.*</key>
<true/>
<key>^.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>

1
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Headers

@ -0,0 +1 @@
Versions/Current/Headers

1
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/JLBmpConvertKit

@ -0,0 +1 @@
Versions/Current/JLBmpConvertKit

1
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Modules

@ -0,0 +1 @@
Versions/Current/Modules

1
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Resources

@ -0,0 +1 @@
Versions/Current/Resources

95
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/A/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 <Foundation/Foundation.h>
#import <JLBmpConvertKit/JLBmpConvertKit.h>
#if TARGET_OS_IOS || TARGET_OS_MACCATALYST
#import <UIKit/UIKit.h>
#elif TARGET_OS_OSX
#import <AppKit/AppKit.h>
#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

24
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/A/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 <Foundation/Foundation.h>
//! 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 <JLBmpConvertKit/PublicHeader.h>
#import <JLBmpConvertKit/JLBmpConvertOption.h>
#import <JLBmpConvertKit/JLBmpConvert.h>
#import <JLBmpConvertKit/JLGifBin.h>
#import <JLBmpConvertKit/JLImageConvertResult.h>

89
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/A/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 <Foundation/Foundation.h>
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

77
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/A/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 <Foundation/Foundation.h>
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

34
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/A/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 <Foundation/Foundation.h>
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

BIN
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/A/JLBmpConvertKit

Binary file not shown.

6
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/A/Modules/module.modulemap

@ -0,0 +1,6 @@
framework module JLBmpConvertKit {
umbrella header "JLBmpConvertKit.h"
export *
module * { export * }
}

48
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/A/Resources/Info.plist

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>25B78</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>JLBmpConvertKit</string>
<key>CFBundleIdentifier</key>
<string>com.zh-jieli.JLBmpConvertKit</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>JLBmpConvertKit</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.5.0</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string>25C57</string>
<key>DTPlatformName</key>
<string>macosx</string>
<key>DTPlatformVersion</key>
<string>26.2</string>
<key>DTSDKBuild</key>
<string>25C57</string>
<key>DTSDKName</key>
<string>macosx26.2</string>
<key>DTXcode</key>
<string>2620</string>
<key>DTXcodeBuild</key>
<string>17C52</string>
<key>LSMinimumSystemVersion</key>
<string>10.13</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2024 www.zh-jieli.com. All rights reserved.</string>
</dict>
</plist>

1
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLBmpConvertKit.xcframework/macos-arm64_x86_64/JLBmpConvertKit.framework/Versions/Current

@ -0,0 +1 @@
A

44
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/Info.plist

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>BinaryPath</key>
<string>JLDialUnit.framework/JLDialUnit</string>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>JLDialUnit.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
<dict>
<key>BinaryPath</key>
<string>JLDialUnit.framework/JLDialUnit</string>
<key>LibraryIdentifier</key>
<string>ios-arm64_x86_64-simulator</string>
<key>LibraryPath</key>
<string>JLDialUnit.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
<string>x86_64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>

127
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 <Foundation/Foundation.h>
#import <JLDialUnit/FatfsObject.h>
#import <JLLogHelper/JLLogHelper.h>
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

92
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 <Foundation/Foundation.h>
#import <JL_BLEKit/JL_BLEKit.h>
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

27
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 <Foundation/Foundation.h>
#import <JL_BLEKit/JL_BLEKit.h>
#import <JLLogHelper/JLLogHelper.h>
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

30
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 <Foundation/Foundation.h>
//! 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 <JLDialUnit/PublicHeader.h>
#import <JLDialUnit/DialManager.h>
#import <JLDialUnit/FatfsObject.h>
#import <JLDialUnit/ZipZap.h>
#import <JLDialUnit/ZZArchive.h>
#import <JLDialUnit/ZZArchiveEntry.h>
#import <JLDialUnit/ZZConstants.h>
#import <JLDialUnit/ZZError.h>
#import <JLDialUnit/JLDialSourceModel.h>
#import <JLDialUnit/JLDialUnitMgr.h>
#import <JLDialUnit/JLSourceUpdate.h>

115
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 <Foundation/Foundation.h>
#import <JL_HashPair/JL_HashPair.h>
#import <JLLogHelper/JLLogHelper.h>
#import <JL_BLEKit/JL_BLEKit.h>
NS_ASSUME_NONNULL_BEGIN
@class JLDialSourceModel;
@interface JLDialUnitMgr : NSObject
/// 设备文件列表
@property (nonatomic,strong)NSMutableArray <JLDialSourceModel *> *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 <JLDialSourceModel *> * _Nullable,NSError * _Nullable))completion;
/// 清空文件列表
/// - Parameter cardType: 设备存储的类型
-(void)cleanFileList;
/// 更新文件列表信息
/// - Parameters:
/// - fileSources: NSArray<JLModel_File *>
/// - completion: 回调
-(void)updateFileSources:(NSArray<JLModel_File *> *)fileSources completion:(void (^)(NSArray<JLDialSourceModel *> * _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 <NSString*>*) 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

94
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 <Foundation/Foundation.h>
#import <JL_BLEKit/JL_BLEKit.h>
#import <JLLogHelper/JLLogHelper.h>
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 <NSObject>
- (void)updateProgress:(double)progress type:(JLSourceUpdateType)type source:(JLSourceInfo *_Nullable)source error:(NSError* _Nullable)error;
- (void)updateUfwFirst:(NSString *)filePath ThenUpdateSrc:(NSArray <JLSourceInfo *>*) filePaths;
@end
/// 资源更新
@interface JLSourceUpdate : NSObject
@property(nonatomic,weak)id<JLSourceUpdateDelegate> delegate;
/// 初始化
/// - Parameters:
/// - mgr: 蓝牙管理对象
/// - fileType: 文件句柄
/// - dialMgr: JLDialUnitMgr
/// - delegate: 代理
-(instancetype)initWithManager:(JL_ManagerM*)mgr HandleType:(JL_FileHandleType) fileType DialMgr:(JLDialUnitMgr *)dialMgr delegate:(id<JLSourceUpdateDelegate>)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

131
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 <Foundation/Foundation.h>
#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<ZZArchiveEntry*>* 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<ZZArchiveEntry*>*)newEntries
error:(out NSError**)error;
@end
NS_ASSUME_NONNULL_END

239
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 <CoreGraphics/CoreGraphics.h>
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
#include <ApplicationServices/ApplicationServices.h>
#endif
#import <Foundation/Foundation.h>
@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<ZZArchiveEntryWriter>)newWriterCanSkipLocalFile:(BOOL)canSkipLocalFile;
@end
NS_ASSUME_NONNULL_END

79
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 <Foundation/Foundation.h>
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;

136
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 <Foundation/Foundation.h>
/**
* 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;
}

45
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 <Foundation/Foundation.h>
#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[];

BIN
local_plugins/ota/ios/Frameworks/JL_HashPair.framework/Info.plist → local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/Info.plist

Binary file not shown.

BIN
local_plugins/device_jieli/ios/device_jieli/Frameworks/JLDialUnit.xcframework/ios-arm64/JLDialUnit.framework/JLDialUnit

Binary file not shown.

6
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 * }
}

127
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 <Foundation/Foundation.h>
#import <JLDialUnit/FatfsObject.h>
#import <JLLogHelper/JLLogHelper.h>
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

92
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 <Foundation/Foundation.h>
#import <JL_BLEKit/JL_BLEKit.h>
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

27
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 <Foundation/Foundation.h>
#import <JL_BLEKit/JL_BLEKit.h>
#import <JLLogHelper/JLLogHelper.h>
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

30
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 <Foundation/Foundation.h>
//! 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 <JLDialUnit/PublicHeader.h>
#import <JLDialUnit/DialManager.h>
#import <JLDialUnit/FatfsObject.h>
#import <JLDialUnit/ZipZap.h>
#import <JLDialUnit/ZZArchive.h>
#import <JLDialUnit/ZZArchiveEntry.h>
#import <JLDialUnit/ZZConstants.h>
#import <JLDialUnit/ZZError.h>
#import <JLDialUnit/JLDialSourceModel.h>
#import <JLDialUnit/JLDialUnitMgr.h>
#import <JLDialUnit/JLSourceUpdate.h>

115
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 <Foundation/Foundation.h>
#import <JL_HashPair/JL_HashPair.h>
#import <JLLogHelper/JLLogHelper.h>
#import <JL_BLEKit/JL_BLEKit.h>
NS_ASSUME_NONNULL_BEGIN
@class JLDialSourceModel;
@interface JLDialUnitMgr : NSObject
/// 设备文件列表
@property (nonatomic,strong)NSMutableArray <JLDialSourceModel *> *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 <JLDialSourceModel *> * _Nullable,NSError * _Nullable))completion;
/// 清空文件列表
/// - Parameter cardType: 设备存储的类型
-(void)cleanFileList;
/// 更新文件列表信息
/// - Parameters:
/// - fileSources: NSArray<JLModel_File *>
/// - completion: 回调
-(void)updateFileSources:(NSArray<JLModel_File *> *)fileSources completion:(void (^)(NSArray<JLDialSourceModel *> * _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 <NSString*>*) 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

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save