@glassly/bluetooth-sdk 0.1.0-dev.100

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 (553) 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/assets/G2Firmware/g2-glassly-cfw-2.2.9.22.evenota.bin +0 -0
  139. package/android/src/main/assets/G2Firmware/g2-stock-2.2.9.22.evenota.bin +0 -0
  140. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdk.kt +2321 -0
  141. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkAnalytics.kt +216 -0
  142. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkDebug.kt +14 -0
  143. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkDefaults.kt +7 -0
  144. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkModule.kt +1087 -0
  145. package/android/src/main/java/com/glassly/bluetoothsdk/Bridge.kt +953 -0
  146. package/android/src/main/java/com/glassly/bluetoothsdk/DeviceManager.kt +2472 -0
  147. package/android/src/main/java/com/glassly/bluetoothsdk/DeviceStore.kt +359 -0
  148. package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedChangelogCatalog.kt +9 -0
  149. package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedReleaseMetadata.kt +11 -0
  150. package/android/src/main/java/com/glassly/bluetoothsdk/LocalNetworkModule.kt +414 -0
  151. package/android/src/main/java/com/glassly/bluetoothsdk/ObservableStore.kt +88 -0
  152. package/android/src/main/java/com/glassly/bluetoothsdk/OtaManifest.kt +191 -0
  153. package/android/src/main/java/com/glassly/bluetoothsdk/ReleaseChangelog.kt +61 -0
  154. package/android/src/main/java/com/glassly/bluetoothsdk/ScopedNetworkReadiness.kt +17 -0
  155. package/android/src/main/java/com/glassly/bluetoothsdk/audio/AudioModels.kt +144 -0
  156. package/android/src/main/java/com/glassly/bluetoothsdk/camera/CameraModels.kt +486 -0
  157. package/android/src/main/java/com/glassly/bluetoothsdk/connection/ConnectionModels.kt +78 -0
  158. package/android/src/main/java/com/glassly/bluetoothsdk/controllers/ControllerManager.kt +158 -0
  159. package/android/src/main/java/com/glassly/bluetoothsdk/controllers/R1.kt +1057 -0
  160. package/android/src/main/java/com/glassly/bluetoothsdk/debug/BleTraceLogger.kt +165 -0
  161. package/android/src/main/java/com/glassly/bluetoothsdk/events/BluetoothEvents.kt +178 -0
  162. package/android/src/main/java/com/glassly/bluetoothsdk/internal/MapParsing.kt +140 -0
  163. package/android/src/main/java/com/glassly/bluetoothsdk/net/LocalIpv4.kt +57 -0
  164. package/android/src/main/java/com/glassly/bluetoothsdk/otaserver/LocalOtaServer.kt +304 -0
  165. package/android/src/main/java/com/glassly/bluetoothsdk/otaserver/OtaServerModule.kt +120 -0
  166. package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistry.java +108 -0
  167. package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoUploadServer.kt +622 -0
  168. package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/PhotoReceiverModule.kt +156 -0
  169. package/android/src/main/java/com/glassly/bluetoothsdk/requests/DisplayRequests.kt +56 -0
  170. package/android/src/main/java/com/glassly/bluetoothsdk/services/Foreground.kt +272 -0
  171. package/android/src/main/java/com/glassly/bluetoothsdk/services/PhoneMic.kt +1195 -0
  172. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Ar99.java +2653 -0
  173. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/BesOtaHeartbeatGuard.kt +49 -0
  174. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/BesOtaProgressMapper.kt +27 -0
  175. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G1.kt +3837 -0
  176. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G1TextSanitizer.kt +38 -0
  177. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2.kt +6457 -0
  178. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassification.kt +49 -0
  179. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwRenderer.kt +1253 -0
  180. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCache.kt +270 -0
  181. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2FlashProtocol.kt +291 -0
  182. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2Flasher.kt +411 -0
  183. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/GlasslyBle.java +43189 -0
  184. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Mach1.kt +1267 -0
  185. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLive.kt +10742 -0
  186. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLiveL2capChannel.kt +274 -0
  187. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLivePairingAdvertisement.kt +54 -0
  188. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraNex.kt +2008 -0
  189. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Nimo.kt +2532 -0
  190. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/SGCManager.kt +470 -0
  191. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Simulated.kt +228 -0
  192. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/Ar99OtaManager.java +566 -0
  193. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaByteUtils.java +18 -0
  194. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaCommandConstants.java +35 -0
  195. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaCrc32Util.java +13 -0
  196. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaGattTransport.java +11 -0
  197. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaProtocol.java +92 -0
  198. package/android/src/main/java/com/glassly/bluetoothsdk/speakerid/SpeakerIdEngine.kt +525 -0
  199. package/android/src/main/java/com/glassly/bluetoothsdk/status/DeviceStatus.kt +669 -0
  200. package/android/src/main/java/com/glassly/bluetoothsdk/status/RuntimeState.kt +201 -0
  201. package/android/src/main/java/com/glassly/bluetoothsdk/status/WifiHotspotStatus.kt +189 -0
  202. package/android/src/main/java/com/glassly/bluetoothsdk/streaming/StreamModels.kt +518 -0
  203. package/android/src/main/java/com/glassly/bluetoothsdk/stt/STTTools.kt +391 -0
  204. package/android/src/main/java/com/glassly/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +518 -0
  205. package/android/src/main/java/com/glassly/bluetoothsdk/stt/VadGateSpeechPolicy.kt +227 -0
  206. package/android/src/main/java/com/glassly/bluetoothsdk/tts/TTSTools.kt +248 -0
  207. package/android/src/main/java/com/glassly/bluetoothsdk/types/DeviceModels.kt +96 -0
  208. package/android/src/main/java/com/glassly/bluetoothsdk/utils/AES.java +106 -0
  209. package/android/src/main/java/com/glassly/bluetoothsdk/utils/AudioSessionMonitor.kt +189 -0
  210. package/android/src/main/java/com/glassly/bluetoothsdk/utils/AvifExifStripper.java +573 -0
  211. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BitmapJavaUtils.java +169 -0
  212. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BleJsonCompact.java +496 -0
  213. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BlePhotoUploadService.java +677 -0
  214. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BleWireProtocol.java +108 -0
  215. package/android/src/main/java/com/glassly/bluetoothsdk/utils/Constants.kt +46 -0
  216. package/android/src/main/java/com/glassly/bluetoothsdk/utils/G1Text.kt +436 -0
  217. package/android/src/main/java/com/glassly/bluetoothsdk/utils/HeifExifTagReader.java +199 -0
  218. package/android/src/main/java/com/glassly/bluetoothsdk/utils/IncidentLogBleRelayNaming.java +29 -0
  219. package/android/src/main/java/com/glassly/bluetoothsdk/utils/IncidentLogBleUploadService.java +77 -0
  220. package/android/src/main/java/com/glassly/bluetoothsdk/utils/K900LengthCodec.java +115 -0
  221. package/android/src/main/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtils.java +827 -0
  222. package/android/src/main/java/com/glassly/bluetoothsdk/utils/MessageChunkReassembler.java +228 -0
  223. package/android/src/main/java/com/glassly/bluetoothsdk/utils/MessageChunker.java +325 -0
  224. package/android/src/main/java/com/glassly/bluetoothsdk/utils/NexSGCUtils.kt +698 -0
  225. package/android/src/main/java/com/glassly/bluetoothsdk/utils/PhoneAudioMonitor.kt +348 -0
  226. package/android/src/main/java/com/glassly/bluetoothsdk/utils/SmartGlassesConnectionState.java +9 -0
  227. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/Ar99OpusPcmDecoder.java +398 -0
  228. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/ByteUtilAudioPlayer.java +655 -0
  229. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/Lc3Player.java +441 -0
  230. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/PCMAudioPlayer.java +431 -0
  231. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/PcmStreamPlayer.kt +328 -0
  232. package/android/src/main/res/values/strings.xml +4 -0
  233. package/android/src/test/java/com/glassly/bluetoothsdk/BluetoothSdkExceptionTest.kt +18 -0
  234. package/android/src/test/java/com/glassly/bluetoothsdk/OtaManifestDowngradeTest.kt +86 -0
  235. package/android/src/test/java/com/glassly/bluetoothsdk/OtaStartResponsePolicyTest.kt +30 -0
  236. package/android/src/test/java/com/glassly/bluetoothsdk/ReleaseChangelogTest.kt +13 -0
  237. package/android/src/test/java/com/glassly/bluetoothsdk/ScopedNetworkReadinessTest.kt +39 -0
  238. package/android/src/test/java/com/glassly/bluetoothsdk/StreamStatusEventTest.kt +29 -0
  239. package/android/src/test/java/com/glassly/bluetoothsdk/camera/PhotoRequestTest.kt +141 -0
  240. package/android/src/test/java/com/glassly/bluetoothsdk/otaserver/LocalOtaServerTest.kt +82 -0
  241. package/android/src/test/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistryTest.java +75 -0
  242. package/android/src/test/java/com/glassly/bluetoothsdk/services/ForegroundServiceTypeTest.kt +69 -0
  243. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/Ar99AdvertisementMatcherTest.java +22 -0
  244. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/BesOtaHeartbeatGuardTest.kt +73 -0
  245. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/BesOtaProgressMapperTest.kt +67 -0
  246. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/EvenRleTest.kt +63 -0
  247. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G1TextSanitizerTest.kt +31 -0
  248. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassificationTest.kt +89 -0
  249. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwRendererTest.kt +344 -0
  250. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCacheTest.kt +200 -0
  251. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2FlashProtocolTest.kt +131 -0
  252. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/MentraLiveGattCharacteristicsTest.kt +39 -0
  253. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/MentraLivePairingAdvertisementParserTest.kt +133 -0
  254. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/NimoProtocolTest.kt +333 -0
  255. package/android/src/test/java/com/glassly/bluetoothsdk/utils/AvifExifStripperTest.java +46 -0
  256. package/android/src/test/java/com/glassly/bluetoothsdk/utils/BinaryMessageChunkerTest.java +84 -0
  257. package/android/src/test/java/com/glassly/bluetoothsdk/utils/BleJsonCompactTest.java +319 -0
  258. package/android/src/test/java/com/glassly/bluetoothsdk/utils/BlePhotoUploadServiceTest.java +107 -0
  259. package/android/src/test/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtilsEndiannessTest.java +172 -0
  260. package/android/src/test/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtilsTest.java +24 -0
  261. package/android/src/test/java/com/glassly/bluetoothsdk/utils/PhoneAudioMonitorTest.kt +45 -0
  262. package/android/src/test/resources/avif_with_exif.avif +0 -0
  263. package/app.plugin.js +3 -0
  264. package/build/BluetoothSdk.types.d.ts +1390 -0
  265. package/build/BluetoothSdk.types.d.ts.map +1 -0
  266. package/build/BluetoothSdk.types.js +41 -0
  267. package/build/BluetoothSdk.types.js.map +1 -0
  268. package/build/_internal.d.ts +17 -0
  269. package/build/_internal.d.ts.map +1 -0
  270. package/build/_internal.js +16 -0
  271. package/build/_internal.js.map +1 -0
  272. package/build/_private/BluetoothSdkModule.d.ts +171 -0
  273. package/build/_private/BluetoothSdkModule.d.ts.map +1 -0
  274. package/build/_private/BluetoothSdkModule.js +300 -0
  275. package/build/_private/BluetoothSdkModule.js.map +1 -0
  276. package/build/_private/LocalNetworkModule.d.ts +38 -0
  277. package/build/_private/LocalNetworkModule.d.ts.map +1 -0
  278. package/build/_private/LocalNetworkModule.js +3 -0
  279. package/build/_private/LocalNetworkModule.js.map +1 -0
  280. package/build/_private/cameraRequestPayload.d.ts +4 -0
  281. package/build/_private/cameraRequestPayload.d.ts.map +1 -0
  282. package/build/_private/cameraRequestPayload.js +32 -0
  283. package/build/_private/cameraRequestPayload.js.map +1 -0
  284. package/build/_private/photoRequestPayload.d.ts +6 -0
  285. package/build/_private/photoRequestPayload.d.ts.map +1 -0
  286. package/build/_private/photoRequestPayload.js +86 -0
  287. package/build/_private/photoRequestPayload.js.map +1 -0
  288. package/build/_private/sdkOtaManifest.d.ts +11 -0
  289. package/build/_private/sdkOtaManifest.d.ts.map +1 -0
  290. package/build/_private/sdkOtaManifest.js +15 -0
  291. package/build/_private/sdkOtaManifest.js.map +1 -0
  292. package/build/changelogs.d.ts +7 -0
  293. package/build/changelogs.d.ts.map +1 -0
  294. package/build/changelogs.js +46 -0
  295. package/build/changelogs.js.map +1 -0
  296. package/build/debug.d.ts +3 -0
  297. package/build/debug.d.ts.map +1 -0
  298. package/build/debug.js +8 -0
  299. package/build/debug.js.map +1 -0
  300. package/build/generated/changelogCatalog.d.ts +12 -0
  301. package/build/generated/changelogCatalog.d.ts.map +1 -0
  302. package/build/generated/changelogCatalog.js +16 -0
  303. package/build/generated/changelogCatalog.js.map +1 -0
  304. package/build/generated/releaseMetadata.d.ts +12 -0
  305. package/build/generated/releaseMetadata.d.ts.map +1 -0
  306. package/build/generated/releaseMetadata.js +10 -0
  307. package/build/generated/releaseMetadata.js.map +1 -0
  308. package/build/index.d.ts +6 -0
  309. package/build/index.d.ts.map +1 -0
  310. package/build/index.js +170 -0
  311. package/build/index.js.map +1 -0
  312. package/build/ota-server/OtaServer.types.d.ts +14 -0
  313. package/build/ota-server/OtaServer.types.d.ts.map +1 -0
  314. package/build/ota-server/OtaServer.types.js +2 -0
  315. package/build/ota-server/OtaServer.types.js.map +1 -0
  316. package/build/ota-server/OtaServerModule.d.ts +21 -0
  317. package/build/ota-server/OtaServerModule.d.ts.map +1 -0
  318. package/build/ota-server/OtaServerModule.js +3 -0
  319. package/build/ota-server/OtaServerModule.js.map +1 -0
  320. package/build/ota-server/OtaServerModule.web.d.ts +10 -0
  321. package/build/ota-server/OtaServerModule.web.d.ts.map +1 -0
  322. package/build/ota-server/OtaServerModule.web.js +12 -0
  323. package/build/ota-server/OtaServerModule.web.js.map +1 -0
  324. package/build/ota-server/index.d.ts +3 -0
  325. package/build/ota-server/index.d.ts.map +1 -0
  326. package/build/ota-server/index.js +3 -0
  327. package/build/ota-server/index.js.map +1 -0
  328. package/build/ota-transport/index.d.ts +67 -0
  329. package/build/ota-transport/index.d.ts.map +1 -0
  330. package/build/ota-transport/index.js +58 -0
  331. package/build/ota-transport/index.js.map +1 -0
  332. package/build/photo-receiver/PhotoReceiver.types.d.ts +18 -0
  333. package/build/photo-receiver/PhotoReceiver.types.d.ts.map +1 -0
  334. package/build/photo-receiver/PhotoReceiver.types.js +2 -0
  335. package/build/photo-receiver/PhotoReceiver.types.js.map +1 -0
  336. package/build/photo-receiver/PhotoReceiverModule.d.ts +10 -0
  337. package/build/photo-receiver/PhotoReceiverModule.d.ts.map +1 -0
  338. package/build/photo-receiver/PhotoReceiverModule.js +3 -0
  339. package/build/photo-receiver/PhotoReceiverModule.js.map +1 -0
  340. package/build/photo-receiver/PhotoReceiverModule.web.d.ts +10 -0
  341. package/build/photo-receiver/PhotoReceiverModule.web.d.ts.map +1 -0
  342. package/build/photo-receiver/PhotoReceiverModule.web.js +12 -0
  343. package/build/photo-receiver/PhotoReceiverModule.web.js.map +1 -0
  344. package/build/photo-receiver/index.d.ts +3 -0
  345. package/build/photo-receiver/index.d.ts.map +1 -0
  346. package/build/photo-receiver/index.js +3 -0
  347. package/build/photo-receiver/index.js.map +1 -0
  348. package/build/react/index.d.ts +5 -0
  349. package/build/react/index.d.ts.map +1 -0
  350. package/build/react/index.js +4 -0
  351. package/build/react/index.js.map +1 -0
  352. package/build/react/useBluetooth.d.ts +100 -0
  353. package/build/react/useBluetooth.d.ts.map +1 -0
  354. package/build/react/useBluetooth.js +167 -0
  355. package/build/react/useBluetooth.js.map +1 -0
  356. package/build/react/useBluetoothEvent.d.ts +6 -0
  357. package/build/react/useBluetoothEvent.d.ts.map +1 -0
  358. package/build/react/useBluetoothEvent.js +21 -0
  359. package/build/react/useBluetoothEvent.js.map +1 -0
  360. package/build/react/useBluetoothScan.d.ts +22 -0
  361. package/build/react/useBluetoothScan.d.ts.map +1 -0
  362. package/build/react/useBluetoothScan.js +135 -0
  363. package/build/react/useBluetoothScan.js.map +1 -0
  364. package/build/react/useBluetoothStatus.d.ts +16 -0
  365. package/build/react/useBluetoothStatus.d.ts.map +1 -0
  366. package/build/react/useBluetoothStatus.js +137 -0
  367. package/build/react/useBluetoothStatus.js.map +1 -0
  368. package/build/react/useGlassesConnection.d.ts +29 -0
  369. package/build/react/useGlassesConnection.d.ts.map +1 -0
  370. package/build/react/useGlassesConnection.js +153 -0
  371. package/build/react/useGlassesConnection.js.map +1 -0
  372. package/build/types/index.d.ts +3 -0
  373. package/build/types/index.d.ts.map +1 -0
  374. package/build/types/index.js +2 -0
  375. package/build/types/index.js.map +1 -0
  376. package/expo-module.config.json +14 -0
  377. package/ios/BackgroundOtaArtifactDownloader.swift +125 -0
  378. package/ios/BluetoothSdkModule.swift +1028 -0
  379. package/ios/GlasslyBluetoothSDK.podspec +106 -0
  380. package/ios/LocalIPv4.swift +150 -0
  381. package/ios/LocalOtaServer.swift +309 -0
  382. package/ios/LocalPhotoUploadServer.swift +754 -0
  383. package/ios/OtaServerModule.swift +147 -0
  384. package/ios/Packages/CoreObjC/CoreObjC.xcodeproj/project.pbxproj +213 -0
  385. package/ios/Packages/CoreObjC/PcmConverter.h +22 -0
  386. package/ios/Packages/CoreObjC/PcmConverter.m +266 -0
  387. package/ios/Packages/CoreObjC/attdet.c +92 -0
  388. package/ios/Packages/CoreObjC/attdet.h +44 -0
  389. package/ios/Packages/CoreObjC/bits.c +375 -0
  390. package/ios/Packages/CoreObjC/bits.h +315 -0
  391. package/ios/Packages/CoreObjC/bwdet.c +129 -0
  392. package/ios/Packages/CoreObjC/bwdet.h +69 -0
  393. package/ios/Packages/CoreObjC/common.h +151 -0
  394. package/ios/Packages/CoreObjC/energy.c +70 -0
  395. package/ios/Packages/CoreObjC/energy.h +43 -0
  396. package/ios/Packages/CoreObjC/fastmath.h +158 -0
  397. package/ios/Packages/CoreObjC/include/PcmConverter.h +22 -0
  398. package/ios/Packages/CoreObjC/lc3.c +704 -0
  399. package/ios/Packages/CoreObjC/lc3.h +313 -0
  400. package/ios/Packages/CoreObjC/lc3_cpp.h +283 -0
  401. package/ios/Packages/CoreObjC/lc3_private.h +163 -0
  402. package/ios/Packages/CoreObjC/ltpf.c +905 -0
  403. package/ios/Packages/CoreObjC/ltpf.h +111 -0
  404. package/ios/Packages/CoreObjC/ltpf_arm.h +506 -0
  405. package/ios/Packages/CoreObjC/ltpf_neon.h +281 -0
  406. package/ios/Packages/CoreObjC/makefile.mk +35 -0
  407. package/ios/Packages/CoreObjC/mdct.c +469 -0
  408. package/ios/Packages/CoreObjC/mdct.h +57 -0
  409. package/ios/Packages/CoreObjC/mdct_neon.h +296 -0
  410. package/ios/Packages/CoreObjC/meson.build +61 -0
  411. package/ios/Packages/CoreObjC/plc.c +61 -0
  412. package/ios/Packages/CoreObjC/plc.h +57 -0
  413. package/ios/Packages/CoreObjC/rnnoise.h +114 -0
  414. package/ios/Packages/CoreObjC/sns.c +880 -0
  415. package/ios/Packages/CoreObjC/sns.h +103 -0
  416. package/ios/Packages/CoreObjC/spec.c +907 -0
  417. package/ios/Packages/CoreObjC/spec.h +119 -0
  418. package/ios/Packages/CoreObjC/tables.c +3457 -0
  419. package/ios/Packages/CoreObjC/tables.h +94 -0
  420. package/ios/Packages/CoreObjC/tns.c +457 -0
  421. package/ios/Packages/CoreObjC/tns.h +99 -0
  422. package/ios/Packages/SherpaOnnx/Model/joiner.onnx +0 -0
  423. package/ios/Packages/SherpaOnnx/Model/tokens.txt +502 -0
  424. package/ios/Packages/SherpaOnnx/SherpaOnnx.swift +2383 -0
  425. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Info.plist +48 -0
  426. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/c-api.h +4689 -0
  427. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/cxx-api.h +2036 -0
  428. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/libsherpa-onnx.a +0 -0
  429. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/c-api.h +4689 -0
  430. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/cxx-api.h +2036 -0
  431. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a +0 -0
  432. package/ios/Packages/libbz2/module.modulemap +5 -0
  433. package/ios/Packages/libbz2/shim.h +1 -0
  434. package/ios/PhotoReceiverModule.swift +129 -0
  435. package/ios/Source/BluetoothSDK.swift +2299 -0
  436. package/ios/Source/BluetoothSDKDebug.swift +12 -0
  437. package/ios/Source/BluetoothSdkDefaults.swift +29 -0
  438. package/ios/Source/Bridge.swift +708 -0
  439. package/ios/Source/Bridging-Header.h +16 -0
  440. package/ios/Source/DeviceManager.swift +2054 -0
  441. package/ios/Source/DeviceStore.swift +350 -0
  442. package/ios/Source/GeneratedChangelogCatalog.swift +8 -0
  443. package/ios/Source/GeneratedReleaseMetadata.swift +11 -0
  444. package/ios/Source/ObservableStore.swift +118 -0
  445. package/ios/Source/OtaManifest.swift +211 -0
  446. package/ios/Source/PrivacyInfo.xcprivacy +23 -0
  447. package/ios/Source/ReleaseChangelog.swift +65 -0
  448. package/ios/Source/Resources/G2Firmware/README.md +10 -0
  449. package/ios/Source/Resources/G2Firmware/g2-glassly-cfw-2.2.9.22.evenota.bin +0 -0
  450. package/ios/Source/Resources/G2Firmware/g2-stock-2.2.9.22.evenota.bin +0 -0
  451. package/ios/Source/audio/AudioModels.swift +178 -0
  452. package/ios/Source/audio/PcmStreamPlayer.swift +410 -0
  453. package/ios/Source/camera/CameraModels.swift +866 -0
  454. package/ios/Source/connection/ScanSession.swift +27 -0
  455. package/ios/Source/controllers/ControllerManager.swift +184 -0
  456. package/ios/Source/controllers/R1.swift +925 -0
  457. package/ios/Source/errors/BluetoothSdkError.swift +19 -0
  458. package/ios/Source/events/BluetoothEvents.swift +342 -0
  459. package/ios/Source/internal/BleTraceLogger.swift +192 -0
  460. package/ios/Source/internal/BluetoothAvailability.swift +78 -0
  461. package/ios/Source/internal/BluetoothSdkAnalytics.swift +199 -0
  462. package/ios/Source/internal/ValueParsing.swift +99 -0
  463. package/ios/Source/requests/DisplayRequests.swift +82 -0
  464. package/ios/Source/services/PhoneMic.swift +662 -0
  465. package/ios/Source/sgcs/Ar99.swift +2451 -0
  466. package/ios/Source/sgcs/G1.swift +2554 -0
  467. package/ios/Source/sgcs/G2.swift +7142 -0
  468. package/ios/Source/sgcs/G2CfwClassification.swift +43 -0
  469. package/ios/Source/sgcs/G2CfwRenderer.swift +1295 -0
  470. package/ios/Source/sgcs/G2CfwTextureCache.swift +233 -0
  471. package/ios/Source/sgcs/G2FlashProtocol.swift +312 -0
  472. package/ios/Source/sgcs/G2Flasher.swift +773 -0
  473. package/ios/Source/sgcs/Mach1.swift +541 -0
  474. package/ios/Source/sgcs/MentraLive.swift +7011 -0
  475. package/ios/Source/sgcs/MentraLiveL2capChannel.swift +183 -0
  476. package/ios/Source/sgcs/MentraLivePairingAdvertisement.swift +64 -0
  477. package/ios/Source/sgcs/MentraNex.swift +3220 -0
  478. package/ios/Source/sgcs/Nimo.swift +1886 -0
  479. package/ios/Source/sgcs/SGCManager.swift +621 -0
  480. package/ios/Source/sgcs/Simulated.swift +274 -0
  481. package/ios/Source/sgcs/ar99/ota/Ar99OtaManager.swift +637 -0
  482. package/ios/Source/sgcs/glassly_ble.pb.swift +4354 -0
  483. package/ios/Source/speakerid/SpeakerIdEngine.swift +573 -0
  484. package/ios/Source/status/DeviceStatus.swift +981 -0
  485. package/ios/Source/status/RuntimeState.swift +359 -0
  486. package/ios/Source/status/WifiHotspotStatus.swift +345 -0
  487. package/ios/Source/streaming/StreamModels.swift +693 -0
  488. package/ios/Source/stt/STTTools.swift +191 -0
  489. package/ios/Source/stt/SherpaOnnxTranscriber.swift +466 -0
  490. package/ios/Source/tts/TTSTools.swift +167 -0
  491. package/ios/Source/types/DeviceModels.swift +177 -0
  492. package/ios/Source/utils/AudioSessionMonitor.swift +283 -0
  493. package/ios/Source/utils/BinaryCodec.swift +61 -0
  494. package/ios/Source/utils/BleJsonCompact.swift +405 -0
  495. package/ios/Source/utils/BleWireProtocol.swift +92 -0
  496. package/ios/Source/utils/Constants.swift +78 -0
  497. package/ios/Source/utils/Enums.swift +95 -0
  498. package/ios/Source/utils/G1Text.swift +2096 -0
  499. package/ios/Source/utils/G2NotifyFile.swift +116 -0
  500. package/ios/Source/utils/JSCExperiment.swift +241 -0
  501. package/ios/Source/utils/MemoryMonitor.swift +44 -0
  502. package/ios/Source/utils/MessageChunkReassembler.swift +190 -0
  503. package/ios/Source/utils/MessageChunker.swift +294 -0
  504. package/ios/Source/utils/Models.swift +135 -0
  505. package/ios/Source/utils/PhoneAudioMonitor.swift +230 -0
  506. package/ios/Source/utils/TarBz2Extractor.swift +257 -0
  507. package/ios/Tests/BluetoothSDKTests/G2CfwClassificationTests.swift +78 -0
  508. package/ios/Tests/BluetoothSDKTests/G2CfwRendererTests.swift +372 -0
  509. package/ios/Tests/BluetoothSDKTests/G2CfwTextureCacheTests.swift +162 -0
  510. package/ios/Tests/BluetoothSDKTests/G2FlashProtocolTests.swift +182 -0
  511. package/ios/Tests/BluetoothSDKTests/G2NotifyFileTests.swift +91 -0
  512. package/ios/Tests/MentraLivePairingAdvertisementTests.swift +160 -0
  513. package/ios/Tests/ReleaseChangelogTests.swift +13 -0
  514. package/package.json +129 -0
  515. package/plugin/build/index.d.ts +10 -0
  516. package/plugin/build/index.js +12 -0
  517. package/plugin/build/withAndroid.d.ts +3 -0
  518. package/plugin/build/withAndroid.js +190 -0
  519. package/plugin/build/withIos.d.ts +5 -0
  520. package/plugin/build/withIos.js +108 -0
  521. package/scripts/inject-ios-sdk-version.mjs +65 -0
  522. package/scripts/public-ota-api.test.mjs +37 -0
  523. package/scripts/verify-release-package.mjs +46 -0
  524. package/scripts/write-release-metadata.mjs +154 -0
  525. package/scripts/write-release-metadata.test.mjs +37 -0
  526. package/src/BluetoothSdk.types.ts +1659 -0
  527. package/src/_internal.ts +16 -0
  528. package/src/_private/BluetoothSdkModule.ts +683 -0
  529. package/src/_private/LocalNetworkModule.ts +50 -0
  530. package/src/_private/cameraRequestPayload.ts +34 -0
  531. package/src/_private/photoRequestPayload.ts +90 -0
  532. package/src/_private/sdkOtaManifest.ts +18 -0
  533. package/src/changelogs.ts +46 -0
  534. package/src/debug.ts +9 -0
  535. package/src/generated/changelogCatalog.ts +17 -0
  536. package/src/generated/releaseMetadata.ts +20 -0
  537. package/src/index.ts +320 -0
  538. package/src/ota-server/OtaServer.types.ts +10 -0
  539. package/src/ota-server/OtaServerModule.ts +23 -0
  540. package/src/ota-server/OtaServerModule.web.ts +17 -0
  541. package/src/ota-server/index.ts +2 -0
  542. package/src/ota-transport/index.ts +116 -0
  543. package/src/photo-receiver/PhotoReceiver.types.ts +20 -0
  544. package/src/photo-receiver/PhotoReceiverModule.ts +11 -0
  545. package/src/photo-receiver/PhotoReceiverModule.web.ts +17 -0
  546. package/src/photo-receiver/index.ts +2 -0
  547. package/src/react/index.ts +21 -0
  548. package/src/react/useBluetooth.ts +309 -0
  549. package/src/react/useBluetoothEvent.ts +35 -0
  550. package/src/react/useBluetoothScan.ts +173 -0
  551. package/src/react/useBluetoothStatus.ts +180 -0
  552. package/src/react/useGlassesConnection.ts +202 -0
  553. package/src/types/index.ts +12 -0
