@glassly/bluetooth-sdk 0.1.0-dev.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (548) hide show
  1. package/README.md +563 -0
  2. package/android/build.gradle +450 -0
  3. package/android/gradle.properties.example +9 -0
  4. package/android/lc3Lib/CMakeLists.txt +6 -0
  5. package/android/lc3Lib/build.gradle +212 -0
  6. package/android/lc3Lib/proguard-rules.pro +33 -0
  7. package/android/lc3Lib/src/main/AndroidManifest.xml +2 -0
  8. package/android/lc3Lib/src/main/cpp/CMakeLists.txt +8 -0
  9. package/android/lc3Lib/src/main/cpp/google_opus_stuff/CMakeLists.txt +30 -0
  10. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/CMakeLists.txt +7 -0
  11. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.cc +96 -0
  12. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.h +53 -0
  13. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/Makefile.am +6 -0
  14. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h +26 -0
  15. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h.in +26 -0
  16. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/ogg.h +209 -0
  17. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/os_types.h +158 -0
  18. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus.h +981 -0
  19. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_custom.h +342 -0
  20. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_defines.h +799 -0
  21. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_multistream.h +660 -0
  22. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_projection.h +568 -0
  23. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_types.h +166 -0
  24. package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.cc +268 -0
  25. package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.h +115 -0
  26. package/android/lc3Lib/src/main/cpp/google_opus_stuff/opus_tools/opus_header.h +59 -0
  27. package/android/lc3Lib/src/main/cpp/liblc3/CMakeLists.txt +38 -0
  28. package/android/lc3Lib/src/main/cpp/liblc3/include/lc3.h +309 -0
  29. package/android/lc3Lib/src/main/cpp/liblc3/include/lc3_private.h +162 -0
  30. package/android/lc3Lib/src/main/cpp/liblc3/include/rnnoise.h +114 -0
  31. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/CMakeLists.txt +19 -0
  32. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.c +92 -0
  33. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.h +44 -0
  34. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.c +375 -0
  35. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.h +315 -0
  36. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.c +129 -0
  37. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.h +69 -0
  38. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/common.h +148 -0
  39. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.c +70 -0
  40. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.h +43 -0
  41. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/fastmath.h +158 -0
  42. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/lc3.c +702 -0
  43. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.c +893 -0
  44. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.h +111 -0
  45. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_arm.h +506 -0
  46. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_neon.h +281 -0
  47. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/makefile.mk +35 -0
  48. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.c +452 -0
  49. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.h +57 -0
  50. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct_neon.h +296 -0
  51. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/meson.build +46 -0
  52. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.c +61 -0
  53. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.h +57 -0
  54. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.c +880 -0
  55. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.h +103 -0
  56. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.c +904 -0
  57. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.h +119 -0
  58. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.c +3457 -0
  59. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.h +94 -0
  60. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.c +457 -0
  61. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.h +99 -0
  62. package/android/lc3Lib/src/main/cpp/liblc3/liblc3.cpp +159 -0
  63. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/_kiss_fft_guts.h +182 -0
  64. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/arch.h +261 -0
  65. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.c +279 -0
  66. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.h +59 -0
  67. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/common.h +48 -0
  68. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/compile.sh +3 -0
  69. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/denoise.c +646 -0
  70. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.c +601 -0
  71. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.h +203 -0
  72. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/opus_types.h +159 -0
  73. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.c +526 -0
  74. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.h +149 -0
  75. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.c +178 -0
  76. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.h +69 -0
  77. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.c +11051 -0
  78. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.h +34 -0
  79. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_reader.c +168 -0
  80. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_train.py +66 -0
  81. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/tansig_table.h +45 -0
  82. package/android/lc3Lib/src/main/java/com/glassly/lc3Lib/Lc3Cpp.java +38 -0
  83. package/android/lc3Lib/third_party/CMakeLists.txt +23 -0
  84. package/android/lc3Lib/third_party/CMakeLists_libogg.txt +18 -0
  85. package/android/lc3Lib/third_party/CMakeLists_libopus.txt +18 -0
  86. package/android/lc3Lib/third_party/CMakeLists_opus-tools.txt +23 -0
  87. package/android/lc3Lib/third_party/opus_tools/src/AUTHORS +5 -0
  88. package/android/lc3Lib/third_party/opus_tools/src/CMakeLists.txt +4 -0
  89. package/android/lc3Lib/third_party/opus_tools/src/COPYING +371 -0
  90. package/android/lc3Lib/third_party/opus_tools/src/ChangeLog +3 -0
  91. package/android/lc3Lib/third_party/opus_tools/src/Makefile.am +42 -0
  92. package/android/lc3Lib/third_party/opus_tools/src/Makefile.unix +23 -0
  93. package/android/lc3Lib/third_party/opus_tools/src/NEWS +0 -0
  94. package/android/lc3Lib/third_party/opus_tools/src/README +0 -0
  95. package/android/lc3Lib/third_party/opus_tools/src/autogen.sh +115 -0
  96. package/android/lc3Lib/third_party/opus_tools/src/configure.ac +293 -0
  97. package/android/lc3Lib/third_party/opus_tools/src/src/arch.h +239 -0
  98. package/android/lc3Lib/third_party/opus_tools/src/src/audio-in.c +1046 -0
  99. package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.c +245 -0
  100. package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.h +28 -0
  101. package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.c +320 -0
  102. package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.h +51 -0
  103. package/android/lc3Lib/third_party/opus_tools/src/src/lpc.c +157 -0
  104. package/android/lc3Lib/third_party/opus_tools/src/src/lpc.h +27 -0
  105. package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.c +286 -0
  106. package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.h +59 -0
  107. package/android/lc3Lib/third_party/opus_tools/src/src/opusdec.c +884 -0
  108. package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.c +1021 -0
  109. package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.h +101 -0
  110. package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.c +639 -0
  111. package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.h +51 -0
  112. package/android/lc3Lib/third_party/opus_tools/src/src/os_support.h +167 -0
  113. package/android/lc3Lib/third_party/opus_tools/src/src/resample.c +1137 -0
  114. package/android/lc3Lib/third_party/opus_tools/src/src/speex_resampler.h +344 -0
  115. package/android/lc3Lib/third_party/opus_tools/src/src/stack_alloc.h +115 -0
  116. package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.c +125 -0
  117. package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.h +62 -0
  118. package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.c +223 -0
  119. package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.h +60 -0
  120. package/android/settings.gradle +4 -0
  121. package/android/silero/LICENSE.md +17 -0
  122. package/android/silero/build.gradle +50 -0
  123. package/android/silero/proguard-rules.pro +24 -0
  124. package/android/silero/src/androidTest/assets/hello.wav +0 -0
  125. package/android/silero/src/androidTest/java/com/konovalov/vad/silero/VadSileroTest.kt +79 -0
  126. package/android/silero/src/main/assets/LICENSE +21 -0
  127. package/android/silero/src/main/assets/silero_vad.onnx +0 -0
  128. package/android/silero/src/main/java/com/konovalov/vad/silero/Vad.kt +160 -0
  129. package/android/silero/src/main/java/com/konovalov/vad/silero/VadSilero.kt +449 -0
  130. package/android/silero/src/main/java/com/konovalov/vad/silero/config/FrameSize.kt +15 -0
  131. package/android/silero/src/main/java/com/konovalov/vad/silero/config/Mode.kt +14 -0
  132. package/android/silero/src/main/java/com/konovalov/vad/silero/config/SampleRate.kt +12 -0
  133. package/android/silero/src/main/java/com/konovalov/vad/silero/utils/AudioUtils.kt +47 -0
  134. package/android/silero/src/main/java/com/konovalov/vad/silero/utils/TensorMap.kt +41 -0
  135. package/android/silero/src/test/java/com/konovalov/vad/silero/utils/AudioUtilsTest.kt +44 -0
  136. package/android/src/main/AndroidManifest.xml +42 -0
  137. package/android/src/main/assets/G2Firmware/README.md +11 -0
  138. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdk.kt +2325 -0
  139. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkAnalytics.kt +216 -0
  140. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkDebug.kt +14 -0
  141. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkDefaults.kt +7 -0
  142. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkModule.kt +1088 -0
  143. package/android/src/main/java/com/glassly/bluetoothsdk/Bridge.kt +953 -0
  144. package/android/src/main/java/com/glassly/bluetoothsdk/DeviceManager.kt +2528 -0
  145. package/android/src/main/java/com/glassly/bluetoothsdk/DeviceStore.kt +360 -0
  146. package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedChangelogCatalog.kt +8 -0
  147. package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedReleaseMetadata.kt +11 -0
  148. package/android/src/main/java/com/glassly/bluetoothsdk/LocalNetworkModule.kt +414 -0
  149. package/android/src/main/java/com/glassly/bluetoothsdk/ObservableStore.kt +88 -0
  150. package/android/src/main/java/com/glassly/bluetoothsdk/OtaManifest.kt +191 -0
  151. package/android/src/main/java/com/glassly/bluetoothsdk/ReleaseChangelog.kt +61 -0
  152. package/android/src/main/java/com/glassly/bluetoothsdk/ScopedNetworkReadiness.kt +17 -0
  153. package/android/src/main/java/com/glassly/bluetoothsdk/audio/AudioModels.kt +144 -0
  154. package/android/src/main/java/com/glassly/bluetoothsdk/camera/CameraModels.kt +486 -0
  155. package/android/src/main/java/com/glassly/bluetoothsdk/connection/ConnectionModels.kt +78 -0
  156. package/android/src/main/java/com/glassly/bluetoothsdk/controllers/ControllerManager.kt +158 -0
  157. package/android/src/main/java/com/glassly/bluetoothsdk/controllers/R1.kt +937 -0
  158. package/android/src/main/java/com/glassly/bluetoothsdk/debug/BleTraceLogger.kt +165 -0
  159. package/android/src/main/java/com/glassly/bluetoothsdk/events/BluetoothEvents.kt +178 -0
  160. package/android/src/main/java/com/glassly/bluetoothsdk/internal/MapParsing.kt +140 -0
  161. package/android/src/main/java/com/glassly/bluetoothsdk/net/LocalIpv4.kt +57 -0
  162. package/android/src/main/java/com/glassly/bluetoothsdk/otaserver/LocalOtaServer.kt +304 -0
  163. package/android/src/main/java/com/glassly/bluetoothsdk/otaserver/OtaServerModule.kt +120 -0
  164. package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistry.java +108 -0
  165. package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoUploadServer.kt +622 -0
  166. package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/PhotoReceiverModule.kt +156 -0
  167. package/android/src/main/java/com/glassly/bluetoothsdk/requests/DisplayRequests.kt +56 -0
  168. package/android/src/main/java/com/glassly/bluetoothsdk/services/Foreground.kt +272 -0
  169. package/android/src/main/java/com/glassly/bluetoothsdk/services/PhoneMic.kt +1195 -0
  170. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Ar99.java +2653 -0
  171. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/BesOtaHeartbeatGuard.kt +49 -0
  172. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/BesOtaProgressMapper.kt +27 -0
  173. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G1.kt +3878 -0
  174. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G1TextSanitizer.kt +38 -0
  175. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2.kt +6428 -0
  176. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassification.kt +49 -0
  177. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwRenderer.kt +1241 -0
  178. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCache.kt +270 -0
  179. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2FlashProtocol.kt +291 -0
  180. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2Flasher.kt +411 -0
  181. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/GlasslyBle.java +43189 -0
  182. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Mach1.kt +1280 -0
  183. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLive.kt +10817 -0
  184. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLiveL2capChannel.kt +274 -0
  185. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLivePairingAdvertisement.kt +54 -0
  186. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraNex.kt +2011 -0
  187. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Nimo.kt +2532 -0
  188. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/SGCManager.kt +470 -0
  189. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Simulated.kt +228 -0
  190. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/Ar99OtaManager.java +566 -0
  191. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaByteUtils.java +18 -0
  192. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaCommandConstants.java +35 -0
  193. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaCrc32Util.java +13 -0
  194. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaGattTransport.java +11 -0
  195. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaProtocol.java +92 -0
  196. package/android/src/main/java/com/glassly/bluetoothsdk/speakerid/SpeakerIdEngine.kt +525 -0
  197. package/android/src/main/java/com/glassly/bluetoothsdk/status/DeviceStatus.kt +669 -0
  198. package/android/src/main/java/com/glassly/bluetoothsdk/status/RuntimeState.kt +201 -0
  199. package/android/src/main/java/com/glassly/bluetoothsdk/status/WifiHotspotStatus.kt +189 -0
  200. package/android/src/main/java/com/glassly/bluetoothsdk/streaming/StreamModels.kt +518 -0
  201. package/android/src/main/java/com/glassly/bluetoothsdk/stt/STTTools.kt +391 -0
  202. package/android/src/main/java/com/glassly/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +518 -0
  203. package/android/src/main/java/com/glassly/bluetoothsdk/stt/VadGateSpeechPolicy.kt +229 -0
  204. package/android/src/main/java/com/glassly/bluetoothsdk/tts/TTSTools.kt +248 -0
  205. package/android/src/main/java/com/glassly/bluetoothsdk/types/DeviceModels.kt +96 -0
  206. package/android/src/main/java/com/glassly/bluetoothsdk/utils/AES.java +106 -0
  207. package/android/src/main/java/com/glassly/bluetoothsdk/utils/AudioSessionMonitor.kt +189 -0
  208. package/android/src/main/java/com/glassly/bluetoothsdk/utils/AvifExifStripper.java +573 -0
  209. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BitmapJavaUtils.java +169 -0
  210. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BleJsonCompact.java +496 -0
  211. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BlePhotoUploadService.java +677 -0
  212. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BleWireProtocol.java +108 -0
  213. package/android/src/main/java/com/glassly/bluetoothsdk/utils/Constants.kt +46 -0
  214. package/android/src/main/java/com/glassly/bluetoothsdk/utils/G1Text.kt +436 -0
  215. package/android/src/main/java/com/glassly/bluetoothsdk/utils/HeifExifTagReader.java +199 -0
  216. package/android/src/main/java/com/glassly/bluetoothsdk/utils/IncidentLogBleRelayNaming.java +29 -0
  217. package/android/src/main/java/com/glassly/bluetoothsdk/utils/IncidentLogBleUploadService.java +77 -0
  218. package/android/src/main/java/com/glassly/bluetoothsdk/utils/K900LengthCodec.java +115 -0
  219. package/android/src/main/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtils.java +827 -0
  220. package/android/src/main/java/com/glassly/bluetoothsdk/utils/MessageChunkReassembler.java +228 -0
  221. package/android/src/main/java/com/glassly/bluetoothsdk/utils/MessageChunker.java +325 -0
  222. package/android/src/main/java/com/glassly/bluetoothsdk/utils/NexSGCUtils.kt +698 -0
  223. package/android/src/main/java/com/glassly/bluetoothsdk/utils/PhoneAudioMonitor.kt +348 -0
  224. package/android/src/main/java/com/glassly/bluetoothsdk/utils/SmartGlassesConnectionState.java +9 -0
  225. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/Ar99OpusPcmDecoder.java +398 -0
  226. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/ByteUtilAudioPlayer.java +655 -0
  227. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/Lc3Player.java +441 -0
  228. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/PCMAudioPlayer.java +431 -0
  229. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/PcmStreamPlayer.kt +328 -0
  230. package/android/src/main/res/values/strings.xml +4 -0
  231. package/android/src/test/java/com/glassly/bluetoothsdk/BluetoothSdkExceptionTest.kt +18 -0
  232. package/android/src/test/java/com/glassly/bluetoothsdk/OtaManifestDowngradeTest.kt +86 -0
  233. package/android/src/test/java/com/glassly/bluetoothsdk/OtaStartResponsePolicyTest.kt +30 -0
  234. package/android/src/test/java/com/glassly/bluetoothsdk/ReleaseChangelogTest.kt +13 -0
  235. package/android/src/test/java/com/glassly/bluetoothsdk/ScopedNetworkReadinessTest.kt +39 -0
  236. package/android/src/test/java/com/glassly/bluetoothsdk/StreamStatusEventTest.kt +29 -0
  237. package/android/src/test/java/com/glassly/bluetoothsdk/camera/PhotoRequestTest.kt +141 -0
  238. package/android/src/test/java/com/glassly/bluetoothsdk/otaserver/LocalOtaServerTest.kt +82 -0
  239. package/android/src/test/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistryTest.java +75 -0
  240. package/android/src/test/java/com/glassly/bluetoothsdk/services/ForegroundServiceTypeTest.kt +69 -0
  241. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/Ar99AdvertisementMatcherTest.java +22 -0
  242. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/BesOtaHeartbeatGuardTest.kt +73 -0
  243. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/BesOtaProgressMapperTest.kt +67 -0
  244. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/EvenRleTest.kt +63 -0
  245. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G1TextSanitizerTest.kt +31 -0
  246. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassificationTest.kt +89 -0
  247. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwRendererTest.kt +344 -0
  248. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCacheTest.kt +200 -0
  249. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2FlashProtocolTest.kt +131 -0
  250. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/MentraLiveGattCharacteristicsTest.kt +39 -0
  251. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/MentraLivePairingAdvertisementParserTest.kt +133 -0
  252. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/NimoProtocolTest.kt +333 -0
  253. package/android/src/test/java/com/glassly/bluetoothsdk/utils/AvifExifStripperTest.java +46 -0
  254. package/android/src/test/java/com/glassly/bluetoothsdk/utils/BinaryMessageChunkerTest.java +84 -0
  255. package/android/src/test/java/com/glassly/bluetoothsdk/utils/BleJsonCompactTest.java +319 -0
  256. package/android/src/test/java/com/glassly/bluetoothsdk/utils/BlePhotoUploadServiceTest.java +107 -0
  257. package/android/src/test/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtilsEndiannessTest.java +172 -0
  258. package/android/src/test/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtilsTest.java +24 -0
  259. package/android/src/test/java/com/glassly/bluetoothsdk/utils/PhoneAudioMonitorTest.kt +45 -0
  260. package/android/src/test/resources/avif_with_exif.avif +0 -0
  261. package/app.plugin.js +3 -0
  262. package/build/BluetoothSdk.types.d.ts +1354 -0
  263. package/build/BluetoothSdk.types.d.ts.map +1 -0
  264. package/build/BluetoothSdk.types.js +41 -0
  265. package/build/BluetoothSdk.types.js.map +1 -0
  266. package/build/_internal.d.ts +17 -0
  267. package/build/_internal.d.ts.map +1 -0
  268. package/build/_internal.js +16 -0
  269. package/build/_internal.js.map +1 -0
  270. package/build/_private/BluetoothSdkModule.d.ts +172 -0
  271. package/build/_private/BluetoothSdkModule.d.ts.map +1 -0
  272. package/build/_private/BluetoothSdkModule.js +300 -0
  273. package/build/_private/BluetoothSdkModule.js.map +1 -0
  274. package/build/_private/LocalNetworkModule.d.ts +38 -0
  275. package/build/_private/LocalNetworkModule.d.ts.map +1 -0
  276. package/build/_private/LocalNetworkModule.js +3 -0
  277. package/build/_private/LocalNetworkModule.js.map +1 -0
  278. package/build/_private/cameraRequestPayload.d.ts +4 -0
  279. package/build/_private/cameraRequestPayload.d.ts.map +1 -0
  280. package/build/_private/cameraRequestPayload.js +32 -0
  281. package/build/_private/cameraRequestPayload.js.map +1 -0
  282. package/build/_private/photoRequestPayload.d.ts +6 -0
  283. package/build/_private/photoRequestPayload.d.ts.map +1 -0
  284. package/build/_private/photoRequestPayload.js +86 -0
  285. package/build/_private/photoRequestPayload.js.map +1 -0
  286. package/build/_private/sdkOtaManifest.d.ts +11 -0
  287. package/build/_private/sdkOtaManifest.d.ts.map +1 -0
  288. package/build/_private/sdkOtaManifest.js +15 -0
  289. package/build/_private/sdkOtaManifest.js.map +1 -0
  290. package/build/changelogs.d.ts +7 -0
  291. package/build/changelogs.d.ts.map +1 -0
  292. package/build/changelogs.js +46 -0
  293. package/build/changelogs.js.map +1 -0
  294. package/build/debug.d.ts +3 -0
  295. package/build/debug.d.ts.map +1 -0
  296. package/build/debug.js +8 -0
  297. package/build/debug.js.map +1 -0
  298. package/build/generated/changelogCatalog.d.ts +9 -0
  299. package/build/generated/changelogCatalog.d.ts.map +1 -0
  300. package/build/generated/changelogCatalog.js +12 -0
  301. package/build/generated/changelogCatalog.js.map +1 -0
  302. package/build/generated/releaseMetadata.d.ts +12 -0
  303. package/build/generated/releaseMetadata.d.ts.map +1 -0
  304. package/build/generated/releaseMetadata.js +10 -0
  305. package/build/generated/releaseMetadata.js.map +1 -0
  306. package/build/index.d.ts +6 -0
  307. package/build/index.d.ts.map +1 -0
  308. package/build/index.js +171 -0
  309. package/build/index.js.map +1 -0
  310. package/build/ota-server/OtaServer.types.d.ts +14 -0
  311. package/build/ota-server/OtaServer.types.d.ts.map +1 -0
  312. package/build/ota-server/OtaServer.types.js +2 -0
  313. package/build/ota-server/OtaServer.types.js.map +1 -0
  314. package/build/ota-server/OtaServerModule.d.ts +21 -0
  315. package/build/ota-server/OtaServerModule.d.ts.map +1 -0
  316. package/build/ota-server/OtaServerModule.js +3 -0
  317. package/build/ota-server/OtaServerModule.js.map +1 -0
  318. package/build/ota-server/OtaServerModule.web.d.ts +10 -0
  319. package/build/ota-server/OtaServerModule.web.d.ts.map +1 -0
  320. package/build/ota-server/OtaServerModule.web.js +12 -0
  321. package/build/ota-server/OtaServerModule.web.js.map +1 -0
  322. package/build/ota-server/index.d.ts +3 -0
  323. package/build/ota-server/index.d.ts.map +1 -0
  324. package/build/ota-server/index.js +3 -0
  325. package/build/ota-server/index.js.map +1 -0
  326. package/build/ota-transport/index.d.ts +67 -0
  327. package/build/ota-transport/index.d.ts.map +1 -0
  328. package/build/ota-transport/index.js +58 -0
  329. package/build/ota-transport/index.js.map +1 -0
  330. package/build/photo-receiver/PhotoReceiver.types.d.ts +18 -0
  331. package/build/photo-receiver/PhotoReceiver.types.d.ts.map +1 -0
  332. package/build/photo-receiver/PhotoReceiver.types.js +2 -0
  333. package/build/photo-receiver/PhotoReceiver.types.js.map +1 -0
  334. package/build/photo-receiver/PhotoReceiverModule.d.ts +10 -0
  335. package/build/photo-receiver/PhotoReceiverModule.d.ts.map +1 -0
  336. package/build/photo-receiver/PhotoReceiverModule.js +3 -0
  337. package/build/photo-receiver/PhotoReceiverModule.js.map +1 -0
  338. package/build/photo-receiver/PhotoReceiverModule.web.d.ts +10 -0
  339. package/build/photo-receiver/PhotoReceiverModule.web.d.ts.map +1 -0
  340. package/build/photo-receiver/PhotoReceiverModule.web.js +12 -0
  341. package/build/photo-receiver/PhotoReceiverModule.web.js.map +1 -0
  342. package/build/photo-receiver/index.d.ts +3 -0
  343. package/build/photo-receiver/index.d.ts.map +1 -0
  344. package/build/photo-receiver/index.js +3 -0
  345. package/build/photo-receiver/index.js.map +1 -0
  346. package/build/react/index.d.ts +5 -0
  347. package/build/react/index.d.ts.map +1 -0
  348. package/build/react/index.js +4 -0
  349. package/build/react/index.js.map +1 -0
  350. package/build/react/useBluetooth.d.ts +100 -0
  351. package/build/react/useBluetooth.d.ts.map +1 -0
  352. package/build/react/useBluetooth.js +167 -0
  353. package/build/react/useBluetooth.js.map +1 -0
  354. package/build/react/useBluetoothEvent.d.ts +6 -0
  355. package/build/react/useBluetoothEvent.d.ts.map +1 -0
  356. package/build/react/useBluetoothEvent.js +21 -0
  357. package/build/react/useBluetoothEvent.js.map +1 -0
  358. package/build/react/useBluetoothScan.d.ts +22 -0
  359. package/build/react/useBluetoothScan.d.ts.map +1 -0
  360. package/build/react/useBluetoothScan.js +135 -0
  361. package/build/react/useBluetoothScan.js.map +1 -0
  362. package/build/react/useBluetoothStatus.d.ts +16 -0
  363. package/build/react/useBluetoothStatus.d.ts.map +1 -0
  364. package/build/react/useBluetoothStatus.js +137 -0
  365. package/build/react/useBluetoothStatus.js.map +1 -0
  366. package/build/react/useGlassesConnection.d.ts +29 -0
  367. package/build/react/useGlassesConnection.d.ts.map +1 -0
  368. package/build/react/useGlassesConnection.js +153 -0
  369. package/build/react/useGlassesConnection.js.map +1 -0
  370. package/build/types/index.d.ts +3 -0
  371. package/build/types/index.d.ts.map +1 -0
  372. package/build/types/index.js +2 -0
  373. package/build/types/index.js.map +1 -0
  374. package/expo-module.config.json +14 -0
  375. package/ios/BackgroundOtaArtifactDownloader.swift +125 -0
  376. package/ios/BluetoothSdkModule.swift +1034 -0
  377. package/ios/GlasslyBluetoothSDK.podspec +106 -0
  378. package/ios/LocalIPv4.swift +150 -0
  379. package/ios/LocalOtaServer.swift +309 -0
  380. package/ios/LocalPhotoUploadServer.swift +754 -0
  381. package/ios/OtaServerModule.swift +147 -0
  382. package/ios/Packages/CoreObjC/CoreObjC.xcodeproj/project.pbxproj +213 -0
  383. package/ios/Packages/CoreObjC/PcmConverter.h +22 -0
  384. package/ios/Packages/CoreObjC/PcmConverter.m +266 -0
  385. package/ios/Packages/CoreObjC/attdet.c +92 -0
  386. package/ios/Packages/CoreObjC/attdet.h +44 -0
  387. package/ios/Packages/CoreObjC/bits.c +375 -0
  388. package/ios/Packages/CoreObjC/bits.h +315 -0
  389. package/ios/Packages/CoreObjC/bwdet.c +129 -0
  390. package/ios/Packages/CoreObjC/bwdet.h +69 -0
  391. package/ios/Packages/CoreObjC/common.h +151 -0
  392. package/ios/Packages/CoreObjC/energy.c +70 -0
  393. package/ios/Packages/CoreObjC/energy.h +43 -0
  394. package/ios/Packages/CoreObjC/fastmath.h +158 -0
  395. package/ios/Packages/CoreObjC/include/PcmConverter.h +22 -0
  396. package/ios/Packages/CoreObjC/lc3.c +704 -0
  397. package/ios/Packages/CoreObjC/lc3.h +313 -0
  398. package/ios/Packages/CoreObjC/lc3_cpp.h +283 -0
  399. package/ios/Packages/CoreObjC/lc3_private.h +163 -0
  400. package/ios/Packages/CoreObjC/ltpf.c +905 -0
  401. package/ios/Packages/CoreObjC/ltpf.h +111 -0
  402. package/ios/Packages/CoreObjC/ltpf_arm.h +506 -0
  403. package/ios/Packages/CoreObjC/ltpf_neon.h +281 -0
  404. package/ios/Packages/CoreObjC/makefile.mk +35 -0
  405. package/ios/Packages/CoreObjC/mdct.c +469 -0
  406. package/ios/Packages/CoreObjC/mdct.h +57 -0
  407. package/ios/Packages/CoreObjC/mdct_neon.h +296 -0
  408. package/ios/Packages/CoreObjC/meson.build +61 -0
  409. package/ios/Packages/CoreObjC/plc.c +61 -0
  410. package/ios/Packages/CoreObjC/plc.h +57 -0
  411. package/ios/Packages/CoreObjC/rnnoise.h +114 -0
  412. package/ios/Packages/CoreObjC/sns.c +880 -0
  413. package/ios/Packages/CoreObjC/sns.h +103 -0
  414. package/ios/Packages/CoreObjC/spec.c +907 -0
  415. package/ios/Packages/CoreObjC/spec.h +119 -0
  416. package/ios/Packages/CoreObjC/tables.c +3457 -0
  417. package/ios/Packages/CoreObjC/tables.h +94 -0
  418. package/ios/Packages/CoreObjC/tns.c +457 -0
  419. package/ios/Packages/CoreObjC/tns.h +99 -0
  420. package/ios/Packages/SherpaOnnx/Model/joiner.onnx +0 -0
  421. package/ios/Packages/SherpaOnnx/Model/tokens.txt +502 -0
  422. package/ios/Packages/SherpaOnnx/SherpaOnnx.swift +2383 -0
  423. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Info.plist +48 -0
  424. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/c-api.h +4689 -0
  425. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/cxx-api.h +2036 -0
  426. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/libsherpa-onnx.a +0 -0
  427. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/c-api.h +4689 -0
  428. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/cxx-api.h +2036 -0
  429. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a +0 -0
  430. package/ios/Packages/libbz2/module.modulemap +5 -0
  431. package/ios/Packages/libbz2/shim.h +1 -0
  432. package/ios/PhotoReceiverModule.swift +129 -0
  433. package/ios/Source/BluetoothSDK.swift +2303 -0
  434. package/ios/Source/BluetoothSDKDebug.swift +12 -0
  435. package/ios/Source/BluetoothSdkDefaults.swift +29 -0
  436. package/ios/Source/Bridge.swift +708 -0
  437. package/ios/Source/Bridging-Header.h +16 -0
  438. package/ios/Source/DeviceManager.swift +2109 -0
  439. package/ios/Source/DeviceStore.swift +336 -0
  440. package/ios/Source/GeneratedChangelogCatalog.swift +7 -0
  441. package/ios/Source/GeneratedReleaseMetadata.swift +11 -0
  442. package/ios/Source/ObservableStore.swift +118 -0
  443. package/ios/Source/OtaManifest.swift +211 -0
  444. package/ios/Source/PrivacyInfo.xcprivacy +23 -0
  445. package/ios/Source/ReleaseChangelog.swift +65 -0
  446. package/ios/Source/Resources/G2Firmware/README.md +10 -0
  447. package/ios/Source/audio/AudioModels.swift +178 -0
  448. package/ios/Source/audio/PcmStreamPlayer.swift +410 -0
  449. package/ios/Source/camera/CameraModels.swift +866 -0
  450. package/ios/Source/connection/ScanSession.swift +27 -0
  451. package/ios/Source/controllers/ControllerManager.swift +184 -0
  452. package/ios/Source/controllers/R1.swift +829 -0
  453. package/ios/Source/errors/BluetoothSdkError.swift +19 -0
  454. package/ios/Source/events/BluetoothEvents.swift +342 -0
  455. package/ios/Source/internal/BleTraceLogger.swift +192 -0
  456. package/ios/Source/internal/BluetoothAvailability.swift +78 -0
  457. package/ios/Source/internal/BluetoothSdkAnalytics.swift +199 -0
  458. package/ios/Source/internal/ValueParsing.swift +99 -0
  459. package/ios/Source/requests/DisplayRequests.swift +82 -0
  460. package/ios/Source/services/PhoneMic.swift +662 -0
  461. package/ios/Source/sgcs/Ar99.swift +2451 -0
  462. package/ios/Source/sgcs/Frame.swift +649 -0
  463. package/ios/Source/sgcs/G1.swift +2554 -0
  464. package/ios/Source/sgcs/G2.swift +6887 -0
  465. package/ios/Source/sgcs/G2CfwClassification.swift +43 -0
  466. package/ios/Source/sgcs/G2CfwRenderer.swift +1279 -0
  467. package/ios/Source/sgcs/G2CfwTextureCache.swift +233 -0
  468. package/ios/Source/sgcs/G2FlashProtocol.swift +312 -0
  469. package/ios/Source/sgcs/G2Flasher.swift +773 -0
  470. package/ios/Source/sgcs/Mach1.swift +541 -0
  471. package/ios/Source/sgcs/MentraLive.swift +7011 -0
  472. package/ios/Source/sgcs/MentraLiveL2capChannel.swift +183 -0
  473. package/ios/Source/sgcs/MentraLivePairingAdvertisement.swift +64 -0
  474. package/ios/Source/sgcs/MentraNex.swift +3221 -0
  475. package/ios/Source/sgcs/Nimo.swift +1886 -0
  476. package/ios/Source/sgcs/SGCManager.swift +608 -0
  477. package/ios/Source/sgcs/Simulated.swift +274 -0
  478. package/ios/Source/sgcs/ar99/ota/Ar99OtaManager.swift +637 -0
  479. package/ios/Source/sgcs/glassly_ble.pb.swift +4354 -0
  480. package/ios/Source/speakerid/SpeakerIdEngine.swift +573 -0
  481. package/ios/Source/status/DeviceStatus.swift +981 -0
  482. package/ios/Source/status/RuntimeState.swift +359 -0
  483. package/ios/Source/status/WifiHotspotStatus.swift +345 -0
  484. package/ios/Source/streaming/StreamModels.swift +693 -0
  485. package/ios/Source/stt/STTTools.swift +191 -0
  486. package/ios/Source/stt/SherpaOnnxTranscriber.swift +466 -0
  487. package/ios/Source/tts/TTSTools.swift +171 -0
  488. package/ios/Source/types/DeviceModels.swift +177 -0
  489. package/ios/Source/utils/AudioSessionMonitor.swift +283 -0
  490. package/ios/Source/utils/BinaryCodec.swift +61 -0
  491. package/ios/Source/utils/BleJsonCompact.swift +405 -0
  492. package/ios/Source/utils/BleWireProtocol.swift +92 -0
  493. package/ios/Source/utils/Constants.swift +78 -0
  494. package/ios/Source/utils/Enums.swift +95 -0
  495. package/ios/Source/utils/G1Text.swift +2096 -0
  496. package/ios/Source/utils/JSCExperiment.swift +241 -0
  497. package/ios/Source/utils/MemoryMonitor.swift +44 -0
  498. package/ios/Source/utils/MessageChunkReassembler.swift +190 -0
  499. package/ios/Source/utils/MessageChunker.swift +294 -0
  500. package/ios/Source/utils/Models.swift +135 -0
  501. package/ios/Source/utils/PhoneAudioMonitor.swift +230 -0
  502. package/ios/Source/utils/TarBz2Extractor.swift +257 -0
  503. package/ios/Tests/BluetoothSDKTests/G2CfwClassificationTests.swift +78 -0
  504. package/ios/Tests/BluetoothSDKTests/G2CfwRendererTests.swift +372 -0
  505. package/ios/Tests/BluetoothSDKTests/G2CfwTextureCacheTests.swift +162 -0
  506. package/ios/Tests/BluetoothSDKTests/G2FlashProtocolTests.swift +182 -0
  507. package/ios/Tests/MentraLivePairingAdvertisementTests.swift +160 -0
  508. package/ios/Tests/ReleaseChangelogTests.swift +13 -0
  509. package/package.json +129 -0
  510. package/plugin/build/index.d.ts +10 -0
  511. package/plugin/build/index.js +12 -0
  512. package/plugin/build/withAndroid.d.ts +3 -0
  513. package/plugin/build/withAndroid.js +190 -0
  514. package/plugin/build/withIos.d.ts +5 -0
  515. package/plugin/build/withIos.js +108 -0
  516. package/scripts/inject-ios-sdk-version.mjs +65 -0
  517. package/scripts/public-ota-api.test.mjs +37 -0
  518. package/scripts/verify-release-package.mjs +45 -0
  519. package/scripts/write-release-metadata.mjs +152 -0
  520. package/scripts/write-release-metadata.test.mjs +37 -0
  521. package/src/BluetoothSdk.types.ts +1623 -0
  522. package/src/_internal.ts +16 -0
  523. package/src/_private/BluetoothSdkModule.ts +684 -0
  524. package/src/_private/LocalNetworkModule.ts +50 -0
  525. package/src/_private/cameraRequestPayload.ts +34 -0
  526. package/src/_private/photoRequestPayload.ts +90 -0
  527. package/src/_private/sdkOtaManifest.ts +18 -0
  528. package/src/changelogs.ts +46 -0
  529. package/src/debug.ts +9 -0
  530. package/src/generated/changelogCatalog.ts +13 -0
  531. package/src/generated/releaseMetadata.ts +20 -0
  532. package/src/index.ts +321 -0
  533. package/src/ota-server/OtaServer.types.ts +10 -0
  534. package/src/ota-server/OtaServerModule.ts +23 -0
  535. package/src/ota-server/OtaServerModule.web.ts +17 -0
  536. package/src/ota-server/index.ts +2 -0
  537. package/src/ota-transport/index.ts +116 -0
  538. package/src/photo-receiver/PhotoReceiver.types.ts +20 -0
  539. package/src/photo-receiver/PhotoReceiverModule.ts +11 -0
  540. package/src/photo-receiver/PhotoReceiverModule.web.ts +17 -0
  541. package/src/photo-receiver/index.ts +2 -0
  542. package/src/react/index.ts +21 -0
  543. package/src/react/useBluetooth.ts +309 -0
  544. package/src/react/useBluetoothEvent.ts +35 -0
  545. package/src/react/useBluetoothScan.ts +173 -0
  546. package/src/react/useBluetoothStatus.ts +180 -0
  547. package/src/react/useGlassesConnection.ts +202 -0
  548. package/src/types/index.ts +12 -0
