@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,937 @@
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
+ val CONFIG_FC = byteArrayOf(0xFC.toByte())
48
+ val CONFIG_11 = byteArrayOf(0x11.toByte())
49
+
50
+ // BleRing1 command header (cmd, module, subCmd) for advStart.
51
+ // From RE: BleRing1Cmd_system=0, BleRing1Module_system=0, BleRing1SubCmd_advStart=9
52
+ const val CMD_SYSTEM: Byte = 0x00
53
+ const val MODULE_SYSTEM: Byte = 0x00
54
+ const val SUBCMD_ADV_START: Byte = 0x09
55
+
56
+ const val GESTURE_MARKER: Byte = 0xFF.toByte()
57
+ }
58
+
59
+ // MARK: - R1 Gesture Types
60
+
61
+ private enum class R1Gesture(val rawValue: String) {
62
+ HOLD("hold"),
63
+ SINGLE_TAP("single_tap"),
64
+ DOUBLE_TAP("double_tap"),
65
+ SWIPE_UP("swipe_up"),
66
+ SWIPE_DOWN("swipe_down");
67
+
68
+ companion object {
69
+ /** Parse gesture from notification data: [0xFF, type, param] */
70
+ fun parse(data: ByteArray): R1Gesture? {
71
+ if (data.size < 3 || data[0] != R1BLE.GESTURE_MARKER) return null
72
+ return when (data[1]) {
73
+ 0x03.toByte() -> HOLD
74
+ 0x04.toByte() -> when (data[2]) {
75
+ 0x01.toByte() -> SINGLE_TAP
76
+ 0x02.toByte() -> DOUBLE_TAP
77
+ else -> null
78
+ }
79
+ 0x05.toByte() -> if ((data[2].toInt() and 0xFF) < 0x80) SWIPE_UP else SWIPE_DOWN
80
+ else -> null
81
+ }
82
+ }
83
+ }
84
+ }
85
+
86
+ // MARK: - R1 Controller
87
+
88
+ class R1 : ControllerManager() {
89
+
90
+ // Connection state
91
+ private val appContext: Context = Bridge.getContext()
92
+ private val prefs = appContext.getSharedPreferences("r1_prefs", Context.MODE_PRIVATE)
93
+
94
+ private var bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
95
+ private var scanner: BluetoothLeScanner? = null
96
+ private var scanCallback: ScanCallback? = null
97
+
98
+ private var ringGatt: BluetoothGatt? = null
99
+ private var isDisconnecting = false
100
+
101
+ // BLE characteristics
102
+ private var writeChar1: BluetoothGattCharacteristic? = null
103
+ private var notifyChar1: BluetoothGattCharacteristic? = null
104
+ private var writeChar2: BluetoothGattCharacteristic? = null
105
+ private var notifyChar2: BluetoothGattCharacteristic? = null
106
+ private var batteryLevelChar: BluetoothGattCharacteristic? = null
107
+ private var notifySubscriptionCount = 0
108
+ private var initSequenceRun = false
109
+
110
+ // Android serializes CCCD writes and reads — queue and drain in callbacks
111
+ private val pendingDescriptorWrites = ArrayDeque<BluetoothGattDescriptor>()
112
+ private val pendingReads = ArrayDeque<BluetoothGattCharacteristic>()
113
+ private var descriptorWriteInFlight = false
114
+ private var readInFlight = false
115
+
116
+ // Device search
117
+ private var deviceSearchId: String = "NOT_SET"
118
+
119
+ // BLE handle used by Android for reconnection (BluetoothDevice.address — may be a
120
+ // resolvable/random address depending on bonding state; not the ring's public MAC).
121
+ private var ringBleAddress: String?
122
+ get() = prefs.getString("r1_ringBleAddress", null)
123
+ set(value) {
124
+ prefs.edit().apply {
125
+ if (value == null) remove("r1_ringBleAddress") else putString("r1_ringBleAddress", value)
126
+ apply()
127
+ }
128
+ }
129
+
130
+ // Public ring MAC parsed from advertisement manufacturer data (last 6 bytes), formatted
131
+ // as "AA:BB:CC:DD:EE:FF". This is what gets published to DeviceStore/controllerMacAddress.
132
+ private var ringMacAddress: String?
133
+ get() = prefs.getString("r1_ringMacAddress", null)
134
+ set(value) {
135
+ prefs.edit().apply {
136
+ if (value == null) remove("r1_ringMacAddress") else putString("r1_ringMacAddress", value)
137
+ apply()
138
+ }
139
+ }
140
+
141
+ // peripheral name -> 6-byte ring MAC (hex string), populated from mfgData on every scan.
142
+ // Mirrors iOS ringMacAddressMap so reconnects re-validate freshness instead of trusting
143
+ // a stale stored MAC.
144
+ private fun loadRingMacAddressMap(): MutableMap<String, String> {
145
+ val raw = prefs.getString("r1_ringMacAddressMap", null) ?: return mutableMapOf()
146
+ val out = mutableMapOf<String, String>()
147
+ for (entry in raw.split(';')) {
148
+ if (entry.isEmpty()) continue
149
+ val idx = entry.indexOf('=')
150
+ if (idx <= 0) continue
151
+ out[entry.substring(0, idx)] = entry.substring(idx + 1)
152
+ }
153
+ return out
154
+ }
155
+
156
+ private fun saveRingMacAddressMap(map: Map<String, String>) {
157
+ val raw = map.entries.joinToString(";") { "${it.key}=${it.value}" }
158
+ prefs.edit().putString("r1_ringMacAddressMap", raw).apply()
159
+ }
160
+
161
+ private fun putRingMacInMap(name: String, mac: String) {
162
+ val map = loadRingMacAddressMap()
163
+ map[name] = mac
164
+ saveRingMacAddressMap(map)
165
+ }
166
+
167
+ private fun getRingMacFromMap(name: String): String? = loadRingMacAddressMap()[name]
168
+
169
+ // Reconnection (defined but currently unwired — matches iOS which leaves it commented)
170
+ private val reconnectionManager = R1ReconnectionManager()
171
+
172
+ // Battery
173
+ private var _batteryLevel: Int = -1
174
+ set(value) {
175
+ val old = field
176
+ field = value
177
+ if (value != old && value >= 0) {
178
+ DeviceStore.apply("glasses", "controllerBatteryLevel", value)
179
+ }
180
+ }
181
+
182
+ // Heartbeat
183
+ private val mainHandler = Handler(Looper.getMainLooper())
184
+ private var heartbeatRunnable: Runnable? = null
185
+
186
+ init {
187
+ type = ControllerTypes.R1
188
+ hasMic = false
189
+ }
190
+
191
+ // MARK: - Permissions
192
+
193
+ private fun hasScanPermission(): Boolean {
194
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return true
195
+ return ContextCompat.checkSelfPermission(
196
+ appContext, Manifest.permission.BLUETOOTH_SCAN
197
+ ) == PackageManager.PERMISSION_GRANTED
198
+ }
199
+
200
+ private fun hasConnectPermission(): Boolean {
201
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return true
202
+ return ContextCompat.checkSelfPermission(
203
+ appContext, Manifest.permission.BLUETOOTH_CONNECT
204
+ ) == PackageManager.PERMISSION_GRANTED
205
+ }
206
+
207
+ // MARK: - BLE Scanning
208
+
209
+ private fun startScan(): Boolean {
210
+ Bridge.log("R1: startScan()")
211
+ val adapter = bluetoothAdapter ?: BluetoothAdapter.getDefaultAdapter().also { bluetoothAdapter = it }
212
+ if (adapter == null) {
213
+ Bridge.log("R1: No Bluetooth adapter")
214
+ return false
215
+ }
216
+ if (!adapter.isEnabled) {
217
+ Bridge.log("R1: Bluetooth not powered on")
218
+ return false
219
+ }
220
+ if (!hasScanPermission() || !hasConnectPermission()) {
221
+ Bridge.log("R1: Missing Bluetooth permissions")
222
+ return false
223
+ }
224
+
225
+ // Already connected — don't start a new scan
226
+ if (ringGatt != null) {
227
+ Bridge.log("R1: Already connected, skipping scan")
228
+ return true
229
+ }
230
+
231
+ isDisconnecting = false
232
+
233
+ // Stop any prior scan before starting a new one (avoids leaking ScanCallback)
234
+ stopScan()
235
+
236
+ // Try address-based reconnection first
237
+ if (connectByBleAddress()) {
238
+ return true
239
+ }
240
+
241
+ val s = adapter.bluetoothLeScanner
242
+ if (s == null) {
243
+ Bridge.log("R1: BluetoothLeScanner not available")
244
+ return false
245
+ }
246
+ scanner = s
247
+
248
+ val cb = object : ScanCallback() {
249
+ override fun onScanResult(callbackType: Int, result: ScanResult?) {
250
+ if (result == null) return
251
+ handleScanResult(result)
252
+ }
253
+
254
+ override fun onScanFailed(errorCode: Int) {
255
+ Bridge.log("R1: Scan failed: $errorCode")
256
+ }
257
+ }
258
+ scanCallback = cb
259
+ val settings = ScanSettings.Builder()
260
+ .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
261
+ .build()
262
+ try {
263
+ s.startScan(null, settings, cb)
264
+ } catch (e: SecurityException) {
265
+ Bridge.log("R1: startScan SecurityException: ${e.message}")
266
+ return false
267
+ }
268
+ return true
269
+ }
270
+
271
+ override fun stopScan() {
272
+ val s = scanner ?: return
273
+ val cb = scanCallback ?: return
274
+ try {
275
+ s.stopScan(cb)
276
+ } catch (e: SecurityException) {
277
+ Bridge.log("R1: stopScan SecurityException: ${e.message}")
278
+ }
279
+ scanCallback = null
280
+ }
281
+
282
+ private fun connectByBleAddress(): Boolean {
283
+ if (deviceSearchId == "NOT_SET" || deviceSearchId.isEmpty()) {
284
+ Bridge.log("R1: No deviceSearchId set, skipping connect by address")
285
+ return false
286
+ }
287
+ val address = ringBleAddress ?: return false
288
+ val adapter = bluetoothAdapter ?: return false
289
+ if (ringGatt != null) {
290
+ Bridge.log("R1: connectByBleAddress skipped — already connected")
291
+ return true
292
+ }
293
+ val device = try {
294
+ adapter.getRemoteDevice(address)
295
+ } catch (e: IllegalArgumentException) {
296
+ Bridge.log("R1: Invalid stored BLE address: $address")
297
+ return false
298
+ }
299
+ try {
300
+ ringGatt = device.connectGatt(appContext, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
301
+ Bridge.log("R1: Reconnecting by address to ${device.name ?: address}")
302
+ } catch (e: SecurityException) {
303
+ Bridge.log("R1: connectGatt SecurityException: ${e.message}")
304
+ return false
305
+ }
306
+ return true
307
+ }
308
+
309
+ private fun matchesNameFilter(name: String?): Boolean {
310
+ if (name == null) return false
311
+ return R1BLE.NAME_FILTERS.any { name.contains(it) }
312
+ }
313
+
314
+ /** Extract a device identifier from the ring name (e.g. "EVEN R1_CEC5BA" -> "CEC5BA") */
315
+ private fun extractRingId(name: String): String? {
316
+ val idx = name.indexOf("R1_")
317
+ if (idx < 0) return null
318
+ val id = name.substring(idx + 3)
319
+ return if (id.isEmpty()) null else id
320
+ }
321
+
322
+ private fun handleScanResult(result: ScanResult) {
323
+ val device = result.device ?: return
324
+ val advertisedName = result.scanRecord?.deviceName
325
+ val deviceName: String? = try {
326
+ device.name ?: advertisedName
327
+ } catch (e: SecurityException) {
328
+ advertisedName
329
+ }
330
+ mainHandler.post {
331
+ // Already connected — ignore further scan results
332
+ if (ringGatt != null) {
333
+ stopScan()
334
+ return@post
335
+ }
336
+ if (!matchesNameFilter(deviceName)) return@post
337
+
338
+ val mfgMap = result.scanRecord?.manufacturerSpecificData
339
+ val mfgBytes: ByteArray? = if (mfgMap != null && mfgMap.size() > 0) mfgMap.valueAt(0) else null
340
+ val mfgHex = mfgBytes?.joinToString(" ") { String.format("%02X", it) } ?: "none"
341
+ Bridge.log("R1: Discovered: ${deviceName ?: "?"} (RSSI: ${result.rssi}) mfgData: $mfgHex")
342
+
343
+ // Extract ring MAC from manufacturer data (last 6 bytes) and store name->MAC map.
344
+ // Android's manufacturerSpecificData strips the 2-byte company ID; iOS keeps it. The
345
+ // resulting "last 6 bytes" land in reversed byte order between platforms (verified in
346
+ // the field), so reverse here to match iOS's stored string ("1B:08:26:8E:0E:E6").
347
+ if (deviceName != null && mfgBytes != null && mfgBytes.size >= 6) {
348
+ val tail = mfgBytes.copyOfRange(mfgBytes.size - 6, mfgBytes.size)
349
+ val macStr = tail.joinToString(":") { String.format("%02X", it) }
350
+ putRingMacInMap(deviceName, macStr)
351
+ }
352
+
353
+ // Emit discovered device
354
+ val id = deviceName?.let { extractRingId(it) }
355
+ if (id != null) {
356
+ Bridge.sendDiscoveredDevice(ControllerTypes.R1, id)
357
+ }
358
+
359
+ // Scan-only mode: don't auto-connect
360
+ if (deviceSearchId == "NOT_SET") return@post
361
+
362
+ // If search ID is specific, check it matches
363
+ val name = deviceName ?: return@post
364
+ val parsedId = extractRingId(name)
365
+ if (parsedId != deviceSearchId && !name.contains(deviceSearchId)) return@post
366
+
367
+ if (ringGatt == null) {
368
+ ringBleAddress = device.address
369
+ try {
370
+ ringGatt = device.connectGatt(appContext, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
371
+ } catch (e: SecurityException) {
372
+ Bridge.log("R1: connectGatt SecurityException: ${e.message}")
373
+ return@post
374
+ }
375
+ stopScan()
376
+ Bridge.log("R1: Connecting to ${name}")
377
+ }
378
+ }
379
+ }
380
+
381
+ // MARK: - Init Sequence
382
+
383
+ private fun runInitSequence() {
384
+ if (initSequenceRun) return
385
+ initSequenceRun = true
386
+
387
+ Bridge.log("R1: Running init sequence")
388
+
389
+ val writeChars = listOfNotNull(writeChar1, writeChar2)
390
+ if (writeChars.isEmpty()) {
391
+ Bridge.log("R1: No write characteristics found, skipping init")
392
+ markConnected()
393
+ return
394
+ }
395
+
396
+ writeChars.forEach { writeNoResponse(it, R1BLE.CONFIG_FC) }
397
+
398
+ mainHandler.postDelayed({
399
+ writeChars.forEach { writeNoResponse(it, R1BLE.CONFIG_11) }
400
+ markConnected()
401
+ }, 200)
402
+ }
403
+
404
+ private fun writeNoResponse(char: BluetoothGattCharacteristic, data: ByteArray) {
405
+ char.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
406
+ char.value = data
407
+ try {
408
+ ringGatt?.writeCharacteristic(char)
409
+ } catch (e: SecurityException) {
410
+ Bridge.log("R1: writeCharacteristic SecurityException: ${e.message}")
411
+ }
412
+ }
413
+
414
+ private fun markConnected() {
415
+ reconnectionManager.stop()
416
+ Bridge.log("R1: Ring connected")
417
+
418
+ val gatt = ringGatt
419
+ val connectedName = try { gatt?.device?.name } catch (e: SecurityException) { null }
420
+ if (connectedName != null) {
421
+ extractRingId(connectedName)?.let {
422
+ DeviceStore.apply("bluetooth", "controller_device_name", it)
423
+ }
424
+ }
425
+
426
+ val mac = ringMacAddress
427
+ if (mac == null) {
428
+ Bridge.log("R1: No ring MAC address found")
429
+ return
430
+ }
431
+ DeviceStore.apply("glasses", "controllerMacAddress", mac)
432
+ DeviceStore.apply("glasses", "controllerConnected", true)
433
+ // DeviceStore.apply("glasses", "controllerFullyBooted", true)
434
+
435
+ // tell the ring to connect to the glasses if we have its mac address:
436
+ connectToGlasses()
437
+
438
+ // after a second, connect the glasses to the controller if needed:
439
+ CoroutineScope(Dispatchers.Main).launch {
440
+ delay(1000)
441
+ DeviceManager.getInstance().sgc?.connectController()
442
+ }
443
+
444
+ startHeartbeat()
445
+ }
446
+
447
+ /**
448
+ * Tells the ring to start advertising / connect to the glasses.
449
+ * Sends BleRing1 advStart (cmd=0, module=0, subCmd=9) with the 6-byte glasses MAC as payload
450
+ * to WRITE_CHAR_2 (BAE80012-…). Reverse-engineered from the Even Realities mobile app
451
+ * (BleRing1CmdProto::advStart -> BleRing1CmdPublicExt.sendCmd).
452
+ *
453
+ * TODO: BleRing1CmdPublicExt.sendCmd may add additional outer framing (length/seq/CRC) around
454
+ * the 3-byte header + MAC. If the ring rejects this raw payload, decode the wrapper.
455
+ */
456
+ private fun connectToGlasses() {
457
+ // Try DeviceStore first; fall back to cached value in SharedPreferences.
458
+ val glassesMac = (DeviceStore.get("glasses", "bluetoothMacAddress") as? String)
459
+ ?: prefs.getString("glasses_btMacAddress", null)
460
+ if (glassesMac == null) {
461
+ Bridge.log("R1: connectToGlasses: no glasses MAC")
462
+ return
463
+ }
464
+ // Cache so we can reconnect even before the glasses are scanned.
465
+ prefs.edit().putString("glasses_btMacAddress", glassesMac).apply()
466
+
467
+ val macBytes = parseMac(glassesMac)
468
+ if (macBytes == null) {
469
+ Bridge.log("R1: connectToGlasses: could not parse glasses MAC")
470
+ return
471
+ }
472
+ val wc = writeChar2 ?: writeChar1
473
+ if (wc == null) {
474
+ Bridge.log("R1: connectToGlasses: no write characteristic")
475
+ return
476
+ }
477
+
478
+ val payload = byteArrayOf(R1BLE.CMD_SYSTEM, R1BLE.MODULE_SYSTEM, R1BLE.SUBCMD_ADV_START) + macBytes
479
+ Bridge.log("R1: advStart sent")
480
+
481
+ wc.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
482
+ wc.value = payload
483
+ try {
484
+ ringGatt?.writeCharacteristic(wc)
485
+ } catch (e: SecurityException) {
486
+ Bridge.log("R1: connectToGlasses writeCharacteristic SecurityException: ${e.message}")
487
+ }
488
+ }
489
+
490
+ /** Parse "AA:BB:CC:DD:EE:FF" or "AABBCCDDEEFF" into 6 raw bytes. */
491
+ private fun parseMac(s: String): ByteArray? {
492
+ val cleaned = s.replace(":", "").replace("-", "")
493
+ if (cleaned.length != 12) return null
494
+ val out = ByteArray(6)
495
+ for (i in 0 until 6) {
496
+ val byte = cleaned.substring(i * 2, i * 2 + 2).toIntOrNull(16) ?: return null
497
+ out[i] = byte.toByte()
498
+ }
499
+ return out
500
+ }
501
+
502
+ // MARK: - Heartbeat
503
+
504
+ private fun startHeartbeat() {
505
+ stopHeartbeat()
506
+ val r = object : Runnable {
507
+ override fun run() {
508
+ mainHandler.postDelayed(this, 30_000L)
509
+ val isConnected = DeviceStore.get("glasses", "controllerConnected") as? Boolean ?: false
510
+ if (!isConnected) return
511
+ val char = batteryLevelChar
512
+ if (char != null) {
513
+ try {
514
+ ringGatt?.readCharacteristic(char)
515
+ } catch (e: SecurityException) {
516
+ Bridge.log("R1: heartbeat read SecurityException: ${e.message}")
517
+ }
518
+ }
519
+ }
520
+ }
521
+ heartbeatRunnable = r
522
+ mainHandler.postDelayed(r, 30_000L)
523
+ }
524
+
525
+ private fun stopHeartbeat() {
526
+ heartbeatRunnable?.let { mainHandler.removeCallbacks(it) }
527
+ heartbeatRunnable = null
528
+ }
529
+
530
+ // MARK: - Incoming Data Handling
531
+
532
+ private fun handleNotification(characteristic: BluetoothGattCharacteristic, data: ByteArray) {
533
+ val hex = data.joinToString(" ") { String.format("%02X", it) }
534
+ val shortUuid = characteristic.uuid.toString().takeLast(4)
535
+ Bridge.log("R1: $shortUuid -> $hex")
536
+
537
+ val timestamp = System.currentTimeMillis()
538
+
539
+ // Gesture: [0xFF, type, param]
540
+ if (data.size >= 3 && data[0] == R1BLE.GESTURE_MARKER) {
541
+ val gesture = R1Gesture.parse(data)
542
+ if (gesture != null) {
543
+ Bridge.log("R1: Gesture: ${gesture.rawValue}")
544
+ Bridge.sendTouchEvent(ControllerTypes.R1, gesture.rawValue, timestamp)
545
+ } else {
546
+ Bridge.log(
547
+ "R1: Unknown gesture type=0x%02X param=0x%02X".format(data[1], data[2])
548
+ )
549
+ }
550
+ return
551
+ }
552
+
553
+ // Battery: 2 bytes, first byte is percentage
554
+ if (data.size == 2 && (data[0].toInt() and 0xFF) <= 100) {
555
+ _batteryLevel = data[0].toInt() and 0xFF
556
+ Bridge.log("R1: Battery: ${_batteryLevel}%")
557
+ return
558
+ }
559
+
560
+ // State: single byte (0x01=ready, 0x00=menu)
561
+ if (data.size == 1) {
562
+ val state = when (data[0]) {
563
+ 0x01.toByte() -> "ready"
564
+ 0x00.toByte() -> "menu"
565
+ else -> "unknown(${data[0].toInt() and 0xFF})"
566
+ }
567
+ Bridge.log("R1: State: $state")
568
+ return
569
+ }
570
+
571
+ // Longer data: check for embedded gesture marker
572
+ if (data.size > 3) {
573
+ val ffIndex = data.indexOfFirst { it == R1BLE.GESTURE_MARKER }
574
+ if (ffIndex >= 0 && ffIndex + 2 < data.size) {
575
+ val slice = byteArrayOf(data[ffIndex], data[ffIndex + 1], data[ffIndex + 2])
576
+ val gesture = R1Gesture.parse(slice)
577
+ if (gesture != null) {
578
+ Bridge.log("R1: Embedded gesture: ${gesture.rawValue}")
579
+ Bridge.sendTouchEvent(ControllerTypes.R1, gesture.rawValue, timestamp)
580
+ return
581
+ }
582
+ }
583
+ }
584
+ }
585
+
586
+ // MARK: - Connection State Reset
587
+
588
+ private fun resetConnectionState() {
589
+ ringGatt = null
590
+ writeChar1 = null
591
+ notifyChar1 = null
592
+ writeChar2 = null
593
+ notifyChar2 = null
594
+ batteryLevelChar = null
595
+ notifySubscriptionCount = 0
596
+ initSequenceRun = false
597
+ pendingDescriptorWrites.clear()
598
+ pendingReads.clear()
599
+ descriptorWriteInFlight = false
600
+ readInFlight = false
601
+ ringMacAddress = null
602
+ ringBleAddress = null
603
+ DeviceStore.apply("glasses", "controllerConnected", false)
604
+ DeviceStore.apply("glasses", "controllerFullyBooted", false)
605
+ }
606
+
607
+ // MARK: - GATT Callback
608
+
609
+ private val gattCallback = object : BluetoothGattCallback() {
610
+ override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
611
+ when (newState) {
612
+ BluetoothGatt.STATE_CONNECTED -> {
613
+ val name = try { gatt.device.name } catch (e: SecurityException) { null }
614
+ Bridge.log("R1: Connected to ${name ?: "ring"}")
615
+
616
+ // Validate against the name->MAC map populated during scan. If the connected
617
+ // peripheral isn't in the map we don't have its true public MAC — drop the
618
+ // connection and rescan rather than publishing a stale/wrong MAC. Mirrors
619
+ // iOS R1.swift didConnect.
620
+ val mappedMac = name?.let { getRingMacFromMap(it) }
621
+ if (mappedMac == null) {
622
+ Bridge.log("R1: No MAC stored in map found for ${name ?: "ring"}")
623
+ mainHandler.post {
624
+ disconnect()
625
+ ringBleAddress = null
626
+ DeviceStore.apply("glasses", "controllerConnected", false)
627
+ DeviceStore.apply("glasses", "controllerFullyBooted", false)
628
+ DeviceStore.apply("glasses", "controllerSearching", true)
629
+ mainHandler.postDelayed({ startScan() }, 1000)
630
+ }
631
+ return
632
+ }
633
+ ringMacAddress = mappedMac
634
+
635
+ try {
636
+ gatt.discoverServices()
637
+ } catch (e: SecurityException) {
638
+ Bridge.log("R1: discoverServices SecurityException: ${e.message}")
639
+ }
640
+ }
641
+ BluetoothGatt.STATE_DISCONNECTED -> {
642
+ Bridge.log("R1: Disconnected (status=$status)")
643
+ mainHandler.post {
644
+ stopHeartbeat()
645
+ try { gatt.close() } catch (e: SecurityException) {}
646
+ if (isDisconnecting) return@post
647
+ resetConnectionState()
648
+ // iOS leaves reconnection timer commented; match that behavior
649
+ }
650
+ }
651
+ }
652
+ }
653
+
654
+ override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
655
+ if (status != BluetoothGatt.GATT_SUCCESS) {
656
+ Bridge.log("R1: onServicesDiscovered status=$status")
657
+ return
658
+ }
659
+ mainHandler.post {
660
+ val services = gatt.services ?: return@post
661
+ for (service in services) {
662
+ for (char in service.characteristics) {
663
+ Bridge.log("R1: char discovered: ${char.uuid}")
664
+ val props = char.properties
665
+ val propStr = buildList {
666
+ if (props and BluetoothGattCharacteristic.PROPERTY_READ != 0) add("read")
667
+ if (props and BluetoothGattCharacteristic.PROPERTY_WRITE != 0) add("write")
668
+ if (props and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE != 0) add("writeNoResp")
669
+ if (props and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0) add("notify")
670
+ if (props and BluetoothGattCharacteristic.PROPERTY_INDICATE != 0) add("indicate")
671
+ }.joinToString(",")
672
+ Bridge.log("R1: char ${char.uuid} props=[$propStr]")
673
+
674
+ // Store known characteristics
675
+ when (char.uuid) {
676
+ R1BLE.WRITE_CHAR_1 -> writeChar1 = char
677
+ R1BLE.NOTIFY_CHAR_1 -> notifyChar1 = char
678
+ R1BLE.WRITE_CHAR_2 -> writeChar2 = char
679
+ R1BLE.NOTIFY_CHAR_2 -> notifyChar2 = char
680
+ R1BLE.BATTERY_LEVEL_CHAR -> batteryLevelChar = char
681
+ }
682
+
683
+ // Subscribe to any notify/indicate characteristic
684
+ val notifyEligible = props and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0
685
+ val indicateEligible = props and BluetoothGattCharacteristic.PROPERTY_INDICATE != 0
686
+ if (notifyEligible || indicateEligible) {
687
+ try {
688
+ gatt.setCharacteristicNotification(char, true)
689
+ } catch (e: SecurityException) {
690
+ Bridge.log("R1: setCharacteristicNotification SecurityException: ${e.message}")
691
+ }
692
+ val descriptor = char.getDescriptor(R1BLE.CCCD_UUID)
693
+ if (descriptor != null) {
694
+ descriptor.value = if (notifyEligible) {
695
+ BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
696
+ } else {
697
+ BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
698
+ }
699
+ pendingDescriptorWrites.addLast(descriptor)
700
+ }
701
+ }
702
+
703
+ // Queue reads (run after CCCD queue drains)
704
+ if (props and BluetoothGattCharacteristic.PROPERTY_READ != 0) {
705
+ pendingReads.addLast(char)
706
+ }
707
+ }
708
+ }
709
+ drainDescriptorQueue(gatt)
710
+ }
711
+ }
712
+
713
+ override fun onDescriptorWrite(
714
+ gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int
715
+ ) {
716
+ mainHandler.post {
717
+ descriptorWriteInFlight = false
718
+ if (status != BluetoothGatt.GATT_SUCCESS) {
719
+ Bridge.log("R1: Descriptor write failed: $status on ${descriptor.characteristic.uuid}")
720
+ } else {
721
+ Bridge.log("R1: Notify enabled on ${descriptor.characteristic.uuid}")
722
+ notifySubscriptionCount += 1
723
+ if (notifySubscriptionCount >= 2 && !initSequenceRun) {
724
+ mainHandler.postDelayed({ runInitSequence() }, 300)
725
+ }
726
+ }
727
+ drainDescriptorQueue(gatt)
728
+ }
729
+ }
730
+
731
+ override fun onCharacteristicChanged(
732
+ gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic
733
+ ) {
734
+ val data = characteristic.value ?: return
735
+ if (data.isEmpty()) return
736
+ mainHandler.post { handleNotification(characteristic, data) }
737
+ }
738
+
739
+ override fun onCharacteristicRead(
740
+ gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int
741
+ ) {
742
+ val data = characteristic.value
743
+ mainHandler.post {
744
+ readInFlight = false
745
+ if (status == BluetoothGatt.GATT_SUCCESS && data != null && data.isNotEmpty()) {
746
+ if (characteristic.uuid == R1BLE.BATTERY_LEVEL_CHAR) {
747
+ _batteryLevel = data[0].toInt() and 0xFF
748
+ Bridge.log("R1: Battery (std): ${_batteryLevel}%")
749
+ } else {
750
+ handleNotification(characteristic, data)
751
+ }
752
+ }
753
+ drainReadQueue(gatt)
754
+ }
755
+ }
756
+
757
+ override fun onCharacteristicWrite(
758
+ gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int
759
+ ) {
760
+ if (status != BluetoothGatt.GATT_SUCCESS) {
761
+ Bridge.log("R1: Write error on ${characteristic.uuid}: $status")
762
+ }
763
+ }
764
+ }
765
+
766
+ private fun drainDescriptorQueue(gatt: BluetoothGatt) {
767
+ if (descriptorWriteInFlight) return
768
+ val next = pendingDescriptorWrites.removeFirstOrNull()
769
+ if (next == null) {
770
+ // Once CCCD queue drained, start reads
771
+ drainReadQueue(gatt)
772
+ return
773
+ }
774
+ descriptorWriteInFlight = true
775
+ try {
776
+ gatt.writeDescriptor(next)
777
+ } catch (e: SecurityException) {
778
+ descriptorWriteInFlight = false
779
+ Bridge.log("R1: writeDescriptor SecurityException: ${e.message}")
780
+ drainDescriptorQueue(gatt)
781
+ }
782
+ }
783
+
784
+ private fun drainReadQueue(gatt: BluetoothGatt) {
785
+ if (readInFlight) return
786
+ val next = pendingReads.removeFirstOrNull() ?: return
787
+ readInFlight = true
788
+ try {
789
+ gatt.readCharacteristic(next)
790
+ } catch (e: SecurityException) {
791
+ readInFlight = false
792
+ Bridge.log("R1: readCharacteristic SecurityException: ${e.message}")
793
+ drainReadQueue(gatt)
794
+ }
795
+ }
796
+
797
+ // MARK: - ControllerManager overrides
798
+
799
+ override fun findCompatibleDevices() {
800
+ Bridge.log("R1: findCompatibleDevices()")
801
+ deviceSearchId = "NOT_SET"
802
+ startScan()
803
+ }
804
+
805
+ override fun connectById(id: String) {
806
+ Bridge.log("R1: connectById($id)")
807
+ deviceSearchId = id
808
+ startScan()
809
+ }
810
+
811
+ override fun disconnect() {
812
+ Bridge.log("R1: disconnect()")
813
+ isDisconnecting = true
814
+ stopHeartbeat()
815
+ reconnectionManager.stop()
816
+ try {
817
+ ringGatt?.disconnect()
818
+ ringGatt?.close()
819
+ } catch (e: SecurityException) {
820
+ Bridge.log("R1: disconnect SecurityException: ${e.message}")
821
+ }
822
+ resetConnectionState()
823
+ }
824
+
825
+ override fun forget() {
826
+ disconnect()
827
+ ringMacAddress = null
828
+ ringBleAddress = null
829
+ prefs.edit().remove("r1_ringMacAddressMap").apply()
830
+ deviceSearchId = "NOT_SET"
831
+ }
832
+
833
+ override fun cleanup() {
834
+ disconnect()
835
+ }
836
+
837
+ override fun getConnectedBluetoothName(): String? {
838
+ return try { ringGatt?.device?.name } catch (e: SecurityException) { null }
839
+ }
840
+
841
+ override fun ping() {
842
+ val char = batteryLevelChar ?: return
843
+ try {
844
+ ringGatt?.readCharacteristic(char)
845
+ } catch (e: SecurityException) {
846
+ Bridge.log("R1: ping SecurityException: ${e.message}")
847
+ }
848
+ }
849
+
850
+ override fun getBatteryStatus() {
851
+ val char = batteryLevelChar ?: return
852
+ try {
853
+ ringGatt?.readCharacteristic(char)
854
+ } catch (e: SecurityException) {
855
+ Bridge.log("R1: getBatteryStatus SecurityException: ${e.message}")
856
+ }
857
+ }
858
+
859
+ // MARK: - No-op implementations (ring has no display/camera/wifi/mic)
860
+
861
+ override fun sendIncidentId(incidentId: String) {}
862
+ override fun setMicEnabled(enabled: Boolean) {}
863
+ override fun sortMicRanking(list: MutableList<String>): MutableList<String> = list
864
+ override fun sendJson(jsonOriginal: Map<String, Any>, wakeUp: Boolean, requireAck: Boolean) {}
865
+ override fun requestPhoto(request: PhotoRequest) {}
866
+ override fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean) {}
867
+ override fun stopVideoRecording(requestId: String) {}
868
+ override fun startStream(message: Map<String, Any>) {}
869
+ override fun stopStream() {}
870
+ override fun sendStreamKeepAlive(message: Map<String, Any>) {}
871
+ override fun sendButtonPhotoSettings() {}
872
+ override fun sendButtonVideoRecordingSettings() {}
873
+ override fun sendButtonMaxRecordingTime() {}
874
+ override fun setBrightness(level: Int, autoMode: Boolean) {}
875
+ override fun clearDisplay() {}
876
+ override fun sendTextWall(text: String) {}
877
+ override fun sendDoubleTextWall(top: String, bottom: String) {}
878
+ override fun displayBitmap(
879
+ base64ImageData: String,
880
+ x: Int?,
881
+ y: Int?,
882
+ width: Int?,
883
+ height: Int?
884
+ ): Boolean = false
885
+ override fun showDashboard() {}
886
+ override fun setDashboardPosition(height: Int, depth: Int) {}
887
+ override fun setHeadUpAngle(angle: Int) {}
888
+ override fun setSilentMode(enabled: Boolean) {}
889
+ override fun exit() {}
890
+ override fun sendShutdown() { disconnect() }
891
+ override fun sendReboot() {}
892
+ override fun sendRgbLedControl(
893
+ requestId: String, packageName: String?, action: String, color: String?,
894
+ onDurationMs: Int, offDurationMs: Int, count: Int
895
+ ) {}
896
+ override fun requestWifiScan() {}
897
+ override fun sendWifiCredentials(ssid: String, password: String) {}
898
+ override fun forgetWifiNetwork(ssid: String) {}
899
+ override fun sendHotspotState(enabled: Boolean) {}
900
+ override fun sendOtaStart(otaVersionUrl: String?) {}
901
+ override fun sendUserEmailToGlasses(email: String) {}
902
+ override fun queryGalleryStatus() {}
903
+ override fun sendGalleryMode() {}
904
+ override fun requestVersionInfo() {}
905
+ }
906
+
907
+ // MARK: - R1 Reconnection Manager
908
+ // Coroutine-based port of Swift's R1ReconnectionManager actor.
909
+ // Currently unwired — matches iOS which leaves the reconnection timer commented out.
910
+
911
+ private class R1ReconnectionManager(
912
+ private val intervalSeconds: Long = 30L,
913
+ private val maxAttempts: Int = -1
914
+ ) {
915
+ private var job: Job? = null
916
+ private var attempts = 0
917
+
918
+ fun start(onAttempt: suspend () -> Boolean) {
919
+ stop()
920
+ attempts = 0
921
+ job = CoroutineScope(Dispatchers.Default).launch {
922
+ while (isActive) {
923
+ delay(intervalSeconds * 1_000L)
924
+ if (!isActive) break
925
+ attempts += 1
926
+ if (maxAttempts > 0 && attempts > maxAttempts) break
927
+ if (onAttempt()) break
928
+ }
929
+ }
930
+ }
931
+
932
+ fun stop() {
933
+ job?.cancel()
934
+ job = null
935
+ attempts = 0
936
+ }
937
+ }