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