@mentra/bluetooth-sdk 0.1.0

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 (278) hide show
  1. package/README.md +278 -0
  2. package/android/build.gradle +167 -0
  3. package/android/lc3Lib/CMakeLists.txt +6 -0
  4. package/android/lc3Lib/build.gradle +109 -0
  5. package/android/lc3Lib/proguard-rules.pro +33 -0
  6. package/android/lc3Lib/src/main/AndroidManifest.xml +2 -0
  7. package/android/lc3Lib/src/main/cpp/CMakeLists.txt +8 -0
  8. package/android/lc3Lib/src/main/cpp/google_opus_stuff/CMakeLists.txt +30 -0
  9. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/CMakeLists.txt +7 -0
  10. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.cc +96 -0
  11. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.h +53 -0
  12. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/Makefile.am +6 -0
  13. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h +26 -0
  14. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h.in +26 -0
  15. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/ogg.h +209 -0
  16. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/os_types.h +158 -0
  17. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus.h +981 -0
  18. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_custom.h +342 -0
  19. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_defines.h +799 -0
  20. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_multistream.h +660 -0
  21. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_projection.h +568 -0
  22. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_types.h +166 -0
  23. package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.cc +268 -0
  24. package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.h +115 -0
  25. package/android/lc3Lib/src/main/cpp/google_opus_stuff/opus_tools/opus_header.h +59 -0
  26. package/android/lc3Lib/src/main/cpp/liblc3/CMakeLists.txt +38 -0
  27. package/android/lc3Lib/src/main/cpp/liblc3/include/lc3.h +309 -0
  28. package/android/lc3Lib/src/main/cpp/liblc3/include/lc3_private.h +162 -0
  29. package/android/lc3Lib/src/main/cpp/liblc3/include/rnnoise.h +114 -0
  30. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/CMakeLists.txt +19 -0
  31. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.c +92 -0
  32. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.h +44 -0
  33. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.c +375 -0
  34. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.h +315 -0
  35. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.c +129 -0
  36. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.h +69 -0
  37. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/common.h +148 -0
  38. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.c +70 -0
  39. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.h +43 -0
  40. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/fastmath.h +158 -0
  41. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/lc3.c +702 -0
  42. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.c +893 -0
  43. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.h +111 -0
  44. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_arm.h +506 -0
  45. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_neon.h +281 -0
  46. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/makefile.mk +35 -0
  47. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.c +452 -0
  48. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.h +57 -0
  49. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct_neon.h +296 -0
  50. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/meson.build +46 -0
  51. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.c +61 -0
  52. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.h +57 -0
  53. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.c +880 -0
  54. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.h +103 -0
  55. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.c +904 -0
  56. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.h +119 -0
  57. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.c +3457 -0
  58. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.h +94 -0
  59. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.c +457 -0
  60. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.h +99 -0
  61. package/android/lc3Lib/src/main/cpp/liblc3/liblc3.cpp +159 -0
  62. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/_kiss_fft_guts.h +182 -0
  63. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/arch.h +261 -0
  64. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.c +279 -0
  65. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.h +59 -0
  66. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/common.h +48 -0
  67. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/compile.sh +3 -0
  68. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/denoise.c +646 -0
  69. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.c +601 -0
  70. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.h +203 -0
  71. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/opus_types.h +159 -0
  72. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.c +526 -0
  73. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.h +149 -0
  74. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.c +178 -0
  75. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.h +69 -0
  76. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.c +11051 -0
  77. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.h +34 -0
  78. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_reader.c +168 -0
  79. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_train.py +66 -0
  80. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/tansig_table.h +45 -0
  81. package/android/lc3Lib/src/main/java/com/mentra/lc3Lib/Lc3Cpp.java +38 -0
  82. package/android/lc3Lib/third_party/CMakeLists.txt +23 -0
  83. package/android/lc3Lib/third_party/CMakeLists_libogg.txt +18 -0
  84. package/android/lc3Lib/third_party/CMakeLists_libopus.txt +18 -0
  85. package/android/lc3Lib/third_party/CMakeLists_opus-tools.txt +23 -0
  86. package/android/lc3Lib/third_party/opus_tools/src/AUTHORS +5 -0
  87. package/android/lc3Lib/third_party/opus_tools/src/CMakeLists.txt +4 -0
  88. package/android/lc3Lib/third_party/opus_tools/src/COPYING +371 -0
  89. package/android/lc3Lib/third_party/opus_tools/src/ChangeLog +3 -0
  90. package/android/lc3Lib/third_party/opus_tools/src/Makefile.am +42 -0
  91. package/android/lc3Lib/third_party/opus_tools/src/Makefile.unix +23 -0
  92. package/android/lc3Lib/third_party/opus_tools/src/NEWS +0 -0
  93. package/android/lc3Lib/third_party/opus_tools/src/README +0 -0
  94. package/android/lc3Lib/third_party/opus_tools/src/autogen.sh +115 -0
  95. package/android/lc3Lib/third_party/opus_tools/src/configure.ac +293 -0
  96. package/android/lc3Lib/third_party/opus_tools/src/src/arch.h +239 -0
  97. package/android/lc3Lib/third_party/opus_tools/src/src/audio-in.c +1046 -0
  98. package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.c +245 -0
  99. package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.h +28 -0
  100. package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.c +320 -0
  101. package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.h +51 -0
  102. package/android/lc3Lib/third_party/opus_tools/src/src/lpc.c +157 -0
  103. package/android/lc3Lib/third_party/opus_tools/src/src/lpc.h +27 -0
  104. package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.c +286 -0
  105. package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.h +59 -0
  106. package/android/lc3Lib/third_party/opus_tools/src/src/opusdec.c +884 -0
  107. package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.c +1021 -0
  108. package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.h +101 -0
  109. package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.c +639 -0
  110. package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.h +51 -0
  111. package/android/lc3Lib/third_party/opus_tools/src/src/os_support.h +167 -0
  112. package/android/lc3Lib/third_party/opus_tools/src/src/resample.c +1137 -0
  113. package/android/lc3Lib/third_party/opus_tools/src/src/speex_resampler.h +344 -0
  114. package/android/lc3Lib/third_party/opus_tools/src/src/stack_alloc.h +115 -0
  115. package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.c +125 -0
  116. package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.h +62 -0
  117. package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.c +223 -0
  118. package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.h +60 -0
  119. package/android/settings.gradle +1 -0
  120. package/android/src/main/AndroidManifest.xml +40 -0
  121. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +528 -0
  122. package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +610 -0
  123. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +1616 -0
  124. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +314 -0
  125. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothModels.kt +1710 -0
  126. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +554 -0
  127. package/android/src/main/java/com/mentra/bluetoothsdk/ObservableStore.kt +68 -0
  128. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +158 -0
  129. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +934 -0
  130. package/android/src/main/java/com/mentra/bluetoothsdk/services/Foreground.kt +175 -0
  131. package/android/src/main/java/com/mentra/bluetoothsdk/services/PhoneMic.kt +1109 -0
  132. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.java +3974 -0
  133. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +3551 -0
  134. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.java +1327 -0
  135. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.java +6959 -0
  136. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +1655 -0
  137. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraosBle.java +53675 -0
  138. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +202 -0
  139. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +224 -0
  140. package/android/src/main/java/com/mentra/bluetoothsdk/stt/STTTools.kt +340 -0
  141. package/android/src/main/java/com/mentra/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +465 -0
  142. package/android/src/main/java/com/mentra/bluetoothsdk/stt/VadGateSpeechPolicy.kt +221 -0
  143. package/android/src/main/java/com/mentra/bluetoothsdk/utils/AES.java +106 -0
  144. package/android/src/main/java/com/mentra/bluetoothsdk/utils/AudioSessionMonitor.kt +189 -0
  145. package/android/src/main/java/com/mentra/bluetoothsdk/utils/BitmapJavaUtils.java +169 -0
  146. package/android/src/main/java/com/mentra/bluetoothsdk/utils/BlePhotoUploadService.java +179 -0
  147. package/android/src/main/java/com/mentra/bluetoothsdk/utils/Constants.kt +44 -0
  148. package/android/src/main/java/com/mentra/bluetoothsdk/utils/G1Text.kt +436 -0
  149. package/android/src/main/java/com/mentra/bluetoothsdk/utils/IncidentLogBleRelayNaming.java +29 -0
  150. package/android/src/main/java/com/mentra/bluetoothsdk/utils/IncidentLogBleUploadService.java +76 -0
  151. package/android/src/main/java/com/mentra/bluetoothsdk/utils/K900ProtocolUtils.java +791 -0
  152. package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunker.java +186 -0
  153. package/android/src/main/java/com/mentra/bluetoothsdk/utils/NexSGCUtils.kt +589 -0
  154. package/android/src/main/java/com/mentra/bluetoothsdk/utils/PhoneAudioMonitor.kt +303 -0
  155. package/android/src/main/java/com/mentra/bluetoothsdk/utils/SmartGlassesConnectionState.java +9 -0
  156. package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/ByteUtilAudioPlayer.java +655 -0
  157. package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/Lc3Player.java +441 -0
  158. package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/PCMAudioPlayer.java +431 -0
  159. package/android/src/main/res/values/strings.xml +4 -0
  160. package/app.plugin.js +1 -0
  161. package/build/BluetoothSdk.types.d.ts +498 -0
  162. package/build/BluetoothSdk.types.d.ts.map +1 -0
  163. package/build/BluetoothSdk.types.js +23 -0
  164. package/build/BluetoothSdk.types.js.map +1 -0
  165. package/build/BluetoothSdkModule.d.ts +71 -0
  166. package/build/BluetoothSdkModule.d.ts.map +1 -0
  167. package/build/BluetoothSdkModule.js +106 -0
  168. package/build/BluetoothSdkModule.js.map +1 -0
  169. package/build/index.d.ts +3 -0
  170. package/build/index.d.ts.map +1 -0
  171. package/build/index.js +5 -0
  172. package/build/index.js.map +1 -0
  173. package/expo-module.config.json +9 -0
  174. package/ios/BluetoothSdkModule.swift +665 -0
  175. package/ios/MentraBluetoothSDK.podspec +77 -0
  176. package/ios/Packages/CoreObjC/CoreObjC.xcodeproj/project.pbxproj +213 -0
  177. package/ios/Packages/CoreObjC/PcmConverter.h +22 -0
  178. package/ios/Packages/CoreObjC/PcmConverter.m +266 -0
  179. package/ios/Packages/CoreObjC/attdet.c +92 -0
  180. package/ios/Packages/CoreObjC/attdet.h +44 -0
  181. package/ios/Packages/CoreObjC/bits.c +375 -0
  182. package/ios/Packages/CoreObjC/bits.h +315 -0
  183. package/ios/Packages/CoreObjC/bwdet.c +129 -0
  184. package/ios/Packages/CoreObjC/bwdet.h +69 -0
  185. package/ios/Packages/CoreObjC/common.h +151 -0
  186. package/ios/Packages/CoreObjC/energy.c +70 -0
  187. package/ios/Packages/CoreObjC/energy.h +43 -0
  188. package/ios/Packages/CoreObjC/fastmath.h +158 -0
  189. package/ios/Packages/CoreObjC/lc3.c +704 -0
  190. package/ios/Packages/CoreObjC/lc3.h +313 -0
  191. package/ios/Packages/CoreObjC/lc3_cpp.h +283 -0
  192. package/ios/Packages/CoreObjC/lc3_private.h +163 -0
  193. package/ios/Packages/CoreObjC/ltpf.c +905 -0
  194. package/ios/Packages/CoreObjC/ltpf.h +111 -0
  195. package/ios/Packages/CoreObjC/ltpf_arm.h +506 -0
  196. package/ios/Packages/CoreObjC/ltpf_neon.h +281 -0
  197. package/ios/Packages/CoreObjC/makefile.mk +35 -0
  198. package/ios/Packages/CoreObjC/mdct.c +469 -0
  199. package/ios/Packages/CoreObjC/mdct.h +57 -0
  200. package/ios/Packages/CoreObjC/mdct_neon.h +296 -0
  201. package/ios/Packages/CoreObjC/meson.build +61 -0
  202. package/ios/Packages/CoreObjC/plc.c +61 -0
  203. package/ios/Packages/CoreObjC/plc.h +57 -0
  204. package/ios/Packages/CoreObjC/rnnoise.h +114 -0
  205. package/ios/Packages/CoreObjC/sns.c +880 -0
  206. package/ios/Packages/CoreObjC/sns.h +103 -0
  207. package/ios/Packages/CoreObjC/spec.c +907 -0
  208. package/ios/Packages/CoreObjC/spec.h +119 -0
  209. package/ios/Packages/CoreObjC/tables.c +3457 -0
  210. package/ios/Packages/CoreObjC/tables.h +94 -0
  211. package/ios/Packages/CoreObjC/tns.c +457 -0
  212. package/ios/Packages/CoreObjC/tns.h +99 -0
  213. package/ios/Packages/SherpaOnnx/Model/joiner.onnx +0 -0
  214. package/ios/Packages/SherpaOnnx/Model/tokens.txt +502 -0
  215. package/ios/Packages/SherpaOnnx/SherpaOnnx.swift +1659 -0
  216. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/cargs.h +162 -0
  217. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/sherpa-onnx/c-api/c-api.h +1852 -0
  218. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/sherpa-onnx/c-api/cxx-api.h +674 -0
  219. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Info.plist +44 -0
  220. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/libsherpa-onnx.a +0 -0
  221. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/sherpa-onnx.a +0 -0
  222. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a +0 -0
  223. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/sherpa-onnx.a +0 -0
  224. package/ios/Packages/VAD/Common/VAD/VADQuality.swift +41 -0
  225. package/ios/Packages/VAD/Common/VAD/VADState.swift +26 -0
  226. package/ios/Packages/VAD/Common/VAD/VADStrategy.swift +29 -0
  227. package/ios/Packages/VAD/Common/VAD/VADType.swift +14 -0
  228. package/ios/Packages/VAD/Data/Configuration.swift +68 -0
  229. package/ios/Packages/VAD/Data/FrameSize.swift +39 -0
  230. package/ios/Packages/VAD/Data/Record.swift +13 -0
  231. package/ios/Packages/VAD/Data/Result.swift +22 -0
  232. package/ios/Packages/VAD/Data/SampleRate.swift +48 -0
  233. package/ios/Packages/VAD/Silero/Model/silero_vad.onnx +0 -0
  234. package/ios/Packages/VAD/Silero/SileroVAD.swift +284 -0
  235. package/ios/Packages/VAD/Silero/SileroVADStrategy.swift +64 -0
  236. package/ios/Packages/libbz2/module.modulemap +5 -0
  237. package/ios/Packages/libbz2/shim.h +1 -0
  238. package/ios/Source/Bridge.swift +353 -0
  239. package/ios/Source/Bridging-Header.h +16 -0
  240. package/ios/Source/DeviceManager.swift +1478 -0
  241. package/ios/Source/DeviceStore.swift +295 -0
  242. package/ios/Source/MentraBluetoothSDK.swift +2439 -0
  243. package/ios/Source/ObservableStore.swift +88 -0
  244. package/ios/Source/PrivacyInfo.xcprivacy +23 -0
  245. package/ios/Source/controllers/ControllerManager.swift +187 -0
  246. package/ios/Source/controllers/R1.swift +827 -0
  247. package/ios/Source/services/PhoneMic.swift +662 -0
  248. package/ios/Source/sgcs/Frame.swift +651 -0
  249. package/ios/Source/sgcs/G1.swift +2505 -0
  250. package/ios/Source/sgcs/G2.swift +3730 -0
  251. package/ios/Source/sgcs/Mach1.swift +543 -0
  252. package/ios/Source/sgcs/MentraLive.swift +4708 -0
  253. package/ios/Source/sgcs/MentraNex.swift +2479 -0
  254. package/ios/Source/sgcs/SGCManager.swift +243 -0
  255. package/ios/Source/sgcs/Simulated.swift +270 -0
  256. package/ios/Source/sgcs/mentraos_ble.pb.swift +3811 -0
  257. package/ios/Source/stt/STTTools.swift +154 -0
  258. package/ios/Source/stt/SherpaOnnxTranscriber.swift +401 -0
  259. package/ios/Source/utils/AudioSessionMonitor.swift +283 -0
  260. package/ios/Source/utils/Constants.swift +74 -0
  261. package/ios/Source/utils/Enums.swift +95 -0
  262. package/ios/Source/utils/G1Text.swift +2067 -0
  263. package/ios/Source/utils/JSCExperiment.swift +241 -0
  264. package/ios/Source/utils/MemoryMonitor.swift +44 -0
  265. package/ios/Source/utils/MessageChunker.swift +164 -0
  266. package/ios/Source/utils/Models.swift +135 -0
  267. package/ios/Source/utils/PhoneAudioMonitor.swift +226 -0
  268. package/ios/Source/utils/TarBz2Extractor.swift +206 -0
  269. package/package.json +78 -0
  270. package/plugin/build/index.d.ts +6 -0
  271. package/plugin/build/index.js +12 -0
  272. package/plugin/build/withAndroid.d.ts +4 -0
  273. package/plugin/build/withAndroid.js +80 -0
  274. package/plugin/build/withIos.d.ts +4 -0
  275. package/plugin/build/withIos.js +64 -0
  276. package/src/BluetoothSdk.types.ts +581 -0
  277. package/src/BluetoothSdkModule.ts +259 -0
  278. package/src/index.ts +4 -0
