@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,953 @@
1
+ //
2
+ // Bridge.kt
3
+ // AOS
4
+ //
5
+ // Created by Matthew Fosse on 3/4/25.
6
+ //
7
+
8
+ package com.glassly.bluetoothsdk
9
+
10
+ import android.util.Base64
11
+ import android.util.Log
12
+ import com.glassly.bluetoothsdk.debug.BleTraceLogger
13
+ import java.util.HashMap
14
+ import java.util.UUID
15
+ import kotlin.jvm.JvmStatic
16
+ import kotlin.jvm.Synchronized
17
+ import kotlin.jvm.Volatile
18
+
19
+ /**
20
+ * Bridge class for SDK communication between Expo modules and native Android code This is the
21
+ * Android equivalent of the iOS Bridge.swift
22
+ */
23
+ public class Bridge private constructor() {
24
+ private var deviceManager: DeviceManager? = null
25
+
26
+ companion object {
27
+ private const val TAG = "Bridge"
28
+ private const val MIC_SAMPLE_RATE = 16_000
29
+ private const val PCM_BITS_PER_SAMPLE = 16
30
+ private const val MIC_CHANNELS = 1
31
+ private const val LC3_FRAME_DURATION_MS = 10
32
+ private const val DEFAULT_LC3_FRAME_SIZE_BYTES = 60
33
+ private val AUDIO_TRACE_METADATA_KEYS =
34
+ listOf(
35
+ "sampleRate",
36
+ "bitsPerSample",
37
+ "channels",
38
+ "encoding",
39
+ "frameDurationMs",
40
+ "frameSizeBytes",
41
+ "bitrate",
42
+ "packetizedFromGlasses",
43
+ "voiceActivityDetectionEnabled",
44
+ )
45
+
46
+ @Volatile private var instance: Bridge? = null
47
+
48
+ private const val DEFAULT_EVENT_SINK_ID = "default"
49
+
50
+ // Event sinks for JS and native consumers.
51
+ private val eventSinks = linkedMapOf<String, (String, Map<String, Any>) -> Unit>()
52
+
53
+ // Android Context for native operations
54
+ private var appContext: android.content.Context? = null
55
+
56
+ @JvmStatic
57
+ @Synchronized
58
+ fun getInstance(): Bridge {
59
+ if (instance == null) {
60
+ instance = Bridge()
61
+ }
62
+ return instance!!
63
+ }
64
+
65
+ /**
66
+ * Initialize the Bridge with event callback and context This should be called from
67
+ * BluetoothSdkModule
68
+ */
69
+ @JvmStatic
70
+ fun initialize(
71
+ context: android.content.Context,
72
+ callback: (String, Map<String, Any>) -> Unit
73
+ ) {
74
+ Log.d(TAG, "Initializing Bridge with context and event callback")
75
+ initialize(context)
76
+ setEventSink(DEFAULT_EVENT_SINK_ID, callback)
77
+ }
78
+
79
+ @JvmStatic
80
+ fun initialize(context: android.content.Context) {
81
+ appContext = context
82
+ }
83
+
84
+ @JvmStatic
85
+ @Synchronized
86
+ fun addEventSink(callback: (String, Map<String, Any>) -> Unit): String {
87
+ val id = UUID.randomUUID().toString()
88
+ setEventSink(id, callback)
89
+ return id
90
+ }
91
+
92
+ @JvmStatic
93
+ @Synchronized
94
+ fun removeEventSink(id: String) {
95
+ eventSinks.remove(id)
96
+ }
97
+
98
+ @Synchronized
99
+ private fun setEventSink(id: String, callback: (String, Map<String, Any>) -> Unit) {
100
+ eventSinks[id] = callback
101
+ }
102
+
103
+ @Synchronized
104
+ private fun getEventSinks(): List<(String, Map<String, Any>) -> Unit> {
105
+ return eventSinks.values.toList()
106
+ }
107
+
108
+ /** Get the Android context for native operations */
109
+ @JvmStatic
110
+ fun getContext(): android.content.Context {
111
+ return appContext ?: throw IllegalStateException("Bridge not initialized with context")
112
+ }
113
+
114
+ /** Log a message and send it to JavaScript */
115
+ @JvmStatic
116
+ fun log(message: String) {
117
+ val data = HashMap<String, Any>()
118
+ data["message"] = message
119
+ sendTypedMessage("log", data as Map<String, Any>)
120
+ }
121
+
122
+ /** Report tar.bz2 extraction progress to JavaScript. */
123
+ @JvmStatic
124
+ fun sendExtractionProgress(percentage: Int, bytesRead: Long, totalBytes: Long) {
125
+ val data = HashMap<String, Any>()
126
+ data["percentage"] = percentage
127
+ data["bytesRead"] = bytesRead
128
+ data["totalBytes"] = totalBytes
129
+ sendTypedMessage("extraction_progress", data as Map<String, Any>)
130
+ }
131
+
132
+ // MARK: - Speaker identification
133
+
134
+ /**
135
+ * One identification window from SpeakerIdEngine. [speakerId]/[name]
136
+ * are omitted for an unknown (unenrolled) voice. Matches iOS.
137
+ */
138
+ @JvmStatic
139
+ fun sendSpeakerDetected(speakerId: String?, name: String?, similarity: Double, startMs: Long, endMs: Long) {
140
+ val body = HashMap<String, Any>()
141
+ body["similarity"] = similarity
142
+ body["startMs"] = startMs
143
+ body["endMs"] = endMs
144
+ if (speakerId != null) body["speakerId"] = speakerId
145
+ if (name != null) body["name"] = name
146
+ sendTypedMessage("speaker_detected", body as Map<String, Any>)
147
+ }
148
+
149
+ @JvmStatic
150
+ fun sendSpeakerEnrollProgress(voicedSeconds: Double, targetSeconds: Double, level: Double) {
151
+ val body = HashMap<String, Any>()
152
+ body["voicedSeconds"] = voicedSeconds
153
+ body["targetSeconds"] = targetSeconds
154
+ body["level"] = level
155
+ sendTypedMessage("speaker_enroll_progress", body as Map<String, Any>)
156
+ }
157
+
158
+ /**
159
+ * Enrollment outcome. Embeddings are base64 Float32Arrays — raw audio
160
+ * is already discarded by the time this fires. Matches iOS.
161
+ */
162
+ @JvmStatic
163
+ fun sendSpeakerEnrollResult(ok: Boolean, reason: String?, embeddings: List<String>, dim: Int, source: String) {
164
+ val body = HashMap<String, Any>()
165
+ body["ok"] = ok
166
+ body["source"] = source
167
+ if (reason != null) body["reason"] = reason
168
+ if (ok) {
169
+ body["embeddings"] = embeddings
170
+ body["dim"] = dim
171
+ }
172
+ sendTypedMessage("speaker_enroll_result", body as Map<String, Any>)
173
+ }
174
+
175
+ /** Send head position event */
176
+ @JvmStatic
177
+ fun sendHeadUp(isUp: Boolean) {
178
+ val data = HashMap<String, Any>()
179
+ data["up"] = isUp
180
+ sendTypedMessage("head_up", data as Map<String, Any>)
181
+ }
182
+
183
+ /** Send pair failure event */
184
+ @JvmStatic
185
+ fun sendPairFailureEvent(error: String) {
186
+ val data = HashMap<String, Any>()
187
+ data["error"] = error
188
+ sendTypedMessage("pair_failure", data as Map<String, Any>)
189
+ }
190
+
191
+ @JvmStatic
192
+ fun sendPairingInfo(
193
+ hadPreviousBond: Boolean,
194
+ pairingCode: String? = null,
195
+ classicBondReady: Boolean = false,
196
+ securePairingCapable: Boolean = true,
197
+ protocolVersion: Int = 1,
198
+ ) {
199
+ val data = HashMap<String, Any>()
200
+ data["had_previous_bond"] = hadPreviousBond
201
+ if (pairingCode != null) data["pairing_code"] = pairingCode
202
+ data["classic_bond_ready"] = classicBondReady
203
+ data["secure_pairing_capable"] = securePairingCapable
204
+ data["protocol_version"] = protocolVersion
205
+ sendTypedMessage("pairing_info", data as Map<String, Any>)
206
+ }
207
+
208
+ /** Send audio connected event - matches iOS implementation for platform parity */
209
+ @JvmStatic
210
+ fun sendAudioConnected(deviceName: String) {
211
+ val data = HashMap<String, Any>()
212
+ data["deviceName"] = deviceName
213
+ sendTypedMessage("audio_connected", data as Map<String, Any>)
214
+ }
215
+
216
+ /** Send audio disconnected event - matches iOS implementation for platform parity */
217
+ @JvmStatic
218
+ fun sendAudioDisconnected() {
219
+ val data = HashMap<String, Any>()
220
+ sendTypedMessage("audio_disconnected", data as Map<String, Any>)
221
+ }
222
+
223
+ @JvmStatic
224
+ fun sendMicPcm(data: ByteArray) {
225
+ val body = micPcmEventBody(data)
226
+ sendTypedMessage("mic_pcm", body as Map<String, Any>)
227
+ }
228
+
229
+ @JvmStatic
230
+ fun sendMicLc3(data: ByteArray) {
231
+ val body = micLc3EventBody(data)
232
+ sendTypedMessage("mic_lc3", body as Map<String, Any>)
233
+ }
234
+
235
+ @JvmStatic
236
+ internal fun sendMicHealth(health: MicHealth, reason: String) {
237
+ val body = HashMap(health.toMap())
238
+ body["reason"] = reason
239
+ body["timestamp"] = System.currentTimeMillis()
240
+ sendTypedMessage("mic_health", body)
241
+ }
242
+
243
+ private fun micPcmEventBody(data: ByteArray): HashMap<String, Any> {
244
+ val voiceActivityDetectionEnabled =
245
+ DeviceStore.get("glasses", "voiceActivityDetectionEnabled") as? Boolean
246
+ ?: BluetoothSdkDefaults.VOICE_ACTIVITY_DETECTION_ENABLED
247
+ val body = HashMap<String, Any>()
248
+ body["pcm"] = data
249
+ body["sampleRate"] = MIC_SAMPLE_RATE
250
+ body["bitsPerSample"] = PCM_BITS_PER_SAMPLE
251
+ body["channels"] = MIC_CHANNELS
252
+ body["encoding"] = "pcm_s16le"
253
+ body["voiceActivityDetectionEnabled"] = voiceActivityDetectionEnabled
254
+ return body
255
+ }
256
+
257
+ private fun micLc3EventBody(data: ByteArray): HashMap<String, Any> {
258
+ val voiceActivityDetectionEnabled =
259
+ DeviceStore.get("glasses", "voiceActivityDetectionEnabled") as? Boolean
260
+ ?: BluetoothSdkDefaults.VOICE_ACTIVITY_DETECTION_ENABLED
261
+ val frameSizeBytes =
262
+ (DeviceStore.store.get("bluetooth", "lc3_frame_size") as? Number)?.toInt()
263
+ ?: DEFAULT_LC3_FRAME_SIZE_BYTES
264
+ val body = HashMap<String, Any>()
265
+ body["lc3"] = data
266
+ body["sampleRate"] = MIC_SAMPLE_RATE
267
+ body["channels"] = MIC_CHANNELS
268
+ body["encoding"] = "lc3"
269
+ body["frameDurationMs"] = LC3_FRAME_DURATION_MS
270
+ body["frameSizeBytes"] = frameSizeBytes
271
+ body["bitrate"] = frameSizeBytes * 8 * (1000 / LC3_FRAME_DURATION_MS)
272
+ body["packetizedFromGlasses"] = false
273
+ body["voiceActivityDetectionEnabled"] = voiceActivityDetectionEnabled
274
+ return body
275
+ }
276
+
277
+ /** Save a setting */
278
+ @JvmStatic
279
+ fun saveSetting(key: String, value: Any) {
280
+ val body = HashMap<String, Any>()
281
+ body["key"] = key
282
+ body["value"] = value
283
+ sendTypedMessage("save_setting", body as Map<String, Any>)
284
+ }
285
+
286
+ /** Send Voice Activity Detection status */
287
+ @JvmStatic
288
+ fun sendVoiceActivityDetectionStatus(enabled: Boolean) {
289
+ DeviceStore.set("glasses", "voiceActivityDetectionEnabled", enabled)
290
+ val body = HashMap<String, Any>()
291
+ body["voiceActivityDetectionEnabled"] = enabled
292
+ sendTypedMessage("voice_activity_detection_status", body as Map<String, Any>)
293
+ }
294
+
295
+ /** Send live speaking status reported by glasses-side Voice Activity Detection. */
296
+ @JvmStatic
297
+ fun sendSpeakingStatus(speaking: Boolean) {
298
+ val body = HashMap<String, Any>()
299
+ body["speaking"] = speaking
300
+ body["timestamp"] = System.currentTimeMillis()
301
+ sendTypedMessage("speaking_status", body as Map<String, Any>)
302
+ }
303
+
304
+ /** Send battery status */
305
+ @JvmStatic
306
+ fun sendBatteryStatus(level: Int, charging: Boolean) {
307
+ val body = HashMap<String, Any>()
308
+ body["level"] = level
309
+ body["charging"] = charging
310
+ body["timestamp"] = System.currentTimeMillis()
311
+ sendTypedMessage("battery_status", body as Map<String, Any>)
312
+ }
313
+
314
+ /** Send discovered device */
315
+ @JvmStatic
316
+ @JvmOverloads
317
+ fun sendDiscoveredDevice(
318
+ deviceModel: String,
319
+ deviceName: String,
320
+ deviceAddress: String = "",
321
+ rssi: Int? = null,
322
+ projectName: String? = null,
323
+ pairingMode: Boolean? = null,
324
+ pairingCode: String? = null,
325
+ securePairingCapable: Boolean? = null,
326
+ ) {
327
+ val searchResults =
328
+ (DeviceStore.store.getCategory("bluetooth")["searchResults"] as? List<*>)
329
+ ?.mapNotNull { result ->
330
+ (result as? Map<*, *>)?.entries
331
+ ?.mapNotNull { (key, value) ->
332
+ if (key is String && value != null) key to value else null
333
+ }
334
+ ?.toMap()
335
+ }
336
+ ?: emptyList()
337
+ val id = listOfNotNull(deviceModel, projectName?.takeIf { it.isNotBlank() }, deviceName).joinToString(":")
338
+ val newResult =
339
+ buildMap<String, Any> {
340
+ put("id", id)
341
+ put("model", deviceModel)
342
+ put("name", deviceName)
343
+ if (deviceAddress.isNotBlank()) {
344
+ put("address", deviceAddress)
345
+ }
346
+ projectName?.takeIf { it.isNotBlank() }?.let { put("projectName", it) }
347
+ rssi?.let { put("rssi", it) }
348
+ pairingMode?.let { put("pairingMode", it) }
349
+ pairingCode?.takeIf { it.isNotBlank() }?.let { put("pairingCode", it) }
350
+ securePairingCapable?.let { put("securePairingCapable", it) }
351
+ }
352
+ // Keep the public searchResults array stable as glasses are added or removed.
353
+ // Duplicate discoveries refresh their existing row; only new glasses append.
354
+ val uniqueResults = mergeStableSearchResults(searchResults, newResult, deviceModel)
355
+ DeviceStore.set("bluetooth", "searchResults", uniqueResults)
356
+ }
357
+
358
+ private fun mergeStableSearchResults(
359
+ currentResults: List<Map<String, Any>>,
360
+ newResult: Map<String, Any>,
361
+ fallbackModel: String
362
+ ): List<Map<String, Any>> {
363
+ val newKey = searchResultKey(newResult, fallbackModel) ?: return currentResults
364
+ val nextResults = currentResults.toMutableList()
365
+ val existingIndex =
366
+ nextResults.indexOfFirst { result ->
367
+ searchResultKey(result, fallbackModel) == newKey
368
+ }
369
+ if (existingIndex >= 0) {
370
+ nextResults[existingIndex] = newResult
371
+ } else {
372
+ nextResults += newResult
373
+ }
374
+ return nextResults
375
+ }
376
+
377
+ private fun searchResultKey(result: Map<String, Any>, fallbackModel: String): String? {
378
+ val id = result["id"] as? String
379
+ if (!id.isNullOrBlank()) {
380
+ return id
381
+ }
382
+ val model = result["model"] as? String ?: fallbackModel
383
+ val name = result["name"] as? String ?: return null
384
+ return "$model:$name"
385
+ }
386
+
387
+ // MARK: - Hardware Events
388
+
389
+ /** Send button press event to React Native - matches iOS implementation */
390
+ @JvmStatic
391
+ fun sendButtonPressEvent(buttonId: String, pressType: String) {
392
+ val buttonData = HashMap<String, Any>()
393
+ buttonData["buttonId"] = buttonId
394
+ buttonData["pressType"] = pressType
395
+ buttonData["timestamp"] = System.currentTimeMillis()
396
+
397
+ sendTypedMessage("button_press", buttonData as Map<String, Any>)
398
+ }
399
+
400
+ /** Send miniapp selection event from glasses dashboard menu */
401
+ @JvmStatic
402
+ fun sendMiniappSelected(packageName: String) {
403
+ val body = HashMap<String, Any>()
404
+ body["packageName"] = packageName
405
+ sendTypedMessage("miniapp_selected", body)
406
+ }
407
+
408
+ /** Send touch/gesture event from glasses - matches iOS implementation */
409
+ @JvmStatic
410
+ @JvmOverloads
411
+ fun sendTouchEvent(
412
+ deviceModel: String,
413
+ gestureName: String,
414
+ timestamp: Long,
415
+ source: Int? = null
416
+ ) {
417
+ val body = HashMap<String, Any>()
418
+ body["type"] = "touch_event"
419
+ body["deviceModel"] = deviceModel
420
+ body["gestureName"] = gestureName
421
+ body["timestamp"] = timestamp
422
+ if (source != null) {
423
+ body["source"] = source
424
+ }
425
+ sendTypedMessage("touch_event", body)
426
+ }
427
+
428
+ /** Send swipe volume control status - matches iOS implementation */
429
+ @JvmStatic
430
+ fun sendSwipeVolumeStatus(enabled: Boolean, timestamp: Long) {
431
+ val body = HashMap<String, Any>()
432
+ body["enabled"] = enabled
433
+ body["timestamp"] = timestamp
434
+ sendTypedMessage("swipe_volume_status", body)
435
+ }
436
+
437
+ /** Send switch status from glasses - matches iOS implementation */
438
+ @JvmStatic
439
+ fun sendSwitchStatus(switchType: Int, value: Int, timestamp: Long) {
440
+ val body = HashMap<String, Any>()
441
+ body["switchType"] = switchType
442
+ body["switchValue"] = value
443
+ body["timestamp"] = timestamp
444
+ sendTypedMessage("switch_status", body)
445
+ }
446
+
447
+ @JvmStatic
448
+ fun sendPhotoError(requestId: String, errorCode: String, errorMessage: String) {
449
+ val timestamp = System.currentTimeMillis()
450
+ val event = HashMap<String, Any>()
451
+ event["type"] = "photo_response"
452
+ event["state"] = "error"
453
+ event["requestId"] = requestId
454
+ event["errorCode"] = errorCode
455
+ event["errorMessage"] = errorMessage
456
+ event["timestamp"] = timestamp
457
+ sendTypedMessage("photo_response", event as Map<String, Any>)
458
+ }
459
+
460
+ @JvmStatic
461
+ fun sendPhotoStatus(statusJson: Map<String, Any>) {
462
+ sendTypedMessage("photo_status", statusJson)
463
+ }
464
+
465
+ @JvmStatic
466
+ fun sendCameraStatus(statusJson: Map<String, Any>) {
467
+ sendTypedMessage("camera_status", statusJson)
468
+ }
469
+
470
+ @JvmStatic
471
+ fun sendPhotoResponse(responseJson: Map<String, Any>) {
472
+ sendTypedMessage("photo_response", responseJson)
473
+ }
474
+
475
+ /** Send RGB LED control response */
476
+ @JvmStatic
477
+ fun sendRgbLedControlResponse(requestId: String, success: Boolean, error: String?) {
478
+ if (requestId.isEmpty()) return
479
+ try {
480
+ val body = HashMap<String, Any>()
481
+ body["type"] = "rgb_led_control_response"
482
+ body["requestId"] = requestId
483
+ body["state"] = if (success) "success" else "error"
484
+ if (!success) {
485
+ body["errorCode"] = error ?: "unknown_error"
486
+ }
487
+ sendTypedMessage("rgb_led_control_response", body)
488
+ } catch (e: Exception) {
489
+ log("Bridge: Error sending rgb_led_control_response: $e")
490
+ }
491
+ }
492
+
493
+ @JvmStatic
494
+ fun sendSettingsAck(values: Map<String, Any>) {
495
+ val body = HashMap<String, Any>()
496
+ body["type"] = "settings_ack"
497
+ values.forEach { (key, value) ->
498
+ body[key] = value
499
+ }
500
+ sendTypedMessage("settings_ack", body)
501
+ }
502
+
503
+ @JvmStatic
504
+ fun sendVideoRecordingStatus(values: Map<String, Any>) {
505
+ val body = HashMap<String, Any>()
506
+ body["type"] = "video_recording_status"
507
+ values.forEach { (key, value) ->
508
+ body[key] = value
509
+ }
510
+ sendTypedMessage("video_recording_status", body)
511
+ }
512
+
513
+ @JvmStatic
514
+ fun sendMediaUploadEvent(type: String, values: Map<String, Any>) {
515
+ val body = HashMap<String, Any>()
516
+ body["type"] = type
517
+ values.forEach { (key, value) ->
518
+ body[key] = value
519
+ }
520
+ sendTypedMessage(type, body)
521
+ }
522
+
523
+ @JvmStatic
524
+ fun sendVersionInfo(values: Map<String, Any>) {
525
+ fun stringField(vararg keys: String): String =
526
+ keys.firstNotNullOfOrNull { key -> values[key] as? String } ?: ""
527
+ val body = HashMap<String, Any>()
528
+ body["type"] = "version_info"
529
+ body["androidVersion"] = stringField("androidVersion", "android_version")
530
+ body["firmwareVersion"] = stringField("firmwareVersion", "firmware_version")
531
+ body["besFirmwareVersion"] = stringField("besFirmwareVersion", "bes_fw_version")
532
+ body["mtkFirmwareVersion"] = stringField("mtkFirmwareVersion", "mtk_fw_version")
533
+ body["buildNumber"] = stringField("buildNumber", "build_number")
534
+ (values["systemTimeMs"] as? Number ?: values["system_time_ms"] as? Number)?.let {
535
+ body["systemTimeMs"] = it.toLong()
536
+ }
537
+ body["otaVersionUrl"] = stringField("otaVersionUrl", "ota_version_url")
538
+ body["appVersion"] = stringField("appVersion", "app_version")
539
+ (values["hotspotOtaVersion"] as? Number
540
+ ?: values["hotspot_ota_version"] as? Number)
541
+ ?.let { body["hotspotOtaVersion"] = it.toInt() }
542
+ sendTypedMessage("version_info", body)
543
+ }
544
+
545
+ /**
546
+ * Send transcription result to server Used by AOSManager to send pre-formatted
547
+ * transcription results Matches the Swift structure exactly
548
+ */
549
+ @JvmStatic
550
+ fun sendLocalTranscription(transcription: Map<String, Any>) {
551
+ val text = transcription["text"] as? String
552
+ if (text == null || text.isEmpty()) {
553
+ log("Skipping empty transcription result")
554
+ return
555
+ }
556
+
557
+ sendTypedMessage("local_transcription", transcription)
558
+ }
559
+
560
+ /** Convenience method for sending local transcription from transcriber */
561
+ @JvmStatic
562
+ fun sendLocalTranscription(text: String, isFinal: Boolean, language: String) {
563
+ if (text.isEmpty()) {
564
+ log("Skipping empty transcription result")
565
+ return
566
+ }
567
+
568
+ val transcription =
569
+ mapOf(
570
+ "text" to text,
571
+ "isFinal" to isFinal,
572
+ "language" to language,
573
+ "type" to "local_transcription"
574
+ )
575
+
576
+ sendTypedMessage("local_transcription", transcription)
577
+ }
578
+
579
+ // Bluetooth SDK bridge funcs:
580
+
581
+ /** Send status update */
582
+ @JvmStatic
583
+ fun sendStatus(statusObj: Map<String, Any>) {
584
+ val body = HashMap<String, Any>()
585
+ body["bluetooth_status"] = statusObj
586
+ sendTypedMessage("bluetooth_status_update", body as Map<String, Any>)
587
+ }
588
+
589
+ /** Send glasses serial number */
590
+ @JvmStatic
591
+ fun sendserialNumber(serialNumber: String, style: String, color: String) {
592
+ val serialData = HashMap<String, Any>()
593
+ serialData["serial_number"] = serialNumber
594
+ serialData["style"] = style
595
+ serialData["color"] = color
596
+
597
+ val body = HashMap<String, Any>()
598
+ body["glasses_serial_number"] = serialData
599
+ sendTypedMessage("glasses_serial_number", body as Map<String, Any>)
600
+ }
601
+
602
+ /**
603
+ * Send WiFi status change. [error] is the glasses' provisioning failure reason
604
+ * (e.g. "connect_timeout", "connected_to_other_network") when this status is the
605
+ * verdict of a failed connect attempt; null for routine link-state updates.
606
+ */
607
+ @JvmStatic
608
+ @JvmOverloads
609
+ fun sendWifiStatusChange(
610
+ connected: Boolean,
611
+ ssid: String?,
612
+ localIp: String?,
613
+ error: String? = null
614
+ ) {
615
+ val status = WifiStatus.fromStoreFields(connected, ssid, localIp) ?: return
616
+ val payload =
617
+ if (error != null) status.toMap() + mapOf("error" to error)
618
+ else status.toMap()
619
+ sendTypedMessage("wifi_status_change", payload)
620
+ }
621
+
622
+ /**
623
+ * Claim the WiFi scan-results store for a newly requested scan. Called by the
624
+ * SDK when it generates the scanId, BEFORE the scan command goes out: store
625
+ * ownership is decided at request time, not by whichever chunk arrives first,
626
+ * so a delayed chunk from an older, abandoned scan can never reset or clobber
627
+ * the current scan's accumulator.
628
+ */
629
+ @JvmStatic
630
+ fun claimWifiScanResults(scanId: String) {
631
+ DeviceStore.apply("bluetooth", "wifiScanActiveScanId", scanId)
632
+ }
633
+
634
+ /** Send WiFi scan results */
635
+ @JvmStatic
636
+ fun updateWifiScanResults(
637
+ networks: List<Map<String, Any>>,
638
+ scanComplete: Boolean,
639
+ scanId: String? = null
640
+ ) {
641
+ // Only chunks echoing the active scanId claimed at request time may mutate
642
+ // the store; foreign chunks are still forwarded to the SDK sink, which
643
+ // drops stale ids itself. Scan-id-less chunks (old firmware) keep the
644
+ // legacy accumulate-forever store behavior.
645
+ val ownsStore =
646
+ scanId == null || scanId == DeviceStore.get("bluetooth", "wifiScanActiveScanId")
647
+ var updatedNetworks: List<Map<String, Any>> = networks
648
+ if (ownsStore) {
649
+ var storedNetworks: List<Map<String, Any>> =
650
+ DeviceStore.get("bluetooth", "wifiScanResults") as? List<Map<String, Any>>
651
+ ?: emptyList()
652
+ val lastScanId = DeviceStore.get("bluetooth", "wifiScanResultsScanId")
653
+ if (scanId != null && scanId != lastScanId) {
654
+ // First chunk of a new scan: drop networks accumulated for a previous scan
655
+ // so stale entries never carry over into this scan's store.
656
+ storedNetworks = emptyList()
657
+ DeviceStore.apply("bluetooth", "wifiScanResultsScanId", scanId)
658
+ }
659
+ // add the networks to the storedNetworks array, removing duplicates by ssid
660
+ val merged = storedNetworks.toMutableList()
661
+ for (network in networks) {
662
+ if (!merged.any { it["ssid"] as? String == network["ssid"] as? String }) {
663
+ merged.add(network)
664
+ }
665
+ }
666
+ DeviceStore.apply("bluetooth", "wifiScanResults", merged)
667
+ updatedNetworks = merged
668
+ }
669
+ val body = HashMap<String, Any>()
670
+ // Correlated scans: the SDK accumulates and dedupes chunks per scanId itself,
671
+ // so forward only this chunk; the merged store list is for UI consumers.
672
+ body["networks"] = if (scanId != null) networks else updatedNetworks
673
+ body["scanComplete"] = scanComplete
674
+ if (scanId != null) {
675
+ body["scanId"] = scanId
676
+ }
677
+ sendTypedMessage("wifi_scan_result", body)
678
+ }
679
+
680
+ /** Send gallery status - matches iOS MentraLive.swift handleGalleryStatus pattern */
681
+ @JvmStatic
682
+ fun sendGalleryStatus(
683
+ photoCount: Int,
684
+ videoCount: Int,
685
+ totalCount: Int,
686
+ totalSize: Long,
687
+ hasContent: Boolean,
688
+ cameraBusy: Boolean,
689
+ cameraBusyReason: String?
690
+ ) {
691
+ val galleryData = HashMap<String, Any>()
692
+ galleryData["type"] = "gallery_status"
693
+ galleryData["photos"] = photoCount
694
+ galleryData["videos"] = videoCount
695
+ galleryData["total"] = totalCount
696
+ galleryData["totalSize"] = totalSize
697
+ galleryData["hasContent"] = hasContent
698
+ galleryData["cameraBusy"] = cameraBusy
699
+ if (!cameraBusyReason.isNullOrBlank()) {
700
+ galleryData["cameraBusyReason"] = cameraBusyReason
701
+ }
702
+
703
+ sendTypedMessage("gallery_status", galleryData as Map<String, Any>)
704
+ }
705
+
706
+ /** Send hotspot status change - matches iOS MentraLive.swift emitHotspotStatusChange */
707
+ @JvmStatic
708
+ fun sendHotspotStatusChange(
709
+ enabled: Boolean,
710
+ ssid: String,
711
+ password: String,
712
+ gatewayIp: String
713
+ ) {
714
+ val status = HotspotStatus.fromStoreFields(enabled, ssid, password, gatewayIp) ?: return
715
+ sendTypedMessage("hotspot_status_change", status.toMap())
716
+ }
717
+
718
+ /** Send hotspot error - notifies React Native of hotspot failures */
719
+ @JvmStatic
720
+ fun sendHotspotError(errorMessage: String, timestamp: Long) {
721
+ val eventBody = HashMap<String, Any>()
722
+ eventBody["errorMessage"] = errorMessage
723
+ eventBody["timestamp"] = timestamp
724
+
725
+ sendTypedMessage("hotspot_error", eventBody as Map<String, Any>)
726
+ }
727
+
728
+ /** Send MTK firmware update complete notification - matches iOS implementation */
729
+ @JvmStatic
730
+ fun sendMtkUpdateComplete(message: String) {
731
+ val eventBody = HashMap<String, Any>()
732
+ eventBody["message"] = message
733
+ eventBody["timestamp"] = System.currentTimeMillis()
734
+ sendTypedMessage("mtk_update_complete", eventBody as Map<String, Any>)
735
+ }
736
+
737
+ /** Send ota_start_ack — glasses confirmed receipt of ota_start command */
738
+ @JvmStatic
739
+ fun sendOtaStartAck() {
740
+ val eventBody = HashMap<String, Any>()
741
+ eventBody["timestamp"] = System.currentTimeMillis()
742
+ sendTypedMessage("ota_start_ack", eventBody as Map<String, Any>)
743
+ }
744
+
745
+ @JvmStatic
746
+ @JvmOverloads
747
+ fun sendOtaStatus(
748
+ sessionId: String,
749
+ totalSteps: Int,
750
+ currentStep: Int,
751
+ stepType: String,
752
+ phase: String,
753
+ stepPercent: Int,
754
+ overallPercent: Int,
755
+ status: String,
756
+ errorMessage: String? = null,
757
+ glassesTimeMs: Long? = null,
758
+ ) {
759
+ val eventBody = HashMap<String, Any>()
760
+ eventBody["session_id"] = sessionId
761
+ eventBody["total_steps"] = totalSteps
762
+ eventBody["current_step"] = currentStep
763
+ eventBody["step_type"] = stepType
764
+ eventBody["phase"] = phase
765
+ eventBody["step_percent"] = stepPercent
766
+ eventBody["overall_percent"] = overallPercent
767
+ eventBody["status"] = status
768
+ errorMessage?.let { eventBody["error_message"] = it }
769
+ if (glassesTimeMs != null && glassesTimeMs > 0) {
770
+ eventBody["glasses_time_ms"] = glassesTimeMs
771
+ }
772
+
773
+ Log.d(TAG, "Bridge: sendOtaStatus: $eventBody")
774
+
775
+ sendTypedMessage("ota_status", eventBody as Map<String, Any>)
776
+ }
777
+
778
+ /** Send stream status - forwards to websocket system (matches iOS) */
779
+ @JvmStatic
780
+ fun sendStreamStatus(statusJson: Map<String, Any>) {
781
+ sendTypedMessage("stream_status", statusJson)
782
+ }
783
+
784
+ /** Send keep alive ACK - forwards to websocket system (matches iOS) */
785
+ @JvmStatic
786
+ fun sendKeepAliveAck(ackJson: Map<String, Any>) {
787
+ sendTypedMessage("keep_alive_ack", ackJson)
788
+ }
789
+
790
+ /** Send IMU data event - matches iOS MentraLive.swift emitImuDataEvent */
791
+ @JvmStatic
792
+ fun sendImuDataEvent(
793
+ accel: DoubleArray,
794
+ gyro: DoubleArray,
795
+ mag: DoubleArray,
796
+ quat: DoubleArray,
797
+ euler: DoubleArray,
798
+ timestamp: Long
799
+ ) {
800
+ val eventBody = HashMap<String, Any>()
801
+ eventBody["accel"] = accel.toList()
802
+ eventBody["gyro"] = gyro.toList()
803
+ eventBody["mag"] = mag.toList()
804
+ eventBody["quat"] = quat.toList()
805
+ eventBody["euler"] = euler.toList()
806
+ eventBody["timestamp"] = timestamp
807
+
808
+ sendTypedMessage("imu_data_event", eventBody as Map<String, Any>)
809
+ }
810
+
811
+ /** Send IMU gesture event - matches iOS MentraLive.swift emitImuGestureEvent */
812
+ @JvmStatic
813
+ fun sendImuGestureEvent(gesture: String, timestamp: Long) {
814
+ val eventBody = HashMap<String, Any>()
815
+ eventBody["gesture"] = gesture
816
+ eventBody["timestamp"] = timestamp
817
+
818
+ sendTypedMessage("imu_gesture_event", eventBody as Map<String, Any>)
819
+ }
820
+
821
+ /**
822
+ * Send a single accelerometer reading from the glasses IMU - matches iOS
823
+ * Bridge.sendAccelEvent. A richer combined IMU event (gyro + magnetometer) is future work.
824
+ */
825
+ @JvmStatic
826
+ fun sendAccelEvent(x: Float, y: Float, z: Float, timestamp: Long) {
827
+ val body = HashMap<String, Any>()
828
+ body["x"] = x
829
+ body["y"] = y
830
+ body["z"] = z
831
+ body["timestamp"] = timestamp
832
+ sendTypedMessage("accel_event", body as Map<String, Any>)
833
+ }
834
+
835
+ // Arbitrary WS Comms (don't use these, make a dedicated function for your use case):
836
+
837
+ /** Send WebSocket text message */
838
+ @JvmStatic
839
+ fun sendWSText(msg: String) {
840
+ val data = HashMap<String, Any>()
841
+ data["text"] = msg
842
+ sendTypedMessage("ws_text", data as Map<String, Any>)
843
+ }
844
+
845
+ /** Send WebSocket binary message */
846
+ @JvmStatic
847
+ fun sendWSBinary(data: ByteArray) {
848
+ val base64String = Base64.encodeToString(data, Base64.NO_WRAP)
849
+ val body = HashMap<String, Any>()
850
+ body["base64"] = base64String
851
+ sendTypedMessage("ws_bin", body as Map<String, Any>)
852
+ }
853
+
854
+ /**
855
+ * Send a typed message to JavaScript Don't call this function directly, instead make a
856
+ * function above that calls this function
857
+ */
858
+ @JvmStatic
859
+ fun sendTypedMessage(type: String, body: Map<String, Any>) {
860
+ var mutableBody = body
861
+ if (body !is HashMap) {
862
+ mutableBody = HashMap(body)
863
+ }
864
+ (mutableBody as HashMap<String, Any>)["type"] = type
865
+
866
+ try {
867
+ val sinks = getEventSinks()
868
+ if (sinks.isEmpty()) {
869
+ Log.w(
870
+ TAG,
871
+ "Cannot send typed message '$type': no event sinks registered (app may be killed/backgrounded)"
872
+ )
873
+ return
874
+ }
875
+
876
+ val tracePayload = tracePayloadForTypedMessage(type, mutableBody as Map<String, Any>)
877
+ if (tracePayload != null) {
878
+ try {
879
+ BleTraceLogger.logMap(
880
+ "phone_to_app",
881
+ "sdk_event_dispatch",
882
+ type,
883
+ tracePayload,
884
+ )
885
+ } catch (e: Exception) {
886
+ Log.d(TAG, "BLE trace logging failed for typed message '$type'", e)
887
+ }
888
+ }
889
+
890
+ // Send directly using type as event name - no JSON serialization
891
+ sinks.forEach { sink ->
892
+ try {
893
+ sink(type, mutableBody as Map<String, Any>)
894
+ } catch (e: Exception) {
895
+ Log.e(
896
+ TAG,
897
+ "Error invoking event sink for type '$type' (listener may be dead)",
898
+ e
899
+ )
900
+ // Don't rethrow - one dead listener should not break other consumers.
901
+ }
902
+ }
903
+ } catch (e: Exception) {
904
+ Log.e(TAG, "Error sending typed message of type '$type'", e)
905
+ }
906
+ }
907
+
908
+ private fun tracePayloadForTypedMessage(
909
+ type: String,
910
+ body: Map<String, Any>
911
+ ): Map<String, Any>? =
912
+ when {
913
+ type == "log" -> null
914
+ isAudioPayloadEvent(type) -> audioTracePayload(type, body)
915
+ else -> body
916
+ }
917
+
918
+ private fun isAudioPayloadEvent(type: String): Boolean =
919
+ type == "mic_pcm" || type == "mic_lc3"
920
+
921
+ private fun audioTracePayload(type: String, body: Map<String, Any>): Map<String, Any> {
922
+ val payload = HashMap<String, Any>()
923
+ payload["type"] = type
924
+ payload["timestamp"] = System.currentTimeMillis()
925
+ payload["payloadOmitted"] = true
926
+ payload["payloadOmittedReason"] = "audio"
927
+
928
+ val audioBytes =
929
+ when (type) {
930
+ "mic_pcm" -> (body["pcm"] as? ByteArray)?.size
931
+ "mic_lc3" -> (body["lc3"] as? ByteArray)?.size
932
+ else -> null
933
+ }
934
+ audioBytes?.let { payload["audioBytes"] = it }
935
+
936
+ AUDIO_TRACE_METADATA_KEYS.forEach { key ->
937
+ val value = body[key]
938
+ if (value != null) {
939
+ payload[key] = value
940
+ }
941
+ }
942
+
943
+ return payload
944
+ }
945
+ }
946
+
947
+ init {
948
+ deviceManager = DeviceManager.Companion.getInstance()
949
+ if (deviceManager == null) {
950
+ Log.e(TAG, "Failed to initialize DeviceManager in Bridge constructor")
951
+ }
952
+ }
953
+ }