@@ -0,0 +1,1057 @@
1
+ package com.glassly.bluetoothsdk.controllers
2
+
3
+ import com.glassly.bluetoothsdk.PhotoRequest
4
+ import android.Manifest
5
+ import android.bluetooth.BluetoothAdapter
6
+ import android.bluetooth.BluetoothDevice
7
+ import android.bluetooth.BluetoothGatt
8
+ import android.bluetooth.BluetoothGattCallback
9
+ import android.bluetooth.BluetoothGattCharacteristic
10
+ import android.bluetooth.BluetoothGattDescriptor
11
+ import android.bluetooth.le.BluetoothLeScanner
12
+ import android.bluetooth.le.ScanCallback
13
+ import android.bluetooth.le.ScanResult
14
+ import android.bluetooth.le.ScanSettings
15
+ import android.content.Context
16
+ import android.content.pm.PackageManager
17
+ import android.os.Build
18
+ import android.os.Handler
19
+ import android.os.Looper
20
+ import androidx.core.content.ContextCompat
21
+ import com.glassly.bluetoothsdk.Bridge
22
+ import com.glassly.bluetoothsdk.DeviceManager
23
+ import com.glassly.bluetoothsdk.DeviceStore
24
+ import com.glassly.bluetoothsdk.utils.ControllerTypes
25
+ import java.util.UUID
26
+ import kotlinx.coroutines.CoroutineScope
27
+ import kotlinx.coroutines.Dispatchers
28
+ import kotlinx.coroutines.Job
29
+ import kotlinx.coroutines.delay
30
+ import kotlinx.coroutines.isActive
31
+ import kotlinx.coroutines.launch
32
+
33
+ // MARK: - R1 BLE Constants
34
+
35
+ private object R1BLE {
36
+ val SERVICE_UUID: UUID = UUID.fromString("BAE80001-4F05-4503-8E65-3AF1F7329D1F")
37
+ val WRITE_CHAR_1: UUID = UUID.fromString("BAE80010-4F05-4503-8E65-3AF1F7329D1F")
38
+ val NOTIFY_CHAR_1: UUID = UUID.fromString("BAE80011-4F05-4503-8E65-3AF1F7329D1F")
39
+ val WRITE_CHAR_2: UUID = UUID.fromString("BAE80012-4F05-4503-8E65-3AF1F7329D1F")
40
+ val NOTIFY_CHAR_2: UUID = UUID.fromString("BAE80013-4F05-4503-8E65-3AF1F7329D1F")
41
+ val BATTERY_SERVICE: UUID = UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb")
42
+ val BATTERY_LEVEL_CHAR: UUID = UUID.fromString("00002a19-0000-1000-8000-00805f9b34fb")
43
+ val CCCD_UUID: UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
44
+
45
+ val NAME_FILTERS = arrayOf("EVEN R1", "BCL60")
46
+
47
+ const val GESTURE_MARKER: Byte = 0xFF.toByte()
48
+ }
49
+
50
+ // MARK: - EUS transport encoder (phone → ring, channel 2)
51
+
52
+ /**
53
+ * Recovered firmware contract (openCFW r1/ research): every phone→ring packet
54
+ * is a 12-byte "model" header
55
+ * [version, module, moduleVersion, serial LE16, status, command, subcommand,
56
+ * totalLength LE16, crc16 LE16]
57
+ * followed by the payload, where the phone-direction CRC-16 (CCITT rounds,
58
+ * init 0xFFFF) runs over the compact bytes [0,1,2,3,5,6,7,8,0x00] and then
59
+ * continues over the payload bytes. The model is carried on channel 2 inside a
60
+ * fragment train [sequence, crc32 LE32, ≤239 bytes]; the CRC-32 (poly
61
+ * 0x1EDC6F41, MSB-first, zero seed, no final xor) covers the whole model and
62
+ * sequence 0 terminates the train — all our packets fit a single fragment.
63
+ * Mirrors iOS R1.swift's R1Eus.
64
+ */
65
+ private object R1Eus {
66
+ const val VERSION = 100
67
+ const val MODULE_SYSTEM = 0x01
68
+ const val MODULE_VERSION = 100
69
+ /** status bit 0x02 marks a SET; advStart requires it */
70
+ const val STATUS_SET = 0x02
71
+
72
+ /** system subcommand 0x08: payload [0x01] claims the phone role; the ring
73
+ * requests link encryption/bonding in response */
74
+ const val SUBCMD_PAIR_AUTH = 0x08
75
+ /** system subcommand 0x0A: 12-byte payload = two 6-byte glasses arm MACs;
76
+ * the ring stores both targets and fast-advertises for them */
77
+ const val SUBCMD_ADV_START = 0x0A
78
+
79
+ private fun crc16Step(crc: Int, byte: Int): Int {
80
+ var c = ((crc shr 8) or (crc shl 8)) and 0xFFFF
81
+ c = c xor (byte and 0xFF)
82
+ c = c xor ((c and 0x00FF) shr 4)
83
+ c = (c xor (c shl 12)) and 0xFFFF
84
+ c = (c xor ((c and 0x00FF) shl 5)) and 0xFFFF
85
+ return c
86
+ }
87
+
88
+ private fun phoneCrc16(header: ByteArray, payload: ByteArray): Int {
89
+ var crc = 0xFFFF
90
+ // compact header: bytes 0-3 and 5-8, then a zero placeholder
91
+ for (b in intArrayOf(
92
+ header[0].toInt(), header[1].toInt(), header[2].toInt(), header[3].toInt(),
93
+ header[5].toInt(), header[6].toInt(), header[7].toInt(), header[8].toInt(), 0
94
+ )) {
95
+ crc = crc16Step(crc, b)
96
+ }
97
+ for (b in payload) {
98
+ crc = crc16Step(crc, b.toInt())
99
+ }
100
+ return crc
101
+ }
102
+
103
+ private fun crc32(data: ByteArray): Long {
104
+ var crc = 0L
105
+ for (byte in data) {
106
+ crc = crc xor ((byte.toLong() and 0xFF) shl 24)
107
+ repeat(8) {
108
+ crc = if ((crc and 0x8000_0000L) != 0L) {
109
+ ((crc shl 1) xor 0x1EDC6F41L) and 0xFFFF_FFFFL
110
+ } else {
111
+ (crc shl 1) and 0xFFFF_FFFFL
112
+ }
113
+ }
114
+ }
115
+ return crc
116
+ }
117
+
118
+ /** Build one single-fragment system-module command packet. */
119
+ fun frame(subcommand: Int, status: Int, serial: Int, payload: ByteArray): ByteArray {
120
+ val length = 12 + payload.size
121
+ val header = byteArrayOf(
122
+ VERSION.toByte(), MODULE_SYSTEM.toByte(), MODULE_VERSION.toByte(),
123
+ (serial and 0xFF).toByte(), ((serial shr 8) and 0xFF).toByte(),
124
+ status.toByte(), 0x00, subcommand.toByte(),
125
+ (length and 0xFF).toByte(), ((length shr 8) and 0xFF).toByte(),
126
+ 0, 0,
127
+ )
128
+ val crc16 = phoneCrc16(header, payload)
129
+ header[10] = (crc16 and 0xFF).toByte()
130
+ header[11] = ((crc16 shr 8) and 0xFF).toByte()
131
+ val model = header + payload
132
+
133
+ val outer = crc32(model)
134
+ return byteArrayOf(
135
+ 0x00, // sequence 0: single-fragment train
136
+ (outer and 0xFF).toByte(),
137
+ ((outer shr 8) and 0xFF).toByte(),
138
+ ((outer shr 16) and 0xFF).toByte(),
139
+ ((outer shr 24) and 0xFF).toByte(),
140
+ ) + model
141
+ }
142
+ }
143
+
144
+ // MARK: - R1 Gesture Types
145
+
146
+ private enum class R1Gesture(val rawValue: String) {
147
+ HOLD("hold"),
148
+ SINGLE_TAP("single_tap"),
149
+ DOUBLE_TAP("double_tap"),
150
+ SWIPE_UP("swipe_up"),
151
+ SWIPE_DOWN("swipe_down");
152
+
153
+ companion object {
154
+ /** Parse gesture from notification data: [0xFF, type, param] */
155
+ fun parse(data: ByteArray): R1Gesture? {
156
+ if (data.size < 3 || data[0] != R1BLE.GESTURE_MARKER) return null
157
+ return when (data[1]) {
158
+ 0x03.toByte() -> HOLD
159
+ 0x04.toByte() -> when (data[2]) {
160
+ 0x01.toByte() -> SINGLE_TAP
161
+ 0x02.toByte() -> DOUBLE_TAP
162
+ else -> null
163
+ }
164
+ 0x05.toByte() -> if ((data[2].toInt() and 0xFF) < 0x80) SWIPE_UP else SWIPE_DOWN
165
+ else -> null
166
+ }
167
+ }
168
+ }
169
+ }
170
+
171
+ // MARK: - R1 Controller
172
+
173
+ class R1 : ControllerManager() {
174
+
175
+ // Connection state
176
+ private val appContext: Context = Bridge.getContext()
177
+ private val prefs = appContext.getSharedPreferences("r1_prefs", Context.MODE_PRIVATE)
178
+
179
+ private var bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
180
+ private var scanner: BluetoothLeScanner? = null
181
+ private var scanCallback: ScanCallback? = null
182
+
183
+ private var ringGatt: BluetoothGatt? = null
184
+ private var isDisconnecting = false
185
+
186
+ // BLE characteristics
187
+ private var writeChar1: BluetoothGattCharacteristic? = null
188
+ private var notifyChar1: BluetoothGattCharacteristic? = null
189
+ private var writeChar2: BluetoothGattCharacteristic? = null
190
+ private var notifyChar2: BluetoothGattCharacteristic? = null
191
+ private var batteryLevelChar: BluetoothGattCharacteristic? = null
192
+ private var notifySubscriptionCount = 0
193
+ private var initSequenceRun = false
194
+
195
+ // Android serializes CCCD writes and reads — queue and drain in callbacks
196
+ private val pendingDescriptorWrites = ArrayDeque<BluetoothGattDescriptor>()
197
+ private val pendingReads = ArrayDeque<BluetoothGattCharacteristic>()
198
+ private var descriptorWriteInFlight = false
199
+ private var readInFlight = false
200
+
201
+ // Device search
202
+ private var deviceSearchId: String = "NOT_SET"
203
+
204
+ // BLE handle used by Android for reconnection (BluetoothDevice.address — may be a
205
+ // resolvable/random address depending on bonding state; not the ring's public MAC).
206
+ private var ringBleAddress: String?
207
+ get() = prefs.getString("r1_ringBleAddress", null)
208
+ set(value) {
209
+ prefs.edit().apply {
210
+ if (value == null) remove("r1_ringBleAddress") else putString("r1_ringBleAddress", value)
211
+ apply()
212
+ }
213
+ }
214
+
215
+ // Public ring MAC parsed from advertisement manufacturer data (last 6 bytes), formatted
216
+ // as "AA:BB:CC:DD:EE:FF". This is what gets published to DeviceStore/controllerMacAddress.
217
+ private var ringMacAddress: String?
218
+ get() = prefs.getString("r1_ringMacAddress", null)
219
+ set(value) {
220
+ prefs.edit().apply {
221
+ if (value == null) remove("r1_ringMacAddress") else putString("r1_ringMacAddress", value)
222
+ apply()
223
+ }
224
+ }
225
+
226
+ // peripheral name -> 6-byte ring MAC (hex string), populated from mfgData on every scan.
227
+ // Mirrors iOS ringMacAddressMap so reconnects re-validate freshness instead of trusting
228
+ // a stale stored MAC.
229
+ private fun loadRingMacAddressMap(): MutableMap<String, String> {
230
+ val raw = prefs.getString("r1_ringMacAddressMap", null) ?: return mutableMapOf()
231
+ val out = mutableMapOf<String, String>()
232
+ for (entry in raw.split(';')) {
233
+ if (entry.isEmpty()) continue
234
+ val idx = entry.indexOf('=')
235
+ if (idx <= 0) continue
236
+ out[entry.substring(0, idx)] = entry.substring(idx + 1)
237
+ }
238
+ return out
239
+ }
240
+
241
+ private fun saveRingMacAddressMap(map: Map<String, String>) {
242
+ val raw = map.entries.joinToString(";") { "${it.key}=${it.value}" }
243
+ prefs.edit().putString("r1_ringMacAddressMap", raw).apply()
244
+ }
245
+
246
+ private fun putRingMacInMap(name: String, mac: String) {
247
+ val map = loadRingMacAddressMap()
248
+ map[name] = mac
249
+ saveRingMacAddressMap(map)
250
+ }
251
+
252
+ private fun getRingMacFromMap(name: String): String? = loadRingMacAddressMap()[name]
253
+
254
+ // Reconnection
255
+ private val reconnectionManager = R1ReconnectionManager()
256
+
257
+ private fun startReconnectionTimer() {
258
+ reconnectionManager.start {
259
+ val connected = DeviceStore.get("glasses", "controllerConnected") as? Boolean ?: false
260
+ if (connected) {
261
+ true // already connected — stop retrying
262
+ } else {
263
+ Bridge.log("R1: Attempting reconnection...")
264
+ mainHandler.post { startScan() }
265
+ false // keep trying
266
+ }
267
+ }
268
+ }
269
+
270
+ // Battery
271
+ private var _batteryLevel: Int = -1
272
+ set(value) {
273
+ val old = field
274
+ field = value
275
+ if (value != old && value >= 0) {
276
+ DeviceStore.apply("glasses", "controllerBatteryLevel", value)
277
+ }
278
+ }
279
+
280
+ // Heartbeat
281
+ private val mainHandler = Handler(Looper.getMainLooper())
282
+ private var heartbeatRunnable: Runnable? = null
283
+
284
+ init {
285
+ type = ControllerTypes.R1
286
+ hasMic = false
287
+ }
288
+
289
+ // MARK: - Permissions
290
+
291
+ private fun hasScanPermission(): Boolean {
292
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return true
293
+ return ContextCompat.checkSelfPermission(
294
+ appContext, Manifest.permission.BLUETOOTH_SCAN
295
+ ) == PackageManager.PERMISSION_GRANTED
296
+ }
297
+
298
+ private fun hasConnectPermission(): Boolean {
299
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return true
300
+ return ContextCompat.checkSelfPermission(
301
+ appContext, Manifest.permission.BLUETOOTH_CONNECT
302
+ ) == PackageManager.PERMISSION_GRANTED
303
+ }
304
+
305
+ // MARK: - BLE Scanning
306
+
307
+ private fun startScan(): Boolean {
308
+ Bridge.log("R1: startScan()")
309
+ val adapter = bluetoothAdapter ?: BluetoothAdapter.getDefaultAdapter().also { bluetoothAdapter = it }
310
+ if (adapter == null) {
311
+ Bridge.log("R1: No Bluetooth adapter")
312
+ return false
313
+ }
314
+ if (!adapter.isEnabled) {
315
+ Bridge.log("R1: Bluetooth not powered on")
316
+ return false
317
+ }
318
+ if (!hasScanPermission() || !hasConnectPermission()) {
319
+ Bridge.log("R1: Missing Bluetooth permissions")
320
+ return false
321
+ }
322
+
323
+ // Already connected — don't start a new scan
324
+ if (ringGatt != null) {
325
+ Bridge.log("R1: Already connected, skipping scan")
326
+ return true
327
+ }
328
+
329
+ isDisconnecting = false
330
+
331
+ // Stop any prior scan before starting a new one (avoids leaking ScanCallback)
332
+ stopScan()
333
+
334
+ // Try address-based reconnection first
335
+ if (connectByBleAddress()) {
336
+ return true
337
+ }
338
+
339
+ val s = adapter.bluetoothLeScanner
340
+ if (s == null) {
341
+ Bridge.log("R1: BluetoothLeScanner not available")
342
+ return false
343
+ }
344
+ scanner = s
345
+
346
+ val cb = object : ScanCallback() {
347
+ override fun onScanResult(callbackType: Int, result: ScanResult?) {
348
+ if (result == null) return
349
+ handleScanResult(result)
350
+ }
351
+
352
+ override fun onScanFailed(errorCode: Int) {
353
+ Bridge.log("R1: Scan failed: $errorCode")
354
+ }
355
+ }
356
+ scanCallback = cb
357
+ val settings = ScanSettings.Builder()
358
+ .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
359
+ .build()
360
+ try {
361
+ s.startScan(null, settings, cb)
362
+ } catch (e: SecurityException) {
363
+ Bridge.log("R1: startScan SecurityException: ${e.message}")
364
+ return false
365
+ }
366
+ return true
367
+ }
368
+
369
+ override fun stopScan() {
370
+ val s = scanner ?: return
371
+ val cb = scanCallback ?: return
372
+ try {
373
+ s.stopScan(cb)
374
+ } catch (e: SecurityException) {
375
+ Bridge.log("R1: stopScan SecurityException: ${e.message}")
376
+ }
377
+ scanCallback = null
378
+ }
379
+
380
+ private fun connectByBleAddress(): Boolean {
381
+ if (deviceSearchId == "NOT_SET" || deviceSearchId.isEmpty()) {
382
+ Bridge.log("R1: No deviceSearchId set, skipping connect by address")
383
+ return false
384
+ }
385
+ val address = ringBleAddress ?: return false
386
+ val adapter = bluetoothAdapter ?: return false
387
+ if (ringGatt != null) {
388
+ Bridge.log("R1: connectByBleAddress skipped — already connected")
389
+ return true
390
+ }
391
+ val device = try {
392
+ adapter.getRemoteDevice(address)
393
+ } catch (e: IllegalArgumentException) {
394
+ Bridge.log("R1: Invalid stored BLE address: $address")
395
+ return false
396
+ }
397
+ try {
398
+ ringGatt = device.connectGatt(appContext, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
399
+ Bridge.log("R1: Reconnecting by address to ${device.name ?: address}")
400
+ } catch (e: SecurityException) {
401
+ Bridge.log("R1: connectGatt SecurityException: ${e.message}")
402
+ return false
403
+ }
404
+ return true
405
+ }
406
+
407
+ private fun matchesNameFilter(name: String?): Boolean {
408
+ if (name == null) return false
409
+ return R1BLE.NAME_FILTERS.any { name.contains(it) }
410
+ }
411
+
412
+ /** Extract a device identifier from the ring name (e.g. "EVEN R1_CEC5BA" -> "CEC5BA") */
413
+ private fun extractRingId(name: String): String? {
414
+ val idx = name.indexOf("R1_")
415
+ if (idx < 0) return null
416
+ val id = name.substring(idx + 3)
417
+ return if (id.isEmpty()) null else id
418
+ }
419
+
420
+ private fun handleScanResult(result: ScanResult) {
421
+ val device = result.device ?: return
422
+ val advertisedName = result.scanRecord?.deviceName
423
+ val deviceName: String? = try {
424
+ device.name ?: advertisedName
425
+ } catch (e: SecurityException) {
426
+ advertisedName
427
+ }
428
+ mainHandler.post {
429
+ // Already connected — ignore further scan results
430
+ if (ringGatt != null) {
431
+ stopScan()
432
+ return@post
433
+ }
434
+ if (!matchesNameFilter(deviceName)) return@post
435
+
436
+ val mfgMap = result.scanRecord?.manufacturerSpecificData
437
+ val mfgBytes: ByteArray? = if (mfgMap != null && mfgMap.size() > 0) mfgMap.valueAt(0) else null
438
+ val mfgHex = mfgBytes?.joinToString(" ") { String.format("%02X", it) } ?: "none"
439
+ Bridge.log("R1: Discovered: ${deviceName ?: "?"} (RSSI: ${result.rssi}) mfgData: $mfgHex")
440
+
441
+ // Extract ring MAC from manufacturer data (last 6 bytes) and store name->MAC map.
442
+ // Android's manufacturerSpecificData strips the 2-byte company ID; iOS keeps it. The
443
+ // resulting "last 6 bytes" land in reversed byte order between platforms (verified in
444
+ // the field), so reverse here to match iOS's stored string ("1B:08:26:8E:0E:E6").
445
+ if (deviceName != null && mfgBytes != null && mfgBytes.size >= 6) {
446
+ val tail = mfgBytes.copyOfRange(mfgBytes.size - 6, mfgBytes.size)
447
+ val macStr = tail.joinToString(":") { String.format("%02X", it) }
448
+ putRingMacInMap(deviceName, macStr)
449
+ }
450
+
451
+ // Emit discovered device
452
+ val id = deviceName?.let { extractRingId(it) }
453
+ if (id != null) {
454
+ Bridge.sendDiscoveredDevice(ControllerTypes.R1, id)
455
+ }
456
+
457
+ // Scan-only mode: don't auto-connect
458
+ if (deviceSearchId == "NOT_SET") return@post
459
+
460
+ // If search ID is specific, check it matches
461
+ val name = deviceName ?: return@post
462
+ val parsedId = extractRingId(name)
463
+ if (parsedId != deviceSearchId && !name.contains(deviceSearchId)) return@post
464
+
465
+ if (ringGatt == null) {
466
+ ringBleAddress = device.address
467
+ try {
468
+ ringGatt = device.connectGatt(appContext, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
469
+ } catch (e: SecurityException) {
470
+ Bridge.log("R1: connectGatt SecurityException: ${e.message}")
471
+ return@post
472
+ }
473
+ stopScan()
474
+ Bridge.log("R1: Connecting to ${name}")
475
+ }
476
+ }
477
+ }
478
+
479
+ // MARK: - EUS commands
480
+
481
+ private var eusSerial = 0
482
+
483
+ private fun nextEusSerial(): Int {
484
+ eusSerial = (eusSerial + 1) and 0xFFFF
485
+ if (eusSerial == 0) eusSerial = 1
486
+ return eusSerial
487
+ }
488
+
489
+ /**
490
+ * EUS system commands go to WRITE_CHAR_2 only — channel 2 is the framed
491
+ * transport; channel 1 speaks the separate legacy protocol.
492
+ */
493
+ private fun sendEusCommand(subcommand: Int, payload: ByteArray, log: String) {
494
+ val wc = writeChar2
495
+ if (wc == null) {
496
+ Bridge.log("R1: $log: no channel-2 write characteristic")
497
+ return
498
+ }
499
+ val packet = R1Eus.frame(subcommand, R1Eus.STATUS_SET, nextEusSerial(), payload)
500
+ writeNoResponse(wc, packet)
501
+ Bridge.log("R1: $log sent (${packet.size} bytes)")
502
+ }
503
+
504
+ // MARK: - Init Sequence
505
+
506
+ /**
507
+ * Runs after notify subscriptions are confirmed: claim the phone role,
508
+ * then mark the phone link ready.
509
+ */
510
+ private fun runInitSequence() {
511
+ if (initSequenceRun) return
512
+ initSequenceRun = true
513
+
514
+ Bridge.log("R1: Running init sequence")
515
+
516
+ if (writeChar2 == null) {
517
+ Bridge.log("R1: No channel-2 write characteristic, skipping init")
518
+ markConnected()
519
+ return
520
+ }
521
+
522
+ sendEusCommand(R1Eus.SUBCMD_PAIR_AUTH, byteArrayOf(0x01), "pairAuth")
523
+
524
+ mainHandler.postDelayed({ markConnected() }, 300)
525
+ }
526
+
527
+ private fun writeNoResponse(char: BluetoothGattCharacteristic, data: ByteArray) {
528
+ char.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
529
+ char.value = data
530
+ try {
531
+ ringGatt?.writeCharacteristic(char)
532
+ } catch (e: SecurityException) {
533
+ Bridge.log("R1: writeCharacteristic SecurityException: ${e.message}")
534
+ }
535
+ }
536
+
537
+ private fun markConnected() {
538
+ reconnectionManager.stop()
539
+ Bridge.log("R1: Ring connected")
540
+
541
+ val gatt = ringGatt
542
+ val connectedName = try { gatt?.device?.name } catch (e: SecurityException) { null }
543
+ if (connectedName != null) {
544
+ extractRingId(connectedName)?.let {
545
+ DeviceStore.apply("bluetooth", "controller_device_name", it)
546
+ }
547
+ }
548
+
549
+ val mac = ringMacAddress
550
+ if (mac == null) {
551
+ Bridge.log("R1: No ring MAC address found")
552
+ return
553
+ }
554
+ DeviceStore.apply("glasses", "controllerMacAddress", mac)
555
+ DeviceStore.apply("glasses", "controllerConnected", true)
556
+ // The phone link owns these flags: a ready phone link IS pairing
557
+ // success (fires handleControllerReady). The glasses link is layered
558
+ // on afterwards via advStart + RING_CONNECT_INFO.
559
+ DeviceStore.apply("glasses", "controllerFullyBooted", true)
560
+
561
+ // tell the ring to connect to the glasses if we have its mac address:
562
+ connectToGlasses()
563
+
564
+ // after a second, connect the glasses to the controller if needed:
565
+ CoroutineScope(Dispatchers.Main).launch {
566
+ delay(1000)
567
+ DeviceManager.getInstance().sgc?.connectController()
568
+ }
569
+
570
+ startHeartbeat()
571
+ }
572
+
573
+ /**
574
+ * Tells the ring which glasses may connect to it: EUS advStart whose
575
+ * 12-byte payload is the two glasses arm MACs. The ring stores both
576
+ * targets, fast-advertises while a glasses role is open, and accepts a
577
+ * connection from either arm. With only one arm MAC known it is sent for
578
+ * both targets — the ring matches against either slot.
579
+ */
580
+ private fun connectToGlasses() {
581
+ fun stored(key: String): String? =
582
+ (DeviceStore.get("glasses", key) as? String)?.takeIf { it.isNotEmpty() }
583
+
584
+ val left = stored("leftMacAddress")
585
+ ?: stored("bluetoothMacAddress")
586
+ ?: prefs.getString("glasses_btMacAddress", null)
587
+ val right = stored("rightMacAddress")
588
+ ?: prefs.getString("glasses_btMacAddressRight", null)
589
+
590
+ if (left == null) {
591
+ Bridge.log("R1: connectToGlasses: no glasses MAC")
592
+ return
593
+ }
594
+ val leftBytes = parseMac(left)
595
+ if (leftBytes == null) {
596
+ Bridge.log("R1: connectToGlasses: could not parse glasses MAC")
597
+ return
598
+ }
599
+ val rightBytes = right?.let { parseMac(it) } ?: leftBytes
600
+
601
+ // Cache so we can re-send even before the glasses are next scanned.
602
+ prefs.edit().apply {
603
+ putString("glasses_btMacAddress", left)
604
+ if (right != null) putString("glasses_btMacAddressRight", right)
605
+ apply()
606
+ }
607
+
608
+ sendEusCommand(R1Eus.SUBCMD_ADV_START, leftBytes + rightBytes, "advStart")
609
+ }
610
+
611
+ /** Parse "AA:BB:CC:DD:EE:FF" or "AABBCCDDEEFF" into 6 raw bytes. */
612
+ private fun parseMac(s: String): ByteArray? {
613
+ val cleaned = s.replace(":", "").replace("-", "")
614
+ if (cleaned.length != 12) return null
615
+ val out = ByteArray(6)
616
+ for (i in 0 until 6) {
617
+ val byte = cleaned.substring(i * 2, i * 2 + 2).toIntOrNull(16) ?: return null
618
+ out[i] = byte.toByte()
619
+ }
620
+ return out
621
+ }
622
+
623
+ // MARK: - Heartbeat
624
+
625
+ private fun startHeartbeat() {
626
+ stopHeartbeat()
627
+ val r = object : Runnable {
628
+ override fun run() {
629
+ mainHandler.postDelayed(this, 30_000L)
630
+ val isConnected = DeviceStore.get("glasses", "controllerConnected") as? Boolean ?: false
631
+ if (!isConnected) return
632
+ val char = batteryLevelChar
633
+ if (char != null) {
634
+ try {
635
+ ringGatt?.readCharacteristic(char)
636
+ } catch (e: SecurityException) {
637
+ Bridge.log("R1: heartbeat read SecurityException: ${e.message}")
638
+ }
639
+ }
640
+ }
641
+ }
642
+ heartbeatRunnable = r
643
+ mainHandler.postDelayed(r, 30_000L)
644
+ }
645
+
646
+ private fun stopHeartbeat() {
647
+ heartbeatRunnable?.let { mainHandler.removeCallbacks(it) }
648
+ heartbeatRunnable = null
649
+ }
650
+
651
+ // MARK: - Incoming Data Handling
652
+
653
+ private fun handleNotification(characteristic: BluetoothGattCharacteristic, data: ByteArray) {
654
+ val hex = data.joinToString(" ") { String.format("%02X", it) }
655
+ val shortUuid = characteristic.uuid.toString().takeLast(4)
656
+ Bridge.log("R1: $shortUuid -> $hex")
657
+
658
+ val timestamp = System.currentTimeMillis()
659
+
660
+ // Gesture: [0xFF, type, param]
661
+ if (data.size >= 3 && data[0] == R1BLE.GESTURE_MARKER) {
662
+ val gesture = R1Gesture.parse(data)
663
+ if (gesture != null) {
664
+ Bridge.log("R1: Gesture: ${gesture.rawValue}")
665
+ Bridge.sendTouchEvent(ControllerTypes.R1, gesture.rawValue, timestamp)
666
+ } else {
667
+ Bridge.log(
668
+ "R1: Unknown gesture type=0x%02X param=0x%02X".format(data[1], data[2])
669
+ )
670
+ }
671
+ return
672
+ }
673
+
674
+ // Battery: 2 bytes, first byte is percentage
675
+ if (data.size == 2 && (data[0].toInt() and 0xFF) <= 100) {
676
+ _batteryLevel = data[0].toInt() and 0xFF
677
+ Bridge.log("R1: Battery: ${_batteryLevel}%")
678
+ return
679
+ }
680
+
681
+ // State: single byte (0x01=ready, 0x00=menu)
682
+ if (data.size == 1) {
683
+ val state = when (data[0]) {
684
+ 0x01.toByte() -> "ready"
685
+ 0x00.toByte() -> "menu"
686
+ else -> "unknown(${data[0].toInt() and 0xFF})"
687
+ }
688
+ Bridge.log("R1: State: $state")
689
+ return
690
+ }
691
+
692
+ // Longer data: check for embedded gesture marker
693
+ if (data.size > 3) {
694
+ val ffIndex = data.indexOfFirst { it == R1BLE.GESTURE_MARKER }
695
+ if (ffIndex >= 0 && ffIndex + 2 < data.size) {
696
+ val slice = byteArrayOf(data[ffIndex], data[ffIndex + 1], data[ffIndex + 2])
697
+ val gesture = R1Gesture.parse(slice)
698
+ if (gesture != null) {
699
+ Bridge.log("R1: Embedded gesture: ${gesture.rawValue}")
700
+ Bridge.sendTouchEvent(ControllerTypes.R1, gesture.rawValue, timestamp)
701
+ return
702
+ }
703
+ }
704
+ }
705
+ }
706
+
707
+ // MARK: - Connection State Reset
708
+
709
+ private fun resetConnectionState() {
710
+ ringGatt = null
711
+ writeChar1 = null
712
+ notifyChar1 = null
713
+ writeChar2 = null
714
+ notifyChar2 = null
715
+ batteryLevelChar = null
716
+ notifySubscriptionCount = 0
717
+ initSequenceRun = false
718
+ pendingDescriptorWrites.clear()
719
+ pendingReads.clear()
720
+ descriptorWriteInFlight = false
721
+ readInFlight = false
722
+ // ringMacAddress/ringBleAddress persist across disconnects so
723
+ // reconnection can find the ring again; only forget() clears them.
724
+ DeviceStore.apply("glasses", "controllerConnected", false)
725
+ DeviceStore.apply("glasses", "controllerFullyBooted", false)
726
+ }
727
+
728
+ // MARK: - GATT Callback
729
+
730
+ private val gattCallback = object : BluetoothGattCallback() {
731
+ override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
732
+ when (newState) {
733
+ BluetoothGatt.STATE_CONNECTED -> {
734
+ val name = try { gatt.device.name } catch (e: SecurityException) { null }
735
+ Bridge.log("R1: Connected to ${name ?: "ring"}")
736
+
737
+ // Validate against the name->MAC map populated during scan. If the connected
738
+ // peripheral isn't in the map we don't have its true public MAC — drop the
739
+ // connection and rescan rather than publishing a stale/wrong MAC. Mirrors
740
+ // iOS R1.swift didConnect.
741
+ val mappedMac = name?.let { getRingMacFromMap(it) }
742
+ if (mappedMac == null) {
743
+ Bridge.log("R1: No MAC stored in map found for ${name ?: "ring"}")
744
+ mainHandler.post {
745
+ disconnect()
746
+ ringBleAddress = null
747
+ DeviceStore.apply("glasses", "controllerConnected", false)
748
+ DeviceStore.apply("glasses", "controllerFullyBooted", false)
749
+ DeviceStore.apply("glasses", "controllerSearching", true)
750
+ mainHandler.postDelayed({ startScan() }, 1000)
751
+ }
752
+ return
753
+ }
754
+ ringMacAddress = mappedMac
755
+
756
+ try {
757
+ gatt.discoverServices()
758
+ } catch (e: SecurityException) {
759
+ Bridge.log("R1: discoverServices SecurityException: ${e.message}")
760
+ }
761
+ }
762
+ BluetoothGatt.STATE_DISCONNECTED -> {
763
+ Bridge.log("R1: Disconnected (status=$status)")
764
+ mainHandler.post {
765
+ stopHeartbeat()
766
+ try { gatt.close() } catch (e: SecurityException) {}
767
+ if (isDisconnecting) return@post
768
+ resetConnectionState()
769
+ startReconnectionTimer()
770
+ }
771
+ }
772
+ }
773
+ }
774
+
775
+ override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
776
+ if (status != BluetoothGatt.GATT_SUCCESS) {
777
+ Bridge.log("R1: onServicesDiscovered status=$status")
778
+ return
779
+ }
780
+ mainHandler.post {
781
+ val services = gatt.services ?: return@post
782
+ for (service in services) {
783
+ for (char in service.characteristics) {
784
+ Bridge.log("R1: char discovered: ${char.uuid}")
785
+ val props = char.properties
786
+ val propStr = buildList {
787
+ if (props and BluetoothGattCharacteristic.PROPERTY_READ != 0) add("read")
788
+ if (props and BluetoothGattCharacteristic.PROPERTY_WRITE != 0) add("write")
789
+ if (props and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE != 0) add("writeNoResp")
790
+ if (props and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0) add("notify")
791
+ if (props and BluetoothGattCharacteristic.PROPERTY_INDICATE != 0) add("indicate")
792
+ }.joinToString(",")
793
+ Bridge.log("R1: char ${char.uuid} props=[$propStr]")
794
+
795
+ // Store known characteristics
796
+ when (char.uuid) {
797
+ R1BLE.WRITE_CHAR_1 -> writeChar1 = char
798
+ R1BLE.NOTIFY_CHAR_1 -> notifyChar1 = char
799
+ R1BLE.WRITE_CHAR_2 -> writeChar2 = char
800
+ R1BLE.NOTIFY_CHAR_2 -> notifyChar2 = char
801
+ R1BLE.BATTERY_LEVEL_CHAR -> batteryLevelChar = char
802
+ }
803
+
804
+ // Subscribe to any notify/indicate characteristic
805
+ val notifyEligible = props and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0
806
+ val indicateEligible = props and BluetoothGattCharacteristic.PROPERTY_INDICATE != 0
807
+ if (notifyEligible || indicateEligible) {
808
+ try {
809
+ gatt.setCharacteristicNotification(char, true)
810
+ } catch (e: SecurityException) {
811
+ Bridge.log("R1: setCharacteristicNotification SecurityException: ${e.message}")
812
+ }
813
+ val descriptor = char.getDescriptor(R1BLE.CCCD_UUID)
814
+ if (descriptor != null) {
815
+ descriptor.value = if (notifyEligible) {
816
+ BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
817
+ } else {
818
+ BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
819
+ }
820
+ pendingDescriptorWrites.addLast(descriptor)
821
+ }
822
+ }
823
+
824
+ // Queue reads (run after CCCD queue drains)
825
+ if (props and BluetoothGattCharacteristic.PROPERTY_READ != 0) {
826
+ pendingReads.addLast(char)
827
+ }
828
+ }
829
+ }
830
+ drainDescriptorQueue(gatt)
831
+ }
832
+ }
833
+
834
+ override fun onDescriptorWrite(
835
+ gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int
836
+ ) {
837
+ mainHandler.post {
838
+ descriptorWriteInFlight = false
839
+ if (status != BluetoothGatt.GATT_SUCCESS) {
840
+ Bridge.log("R1: Descriptor write failed: $status on ${descriptor.characteristic.uuid}")
841
+ } else {
842
+ Bridge.log("R1: Notify enabled on ${descriptor.characteristic.uuid}")
843
+ notifySubscriptionCount += 1
844
+ if (notifySubscriptionCount >= 2 && !initSequenceRun) {
845
+ mainHandler.postDelayed({ runInitSequence() }, 300)
846
+ }
847
+ }
848
+ drainDescriptorQueue(gatt)
849
+ }
850
+ }
851
+
852
+ override fun onCharacteristicChanged(
853
+ gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic
854
+ ) {
855
+ val data = characteristic.value ?: return
856
+ if (data.isEmpty()) return
857
+ mainHandler.post { handleNotification(characteristic, data) }
858
+ }
859
+
860
+ override fun onCharacteristicRead(
861
+ gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int
862
+ ) {
863
+ val data = characteristic.value
864
+ mainHandler.post {
865
+ readInFlight = false
866
+ if (status == BluetoothGatt.GATT_SUCCESS && data != null && data.isNotEmpty()) {
867
+ if (characteristic.uuid == R1BLE.BATTERY_LEVEL_CHAR) {
868
+ _batteryLevel = data[0].toInt() and 0xFF
869
+ Bridge.log("R1: Battery (std): ${_batteryLevel}%")
870
+ } else {
871
+ handleNotification(characteristic, data)
872
+ }
873
+ }
874
+ drainReadQueue(gatt)
875
+ }
876
+ }
877
+
878
+ override fun onCharacteristicWrite(
879
+ gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int
880
+ ) {
881
+ if (status != BluetoothGatt.GATT_SUCCESS) {
882
+ Bridge.log("R1: Write error on ${characteristic.uuid}: $status")
883
+ }
884
+ }
885
+ }
886
+
887
+ private fun drainDescriptorQueue(gatt: BluetoothGatt) {
888
+ if (descriptorWriteInFlight) return
889
+ val next = pendingDescriptorWrites.removeFirstOrNull()
890
+ if (next == null) {
891
+ // Once CCCD queue drained, start reads
892
+ drainReadQueue(gatt)
893
+ return
894
+ }
895
+ descriptorWriteInFlight = true
896
+ try {
897
+ gatt.writeDescriptor(next)
898
+ } catch (e: SecurityException) {
899
+ descriptorWriteInFlight = false
900
+ Bridge.log("R1: writeDescriptor SecurityException: ${e.message}")
901
+ drainDescriptorQueue(gatt)
902
+ }
903
+ }
904
+
905
+ private fun drainReadQueue(gatt: BluetoothGatt) {
906
+ if (readInFlight) return
907
+ val next = pendingReads.removeFirstOrNull() ?: return
908
+ readInFlight = true
909
+ try {
910
+ gatt.readCharacteristic(next)
911
+ } catch (e: SecurityException) {
912
+ readInFlight = false
913
+ Bridge.log("R1: readCharacteristic SecurityException: ${e.message}")
914
+ drainReadQueue(gatt)
915
+ }
916
+ }
917
+
918
+ // MARK: - ControllerManager overrides
919
+
920
+ override fun findCompatibleDevices() {
921
+ Bridge.log("R1: findCompatibleDevices()")
922
+ deviceSearchId = "NOT_SET"
923
+ startScan()
924
+ }
925
+
926
+ override fun connectById(id: String) {
927
+ Bridge.log("R1: connectById($id)")
928
+ deviceSearchId = id
929
+ startScan()
930
+ }
931
+
932
+ override fun disconnect() {
933
+ Bridge.log("R1: disconnect()")
934
+ isDisconnecting = true
935
+ stopHeartbeat()
936
+ reconnectionManager.stop()
937
+ try {
938
+ ringGatt?.disconnect()
939
+ ringGatt?.close()
940
+ } catch (e: SecurityException) {
941
+ Bridge.log("R1: disconnect SecurityException: ${e.message}")
942
+ }
943
+ resetConnectionState()
944
+ }
945
+
946
+ override fun forget() {
947
+ disconnect()
948
+ ringMacAddress = null
949
+ ringBleAddress = null
950
+ prefs.edit().remove("r1_ringMacAddressMap").apply()
951
+ deviceSearchId = "NOT_SET"
952
+ }
953
+
954
+ override fun cleanup() {
955
+ disconnect()
956
+ }
957
+
958
+ override fun getConnectedBluetoothName(): String? {
959
+ return try { ringGatt?.device?.name } catch (e: SecurityException) { null }
960
+ }
961
+
962
+ override fun ping() {
963
+ val char = batteryLevelChar ?: return
964
+ try {
965
+ ringGatt?.readCharacteristic(char)
966
+ } catch (e: SecurityException) {
967
+ Bridge.log("R1: ping SecurityException: ${e.message}")
968
+ }
969
+ }
970
+
971
+ override fun getBatteryStatus() {
972
+ val char = batteryLevelChar ?: return
973
+ try {
974
+ ringGatt?.readCharacteristic(char)
975
+ } catch (e: SecurityException) {
976
+ Bridge.log("R1: getBatteryStatus SecurityException: ${e.message}")
977
+ }
978
+ }
979
+
980
+ // MARK: - No-op implementations (ring has no display/camera/wifi/mic)
981
+
982
+ override fun sendIncidentId(incidentId: String) {}
983
+ override fun setMicEnabled(enabled: Boolean) {}
984
+ override fun sortMicRanking(list: MutableList<String>): MutableList<String> = list
985
+ override fun sendJson(jsonOriginal: Map<String, Any>, wakeUp: Boolean, requireAck: Boolean) {}
986
+ override fun requestPhoto(request: PhotoRequest) {}
987
+ override fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean) {}
988
+ override fun stopVideoRecording(requestId: String) {}
989
+ override fun startStream(message: Map<String, Any>) {}
990
+ override fun stopStream() {}
991
+ override fun sendStreamKeepAlive(message: Map<String, Any>) {}
992
+ override fun sendButtonPhotoSettings() {}
993
+ override fun sendButtonVideoRecordingSettings() {}
994
+ override fun sendButtonMaxRecordingTime() {}
995
+ override fun setBrightness(level: Int, autoMode: Boolean) {}
996
+ override fun clearDisplay() {}
997
+ override fun sendTextWall(text: String) {}
998
+ override fun sendDoubleTextWall(top: String, bottom: String) {}
999
+ override fun displayBitmap(
1000
+ base64ImageData: String,
1001
+ x: Int?,
1002
+ y: Int?,
1003
+ width: Int?,
1004
+ height: Int?
1005
+ ): Boolean = false
1006
+ override fun showDashboard() {}
1007
+ override fun setDashboardPosition(height: Int, depth: Int) {}
1008
+ override fun setHeadUpAngle(angle: Int) {}
1009
+ override fun setSilentMode(enabled: Boolean) {}
1010
+ override fun exit() {}
1011
+ override fun sendShutdown() { disconnect() }
1012
+ override fun sendReboot() {}
1013
+ override fun sendRgbLedControl(
1014
+ requestId: String, packageName: String?, action: String, color: String?,
1015
+ onDurationMs: Int, offDurationMs: Int, count: Int
1016
+ ) {}
1017
+ override fun requestWifiScan() {}
1018
+ override fun sendWifiCredentials(ssid: String, password: String) {}
1019
+ override fun forgetWifiNetwork(ssid: String) {}
1020
+ override fun sendHotspotState(enabled: Boolean) {}
1021
+ override fun sendOtaStart(otaVersionUrl: String?) {}
1022
+ override fun sendUserEmailToGlasses(email: String) {}
1023
+ override fun queryGalleryStatus() {}
1024
+ override fun sendGalleryMode() {}
1025
+ override fun requestVersionInfo() {}
1026
+ }
1027
+
1028
+ // MARK: - R1 Reconnection Manager
1029
+ // Coroutine-based port of Swift's R1ReconnectionManager actor.
1030
+
1031
+ private class R1ReconnectionManager(
1032
+ private val intervalSeconds: Long = 30L,
1033
+ private val maxAttempts: Int = -1
1034
+ ) {
1035
+ private var job: Job? = null
1036
+ private var attempts = 0
1037
+
1038
+ fun start(onAttempt: suspend () -> Boolean) {
1039
+ stop()
1040
+ attempts = 0
1041
+ job = CoroutineScope(Dispatchers.Default).launch {
1042
+ while (isActive) {
1043
+ delay(intervalSeconds * 1_000L)
1044
+ if (!isActive) break
1045
+ attempts += 1
1046
+ if (maxAttempts > 0 && attempts > maxAttempts) break
1047
+ if (onAttempt()) break
1048
+ }
1049
+ }
1050
+ }
1051
+
1052
+ fun stop() {
1053
+ job?.cancel()
1054
+ job = null
1055
+ attempts = 0
1056
+ }
1057
+ }