@@ -0,0 +1,1327 @@
1
+ package com.mentra.bluetoothsdk.sgcs;
2
+
3
+ import android.content.Context;
4
+ import android.content.res.Resources;
5
+ import android.graphics.Bitmap;
6
+ import android.graphics.BitmapFactory;
7
+ import android.graphics.drawable.Drawable;
8
+ import android.os.Handler;
9
+ import android.os.Looper;
10
+ import android.util.Log;
11
+
12
+ import androidx.annotation.NonNull;
13
+ import androidx.annotation.Nullable;
14
+ import androidx.lifecycle.LiveData;
15
+ import androidx.lifecycle.Observer;
16
+
17
+ // Mentra
18
+ import com.mentra.bluetoothsdk.sgcs.SGCManager;
19
+ import com.mentra.bluetoothsdk.DeviceManager;
20
+ import com.mentra.bluetoothsdk.Bridge;
21
+ import com.mentra.bluetoothsdk.utils.DeviceTypes;
22
+ import com.mentra.bluetoothsdk.utils.ConnTypes;
23
+ import com.mentra.bluetoothsdk.utils.BitmapJavaUtils;
24
+ import com.mentra.bluetoothsdk.utils.SmartGlassesConnectionState;
25
+ import com.mentra.bluetoothsdk.utils.K900ProtocolUtils;
26
+ import com.mentra.bluetoothsdk.utils.MessageChunker;
27
+ import com.mentra.bluetoothsdk.utils.audio.Lc3Player;
28
+ import com.mentra.bluetoothsdk.utils.BlePhotoUploadService;
29
+ import com.mentra.bluetoothsdk.DeviceStore;
30
+
31
+ // import com.augmentos.augmentos_core.R;
32
+ // import com.augmentos.augmentos_core.smarterglassesmanager.smartglassescommunicators.SmartGlassesCommunicator;
33
+ // import com.augmentos.augmentos_core.smarterglassesmanager.smartglassescommunicators.SmartGlassesFontSize;
34
+ // import com.augmentos.augmentos_core.smarterglassesmanager.utils.SmartGlassesConnectionState;
35
+ import com.squareup.picasso.Picasso;
36
+ import com.squareup.picasso.Target;
37
+ // import com.augmentos.augmentos_core.smarterglassesmanager.eventbusmessages.BatteryLevelEvent;
38
+ // import com.augmentos.augmentos_core.smarterglassesmanager.eventbusmessages.GlassesBluetoothSearchDiscoverEvent;
39
+ // import com.augmentos.augmentos_core.smarterglassesmanager.eventbusmessages.GlassesDisplayPowerEvent;
40
+ // import com.augmentos.augmentos_core.smarterglassesmanager.supportedglasses.SmartGlassesDevice;
41
+ import com.vuzix.ultralite.Anchor;
42
+ import com.vuzix.ultralite.BatteryStatus;
43
+ import com.vuzix.ultralite.EventListener;
44
+ import com.vuzix.ultralite.Layout;
45
+ import com.vuzix.ultralite.TextAlignment;
46
+ import com.vuzix.ultralite.TextWrapMode;
47
+ import com.vuzix.ultralite.UltraliteColor;
48
+ import com.vuzix.ultralite.UltraliteSDK;
49
+
50
+ import org.greenrobot.eventbus.EventBus;
51
+
52
+ import java.util.ArrayList;
53
+ import java.util.Arrays;
54
+ import java.util.Collections;
55
+ import java.util.List;
56
+ import java.util.Map;
57
+
58
+
59
+ //communicate with ActiveLook smart glasses
60
+ public class Mach1 extends SGCManager {
61
+ private static final String TAG = "WearableAi_UltraliteSGC";
62
+
63
+ UltraliteSDK ultraliteSdk;
64
+ UltraliteSDK.Canvas ultraliteCanvas;
65
+ UltraliteListener ultraliteListener;
66
+ Layout currentUltraliteLayout;
67
+ boolean screenToggleOff = false; //should we keep the screen off?
68
+ Context context;
69
+ public static final int cardLingerTime = 15;
70
+
71
+ private ArrayList rowTextsLiveNow;
72
+
73
+ //ultralite pixel buffer on left side of screen
74
+ int ultraliteLeftSidePixelBuffer = 40;
75
+
76
+ //handler to turn off screen
77
+ Handler goHomeHandler;
78
+ Runnable goHomeRunnable;
79
+
80
+ //handler to turn off screen/toggle
81
+ Handler screenOffHandler;
82
+ Runnable screenOffRunnable;
83
+
84
+ //handler to check battery life
85
+ Handler batteryHandler;
86
+ Runnable batteryRunnable;
87
+
88
+ //handler to disconnect
89
+ Handler killHandler;
90
+
91
+ boolean hasUltraliteControl;
92
+ boolean screenIsClear;
93
+ boolean isHeadUp = false; // Dashboard state toggled by taps
94
+ // SmartGlassesDevice smartGlassesDevice;
95
+ private static final long TAP_DEBOUNCE_TIME = 80; // milliseconds
96
+ private long lastTapTime = 0;
97
+ private int totalDashboardsIdk = 0;
98
+
99
+ private boolean hasBattery() {
100
+ Object level = DeviceStore.INSTANCE.get("glasses", "batteryLevel");
101
+ return level instanceof Number && ((Number) level).intValue() != -1;
102
+ }
103
+
104
+ private void updateConnectionState(String state) {
105
+ boolean isEqual = state.equals(getConnectionState());
106
+ if (isEqual) {
107
+ return;
108
+ }
109
+
110
+ // Update the connection state
111
+ DeviceStore.INSTANCE.apply("glasses", "connectionState", state);
112
+
113
+ if (state.equals(ConnTypes.CONNECTED)) {
114
+ // Match iOS: only declare fully booted once we have battery info too
115
+ if (hasBattery()) {
116
+ DeviceStore.INSTANCE.apply("glasses", "connected", true);
117
+ DeviceStore.INSTANCE.apply("glasses", "fullyBooted", true);
118
+ }
119
+ } else if (state.equals(ConnTypes.DISCONNECTED)) {
120
+ DeviceStore.INSTANCE.apply("glasses", "fullyBooted", false);
121
+ DeviceStore.INSTANCE.apply("glasses", "connected", false);
122
+ }
123
+ }
124
+
125
+ @Override
126
+ public void setMicEnabled(boolean enabled) {
127
+ }
128
+
129
+ @Override
130
+ public List<String> sortMicRanking(List<String> list) {
131
+ return list;
132
+ }
133
+
134
+ @Override
135
+ public void requestPhoto(@NonNull String requestId, @NonNull String appId, @NonNull String size, @Nullable String webhookUrl, @Nullable String authToken, @Nullable String compress, boolean flash, boolean sound) {
136
+
137
+ }
138
+
139
+ @Override
140
+ public void startStream(@NonNull Map<String, Object> message) {
141
+
142
+ }
143
+
144
+ @Override
145
+ public void stopStream() {
146
+
147
+ }
148
+
149
+ @Override
150
+ public void sendStreamKeepAlive(@NonNull Map<String, Object> message) {
151
+
152
+ }
153
+
154
+ @Override
155
+ public void startVideoRecording(@NonNull String requestId, boolean save, boolean flash, boolean sound) {
156
+
157
+ }
158
+
159
+ @Override
160
+ public void stopVideoRecording(@NonNull String requestId) {
161
+
162
+ }
163
+
164
+ @Override
165
+ public void sendButtonPhotoSettings() {
166
+
167
+ }
168
+
169
+ @Override
170
+ public void sendButtonVideoRecordingSettings() {
171
+
172
+ }
173
+
174
+ @Override
175
+ public void sendButtonMaxRecordingTime() {
176
+
177
+ }
178
+
179
+ @Override
180
+ public void sendButtonCameraLedSetting() {
181
+
182
+ }
183
+
184
+ @Override
185
+ public void sendCameraFovSetting() {
186
+ }
187
+
188
+ @Override
189
+ public void setBrightness(int level, boolean autoMode) {
190
+ Bridge.log("Mach1: setBrightness() - level: " + level + "%, autoMode: " + autoMode);
191
+ updateGlassesBrightness(level);
192
+ updateGlassesAutoBrightness(autoMode);
193
+ }
194
+
195
+ @Override
196
+ public void clearDisplay() {
197
+ Bridge.log("Mach1: clearDisplay()");
198
+ blankScreen();
199
+ }
200
+
201
+ @Override
202
+ public void sendTextWall(@NonNull String text) {
203
+ Bridge.log("Mach1: sendTextWall()");
204
+ displayTextWall(text);
205
+ }
206
+
207
+ @Override
208
+ public void sendDoubleTextWall(@NonNull String top, @NonNull String bottom) {
209
+ Bridge.log("Mach1: sendDoubleTextWall()");
210
+ displayDoubleTextWall(top, bottom);
211
+ }
212
+
213
+ @Override
214
+ public boolean displayBitmap(@NonNull String base64ImageData) {
215
+ try {
216
+ Bridge.log("Mach1: displayBitmap() - decoding base64");
217
+ // Decode base64 to byte array
218
+ byte[] bmpData = android.util.Base64.decode(base64ImageData, android.util.Base64.DEFAULT);
219
+
220
+ if (bmpData == null || bmpData.length == 0) {
221
+ Log.e(TAG, "Failed to decode base64 image data");
222
+ return false;
223
+ }
224
+
225
+ // Convert byte array to Bitmap
226
+ Bitmap bitmap = BitmapFactory.decodeByteArray(bmpData, 0, bmpData.length);
227
+
228
+ if (bitmap == null) {
229
+ Log.e(TAG, "Failed to decode bitmap from byte array");
230
+ return false;
231
+ }
232
+
233
+ // Call internal implementation
234
+ displayBitmap(bitmap);
235
+ return true;
236
+ } catch (Exception e) {
237
+ Log.e(TAG, "Error displaying bitmap from base64", e);
238
+ return false;
239
+ }
240
+ }
241
+
242
+ @Override
243
+ public void showDashboard() {
244
+
245
+ }
246
+
247
+ @Override
248
+ public void ping() {
249
+ }
250
+
251
+ @Override
252
+ public void dbg1() {}
253
+
254
+ @Override
255
+ public void dbg2() {}
256
+
257
+ @Override
258
+ public void setDashboardPosition(int height, int depth) {
259
+
260
+ }
261
+
262
+ @Override
263
+ public void setHeadUpAngle(int angle) {
264
+
265
+ }
266
+
267
+ @Override
268
+ public void getBatteryStatus() {
269
+
270
+ }
271
+
272
+ @Override
273
+ public void setSilentMode(boolean enabled) {
274
+
275
+ }
276
+
277
+ @Override
278
+ public void exit() {
279
+
280
+ }
281
+
282
+ @Override
283
+ public void sendShutdown() {
284
+ Bridge.log("sendShutdown - not supported on Mach1");
285
+ }
286
+
287
+ @Override
288
+ public void sendReboot() {
289
+ Bridge.log("sendReboot - not supported on Mach1");
290
+ }
291
+
292
+ @Override
293
+ public void sendRgbLedControl(String requestId, String packageName, String action, String color, int ontime, int offtime, int count) {
294
+ Bridge.log("sendRgbLedControl - not supported on Mach1");
295
+ Bridge.sendRgbLedControlResponse(requestId, false, "device_not_supported");
296
+ }
297
+
298
+ @Override
299
+ public void disconnect() {
300
+ Bridge.log("Mach1: disconnect()");
301
+ if (ultraliteSdk != null) {
302
+ ultraliteSdk.releaseControl();
303
+ updateConnectionState(ConnTypes.DISCONNECTED);
304
+ }
305
+ }
306
+
307
+ @Override
308
+ public void forget() {
309
+ Bridge.log("Mach1: forget()");
310
+ // Release control and clean up
311
+ destroy();
312
+ }
313
+
314
+ @Override
315
+ public void findCompatibleDevices() {
316
+ Bridge.log("Mach1: findCompatibleDevices()");
317
+ findCompatibleDeviceNames();
318
+ }
319
+
320
+ @Override
321
+ public void stopScan() {
322
+ Bridge.log("Mach1: stopScan() - no active BLE scanner owned by Mach1");
323
+ }
324
+
325
+ @Override
326
+ public void connectById(@NonNull String id) {
327
+ Bridge.log("Mach1: connectById() - id: " + id);
328
+ // The Mach1 uses UltraliteSDK which handles connection internally
329
+ // We just need to trigger the connection process
330
+ connectToSmartGlasses();
331
+ }
332
+
333
+ @NonNull
334
+ @Override
335
+ public String getConnectedBluetoothName() {
336
+ // Mach1/Ultralite SDK doesn't expose the peripheral name directly
337
+ // Return device type if connected
338
+ if (ultraliteSdk != null) {
339
+ LiveData<Boolean> connected = ultraliteSdk.getConnected();
340
+ if (connected != null && Boolean.TRUE.equals(connected.getValue())) {
341
+ return "Vuzix Ultralite";
342
+ }
343
+ }
344
+ return "";
345
+ }
346
+
347
+ @Override
348
+ public void cleanup() {
349
+ destroy();
350
+ }
351
+
352
+ @Override
353
+ public void requestWifiScan() {
354
+
355
+ }
356
+
357
+ @Override
358
+ public void sendWifiCredentials(@NonNull String ssid, @NonNull String password) {
359
+
360
+ }
361
+
362
+ @Override
363
+ public void forgetWifiNetwork(String ssid) {
364
+ // Mach1 doesn't support WiFi
365
+ }
366
+
367
+ @Override
368
+ public void sendHotspotState(boolean enabled) {
369
+ // Mach1 doesn't support hotspot
370
+ }
371
+
372
+ @Override
373
+ public void sendUserEmailToGlasses(String email) {
374
+ // Mach1 doesn't support user email (no ASG client)
375
+ }
376
+
377
+ @Override
378
+ public void sendIncidentId(String incidentId, String apiBaseUrl) {
379
+ // Mach1 doesn't support incident reporting (no ASG client)
380
+ }
381
+
382
+ @Override
383
+ public void queryGalleryStatus() {
384
+
385
+ }
386
+
387
+ @Override
388
+ public void sendGalleryMode() {
389
+ // Mach1 doesn't have a built-in camera/gallery system
390
+ Bridge.log("Mach1: sendGalleryModeActive - not supported on Mach1");
391
+ }
392
+
393
+ @Override
394
+ public void requestVersionInfo() {
395
+ // Mach1 doesn't support version info requests
396
+ Bridge.log("Mach1: requestVersionInfo - not supported on Mach1");
397
+ }
398
+
399
+ public class UltraliteListener implements EventListener{
400
+ @Override
401
+ public void onTap(int tapCount) {
402
+ long currentTime = System.currentTimeMillis();
403
+ if (currentTime - lastTapTime < TAP_DEBOUNCE_TIME) {
404
+ Log.d(TAG, "Ignoring duplicate tap event");
405
+ return;
406
+ }
407
+ totalDashboardsIdk++;
408
+ Log.d(TAG, "TOTAL NUMBER OF DASHBOARD TOGGLEZ: " + totalDashboardsIdk);
409
+
410
+ lastTapTime = currentTime;
411
+ Log.d(TAG, "Ultralite go tap n times: " + tapCount);
412
+
413
+ // Toggle dashboard on 2+ taps (same as iOS implementation)
414
+ if (tapCount >= 2) {
415
+ isHeadUp = !isHeadUp;
416
+ // Notify DeviceManager of head up state change (same as G1 does with IMU)
417
+ DeviceStore.INSTANCE.apply("glasses", "headUp", isHeadUp);
418
+ Log.d(TAG, "Mach1: Dashboard toggled via tap, isHeadUp: " + isHeadUp);
419
+
420
+ // Auto turn off the dashboard after 15 seconds
421
+ if (isHeadUp) {
422
+ goHomeHandler.postDelayed(() -> {
423
+ if (isHeadUp) {
424
+ isHeadUp = false;
425
+ DeviceStore.INSTANCE.apply("glasses", "headUp", false);
426
+ Log.d(TAG, "Mach1: Auto-disabling dashboard after 15 seconds");
427
+ }
428
+ }, 15000);
429
+ }
430
+ }
431
+ }
432
+
433
+ @Override
434
+ public void onDisplayTimeout() {
435
+ Log.d(TAG, "Ultralite display timeout.");
436
+ }
437
+
438
+ @Override
439
+ public void onPowerButtonPress(boolean turningOn) {
440
+ //since we implement our own state for the power turn on/off, we ignore what the ultralite thinks ('turningOn') and use our own state
441
+ Log.d(TAG, "Ultralites power button pressed: " + turningOn);
442
+
443
+ //flip value of screen toggle
444
+ screenToggleOff = !screenToggleOff;
445
+
446
+ if (!screenToggleOff) {
447
+ Log.d(TAG, "screen toggle off NOT on, showing turn ON message");
448
+ // EventBus.getDefault().post(new GlassesDisplayPowerEvent(screenToggleOff));
449
+ } else {
450
+ Log.d(TAG, "screen toggle off IS on");
451
+ }
452
+ }
453
+ }
454
+ private LiveData<Boolean> ultraliteConnectedLive;
455
+ private LiveData<Boolean> ultraliteControlled;
456
+ private LiveData<BatteryStatus> batteryStatusObserver;
457
+
458
+ // Observer references for cleanup
459
+ private Observer<Boolean> connectedObserver;
460
+ private Observer<Boolean> controlledObserver;
461
+ private Observer<BatteryStatus> batteryObserver;
462
+
463
+ public Mach1() {
464
+ super();
465
+ this.type = DeviceTypes.MACH1;
466
+ Log.d(TAG, "Mach1 constructor started");
467
+
468
+ try {
469
+ // Get context from Bridge (like G1 does)
470
+ this.context = Bridge.getContext();
471
+ Log.d(TAG, "Mach1: Got context from Bridge");
472
+
473
+ hasUltraliteControl = false;
474
+ screenIsClear = true;
475
+ goHomeHandler = new Handler(Looper.getMainLooper());
476
+ screenOffHandler = new Handler(Looper.getMainLooper());
477
+ killHandler = new Handler(Looper.getMainLooper());
478
+ Log.d(TAG, "Mach1: Handlers created");
479
+
480
+ rowTextsLiveNow = new ArrayList<Integer>();
481
+
482
+ // Initialize UltraliteSDK with valid context
483
+ Log.d(TAG, "Mach1: About to initialize UltraliteSDK");
484
+ ultraliteSdk = UltraliteSDK.get(context);
485
+ Log.d(TAG, "Mach1: UltraliteSDK initialized");
486
+
487
+ ultraliteListener = new UltraliteListener();
488
+ ultraliteSdk.addEventListener(ultraliteListener);
489
+ Log.d(TAG, "Mach1: Event listener added");
490
+
491
+ // Set up LiveData observers using observeForever (no LifecycleOwner needed)
492
+ ultraliteConnectedLive = ultraliteSdk.getConnected();
493
+ ultraliteControlled = ultraliteSdk.getControlledByMe();
494
+ batteryStatusObserver = ultraliteSdk.getBatteryStatus();
495
+ Log.d(TAG, "Mach1: LiveData references obtained");
496
+
497
+ // Create observers
498
+ connectedObserver = isConnected -> {
499
+ onUltraliteConnectedChange(isConnected);
500
+ };
501
+
502
+ controlledObserver = isControlled -> {
503
+ onUltraliteControlChanged(isControlled);
504
+ };
505
+
506
+ batteryObserver = batteryStatus -> {
507
+ onUltraliteBatteryChanged(batteryStatus);
508
+ };
509
+
510
+ // Register observers on main thread (observeForever requires main thread)
511
+ new Handler(Looper.getMainLooper()).post(() -> {
512
+ try {
513
+ ultraliteConnectedLive.observeForever(connectedObserver);
514
+ Log.d(TAG, "Mach1: Connected observer registered");
515
+
516
+ ultraliteControlled.observeForever(controlledObserver);
517
+ Log.d(TAG, "Mach1: Controlled observer registered");
518
+
519
+ batteryStatusObserver.observeForever(batteryObserver);
520
+ Log.d(TAG, "Mach1: Battery observer registered");
521
+
522
+ Log.d(TAG, "Mach1: All observers registered on main thread");
523
+ } catch (Exception e) {
524
+ Log.e(TAG, "Mach1: Failed to register observers: " + e.getMessage(), e);
525
+ }
526
+ });
527
+
528
+ Log.d(TAG, "Mach1 initialized with context and observers");
529
+ } catch (Exception e) {
530
+ Log.e(TAG, "Mach1 constructor FAILED with exception: " + e.getMessage(), e);
531
+ Bridge.log("Mach1 constructor FAILED: " + e.getMessage());
532
+ }
533
+ }
534
+
535
+ public void updateGlassesBrightness(int brightness) {
536
+ // TODO: Implement this method
537
+ }
538
+
539
+ public void updateGlassesAutoBrightness(boolean autoBrightness) {
540
+ // TODO: Implement this method
541
+ }
542
+
543
+ private void onUltraliteConnectedChange(boolean isConnected) {
544
+ Log.d(TAG, "Ultralite CONNECT changed to: " + isConnected);
545
+ if (isConnected) {
546
+ Log.d(TAG, "Ultralite requesting control...");
547
+ boolean isControlled = ultraliteSdk.requestControl();
548
+ if (isControlled){
549
+ // setupUltraliteCanvas();
550
+ // changeUltraliteLayout(Layout.CANVAS);
551
+ showHomeScreen();
552
+ } else {
553
+ return;
554
+ }
555
+ Log.d(TAG, "Ultralite RESULT control request: " + isControlled);
556
+ updateConnectionState(ConnTypes.CONNECTED);
557
+ // connectionEvent(SmartGlassesConnectionState.CONNECTED);
558
+ } else {
559
+ Log.d(TAG, "Ultralite not connected.");
560
+ updateConnectionState(ConnTypes.DISCONNECTED);
561
+ // connectionEvent(SmartGlassesConnectionState.DISCONNECTED);
562
+ }
563
+ }
564
+
565
+ private void onUltraliteControlChanged(boolean isControlledByMe) {
566
+ Log.d(TAG, "Ultralite CONTROL changed to: " + isControlledByMe);
567
+ if(isControlledByMe) {
568
+ hasUltraliteControl = true;
569
+ // connectionEvent(SmartGlassesConnectionState.CONNECTED);
570
+ updateConnectionState(ConnTypes.CONNECTED);
571
+ } else {
572
+ hasUltraliteControl = false;
573
+ }
574
+ }
575
+
576
+ private void onUltraliteBatteryChanged(BatteryStatus batteryStatus) {
577
+ // Guard against null batteryStatus which can occur during connection/disconnection
578
+ // See: MENTRA-OS-154
579
+ if (batteryStatus == null) {
580
+ Log.d(TAG, "Ultralite battery status is null, ignoring");
581
+ return;
582
+ }
583
+ Log.d(TAG, "Ultralite new battery status: " + batteryStatus.getLevel());
584
+ DeviceStore.INSTANCE.apply("glasses", "batteryLevel", batteryStatus.getLevel());
585
+
586
+ // Match iOS: if we're already connected but weren't fully booted yet (waiting
587
+ // for battery), now that we have battery info we can declare fully booted.
588
+ if (getConnectionState().equals(ConnTypes.CONNECTED) && !getFullyBooted()) {
589
+ DeviceStore.INSTANCE.apply("glasses", "connected", true);
590
+ DeviceStore.INSTANCE.apply("glasses", "fullyBooted", true);
591
+ }
592
+ }
593
+
594
+
595
+ protected void setFontSizes(){
596
+ }
597
+
598
+ public void findCompatibleDeviceNames() {
599
+ // EventBus.getDefault().post(new GlassesBluetoothSearchDiscoverEvent(smartGlassesDevice.deviceModelName, "NOTREQUIREDSKIP"));
600
+ Bridge.sendDiscoveredDevice(this.type, "NOTREQUIREDSKIP"); // Use this.type to support both Mach1 and Z100
601
+ //this.destroy();
602
+ }
603
+
604
+ public void connectToSmartGlasses(){
605
+ Log.d(TAG, "connectToSmartGlasses running...");
606
+ // int mCount = 10;
607
+ // while ((mConnectState != 2) && (!hasUltraliteControl) && (mCount > 0)){
608
+ // mCount--;
609
+ // try {
610
+ // Log.d(TAG, "Don't have Ultralite yet, let's wait for it...");
611
+ // Thread.sleep(200);
612
+ // } catch (InterruptedException e) {
613
+ // e.printStackTrace();
614
+ // }
615
+ // }
616
+ // Log.d(TAG, "Connected to Ultralites.");
617
+ // Log.d(TAG, "mCOnnectestate: " + mConnectState);
618
+ // Log.d(TAG, "mCOunt: " + mCount);
619
+ // displayReferenceCardSimple("Connected to AugmentOS", "");
620
+ // connectionEvent(mConnectState);
621
+ Log.d(TAG, "connectToSmartGlasses finished");
622
+ }
623
+
624
+ public void displayTextLine(String text){
625
+ displayReferenceCardSimple("", text);
626
+ }
627
+
628
+ /**
629
+ * Display pre-wrapped text on the glasses.
630
+ *
631
+ * Text wrapping and processing is handled by DisplayProcessor in React Native.
632
+ * This method is a "dumb pipe" - it just sends the text to the Vuzix SDK.
633
+ */
634
+ public void displayTextWall(String text) {
635
+ if (screenToggleOff) {
636
+ return;
637
+ }
638
+
639
+ goHomeHandler.removeCallbacksAndMessages(null);
640
+ goHomeHandler.removeCallbacksAndMessages(goHomeRunnable);
641
+
642
+ // Text is already wrapped by DisplayProcessor - just send it
643
+ changeUltraliteLayout(Layout.TEXT_BOTTOM_LEFT_ALIGN);
644
+ ultraliteSdk.sendText(text);
645
+
646
+ if (ultraliteCanvas != null) {
647
+ ultraliteCanvas.commit();
648
+ }
649
+ screenIsClear = false;
650
+ }
651
+
652
+ /**
653
+ * Display pre-composed double text wall (two columns) on the glasses.
654
+ *
655
+ * NOTE: DisplayProcessor now composes double_text_wall into a single text_wall
656
+ * with pixel-precise column alignment using ColumnComposer. This method may
657
+ * not be called anymore, but is kept for backwards compatibility.
658
+ *
659
+ * Column composition is handled by DisplayProcessor in React Native.
660
+ * This method is a "dumb pipe" - it just sends the text to the Vuzix SDK.
661
+ */
662
+ public void displayDoubleTextWall(String textTop, String textBottom) {
663
+ if (screenToggleOff) {
664
+ return;
665
+ }
666
+
667
+ goHomeHandler.removeCallbacksAndMessages(null);
668
+ goHomeHandler.removeCallbacksAndMessages(goHomeRunnable);
669
+
670
+ // Text is already composed by DisplayProcessor's ColumnComposer
671
+ // Just combine and send - no custom logic needed
672
+ String combinedText = textTop + "\n\n\n" + textBottom;
673
+
674
+ changeUltraliteLayout(Layout.TEXT_BOTTOM_LEFT_ALIGN);
675
+ ultraliteSdk.sendText(combinedText);
676
+
677
+ if (ultraliteCanvas != null) {
678
+ ultraliteCanvas.commit();
679
+ }
680
+ screenIsClear = false;
681
+ }
682
+
683
+ public void displayCustomContent(String json) {
684
+ displayReferenceCardSimple("CustomDisplayNotImplemented", json);
685
+ }
686
+
687
+
688
+ public void showNaturalLanguageCommandScreen(String prompt, String naturalLanguageInput){
689
+ // int boxDelta = 3;
690
+ //
691
+ // if (connectedGlasses != null) {
692
+ // connectedGlasses.clear();
693
+ // showPromptCircle();
694
+ //
695
+ // //show the prompt
696
+ // lastLocNaturalLanguageArgsTextView = displayText(new TextLineSG(prompt, SMALL_FONT), new Point(0, 11), true);
697
+ // lastLocNaturalLanguageArgsTextView = new Point(lastLocNaturalLanguageArgsTextView.x, lastLocNaturalLanguageArgsTextView.y + boxDelta); //margin down a tad
698
+ //
699
+ // //show the final "finish command" prompt
700
+ // int finishY = 90;
701
+ // displayLine(new Point(0, finishY), new Point(100, finishY));
702
+ // displayText(new TextLineSG(finishNaturalLanguageString, SMALL_FONT), new Point(0, finishY + 2), true);
703
+ //
704
+ // //show the natural language args in a scroll box
705
+ //// ArrayList<TextLineSG> nli = new ArrayList<>();
706
+ //// nli.add(new TextLineSG(naturalLanguageInput, SMALL_FONT));
707
+ //// lastLocNaturalLanguageArgsTextView = scrollTextShow(nli, startScrollBoxY.y + boxDelta, finishY - boxDelta);
708
+ // }
709
+ }
710
+
711
+ public void updateNaturalLanguageCommandScreen(String naturalLanguageArgs){
712
+ // Log.d(TAG, "Displaynig: " + naturalLanguageArgs);
713
+ // displayText(new TextLineSG(naturalLanguageArgs, SMALL_FONT), new Point(0, lastLocNaturalLanguageArgsTextView.y));
714
+ }
715
+
716
+ public void blankScreen(){
717
+ // if (connectedGlasses != null){
718
+ // connectedGlasses.clear();
719
+ // }
720
+ }
721
+
722
+ public void destroy() {
723
+ try {
724
+ // Remove LiveData observers (using observeForever, so use removeObserver)
725
+ if (ultraliteConnectedLive != null && connectedObserver != null) {
726
+ ultraliteConnectedLive.removeObserver(connectedObserver);
727
+ }
728
+ if (ultraliteControlled != null && controlledObserver != null) {
729
+ ultraliteControlled.removeObserver(controlledObserver);
730
+ }
731
+ if (batteryStatusObserver != null && batteryObserver != null) {
732
+ batteryStatusObserver.removeObserver(batteryObserver);
733
+ }
734
+
735
+ if (ultraliteSdk != null) {
736
+ // Remove event listeners and release control
737
+ ultraliteSdk.removeEventListener(ultraliteListener);
738
+ ultraliteSdk.releaseControl();
739
+ ultraliteSdk = null; // Nullify reference
740
+ }
741
+
742
+ // Cancel all pending handlers and callbacks
743
+ if (goHomeHandler != null) {
744
+ goHomeHandler.removeCallbacksAndMessages(null);
745
+ }
746
+ if (screenOffHandler != null) {
747
+ screenOffHandler.removeCallbacksAndMessages(null);
748
+ }
749
+ if (killHandler != null) {
750
+ killHandler.removeCallbacksAndMessages(null);
751
+ }
752
+
753
+ // Clear canvas and other resources
754
+ if (ultraliteCanvas != null) {
755
+ ultraliteCanvas.clear();
756
+ ultraliteCanvas.commit();
757
+ ultraliteCanvas = null;
758
+ }
759
+
760
+ // Reset state variables
761
+ rowTextsLiveNow.clear();
762
+ screenToggleOff = false;
763
+ screenIsClear = true;
764
+ lastTapTime = 0;
765
+ totalDashboardsIdk = 0;
766
+ currentUltraliteLayout = null;
767
+
768
+ // Free up references
769
+ this.context = null;
770
+ connectedObserver = null;
771
+ controlledObserver = null;
772
+ batteryObserver = null;
773
+
774
+ Log.d(TAG, "UltraliteSGC destroyed successfully.");
775
+ } catch (Exception e) {
776
+ Log.e(TAG, "Error during destroy: ", e);
777
+ }
778
+ }
779
+
780
+
781
+ public void showHomeScreen() {
782
+ Log.d(TAG, "SHOW HOME SCREEN");
783
+ ultraliteSdk.screenOff();
784
+ screenIsClear = true;
785
+ }
786
+
787
+ public void setupUltraliteCanvas(){
788
+ Log.d(TAG, "Setting up ultralite canvas");
789
+ if (ultraliteSdk != null) {
790
+ ultraliteCanvas = ultraliteSdk.getCanvas();
791
+ }
792
+ }
793
+
794
+ public void changeUltraliteLayout(Layout chosenLayout) {
795
+ //don't update layout if it's already setup
796
+ if (currentUltraliteLayout != null && currentUltraliteLayout == chosenLayout){
797
+ return;
798
+ }
799
+
800
+ ultraliteSdk.screenOn();
801
+
802
+ currentUltraliteLayout = chosenLayout;
803
+ ultraliteSdk.setLayout(chosenLayout, 0, true, false, 2);
804
+
805
+ if (chosenLayout.equals(Layout.CANVAS)){
806
+ if (ultraliteCanvas == null){
807
+ setupUltraliteCanvas();
808
+ }
809
+ }
810
+ }
811
+
812
+ // public void startScrollingTextViewMode(String title){
813
+ // super.startScrollingTextViewMode(title);
814
+
815
+ // if (ultraliteSdk == null) {
816
+ // return;
817
+ // }
818
+
819
+ // //clear the screen
820
+ // ultraliteCanvas.clear();
821
+ // drawTextOnUltralite(title);
822
+ // }
823
+
824
+
825
+ /**
826
+ * Draw text on Ultralite canvas.
827
+ * Text is expected to be pre-wrapped by DisplayProcessor.
828
+ */
829
+ public void drawTextOnUltralite(String text){
830
+ UltraliteColor ultraliteColor = UltraliteColor.WHITE;
831
+ Anchor ultraliteAnchor = Anchor.TOP_LEFT;
832
+ TextAlignment ultraliteAlignment = TextAlignment.LEFT;
833
+ changeUltraliteLayout(Layout.CANVAS);
834
+ ultraliteCanvas.clear();
835
+ ultraliteCanvas.clearBackground(UltraliteColor.DIM);
836
+ ultraliteCanvas.createText(text, ultraliteAlignment, ultraliteColor, ultraliteAnchor, true);
837
+ ultraliteCanvas.commit();
838
+ screenIsClear = false;
839
+ }
840
+
841
+ // public Bitmap getBitmapFromDrawable(Resources res) {
842
+ // return BitmapFactory.decodeResource(res, R.drawable.vuzix_shield);
843
+ // }
844
+
845
+ // public void displayReferenceCardSimple(String title, String body, int lingerTime){
846
+ // if (!isConnected()) {
847
+ // Log.d(TAG, "Not showing reference card because not connected to Ultralites...");
848
+ // return;
849
+ // }
850
+ //
851
+ //// String [] bulletPoints = {"first one", "second one", "dogs and cats"};
852
+ //// displayBulletList("Cool Bullets:", bulletPoints, 15);
853
+ //
854
+ // Log.d(TAG, "Sending text to Ultralite SDK: \n" + body);
855
+ //// ultraliteSdk.sendText("hello world"); //this is BROKEN in Vuzix ultralite 0.4.2 SDK - crashes Vuzix OEM Platform android app
856
+ //
857
+ // //edit the text to add new lines to it because ultralite wrapping doesn't work
858
+ //// String titleWrapped = addNewlineEveryNWords(title, 6);
859
+ //// String bodyWrapped = addNewlineEveryNWords(body, 6);
860
+ //
861
+ // //display the title at the top of the screen
862
+ // UltraliteColor ultraliteColor = UltraliteColor.WHITE;
863
+ // Anchor ultraliteAnchor = Anchor.TOP_LEFT;
864
+ // TextAlignment ultraliteAlignment = TextAlignment.LEFT;
865
+ // changeUltraliteLayout(Layout.CANVAS);
866
+ // ultraliteCanvas.clear();
867
+ // ultraliteCanvas.createText(title, TextAlignment.AUTO, UltraliteColor.WHITE, Anchor.TOP_LEFT, ultraliteLeftSidePixelBuffer, 120, 640 - ultraliteLeftSidePixelBuffer, -1, TextWrapMode.WRAP, true);
868
+ // ultraliteCanvas.createText(body, TextAlignment.AUTO, UltraliteColor.WHITE, Anchor.MIDDLE_LEFT, ultraliteLeftSidePixelBuffer, 0, 640 - ultraliteLeftSidePixelBuffer, -1, TextWrapMode.WRAP, true);
869
+ // ultraliteCanvas.commit();
870
+ // screenIsClear = false;
871
+ //
872
+ // homeScreenInNSeconds(lingerTime);
873
+ // }
874
+
875
+ // public void setFontSize(SmartGlassesFontSize fontSize){
876
+ // int textSize;
877
+ // switch (fontSize){
878
+ // case SMALL:
879
+ // textSize = 24;
880
+ // maxLines = 14;
881
+ // maxCharsPerLine = 42;
882
+ // break;
883
+ // case MEDIUM:
884
+ // textSize = 29;
885
+ // maxLines = 12; // Adjusted from 11.5 for practical use
886
+ // maxCharsPerLine = 38; // Assuming max 27 characters fit per line on your display
887
+ // break;
888
+ // case LARGE:
889
+ // textSize = 40;
890
+ // maxLines = 7;
891
+ // maxCharsPerLine = 28;
892
+ // break;
893
+ // default:
894
+ // throw new IllegalArgumentException("Unknown font size: " + fontSize);
895
+ // }
896
+ // ultraliteSdk.setFont(null, 0, textSize);
897
+ // }
898
+
899
+ public void displayReferenceCardSimple(String titleStr, String bodyStr){
900
+ if (screenToggleOff){
901
+ return;
902
+ }
903
+
904
+ String title = maybeReverseRTLString(titleStr);
905
+ String body = maybeReverseRTLString(bodyStr);
906
+ if (!getConnectionState().equals(ConnTypes.CONNECTED)) {
907
+ Log.d(TAG, "Not showing reference card because not connected to Ultralites...");
908
+ return;
909
+ }
910
+
911
+ changeUltraliteLayout(Layout.CANVAS);
912
+ ultraliteCanvas.clear();
913
+
914
+ // String [] bulletPoints = {"first one", "second one", "dogs and cats"};
915
+ // displayBulletList("Cool Bullets:", bulletPoints, 15);
916
+
917
+ Log.d(TAG, "Sending text to Ultralite SDK: \n" + body);
918
+ // ultraliteSdk.sendText("hello world"); //this is BROKEN in Vuzix ultralite 0.4.2 SDK - crashes Vuzix OEM Platform android app
919
+
920
+ //edit the text to add new lines to it because ultralite wrapping doesn't work
921
+ // String titleWrapped = addNewlineEveryNWords(title, 6);
922
+ // String bodyWrapped = addNewlineEveryNWords(body, 6);
923
+
924
+ //display title top of scren adn text middle of screen
925
+ // UltraliteColor ultraliteColor = UltraliteColor.WHITE;
926
+ // Anchor ultraliteAnchor = Anchor.TOP_LEFT;
927
+ // TextAlignment ultraliteAlignment = TextAlignment.LEFT;
928
+ // ultraliteCanvas.createText(title, TextAlignment.AUTO, UltraliteColor.WHITE, Anchor.TOP_LEFT, ultraliteLeftSidePixelBuffer, 120, 640 - ultraliteLeftSidePixelBuffer, -1, TextWrapMode.WRAP, true);
929
+ // ultraliteCanvas.createText(body, TextAlignment.AUTO, UltraliteColor.WHITE, Anchor.MIDDLE_LEFT, ultraliteLeftSidePixelBuffer, 0, 640 - ultraliteLeftSidePixelBuffer, -1, TextWrapMode.WRAP, true);
930
+
931
+ //concat body and title, put text on top right of screen (to not block main view)
932
+ UltraliteColor ultraliteColor = UltraliteColor.WHITE;
933
+ Anchor ultraliteAnchor = Anchor.TOP_CENTER;
934
+ TextAlignment ultraliteAlignment = TextAlignment.LEFT;
935
+ //ultraliteCanvas.createText(body, TextAlignment.AUTO, UltraliteColor.WHITE, Anchor.TOP_RIGHT, 0, 0, (640 / 2) - ultraliteLeftSidePixelBuffer, -1, TextWrapMode.WRAP, true);
936
+ if (!title.isEmpty() && !title.equals("")){
937
+ ultraliteCanvas.createText(title + ": " + body, TextAlignment.AUTO, UltraliteColor.WHITE, Anchor.TOP_RIGHT, 0, 0, 640 / 2, -1, TextWrapMode.WRAP, true);
938
+ } else {
939
+ ultraliteCanvas.createText(body, TextAlignment.AUTO, UltraliteColor.WHITE, Anchor.TOP_RIGHT, 0, 0, 640 / 2, -1, TextWrapMode.WRAP, true);
940
+ }
941
+
942
+ //NOTE:
943
+ // int createText(@NonNull
944
+ // String text,
945
+ // @NonNull
946
+ // TextAlignment alignment,
947
+ // @NonNull
948
+ // UltraliteColor color,
949
+ // @NonNull
950
+ // Anchor anchor,
951
+ // int offsetX,
952
+ // int offsetY,
953
+ // int width,
954
+ // int height,
955
+ // @Nullable
956
+ // TextWrapMode wrap,
957
+ // boolean visible)
958
+
959
+ ultraliteCanvas.commit();
960
+ screenIsClear = false;
961
+ }
962
+
963
+
964
+ public void displayBulletList(String title, String [] bullets){
965
+ displayBulletList(title, bullets, 14);
966
+ }
967
+
968
+ public void displayRowsCard(String[] rowStrings){
969
+ displayRowsCard(rowStrings, cardLingerTime);
970
+ }
971
+
972
+ public void displayRowsCard(String[] rowStringList, int lingerTime){
973
+ if (screenToggleOff){
974
+ return;
975
+ }
976
+
977
+ String[] rowStrings = maybeReverseRTLStringList(rowStringList);
978
+ if (!getConnectionState().equals(ConnTypes.CONNECTED)) {
979
+ Log.d(TAG, "Not showing rows card because not connected to Ultralites...");
980
+ return;
981
+ }
982
+
983
+ // changeUltraliteLayout(Layout.CANVAS);
984
+ // ultraliteCanvas.clear();
985
+
986
+ //make lines to draw on screen to delineate rows
987
+ int line_thickness = 3;
988
+ for (int y = 120; y < 480; y += 120) {
989
+ ultraliteCanvas.clearBackgroundRect(0, y, 640, line_thickness, UltraliteColor.DIM);
990
+ }
991
+
992
+ //clear old text
993
+ for (int i = 0; i < rowTextsLiveNow.size(); i++){
994
+ ultraliteCanvas.removeText(i);
995
+ }
996
+ //old way to clear old text - vuzix ultralite sdk bug that clear background doesn't clear text?
997
+ // for (int y = 0; y < 480; y += 120) {
998
+ // //clear previous text
999
+ // ultraliteCanvas.clearBackgroundRect(0, y + line_thickness, 640, 120 - line_thickness, UltraliteColor.DIM);
1000
+ // ultraliteCanvas.clearBackgroundRect(0, y + line_thickness, 640, 120 - line_thickness, UltraliteColor.BLACK);
1001
+ // }
1002
+ // ultraliteCanvas.commit();
1003
+
1004
+ //display the title at the top of the screen
1005
+ UltraliteColor ultraliteColor = UltraliteColor.WHITE;
1006
+ Anchor ultraliteAnchor = Anchor.TOP_LEFT;
1007
+ TextAlignment ultraliteAlignment = TextAlignment.LEFT;
1008
+
1009
+ //if no input, just show the lines
1010
+ if (rowStrings.length == 0){
1011
+ ultraliteCanvas.commit();
1012
+ screenIsClear = false;
1013
+ return;
1014
+ }
1015
+
1016
+ //go throw rows, draw the text, don't do more than 4
1017
+ int y_start_height = 55;
1018
+ // Reverse rowStrings array
1019
+ Collections.reverse(Arrays.asList(rowStrings));
1020
+ int numRows = 4;
1021
+ int actualRows = Math.min(rowStrings.length, numRows);
1022
+ for (int i = 0; i < actualRows; i++) {
1023
+ // Calculate the offset to start from the bottom for 1, 2, or 3 values
1024
+ int yOffset = (numRows - actualRows) * 112;
1025
+ int textId = ultraliteCanvas.createText(rowStrings[i], TextAlignment.CENTER, UltraliteColor.WHITE, Anchor.TOP_LEFT, ultraliteLeftSidePixelBuffer, y_start_height + yOffset + (i * 112), 640 - ultraliteLeftSidePixelBuffer, -1, TextWrapMode.WRAP, true);
1026
+ rowTextsLiveNow.add(textId);
1027
+ }
1028
+
1029
+ ultraliteCanvas.commit();
1030
+ screenIsClear = false;
1031
+ }
1032
+
1033
+ public void displayBulletList(String title, String [] bulletList, int lingerTime){
1034
+ if (screenToggleOff){
1035
+ return;
1036
+ }
1037
+
1038
+ String[] bullets = maybeReverseRTLStringList(bulletList);
1039
+ if (!getConnectionState().equals(ConnTypes.CONNECTED)) {
1040
+ Log.d(TAG, "Not showing bullet point list because not connected to Ultralites...");
1041
+ return;
1042
+ }
1043
+
1044
+ Log.d(TAG, "Sending bullets to Ultralite SDK: " + title);
1045
+
1046
+ //display the title at the top of the screen
1047
+ UltraliteColor ultraliteColor = UltraliteColor.WHITE;
1048
+ Anchor ultraliteAnchor = Anchor.TOP_LEFT;
1049
+ TextAlignment ultraliteAlignment = TextAlignment.LEFT;
1050
+ changeUltraliteLayout(Layout.CANVAS);
1051
+ ultraliteCanvas.clear();
1052
+
1053
+ ultraliteCanvas.createText(title, TextAlignment.AUTO, UltraliteColor.WHITE, Anchor.TOP_LEFT, 0, 0, 640, -1, TextWrapMode.WRAP, true);
1054
+ int displaceY = 25;
1055
+ int displaceX = 25;
1056
+ for (String bullet : bullets){
1057
+ ultraliteCanvas.createText("⬤ " + bullet, TextAlignment.AUTO, UltraliteColor.WHITE, Anchor.TOP_LEFT, displaceX, displaceY, 640 - displaceX, -1, TextWrapMode.WRAP, true);
1058
+ displaceY += 125;
1059
+ }
1060
+
1061
+ ultraliteCanvas.commit();
1062
+ screenIsClear = false;
1063
+ }
1064
+
1065
+ // public void homeScreenInNSeconds(int n){
1066
+ // if (n == -1){
1067
+ // return;
1068
+ // }
1069
+ //
1070
+ // //disconnect after slight delay, so our above text gets a chance to show up
1071
+ // goHomeHandler.removeCallbacksAndMessages(null);
1072
+ // goHomeHandler.removeCallbacksAndMessages(goHomeRunnable);
1073
+ // goHomeRunnable = new Runnable() {
1074
+ // @Override
1075
+ // public void run() {
1076
+ // showHomeScreen();
1077
+ // }};
1078
+ // goHomeHandler.postDelayed(goHomeRunnable, n * 1000);
1079
+ // }
1080
+
1081
+ public void displayBitmap(Bitmap bmp) {
1082
+ Bitmap resizedBmp = Bitmap.createScaledBitmap(bmp, 620, 460, true); // 640 x 480
1083
+
1084
+ changeUltraliteLayout(Layout.CANVAS);
1085
+ screenIsClear = false;
1086
+
1087
+ Log.d(TAG, "Sending bitmap to Ultralite");
1088
+ ultraliteCanvas.drawBackground(resizedBmp, 50, 80);
1089
+ ultraliteCanvas.commit();
1090
+ }
1091
+
1092
+ //don't show images on activelook (screen is too low res)
1093
+ public void displayReferenceCardImage(String title, String body, String imgUrl){
1094
+ if (screenToggleOff){
1095
+ return;
1096
+ }
1097
+
1098
+ changeUltraliteLayout(Layout.CANVAS);
1099
+ ultraliteCanvas.clear();
1100
+
1101
+ //make image
1102
+ //below works, but only for very, very low res/size images
1103
+ Anchor ultraliteImageAnchor = Anchor.CENTER;
1104
+ Picasso.get()
1105
+ .load(imgUrl)
1106
+ .into(new Target() {
1107
+ @Override
1108
+ public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
1109
+ // Use the bitmap
1110
+ // LVGLImage ultraliteImage = LVGLImage.fromBitmap(getBitmapFromDrawable(context.getResources()), CF_INDEXED_2_BIT);
1111
+ // LVGLImage ultraliteImage = LVGLImage.fromBitmap(bitmap, CF_INDEXED_2_BIT);
1112
+ changeUltraliteLayout(Layout.CANVAS);
1113
+
1114
+ //send text first, cuz this is fast
1115
+ ultraliteCanvas.createText(title, TextAlignment.AUTO, UltraliteColor.WHITE, Anchor.TOP_LEFT, 0, 0, 640, -1, TextWrapMode.WRAP, true);
1116
+ ultraliteCanvas.createText(body, TextAlignment.AUTO, UltraliteColor.WHITE, Anchor.BOTTOM_LEFT, 0, 0, 640, -1, TextWrapMode.WRAP, true);
1117
+ ultraliteCanvas.commit();
1118
+ screenIsClear = false;
1119
+
1120
+ Log.d(TAG, "Sending image to Ultralite");
1121
+ // ultraliteCanvas.createImage(ultraliteImage, ultraliteImageAnchor, 0, 0, true);
1122
+ ultraliteCanvas.drawBackground(bitmap, 0, 0);
1123
+
1124
+ //sending text again to ultralite in case image overwrote it
1125
+ // ultraliteCanvas.createText(title + "2", TextAlignment.AUTO, UltraliteColor.WHITE, Anchor.BOTTOM_LEFT, 0, 0, 640, -1, TextWrapMode.WRAP, true);
1126
+ // ultraliteCanvas.createText(body + "2", TextAlignment.AUTO, UltraliteColor.WHITE, Anchor.MIDDLE_LEFT, 0, 0, 640, -1, TextWrapMode.WRAP, true);
1127
+ // ultraliteCanvas.commit();
1128
+
1129
+ // //display the title at the top of the screen
1130
+ // UltraliteColor ultraliteColor = UltraliteColor.WHITE;
1131
+ // TextAlignment ultraliteAlignment = TextAlignment.LEFT;
1132
+ // // ultraliteCanvas.clearBackground(UltraliteColor.DIM);
1133
+ // ultraliteCanvas.createText(titleWrapped, ultraliteAlignment, ultraliteColor, Anchor.TOP_LEFT, true); //, 0, 0, -1, -1, TextWrapMode.WRAP, true);
1134
+ // ultraliteCanvas.createText(bodyWrapped, ultraliteAlignment, ultraliteColor, Anchor.BOTTOM_LEFT, true); //, 0, 0, -1, -1, TextWrapMode.WRAP, true);
1135
+ // ultraliteCanvas.commit();
1136
+ }
1137
+
1138
+ @Override
1139
+ public void onBitmapFailed(Exception e, Drawable errorDrawable) {
1140
+ // Handle the error
1141
+ Log.d(TAG, "Bitmap failed");
1142
+ e.printStackTrace();
1143
+ }
1144
+
1145
+ @Override
1146
+ public void onPrepareLoad(Drawable placeHolderDrawable) {
1147
+ // Called before the image is loaded. You can set a placeholder if needed.
1148
+ }
1149
+ });
1150
+
1151
+ //edit the text to add new lines to it because ultralite wrapping doesn't work
1152
+ // String titleWrapped = addNewlineEveryNWords(title, 6);
1153
+ // String bodyWrapped = addNewlineEveryNWords(body, 6);
1154
+ //
1155
+ // //display the title at the top of the screen
1156
+ // UltraliteColor ultraliteColor = UltraliteColor.WHITE;
1157
+ // TextAlignment ultraliteAlignment = TextAlignment.LEFT;
1158
+ // //ultraliteCanvas.clearBackground(UltraliteColor.DIM);
1159
+ // ultraliteCanvas.createText(titleWrapped, ultraliteAlignment, ultraliteColor, Anchor.TOP_LEFT, true); //, 0, 0, -1, -1, TextWrapMode.WRAP, true);
1160
+ // ultraliteCanvas.createText(bodyWrapped, ultraliteAlignment, ultraliteColor, Anchor.BOTTOM_LEFT, true); //, 0, 0, -1, -1, TextWrapMode.WRAP, true);
1161
+ // ultraliteCanvas.commit();
1162
+ // screenIsClear = false;
1163
+ }
1164
+
1165
+ //handles text wrapping, returns final position of last line printed
1166
+ // private Point displayText(TextLineSG textLine, Point percentLoc, boolean centered){
1167
+ // if (!isConnected()){
1168
+ // return null;
1169
+ // }
1170
+ //
1171
+ // //get info about the wrapping
1172
+ // Pair wrapInfo = computeStringWrapInfo(textLine);
1173
+ // int numWraps = (int)wrapInfo.first;
1174
+ // int wrapLenNumChars = (int)wrapInfo.second;
1175
+ //
1176
+ // //loop through the text, writing out individual lines to the glasses
1177
+ // ArrayList<String> chunkedText = new ArrayList<>();
1178
+ // Point textPoint = percentLoc;
1179
+ // int textMarginY = computeMarginPercent(textLine.getFontSizeCode()); //(fontToSize.get(textLine.getFontSize()) * 1.3)
1180
+ // for (int i = 0; i <= numWraps; i++){
1181
+ // int startIdx = wrapLenNumChars * i;
1182
+ // int endIdx = Math.min(startIdx + wrapLenNumChars, textLine.getText().length());
1183
+ // String subText = textLine.getText().substring(startIdx, endIdx).trim();
1184
+ // chunkedText.add(subText);
1185
+ // TextLineSG thisTextLine = new TextLineSG(subText, textLine.getFontSizeCode());
1186
+ // if (!centered) {
1187
+ // sendTextToGlasses(thisTextLine, textPoint);
1188
+ // } else {
1189
+ // int xPercentLoc = computeStringCenterInfo(thisTextLine);
1190
+ // sendTextToGlasses(thisTextLine, new Point(xPercentLoc, textPoint.y));
1191
+ // }
1192
+ // textPoint = new Point(textPoint.x, textPoint.y + pixelToPercent(displayHeightPixels, fontToSize.get(textLine.getFontSizeCode())) + textMarginY); //lower our text for the next loop
1193
+ // }
1194
+ //
1195
+ // return textPoint;
1196
+ // }
1197
+
1198
+ public void stopScrollingTextViewMode() {
1199
+ // if (connectedGlasses == null) {
1200
+ // return;
1201
+ // }
1202
+ //
1203
+ // //clear the screen
1204
+ // connectedGlasses.clear();
1205
+ }
1206
+
1207
+ public void scrollingTextViewIntermediateText(String text){
1208
+ }
1209
+
1210
+ public void scrollingTextViewFinalText(String text){
1211
+ if (!getConnectionState().equals(ConnTypes.CONNECTED)) {
1212
+ return;
1213
+ }
1214
+
1215
+ // //save to our saved list of final scrolling text strings
1216
+ // finalScrollingTextStrings.add(text);
1217
+ //
1218
+ // //get the max number of wraps allows
1219
+ // float allowedTextRows = computeAllowedTextRows(fontToSize.get(scrollingTextTitleFontSize), fontToSize.get(scrollingTextTextFontSize), percentToPixel(displayHeightPixels, computeMarginPercent(scrollingTextTextFontSize)));
1220
+ //
1221
+ // //figure out the maximum we can display
1222
+ // int totalRows = 0;
1223
+ // ArrayList<String> finalTextToDisplay = new ArrayList<>();
1224
+ // boolean hitBottom = false;
1225
+ // for (int i = finalScrollingTextStrings.toArray().length - 1; i >= 0; i--){
1226
+ // String finalText = finalScrollingTextStrings.get(i);
1227
+ // //convert to a TextLine type with small font
1228
+ // TextLineSG tlString = new TextLineSG(finalText, SMALL_FONT);
1229
+ // //get info about the wrapping of this string
1230
+ // Pair wrapInfo = computeStringWrapInfo(tlString);
1231
+ // int numWraps = (int)wrapInfo.first;
1232
+ // int wrapLenNumChars = (int)wrapInfo.second;
1233
+ // totalRows += numWraps + 1;
1234
+ //
1235
+ // if (totalRows > allowedTextRows){
1236
+ // finalScrollingTextStrings = finalTextToDisplay;
1237
+ // lastLocScrollingTextView = belowTitleLocScrollingTextView;
1238
+ // //clear the glasses as we hit our limit and need to redraw
1239
+ // connectedGlasses.color((byte)0x00);
1240
+ // connectedGlasses.rectf(percentScreenToPixelsLocation(belowTitleLocScrollingTextView.x, belowTitleLocScrollingTextView.y), percentScreenToPixelsLocation(100, 100));
1241
+ // //stop looping, as we've ran out of room
1242
+ // hitBottom = true;
1243
+ // } else {
1244
+ // finalTextToDisplay.add(0, finalText);
1245
+ // }
1246
+ // }
1247
+ //
1248
+ // //display all of the text that we can
1249
+ // if (hitBottom) { //if we ran out of room, we need to redraw all the text
1250
+ // for (String finalString : finalTextToDisplay) {
1251
+ // TextLineSG tlString = new TextLineSG(finalString, scrollingTextTextFontSize);
1252
+ // //write this text at the last location + margin
1253
+ // Log.d(TAG, "Writing string: " + tlString.getText() + finalTextToDisplay.size());
1254
+ // lastLocScrollingTextView = displayText(tlString, new Point(0, lastLocScrollingTextView.y));
1255
+ // }
1256
+ // } else { //if we didn't hit the bottom, and there's room, we can just display the next line
1257
+ // TextLineSG tlString = new TextLineSG(text, scrollingTextTextFontSize);
1258
+ // lastLocScrollingTextView = displayText(tlString, new Point(0, lastLocScrollingTextView.y));
1259
+ // }
1260
+
1261
+ }
1262
+
1263
+ public static String maybeReverseRTLString(String text) {
1264
+ StringBuilder result = new StringBuilder();
1265
+ StringBuilder rtlBuffer = new StringBuilder();
1266
+
1267
+ for (char c : text.toCharArray()) {
1268
+ if (isRTLCharacter(c)) {
1269
+ rtlBuffer.append(c); // Append RTL characters to a buffer
1270
+ } else {
1271
+ if (rtlBuffer.length() > 0) {
1272
+ result.append(rtlBuffer.reverse()); // Reverse and append RTL text when a non-RTL character is found
1273
+ rtlBuffer.setLength(0); // Clear the buffer
1274
+ }
1275
+ result.append(c); // Append non-RTL characters directly to the result
1276
+ }
1277
+ }
1278
+
1279
+ if (rtlBuffer.length() > 0) {
1280
+ result.append(rtlBuffer.reverse()); // Append any remaining RTL text in reverse
1281
+ }
1282
+
1283
+ return result.toString();
1284
+ }
1285
+
1286
+ private static boolean isRTLCharacter(char c) {
1287
+ Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
1288
+ return block == Character.UnicodeBlock.ARABIC ||
1289
+ block == Character.UnicodeBlock.HEBREW ||
1290
+ block == Character.UnicodeBlock.SYRIAC ||
1291
+ block == Character.UnicodeBlock.ARABIC_SUPPLEMENT ||
1292
+ block == Character.UnicodeBlock.THAANA ||
1293
+ block == Character.UnicodeBlock.NKO ||
1294
+ block == Character.UnicodeBlock.SAMARITAN ||
1295
+ block == Character.UnicodeBlock.MANDAIC ||
1296
+ block == Character.UnicodeBlock.ARABIC_EXTENDED_A;
1297
+ // Add other RTL blocks as needed
1298
+ }
1299
+ public String[] maybeReverseRTLStringList(String[] in){
1300
+ String[] out = new String[in.length];
1301
+ for(int i = 0; i < in.length; i++)
1302
+ out[i] = maybeReverseRTLString(in[i]);
1303
+ return out;
1304
+ }
1305
+
1306
+ public void displayPromptView(String prompt, String [] options){
1307
+ if (!getConnectionState().equals(ConnTypes.CONNECTED)) {
1308
+ return;
1309
+ }
1310
+
1311
+ // ultraliteCanvas.clear();
1312
+ // connectedGlasses.clear();
1313
+ // showPromptCircle();
1314
+ //
1315
+ // //show the prompt and options, if any
1316
+ // ArrayList<Object> promptPageElements = new ArrayList<>();
1317
+ // promptPageElements.add(new TextLineSG(prompt, LARGE_FONT));
1318
+ // if (options != null) {
1319
+ // //make an array list of options
1320
+ // for (String s : options){
1321
+ // promptPageElements.add(new TextLineSG(s, SMALL_FONT));
1322
+ // }
1323
+ // }
1324
+ // displayLinearStuff(promptPageElements, new Point(0, 11), true);
1325
+ }
1326
+
1327
+ }