@@ -0,0 +1,294 @@
1
+ import Foundation
2
+
3
+ /**
4
+ * Handles chunking of large messages that exceed BLE transmission limits.
5
+ * Messages are split at the JSON layer to work within MCU protocol constraints.
6
+ *
7
+ * Uses compact keys to minimize overhead per chunk:
8
+ * t = "ck" (chunk type identifier)
9
+ * id = chunk session ID
10
+ * c = chunk index (0-based)
11
+ * n = total number of chunks
12
+ * d = chunk data payload
13
+ *
14
+ * Each chunk after C-wrapping + K900 framing must fit within the BES2700's
15
+ * 253-byte BLE write limit. Payload size is selected by measuring the final
16
+ * packed chunk so JSON escaping cannot push a chunk over the BLE limit.
17
+ */
18
+ class MessageChunker {
19
+ // Threshold: if C-wrapped message exceeds this, chunking is triggered.
20
+ // BES2700 limit is 253 bytes; anything over ~200 bytes packed needs chunking.
21
+ private static let MESSAGE_SIZE_THRESHOLD = 200
22
+
23
+ private static let INITIAL_CHUNK_DATA_SIZE = 80
24
+ private static let MIN_CHUNK_DATA_SIZE = 4
25
+ private static let MAX_PACKED_CHUNK_SIZE = 253
26
+ private static let K900_FRAME_OVERHEAD = 7
27
+ private static let MAX_BINARY_FRAGMENT_PAYLOAD = BleWireProtocol.maxFragmentPayload
28
+ private static let MAX_BINARY_FRAME_SIZE = BleWireProtocol.mtuTarget
29
+
30
+ /**
31
+ * Check if a message needs to be chunked
32
+ * @param message The complete message string (already C-wrapped)
33
+ * @return true if message exceeds threshold and needs chunking
34
+ */
35
+ static func needsChunking(_ message: String?) -> Bool {
36
+ guard let message = message else {
37
+ return false
38
+ }
39
+
40
+ let messageBytes = message.data(using: .utf8)?.count ?? 0
41
+ let needsChunking = messageBytes > MESSAGE_SIZE_THRESHOLD
42
+
43
+ if needsChunking {
44
+ print("MessageChunker: Message size \(messageBytes) exceeds threshold \(MESSAGE_SIZE_THRESHOLD), will chunk")
45
+ }
46
+
47
+ return needsChunking
48
+ }
49
+
50
+ /**
51
+ * Create chunks from a message that's too large for single transmission.
52
+ * Uses compact keys to minimize per-chunk overhead.
53
+ * @param originalJson The original JSON string to be sent (before C-wrapping)
54
+ * @param messageId The message ID for ACK tracking (if applicable)
55
+ * @return Array of chunk dictionaries ready to be C-wrapped and sent
56
+ */
57
+ static func createChunks(originalJson: String, messageId: Int64 = -1, wakeUp: Bool = false) -> [[String: Any]] {
58
+ guard let messageData = originalJson.data(using: .utf8) else {
59
+ print("MessageChunker: Failed to convert message to data")
60
+ return []
61
+ }
62
+
63
+ let totalBytes = messageData.count
64
+
65
+ // Compact chunk session ID: messageId_timestamp (no "chunk_" prefix)
66
+ let chunkId = "\(messageId)_\(Int(Date().timeIntervalSince1970 * 1000))"
67
+
68
+ for chunkSize in stride(from: INITIAL_CHUNK_DATA_SIZE, through: MIN_CHUNK_DATA_SIZE, by: -1) {
69
+ let chunks = buildChunks(messageData, chunkId: chunkId, messageId: messageId, chunkSize: chunkSize)
70
+ if allChunksFit(chunks, wakeUp: wakeUp) {
71
+ print("MessageChunker: Creating \(chunks.count) chunks for message of size \(totalBytes) bytes using \(chunkSize)-byte UTF-8 slices")
72
+ return chunks
73
+ }
74
+ }
75
+
76
+ print("MessageChunker: Unable to create K900 chunks within \(MAX_PACKED_CHUNK_SIZE) bytes")
77
+ return []
78
+ }
79
+
80
+ private static func buildChunks(_ messageData: Data, chunkId: String, messageId: Int64, chunkSize: Int) -> [[String: Any]] {
81
+ var chunks: [[String: Any]] = []
82
+ let chunkStrings = splitUtf8(messageData, chunkSize: chunkSize)
83
+ let totalChunks = chunkStrings.count
84
+
85
+ for i in 0 ..< totalChunks {
86
+ let chunkString = chunkStrings[i]
87
+
88
+ // Create chunk dictionary with compact keys
89
+ var chunk: [String: Any] = [
90
+ "t": "ck",
91
+ "id": chunkId,
92
+ "c": i,
93
+ "n": totalChunks,
94
+ "d": chunkString,
95
+ ]
96
+
97
+ // Add message ID to final chunk only for ACK tracking
98
+ if i == totalChunks - 1, messageId != -1 {
99
+ chunk["mId"] = messageId
100
+ }
101
+
102
+ chunks.append(chunk)
103
+
104
+ print("MessageChunker: Created chunk \(i)/\(totalChunks - 1) with \(chunkString.data(using: .utf8)?.count ?? 0) bytes")
105
+ }
106
+
107
+ return chunks
108
+ }
109
+
110
+ private static func allChunksFit(_ chunks: [[String: Any]], wakeUp: Bool) -> Bool {
111
+ for (index, chunk) in chunks.enumerated() {
112
+ let packedLength = packedK900Length(chunk, wakeUp: wakeUp && index == 0)
113
+ if packedLength == nil || packedLength! > MAX_PACKED_CHUNK_SIZE {
114
+ print("MessageChunker: Chunk \(index) packed to \(packedLength ?? 0) bytes, exceeding \(MAX_PACKED_CHUNK_SIZE)")
115
+ return false
116
+ }
117
+ }
118
+ return true
119
+ }
120
+
121
+ private static func packedK900Length(_ chunk: [String: Any], wakeUp: Bool) -> Int? {
122
+ guard let chunkData = try? JSONSerialization.data(withJSONObject: chunk),
123
+ let chunkString = String(data: chunkData, encoding: .utf8)
124
+ else {
125
+ return nil
126
+ }
127
+
128
+ var wrapper: [String: Any] = ["C": chunkString]
129
+ if wakeUp {
130
+ wrapper["W"] = 1
131
+ }
132
+
133
+ guard let wrappedData = try? JSONSerialization.data(withJSONObject: wrapper) else {
134
+ return nil
135
+ }
136
+
137
+ return wrappedData.count + K900_FRAME_OVERHEAD
138
+ }
139
+
140
+ private static func splitUtf8(_ messageData: Data, chunkSize: Int) -> [String] {
141
+ var chunkStrings: [String] = []
142
+ var offset = 0
143
+ while offset < messageData.count {
144
+ let endIndex = findUtf8ChunkEnd(messageData, startIndex: offset, chunkSize: chunkSize)
145
+ let chunkData = messageData.subdata(in: offset ..< endIndex)
146
+ chunkStrings.append(String(data: chunkData, encoding: .utf8)!)
147
+ offset = endIndex
148
+ }
149
+ return chunkStrings
150
+ }
151
+
152
+ private static func findUtf8ChunkEnd(_ messageData: Data, startIndex: Int, chunkSize: Int) -> Int {
153
+ var endIndex = min(startIndex + chunkSize, messageData.count)
154
+ while endIndex > startIndex, endIndex < messageData.count, isUtf8ContinuationByte(messageData[endIndex]) {
155
+ endIndex -= 1
156
+ }
157
+ return endIndex > startIndex ? endIndex : min(startIndex + chunkSize, messageData.count)
158
+ }
159
+
160
+ private static func isUtf8ContinuationByte(_ value: UInt8) -> Bool {
161
+ (value & 0xC0) == 0x80
162
+ }
163
+
164
+ /**
165
+ * Check if a received message is a chunked message.
166
+ * Supports both verbose ("type":"chunked_msg") and compact ("t":"ck") formats.
167
+ * @param json The received dictionary (after C-unwrapping)
168
+ * @return true if this is a chunked message
169
+ */
170
+ static func isChunkedMessage(_ json: [String: Any]?) -> Bool {
171
+ guard let json = json else {
172
+ return false
173
+ }
174
+
175
+ let type = json["type"] as? String ?? json["t"] as? String ?? ""
176
+ return type == "chunked_msg" || type == "ck"
177
+ }
178
+
179
+ /**
180
+ * Extract chunk information from a chunked message.
181
+ * Supports both verbose and compact key formats.
182
+ */
183
+ static func getChunkInfo(_ json: [String: Any]) -> ChunkInfo? {
184
+ guard isChunkedMessage(json) else {
185
+ return nil
186
+ }
187
+
188
+ // Support both verbose and compact keys
189
+ guard let chunkId = (json["chunkId"] as? String) ?? (json["id"] as? String),
190
+ let chunkIndex = (json["chunk"] as? Int) ?? (json["c"] as? Int),
191
+ let totalChunks = (json["total"] as? Int) ?? (json["n"] as? Int),
192
+ let data = (json["data"] as? String) ?? (json["d"] as? String)
193
+ else {
194
+ print("MessageChunker: Failed to extract chunk info from JSON")
195
+ return nil
196
+ }
197
+
198
+ let messageId = json["mId"] as? Int64 ?? -1
199
+
200
+ return ChunkInfo(
201
+ chunkId: chunkId,
202
+ chunkIndex: chunkIndex,
203
+ totalChunks: totalChunks,
204
+ data: data,
205
+ messageId: messageId
206
+ )
207
+ }
208
+
209
+ static func needsBinaryFragmenting(_ payload: Data) -> Bool {
210
+ payload.count > MAX_BINARY_FRAGMENT_PAYLOAD
211
+ }
212
+
213
+ static func needsBinaryFragmenting(_ json: String) -> Bool {
214
+ guard let data = json.data(using: .utf8) else { return false }
215
+ return needsBinaryFragmenting(data)
216
+ }
217
+
218
+ struct BinaryFragment {
219
+ let flags: UInt8
220
+ let msgId: UInt16
221
+ let fragIdx: UInt8
222
+ let fragCount: UInt8
223
+ let payload: Data
224
+ }
225
+
226
+ static func createBinaryFragments(
227
+ payload: Data,
228
+ msgId: UInt16 = 0,
229
+ wakeUp: Bool = false,
230
+ ackRequested: Bool = false
231
+ ) -> [BinaryFragment] {
232
+ let fragCount = max(1, (payload.count + MAX_BINARY_FRAGMENT_PAYLOAD - 1) / MAX_BINARY_FRAGMENT_PAYLOAD)
233
+ guard fragCount <= 255 else { return [] }
234
+
235
+ var fragments: [BinaryFragment] = []
236
+ for i in 0 ..< fragCount {
237
+ let offset = i * MAX_BINARY_FRAGMENT_PAYLOAD
238
+ let end = min(offset + MAX_BINARY_FRAGMENT_PAYLOAD, payload.count)
239
+ let fragPayload = payload.subdata(in: offset ..< end)
240
+
241
+ var flags: UInt8 = 0
242
+ if i == 0 { flags |= BleWireProtocol.flagFirstFrag }
243
+ if i == fragCount - 1 { flags |= BleWireProtocol.flagLastFrag }
244
+ if wakeUp, i == 0 { flags |= BleWireProtocol.flagWake }
245
+ if ackRequested, i == fragCount - 1 { flags |= BleWireProtocol.flagAckRequested }
246
+
247
+ fragments.append(
248
+ BinaryFragment(
249
+ flags: flags,
250
+ msgId: msgId,
251
+ fragIdx: UInt8(i),
252
+ fragCount: UInt8(fragCount),
253
+ payload: fragPayload
254
+ )
255
+ )
256
+ }
257
+
258
+ print("MessageChunker: Created \(fragments.count) binary fragments for \(payload.count) bytes")
259
+ return fragments
260
+ }
261
+
262
+ static func allBinaryFragmentsFit(_ fragments: [BinaryFragment]) -> Bool {
263
+ for fragment in fragments {
264
+ guard let packed = BleWireProtocol.packBinaryFragment(
265
+ flags: fragment.flags,
266
+ msgId: fragment.msgId,
267
+ fragIdx: fragment.fragIdx,
268
+ fragCount: fragment.fragCount,
269
+ payload: fragment.payload
270
+ ) else {
271
+ return false
272
+ }
273
+ if packed.count > MAX_BINARY_FRAME_SIZE {
274
+ return false
275
+ }
276
+ }
277
+ return true
278
+ }
279
+
280
+ /**
281
+ * Container for chunk information
282
+ */
283
+ struct ChunkInfo {
284
+ let chunkId: String
285
+ let chunkIndex: Int
286
+ let totalChunks: Int
287
+ let data: String
288
+ let messageId: Int64
289
+
290
+ var isFinalChunk: Bool {
291
+ return chunkIndex == totalChunks - 1
292
+ }
293
+ }
294
+ }
@@ -0,0 +1,135 @@
1
+ //
2
+ // Models.swift
3
+ // Glassly_Manager
4
+ //
5
+ // Created by Matthew Fosse on 3/3/25.
6
+ //
7
+
8
+ import Foundation
9
+
10
+ struct AiResponseToG1Model {
11
+ var lines: [String]
12
+ var totalPages: UInt8
13
+ var newScreen: Bool
14
+ var currentPage: UInt8 {
15
+ didSet {
16
+ print("SET : currentPage :\(currentPage)")
17
+ }
18
+ }
19
+
20
+ var maxPages: UInt8
21
+ var status: DisplayStatus
22
+ }
23
+
24
+ struct ThirdPartyCloudApp {
25
+ let packageName: String
26
+ let name: String
27
+ let description: String
28
+ let webhookURL: String
29
+ let logoURL: String
30
+ let isRunning: Bool
31
+ }
32
+
33
+ /// NCSNotification structure
34
+ struct NCSNotification: Codable {
35
+ let msgId: Int
36
+ let type: Int
37
+ let appIdentifier: String
38
+ let title: String
39
+ let subtitle: String
40
+ let message: String
41
+ let timeS: Int
42
+ let date: String
43
+ let displayName: String
44
+
45
+ enum CodingKeys: String, CodingKey {
46
+ case msgId = "msg_id"
47
+ case type
48
+ case appIdentifier = "app_identifier"
49
+ case title
50
+ case subtitle
51
+ case message
52
+ case timeS = "time_s"
53
+ case date
54
+ case displayName = "display_name"
55
+ }
56
+
57
+ init(msgId: Int, appIdentifier: String, title: String, subtitle: String, message: String, displayName: String, type: Int = 1) {
58
+ self.msgId = msgId
59
+ self.type = type
60
+ self.appIdentifier = appIdentifier
61
+ self.title = title
62
+ self.subtitle = subtitle
63
+ self.message = message
64
+ timeS = Int(Date().timeIntervalSince1970)
65
+
66
+ let dateFormatter = DateFormatter()
67
+ dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
68
+ date = dateFormatter.string(from: Date())
69
+
70
+ self.displayName = displayName
71
+ }
72
+ }
73
+
74
+ /// Notification structure
75
+ struct G1Notification: Codable {
76
+ let ncsNotification: NCSNotification
77
+ let type: String = "Add"
78
+
79
+ enum CodingKeys: String, CodingKey {
80
+ case ncsNotification = "ncs_notification"
81
+ case type
82
+ }
83
+
84
+ /// Convert to JSON dictionary
85
+ func toJson() -> [String: Any] {
86
+ guard let data = try? JSONEncoder().encode(self) else { return [:] }
87
+ guard let jsonObject = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
88
+ return [:]
89
+ }
90
+ return jsonObject
91
+ }
92
+
93
+ /// Convert to JSON Data
94
+ func toData() -> Data? {
95
+ guard let jsonString = try? JSONEncoder().encode(self) else { return nil }
96
+ return jsonString
97
+ }
98
+
99
+ /// Build notification chunks
100
+ func constructNotification() async -> [[UInt8]] {
101
+ guard let jsonData = toData() else { return [] }
102
+
103
+ let maxChunkSize = 176 // 180 - 4 bytes for header
104
+ var chunks: [Data] = []
105
+
106
+ // Split data into chunks
107
+ var offset = 0
108
+ while offset < jsonData.count {
109
+ let endIndex = min(offset + maxChunkSize, jsonData.count)
110
+ let chunkData = jsonData.subdata(in: offset ..< endIndex)
111
+ chunks.append(chunkData)
112
+ offset = endIndex
113
+ }
114
+
115
+ let totalChunks = UInt8(chunks.count)
116
+ var encodedChunks: [[UInt8]] = []
117
+
118
+ // Create packets with headers
119
+ for (index, chunk) in chunks.enumerated() {
120
+ let notifyId: UInt8 = 0 // Set appropriate notification ID
121
+ let header: [UInt8] = [0x4B, notifyId, totalChunks, UInt8(index)]
122
+
123
+ // Convert chunk data to array of bytes
124
+ var chunkBytes = [UInt8](chunk)
125
+
126
+ // Combine header and chunk
127
+ var encodedChunk = header
128
+ encodedChunk.append(contentsOf: chunkBytes)
129
+
130
+ encodedChunks.append(encodedChunk)
131
+ }
132
+
133
+ return encodedChunks
134
+ }
135
+ }
@@ -0,0 +1,230 @@
1
+ //
2
+ // PhoneAudioMonitor.swift
3
+ // AOS
4
+ //
5
+ // Monitors iOS system audio playback state
6
+ //
7
+ // Used to detect when the phone is playing audio (music, podcasts, etc.)
8
+ // so we can temporarily suspend the LC3 microphone on MentraLive glasses
9
+ // to avoid overloading the MCU when both A2DP output and LC3 mic input
10
+ // are active simultaneously.
11
+ //
12
+ // This class mirrors the Android PhoneAudioMonitor.kt implementation.
13
+ //
14
+
15
+ import AVFoundation
16
+ import Foundation
17
+
18
+ /// Listener protocol for phone audio state changes
19
+ protocol PhoneAudioMonitorListener: AnyObject {
20
+ func onPhoneAudioStateChanged(isPlaying: Bool)
21
+ }
22
+
23
+ /// Monitors system audio playback to detect when the phone is playing audio
24
+ class PhoneAudioMonitor {
25
+ /// Singleton instance
26
+ private static var instance: PhoneAudioMonitor?
27
+
28
+ private weak var listener: PhoneAudioMonitorListener?
29
+ private var isMonitoring = false
30
+ private var lastKnownState = false
31
+
32
+ /// Track our own app's audio playback state
33
+ /// isOtherAudioPlaying only detects OTHER apps, not our own
34
+ private var ownAppAudioPlaying = false
35
+
36
+ // Polling timer for fallback detection
37
+ private var pollingTimer: Timer?
38
+ private let pollingIntervalSeconds: TimeInterval = 1.0
39
+
40
+ private init() {
41
+ Bridge.log("PhoneAudioMonitor: Initialized")
42
+ }
43
+
44
+ static func getInstance() -> PhoneAudioMonitor {
45
+ if instance == nil {
46
+ instance = PhoneAudioMonitor()
47
+ }
48
+ return instance!
49
+ }
50
+
51
+ /// Check if any audio is currently playing on the device (including our own app)
52
+ func isPlaying() -> Bool {
53
+ let session = AVAudioSession.sharedInstance()
54
+ // Combine: other apps playing OR our own app playing
55
+ return session.isOtherAudioPlaying || ownAppAudioPlaying
56
+ }
57
+
58
+ func isOwnAppAudioPlaying() -> Bool {
59
+ ownAppAudioPlaying
60
+ }
61
+
62
+ /// Notify the monitor that our own app started/stopped playing audio
63
+ /// Called from RN AudioPlaybackService via Bridge
64
+ func setOwnAppAudioPlaying(_ playing: Bool) {
65
+ guard ownAppAudioPlaying != playing else { return }
66
+
67
+ ownAppAudioPlaying = playing
68
+ Bridge.log("PhoneAudioMonitor: Own app audio -> \(playing ? "PLAYING" : "STOPPED")")
69
+
70
+ // Immediately notify listener if overall state changed
71
+ notifyIfStateChanged(isPlaying())
72
+ }
73
+
74
+ /// Start monitoring for phone audio playback changes
75
+ ///
76
+ /// - Parameter listener: Callback to receive audio state change notifications
77
+ func startMonitoring(listener: PhoneAudioMonitorListener) {
78
+ guard !isMonitoring else {
79
+ Bridge.log("PhoneAudioMonitor: Already monitoring")
80
+ return
81
+ }
82
+
83
+ self.listener = listener
84
+ lastKnownState = isPlaying()
85
+ isMonitoring = true
86
+
87
+ Bridge.log(
88
+ "PhoneAudioMonitor: Starting audio playback monitoring (initial state: \(lastKnownState ? "playing" : "not playing"))"
89
+ )
90
+
91
+ // Register for silenceSecondaryAudioHint notification
92
+ // This is fired when other apps start/stop playing audio
93
+ NotificationCenter.default.addObserver(
94
+ self,
95
+ selector: #selector(handleSilenceSecondaryAudioHint),
96
+ name: AVAudioSession.silenceSecondaryAudioHintNotification,
97
+ object: nil
98
+ )
99
+
100
+ // Also register for interruption notifications as a supplement
101
+ NotificationCenter.default.addObserver(
102
+ self,
103
+ selector: #selector(handleAudioInterruption),
104
+ name: AVAudioSession.interruptionNotification,
105
+ object: nil
106
+ )
107
+
108
+ // Start polling as a fallback mechanism
109
+ // Some audio sources might not trigger notifications reliably
110
+ startPolling()
111
+ }
112
+
113
+ /// Stop monitoring for phone audio playback changes
114
+ func stopMonitoring() {
115
+ guard isMonitoring else {
116
+ Bridge.log("PhoneAudioMonitor: Not currently monitoring")
117
+ return
118
+ }
119
+
120
+ Bridge.log("PhoneAudioMonitor: Stopping audio playback monitoring")
121
+
122
+ NotificationCenter.default.removeObserver(
123
+ self,
124
+ name: AVAudioSession.silenceSecondaryAudioHintNotification,
125
+ object: nil
126
+ )
127
+
128
+ NotificationCenter.default.removeObserver(
129
+ self,
130
+ name: AVAudioSession.interruptionNotification,
131
+ object: nil
132
+ )
133
+
134
+ stopPolling()
135
+
136
+ listener = nil
137
+ isMonitoring = false
138
+ }
139
+
140
+ /// Handle silenceSecondaryAudioHint notification
141
+ @objc private func handleSilenceSecondaryAudioHint(_ notification: Notification) {
142
+ guard let userInfo = notification.userInfo,
143
+ let typeValue = userInfo[AVAudioSessionSilenceSecondaryAudioHintTypeKey] as? UInt,
144
+ let type = AVAudioSession.SilenceSecondaryAudioHintType(rawValue: typeValue)
145
+ else {
146
+ return
147
+ }
148
+
149
+ let isPlaying: Bool
150
+ switch type {
151
+ case .begin:
152
+ // Another app started playing audio
153
+ isPlaying = true
154
+ Bridge.log("PhoneAudioMonitor: Received silenceSecondaryAudioHint - begin")
155
+ case .end:
156
+ // Another app stopped playing audio
157
+ isPlaying = false
158
+ Bridge.log("PhoneAudioMonitor: Received silenceSecondaryAudioHint - end")
159
+ @unknown default:
160
+ // Check actual state for unknown types
161
+ isPlaying = self.isPlaying()
162
+ }
163
+
164
+ notifyIfStateChanged(isPlaying)
165
+ }
166
+
167
+ /// Handle audio interruption notifications
168
+ @objc private func handleAudioInterruption(_ notification: Notification) {
169
+ guard let userInfo = notification.userInfo,
170
+ let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
171
+ let type = AVAudioSession.InterruptionType(rawValue: typeValue)
172
+ else {
173
+ return
174
+ }
175
+
176
+ switch type {
177
+ case .began:
178
+ // Audio was interrupted (e.g., phone call, other app took audio focus)
179
+ Bridge.log("PhoneAudioMonitor: Audio interruption began")
180
+ // Don't necessarily mean audio is playing - check actual state
181
+ notifyIfStateChanged(isPlaying())
182
+ case .ended:
183
+ Bridge.log("PhoneAudioMonitor: Audio interruption ended")
184
+ // Small delay to let audio state settle
185
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
186
+ guard let self = self else { return }
187
+ self.notifyIfStateChanged(self.isPlaying())
188
+ }
189
+ @unknown default:
190
+ break
191
+ }
192
+ }
193
+
194
+ /// Start polling isOtherAudioPlaying as a fallback mechanism
195
+ private func startPolling() {
196
+ pollingTimer = Timer.scheduledTimer(
197
+ withTimeInterval: pollingIntervalSeconds,
198
+ repeats: true
199
+ ) { [weak self] _ in
200
+ guard let self = self, self.isMonitoring else { return }
201
+
202
+ let currentState = self.isPlaying()
203
+ self.notifyIfStateChanged(currentState)
204
+ }
205
+ }
206
+
207
+ /// Stop the polling mechanism
208
+ private func stopPolling() {
209
+ pollingTimer?.invalidate()
210
+ pollingTimer = nil
211
+ }
212
+
213
+ /// Notify listener if state has changed
214
+ private func notifyIfStateChanged(_ isPlaying: Bool) {
215
+ guard isPlaying != lastKnownState else { return }
216
+
217
+ lastKnownState = isPlaying
218
+ Bridge.log("PhoneAudioMonitor: Audio state changed -> \(isPlaying ? "PLAYING" : "STOPPED")")
219
+
220
+ DispatchQueue.main.async { [weak self] in
221
+ self?.listener?.onPhoneAudioStateChanged(isPlaying: isPlaying)
222
+ }
223
+ }
224
+
225
+ /// Clean up resources
226
+ func destroy() {
227
+ stopMonitoring()
228
+ PhoneAudioMonitor.instance = nil
229
+ }
230
+ }