spotny-sdk 1.0.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 (213) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +589 -0
  3. package/SpotnySdk.podspec +35 -0
  4. package/android/build.gradle +70 -0
  5. package/android/src/main/AndroidManifest.xml +33 -0
  6. package/android/src/main/java/com/spotnysdk/SpotnySdkModule.kt +685 -0
  7. package/android/src/main/java/com/spotnysdk/SpotnySdkPackage.kt +31 -0
  8. package/ios/Frameworks/CBORCoding.xcframework/Info.plist +44 -0
  9. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64/CBORCoding.framework/CBORCoding +0 -0
  10. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64/CBORCoding.framework/Headers/CBORCoding-Swift.h +317 -0
  11. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64/CBORCoding.framework/Info.plist +0 -0
  12. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64/CBORCoding.framework/Modules/CBORCoding.swiftmodule/arm64-apple-ios.abi.json +7021 -0
  13. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64/CBORCoding.framework/Modules/CBORCoding.swiftmodule/arm64-apple-ios.private.swiftinterface +193 -0
  14. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64/CBORCoding.framework/Modules/CBORCoding.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  15. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64/CBORCoding.framework/Modules/CBORCoding.swiftmodule/arm64-apple-ios.swiftinterface +193 -0
  16. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64/CBORCoding.framework/Modules/module.modulemap +4 -0
  17. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/CBORCoding +0 -0
  18. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Headers/CBORCoding-Swift.h +630 -0
  19. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Info.plist +0 -0
  20. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/CBORCoding.swiftmodule/arm64-apple-ios-simulator.abi.json +7021 -0
  21. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/CBORCoding.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +193 -0
  22. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/CBORCoding.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  23. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/CBORCoding.swiftmodule/arm64-apple-ios-simulator.swiftinterface +193 -0
  24. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/CBORCoding.swiftmodule/x86_64-apple-ios-simulator.abi.json +7021 -0
  25. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/CBORCoding.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +193 -0
  26. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/CBORCoding.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  27. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/CBORCoding.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +193 -0
  28. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/module.modulemap +4 -0
  29. package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/_CodeSignature/CodeResources +267 -0
  30. package/ios/Frameworks/Half.xcframework/Info.plist +44 -0
  31. package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Half +0 -0
  32. package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Headers/Half-Swift.h +317 -0
  33. package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Headers/half.h +95 -0
  34. package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Info.plist +0 -0
  35. package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Modules/Half.swiftmodule/arm64-apple-ios.abi.json +4942 -0
  36. package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Modules/Half.swiftmodule/arm64-apple-ios.private.swiftinterface +650 -0
  37. package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Modules/Half.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  38. package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Modules/Half.swiftmodule/arm64-apple-ios.swiftinterface +650 -0
  39. package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Modules/module.modulemap +11 -0
  40. package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Half +0 -0
  41. package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Headers/Half-Swift.h +630 -0
  42. package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Headers/half.h +95 -0
  43. package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Info.plist +0 -0
  44. package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/Half.swiftmodule/arm64-apple-ios-simulator.abi.json +4942 -0
  45. package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/Half.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +650 -0
  46. package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/Half.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  47. package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/Half.swiftmodule/arm64-apple-ios-simulator.swiftinterface +650 -0
  48. package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/Half.swiftmodule/x86_64-apple-ios-simulator.abi.json +4973 -0
  49. package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/Half.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +660 -0
  50. package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/Half.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  51. package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/Half.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +660 -0
  52. package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/module.modulemap +11 -0
  53. package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/_CodeSignature/CodeResources +245 -0
  54. package/ios/Frameworks/KontaktSDK.xcframework/Info.plist +44 -0
  55. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Base.lproj/Localizable.strings +0 -0
  56. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/CLBeacon+Kontakt.h +37 -0
  57. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKAction.h +90 -0
  58. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKActionContent.h +80 -0
  59. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKBeaconManager.h +326 -0
  60. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKBeaconRegion.h +102 -0
  61. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKCloudClient.h +241 -0
  62. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKCloudClientJSONResponseSerializer.h +20 -0
  63. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKCloudClientResponseSerializer.h +35 -0
  64. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKCloudClientSessionManager.h +207 -0
  65. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKCloudDefinitions.h +13 -0
  66. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKCloudModel.h +36 -0
  67. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDevice.h +179 -0
  68. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceCCOperationDelegate.h +13 -0
  69. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceConfiguration.h +329 -0
  70. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceConfigurationGPIO.h +24 -0
  71. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceConnection.h +379 -0
  72. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceConnectionOperation.h +65 -0
  73. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceDataLoggerConnection.h +56 -0
  74. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceDataLoggerReading.h +49 -0
  75. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceDefinitions.h +853 -0
  76. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceGATTDefinitions.h +106 -0
  77. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceGATTOperation.h +68 -0
  78. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceGatewayConfigurationType.h +43 -0
  79. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceGatewayConnection.h +101 -0
  80. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceGatewayDiagnostic.h +61 -0
  81. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceGatewayWiFiNetwork.h +76 -0
  82. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceKontaktRecognitionBox.h +26 -0
  83. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceListenOperation.h +20 -0
  84. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceNotifyOperation.h +34 -0
  85. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDevicePowerSaving.h +86 -0
  86. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceWriteOperation.h +51 -0
  87. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDevicesManager.h +182 -0
  88. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKEddystone.h +101 -0
  89. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKEddystoneFrame.h +47 -0
  90. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKEddystoneManager.h +160 -0
  91. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKEddystoneRegion.h +85 -0
  92. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKEddystoneTLM.h +49 -0
  93. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKEddystoneUID.h +55 -0
  94. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKEddystoneURL.h +33 -0
  95. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKEddystoneURLValueTransformer.h +13 -0
  96. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKFirmware.h +98 -0
  97. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKGroupOperation.h +100 -0
  98. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKKontaktResponse.h +85 -0
  99. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKManager.h +121 -0
  100. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKNearbyDevice.h +135 -0
  101. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKNearbyDeviceTelemetry.h +513 -0
  102. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKOperation.h +116 -0
  103. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKPersonPosition.h +22 -0
  104. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKSecureBeaconRegion.h +86 -0
  105. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKSecureEddystoneRegion.h +59 -0
  106. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKTrigger.h +94 -0
  107. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKTriggerContext.h +79 -0
  108. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKVenue.h +105 -0
  109. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/Kontakt.h +187 -0
  110. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KontaktSDK-Swift.h +668 -0
  111. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KontaktSDK.h +43 -0
  112. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/NSData+Kontakt.h +115 -0
  113. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/NSError+Kontakt.h +26 -0
  114. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/NSString+Kontakt.h +25 -0
  115. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/NSURLRequest+Kontakt.h +44 -0
  116. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Info.plist +0 -0
  117. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/KontaktSDK +0 -0
  118. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/arm64-apple-ios.abi.json +14106 -0
  119. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +428 -0
  120. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  121. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/arm64-apple-ios.swiftinterface +428 -0
  122. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Modules/module.modulemap +11 -0
  123. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/strip-frameworks.sh +72 -0
  124. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Base.lproj/Localizable.strings +0 -0
  125. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/CLBeacon+Kontakt.h +37 -0
  126. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKAction.h +90 -0
  127. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKActionContent.h +80 -0
  128. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKBeaconManager.h +326 -0
  129. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKBeaconRegion.h +102 -0
  130. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKCloudClient.h +241 -0
  131. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKCloudClientJSONResponseSerializer.h +20 -0
  132. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKCloudClientResponseSerializer.h +35 -0
  133. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKCloudClientSessionManager.h +207 -0
  134. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKCloudDefinitions.h +13 -0
  135. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKCloudModel.h +36 -0
  136. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDevice.h +179 -0
  137. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceCCOperationDelegate.h +13 -0
  138. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceConfiguration.h +329 -0
  139. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceConfigurationGPIO.h +24 -0
  140. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceConnection.h +379 -0
  141. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceConnectionOperation.h +65 -0
  142. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceDataLoggerConnection.h +56 -0
  143. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceDataLoggerReading.h +49 -0
  144. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceDefinitions.h +853 -0
  145. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceGATTDefinitions.h +106 -0
  146. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceGATTOperation.h +68 -0
  147. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceGatewayConfigurationType.h +43 -0
  148. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceGatewayConnection.h +101 -0
  149. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceGatewayDiagnostic.h +61 -0
  150. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceGatewayWiFiNetwork.h +76 -0
  151. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceKontaktRecognitionBox.h +26 -0
  152. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceListenOperation.h +20 -0
  153. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceNotifyOperation.h +34 -0
  154. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDevicePowerSaving.h +86 -0
  155. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceWriteOperation.h +51 -0
  156. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDevicesManager.h +182 -0
  157. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKEddystone.h +101 -0
  158. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKEddystoneFrame.h +47 -0
  159. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKEddystoneManager.h +160 -0
  160. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKEddystoneRegion.h +85 -0
  161. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKEddystoneTLM.h +49 -0
  162. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKEddystoneUID.h +55 -0
  163. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKEddystoneURL.h +33 -0
  164. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKEddystoneURLValueTransformer.h +13 -0
  165. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKFirmware.h +98 -0
  166. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKGroupOperation.h +100 -0
  167. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKKontaktResponse.h +85 -0
  168. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKManager.h +121 -0
  169. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKNearbyDevice.h +135 -0
  170. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKNearbyDeviceTelemetry.h +513 -0
  171. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKOperation.h +116 -0
  172. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKPersonPosition.h +22 -0
  173. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKSecureBeaconRegion.h +86 -0
  174. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKSecureEddystoneRegion.h +59 -0
  175. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKTrigger.h +94 -0
  176. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKTriggerContext.h +79 -0
  177. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKVenue.h +105 -0
  178. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/Kontakt.h +187 -0
  179. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KontaktSDK-Swift.h +1332 -0
  180. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KontaktSDK.h +43 -0
  181. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/NSData+Kontakt.h +115 -0
  182. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/NSError+Kontakt.h +26 -0
  183. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/NSString+Kontakt.h +25 -0
  184. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/NSURLRequest+Kontakt.h +44 -0
  185. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Info.plist +0 -0
  186. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/KontaktSDK +0 -0
  187. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +14106 -0
  188. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +428 -0
  189. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  190. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +428 -0
  191. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +14106 -0
  192. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +428 -0
  193. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  194. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +428 -0
  195. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/module.modulemap +11 -0
  196. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/_CodeSignature/CodeResources +894 -0
  197. package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/strip-frameworks.sh +72 -0
  198. package/ios/SpotnyBeaconScanner.swift +938 -0
  199. package/ios/SpotnySdk.h +10 -0
  200. package/ios/SpotnySdk.mm +127 -0
  201. package/lib/module/NativeSpotnySdk.js +5 -0
  202. package/lib/module/NativeSpotnySdk.js.map +1 -0
  203. package/lib/module/index.js +96 -0
  204. package/lib/module/index.js.map +1 -0
  205. package/lib/module/package.json +1 -0
  206. package/lib/typescript/package.json +1 -0
  207. package/lib/typescript/src/NativeSpotnySdk.d.ts +18 -0
  208. package/lib/typescript/src/NativeSpotnySdk.d.ts.map +1 -0
  209. package/lib/typescript/src/index.d.ts +81 -0
  210. package/lib/typescript/src/index.d.ts.map +1 -0
  211. package/package.json +169 -0
  212. package/src/NativeSpotnySdk.ts +29 -0
  213. package/src/index.tsx +151 -0
@@ -0,0 +1,685 @@
1
+ package com.spotnysdk
2
+
3
+ import android.content.Context
4
+ import android.os.RemoteException
5
+ import android.util.Log
6
+ import com.facebook.react.bridge.*
7
+ import com.facebook.react.modules.core.DeviceEventManagerModule
8
+ import org.altbeacon.beacon.*
9
+ import java.io.File
10
+ import java.net.HttpURLConnection
11
+ import java.net.URL
12
+ import java.text.SimpleDateFormat
13
+ import java.util.*
14
+ import java.util.concurrent.Executors
15
+ import kotlin.math.abs
16
+
17
+ // ── Kontakt.io iBeacon layout ─────────────────────────────────────────────────
18
+ private const val IBEACON_LAYOUT = "m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"
19
+ private const val BEACON_UUID = "f7826da6-4fa2-4e98-8024-bc5b71e0893e"
20
+ private const val TAG = "SpotnySDK"
21
+
22
+ // ── Campaign data ─────────────────────────────────────────────────────────────
23
+ private data class CampaignData(
24
+ val campaignId: Int?,
25
+ val screenId: Int,
26
+ val sessionId: String?,
27
+ val inQueue: Boolean,
28
+ val major: Int,
29
+ val minor: Int
30
+ )
31
+
32
+ class SpotnySdkModule(private val reactContext: ReactApplicationContext) :
33
+ NativeSpotnySdkSpec(reactContext) {
34
+
35
+ // ── Managers ──────────────────────────────────────────────────────────────
36
+ private val beaconManager: BeaconManager = BeaconManager.getInstanceForApplication(reactContext)
37
+ private val ioExecutor = Executors.newCachedThreadPool()
38
+
39
+ // ── Session state ─────────────────────────────────────────────────────────
40
+ @Volatile private var scanning = false
41
+ /** Authenticated user ID — required at initialize(). Embedded in JWT via /api/app/sdk/verify. */
42
+ private var userId: String? = null
43
+
44
+ // ── Configuration ─────────────────────────────────────────────────────────
45
+ // backendURL is fixed — not overridable by consumers.
46
+ private val backendURL = "https://api.spotny.app"
47
+ private var maxDetectionDistance = 8.0 // metres
48
+ /** Multiplier applied to raw BLE distance. Default 0.5 matches Kontakt.io -12 dBm beacons. */
49
+ private var distanceCorrectionFactor = 0.5
50
+ /** API key identifying the SDK consumer (e.g. "nike"). Set during initialize(). */
51
+ private var apiKey: String? = null
52
+ /** JWT returned by /api/app/sdk/verify. Injected as Authorization header on all subsequent calls. */
53
+ private var sdkToken: String? = null
54
+ /** Expiry time of the current JWT in ms (epoch). When exceeded the JWT is auto-refreshed. */
55
+ private var sdkTokenExpiry: Long? = null
56
+ /** Original SDK token — persisted so JWT can be refreshed without calling initialize() again. */
57
+ private var sdkCredential: String? = null
58
+
59
+ // ── Timing constants ──────────────────────────────────────────────────────
60
+ private var campaignFetchCooldown = 5_000L // ms
61
+ private var proximityDistanceThreshold = 0.75
62
+ private var impressionEventInterval = 10_000L // ms
63
+ private val impressionDistance = 2.0
64
+ private var debounceInterval = 5_000L // ms
65
+
66
+ // ── Per-beacon state ──────────────────────────────────────────────────────
67
+ private val activeCampaigns = mutableMapOf<String, CampaignData>()
68
+ private val lastProximityEventSent = mutableMapOf<String, Long>()
69
+ private val lastProximityDistance = mutableMapOf<String, Double>()
70
+ private val lastImpressionEventSent = mutableMapOf<String, Long>()
71
+ private val lastCampaignFetchAttempt = mutableMapOf<String, Long>()
72
+ private val fetchInProgress = mutableMapOf<String, Boolean>()
73
+ private val proximityEventInProgress = mutableMapOf<String, Boolean>()
74
+ private val impressionEventInProgress = mutableMapOf<String, Boolean>()
75
+
76
+ // ── Module registration ───────────────────────────────────────────────────
77
+
78
+ override fun getName() = NAME
79
+
80
+ override fun initialize() {
81
+ super.initialize()
82
+ setupBeaconManager()
83
+ resumeStoredSession()
84
+ }
85
+
86
+ // ── Setup ─────────────────────────────────────────────────────────────────
87
+
88
+ private fun setupBeaconManager() {
89
+ BeaconManager.setDebug(false)
90
+ beaconManager.beaconParsers.add(BeaconParser().setBeaconLayout(IBEACON_LAYOUT))
91
+ beaconManager.foregroundScanPeriod = 1_100L
92
+ beaconManager.foregroundBetweenScanPeriod = 0L
93
+ beaconManager.backgroundScanPeriod = 10_000L
94
+ beaconManager.backgroundBetweenScanPeriod = 5_000L
95
+ }
96
+
97
+ private fun resumeStoredSession() {
98
+ val prefs = reactContext.getSharedPreferences("SpotnySDK", Context.MODE_PRIVATE)
99
+ if (!prefs.getBoolean("sessionActive", false)) return
100
+ // Restore persisted JWT + credentials so all API calls work without initialize()
101
+ sdkToken = prefs.getString("jwt", null)
102
+ sdkCredential = prefs.getString("sdkCredential", null)
103
+ apiKey = prefs.getString("sdkApiKey", null)
104
+ userId = prefs.getString("sdkUserId", null)
105
+ val expiry = prefs.getLong("jwtExpiry", -1L).takeIf { it > 0 }
106
+ if (expiry != null) sdkTokenExpiry = expiry
107
+ Log.d(TAG, "Resuming session (device: ${getDeviceId()})")
108
+ startBeaconScanning()
109
+ scanning = true
110
+ }
111
+
112
+ // ── Turbo Module methods ──────────────────────────────────────────────────
113
+
114
+ override fun startScanner(promise: Promise) {
115
+ if (scanning) { promise.resolve("Already scanning"); return }
116
+
117
+ val prefs = reactContext.getSharedPreferences("SpotnySDK", Context.MODE_PRIVATE).edit()
118
+ prefs.putBoolean("sessionActive", true)
119
+ prefs.apply()
120
+
121
+ startBeaconScanning()
122
+ scanning = true
123
+ Log.d(TAG, "Started scanning (device: ${getDeviceId()})")
124
+ promise.resolve("Scanning started")
125
+ }
126
+
127
+ override fun stopScanner(promise: Promise) {
128
+ try {
129
+ beaconManager.removeAllMonitorNotifiers()
130
+ beaconManager.removeAllRangeNotifiers()
131
+ beaconManager.stopRangingBeaconsInRegion(Region("SpotnySDK_General", null, null, null))
132
+ beaconManager.stopMonitoringBeaconsInRegion(Region("SpotnySDK_General", null, null, null))
133
+ } catch (_: RemoteException) {}
134
+
135
+ cleanupAllState()
136
+
137
+ val prefs = reactContext.getSharedPreferences("SpotnySDK", Context.MODE_PRIVATE).edit()
138
+ prefs.remove("sessionActive"); prefs.apply()
139
+
140
+ scanning = false
141
+ Log.d(TAG, "Stopped scanning")
142
+ promise.resolve("Scanning stopped")
143
+ }
144
+
145
+ override fun isScanning(promise: Promise) = promise.resolve(scanning)
146
+
147
+ override fun initialize(config: ReadableMap?, promise: Promise) {
148
+ config?.getDouble("maxDetectionDistance").takeIf { config?.hasKey("maxDetectionDistance") == true }
149
+ ?.let { maxDetectionDistance = it; Log.d(TAG, "maxDetectionDistance = $it m") }
150
+ config?.getString("apiKey")?.let { apiKey = it; Log.d(TAG, "apiKey = $it") }
151
+ config?.getDouble("distanceCorrectionFactor")
152
+ .takeIf { config?.hasKey("distanceCorrectionFactor") == true && it > 0 }
153
+ ?.let { distanceCorrectionFactor = it; Log.d(TAG, "distanceCorrectionFactor = $it") }
154
+ val token = config?.getString("token")?.takeIf { it.isNotBlank() }
155
+ if (token == null) {
156
+ promise.reject("MISSING_TOKEN", "initialize() requires a token")
157
+ return
158
+ }
159
+ val key = apiKey?.takeIf { it.isNotBlank() }
160
+ if (key == null) {
161
+ promise.reject("MISSING_API_KEY", "initialize() requires an apiKey")
162
+ return
163
+ }
164
+ config?.getString("userId")?.takeIf { it.isNotBlank() }?.let { userId = it }
165
+ val uid = userId?.takeIf { it.isNotBlank() }
166
+ if (uid == null) {
167
+ promise.reject("MISSING_USER_ID", "initialize() requires a userId")
168
+ return
169
+ }
170
+
171
+ // Send token + apiKey + userId to backend — response returns a JWT used for all subsequent calls
172
+ // sdkToken is null here so no Authorization header is injected on this call
173
+ val verifyPayload = mapOf("token" to token, "api_key" to key, "user_id" to uid)
174
+ post("/api/app/sdk/verify", verifyPayload) { status, body ->
175
+ when {
176
+ status in 200..299 -> {
177
+ val verifyJson = try { parseJsonObject(body) } catch (_: Exception) { null }
178
+ val verifyData = verifyJson?.get("data") as? Map<*, *>
179
+ val jwt = (verifyData?.get("jwt") as? String)?.takeIf { it.isNotBlank() }
180
+ if (jwt == null) {
181
+ promise.reject("VERIFY_FAILED", "SDK verification response missing JWT")
182
+ return@post
183
+ }
184
+ sdkToken = jwt
185
+ sdkCredential = token
186
+ val expiresAt = (verifyData?.get("expires_at") as? Number)?.toLong()
187
+ if (expiresAt != null) sdkTokenExpiry = expiresAt * 1000L // seconds → ms
188
+ persistJWT()
189
+ Log.d(TAG, "SDK initialized — JWT stored, expires: ${sdkTokenExpiry?.let { java.util.Date(it) } ?: "no expiry"}")
190
+ promise.resolve("SDK initialized")
191
+ }
192
+ status == 401 || status == 403 -> {
193
+ Log.w(TAG, "Token unauthorized ($status)")
194
+ promise.reject("UNAUTHORIZED", "Invalid or expired SDK token")
195
+ }
196
+ else -> {
197
+ Log.w(TAG, "Verification failed ($status)")
198
+ promise.reject("VERIFY_FAILED", "SDK verification returned status $status")
199
+ }
200
+ }
201
+ }
202
+ }
203
+
204
+ override fun requestNotificationPermissions(promise: Promise) {
205
+ // On Android, notification permission is handled at the app level (API 33+).
206
+ // We just resolve — the app should request POST_NOTIFICATIONS separately.
207
+ promise.resolve("handled_by_app")
208
+ }
209
+
210
+ override fun getDebugLogs(promise: Promise) {
211
+ val file = logFile()
212
+ promise.resolve(if (file.exists()) file.readText() else "No logs found")
213
+ }
214
+
215
+ override fun clearDebugLogs(promise: Promise) {
216
+ logFile().takeIf { it.exists() }?.delete()
217
+ promise.resolve("Logs cleared")
218
+ }
219
+
220
+ override fun setDebounceInterval(interval: Double, promise: Promise) {
221
+ debounceInterval = interval.toLong() * 1000L
222
+ promise.resolve("Debounce interval set to ${interval}s")
223
+ }
224
+
225
+ override fun clearDebounceCache(promise: Promise) {
226
+ lastCampaignFetchAttempt.clear(); fetchInProgress.clear()
227
+ promise.resolve("Debounce cache cleared")
228
+ }
229
+
230
+
231
+ override fun getDebounceStatus(promise: Promise) {
232
+ val map = WritableNativeMap()
233
+ for ((key, _) in activeCampaigns) {
234
+ val entry = WritableNativeMap()
235
+ lastCampaignFetchAttempt[key]?.let { entry.putDouble("lastFetchAttempt", it.toDouble()) }
236
+ fetchInProgress[key]?.let { entry.putBoolean("fetchInProgress", it) }
237
+ lastProximityEventSent[key]?.let { entry.putDouble("lastProximityEvent", it.toDouble()) }
238
+ lastImpressionEventSent[key]?.let { entry.putDouble("lastImpressionEvent", it.toDouble()) }
239
+ map.putMap(key, entry)
240
+ }
241
+ promise.resolve(map)
242
+ }
243
+
244
+ // Required by NativeEventEmitter contract in new-arch
245
+ override fun addListener(eventName: String) {}
246
+ override fun removeListeners(count: Double) {}
247
+
248
+ // ── Beacon scanning ──────────────────────────────────────────────────────
249
+
250
+ private fun startBeaconScanning() {
251
+ val region = Region("SpotnySDK_General", Identifier.parse(BEACON_UUID), null, null)
252
+
253
+ beaconManager.addRangeNotifier(rangeNotifier)
254
+ beaconManager.addMonitorNotifier(monitorNotifier)
255
+
256
+ beaconManager.startRangingBeaconsInRegion(region)
257
+ beaconManager.startMonitoringBeaconsInRegion(region)
258
+
259
+ Log.d(TAG, "Scanning for UUID $BEACON_UUID")
260
+ }
261
+
262
+ // ── Range notifier ────────────────────────────────────────────────────────
263
+
264
+ private val rangeNotifier = RangeNotifier { beacons, region ->
265
+ val now = System.currentTimeMillis()
266
+
267
+ // JS event payload
268
+ val jsBeacons = WritableNativeArray()
269
+ for (beacon in beacons) {
270
+ val raw = beacon.distance
271
+ val adjusted = raw * distanceCorrectionFactor
272
+ if (adjusted <= 0 || adjusted > maxDetectionDistance) continue
273
+
274
+ val b = WritableNativeMap().apply {
275
+ putString("uuid", beacon.id1?.toString() ?: BEACON_UUID)
276
+ putInt("major", beacon.id2?.toInt() ?: 0)
277
+ putInt("minor", beacon.id3?.toInt() ?: 0)
278
+ putDouble("distance", adjusted)
279
+ putInt("rssi", beacon.rssi)
280
+ putString("proximity", proximityLabel(adjusted))
281
+ }
282
+ jsBeacons.pushMap(b)
283
+ }
284
+ if (jsBeacons.size() > 0) {
285
+ val payload = WritableNativeMap().apply {
286
+ putArray("beacons", jsBeacons)
287
+ putString("region", region.uniqueId)
288
+ }
289
+ sendEvent("onBeaconsRanged", payload)
290
+ }
291
+
292
+ // Per-beacon logic
293
+ for (beacon in beacons) {
294
+ val major = beacon.id2?.toInt() ?: continue
295
+ val minor = beacon.id3?.toInt() ?: continue
296
+ val key = beaconKey(major, minor)
297
+ val distance = beacon.distance * distanceCorrectionFactor
298
+
299
+ if (distance <= 0 || distance > maxDetectionDistance) continue
300
+
301
+ if (activeCampaigns.containsKey(key)) {
302
+ val isFirst = !lastProximityEventSent.containsKey(key)
303
+ if (isFirst) {
304
+ sendProximity("NEARBY", key, distance)
305
+ lastProximityDistance[key] = distance
306
+ lastProximityEventSent[key] = now
307
+ } else if (distance >= 1.0) {
308
+ val lastDist = lastProximityDistance[key] ?: 0.0
309
+ if (abs(distance - lastDist) >= proximityDistanceThreshold) {
310
+ sendProximity("NEARBY", key, distance)
311
+ lastProximityDistance[key] = distance
312
+ lastProximityEventSent[key] = now
313
+ }
314
+ }
315
+ val campaign = activeCampaigns[key]
316
+ if (campaign?.campaignId != null && campaign.inQueue == false && distance <= impressionDistance) {
317
+ val lastImp = lastImpressionEventSent[key]
318
+ if (lastImp == null || now - lastImp >= impressionEventInterval) {
319
+ sendImpression(key, distance)
320
+ lastImpressionEventSent[key] = now
321
+ }
322
+ }
323
+ } else {
324
+ fetchCampaign(major, minor)
325
+ }
326
+ }
327
+ }
328
+
329
+ // ── Monitor notifier ──────────────────────────────────────────────────────
330
+
331
+ private val monitorNotifier = object : MonitorNotifier {
332
+ override fun didEnterRegion(region: Region) {
333
+ Log.d(TAG, "Entered region: ${region.uniqueId}")
334
+ val payload = WritableNativeMap().apply {
335
+ putString("region", region.uniqueId)
336
+ putString("event", "enter")
337
+ }
338
+ sendEvent("onBeaconRegionEvent", payload)
339
+ try { beaconManager.startRangingBeaconsInRegion(region) } catch (_: RemoteException) {}
340
+ }
341
+
342
+ override fun didExitRegion(region: Region) {
343
+ Log.d(TAG, "Exited region: ${region.uniqueId}")
344
+ val payload = WritableNativeMap().apply {
345
+ putString("region", region.uniqueId)
346
+ putString("event", "exit")
347
+ }
348
+ sendEvent("onBeaconRegionEvent", payload)
349
+ for (key in activeCampaigns.keys.toList()) {
350
+ cleanupBeacon(key)
351
+ }
352
+ try { beaconManager.stopRangingBeaconsInRegion(region) } catch (_: RemoteException) {}
353
+ }
354
+
355
+ override fun didDetermineStateForRegion(state: Int, region: Region) {
356
+ val label = if (state == MonitorNotifier.INSIDE) "inside" else "outside"
357
+ val payload = WritableNativeMap().apply {
358
+ putString("region", region.uniqueId)
359
+ putString("event", "determined")
360
+ putString("state", label)
361
+ }
362
+ sendEvent("onBeaconRegionEvent", payload)
363
+ }
364
+ }
365
+
366
+ // ── Helpers ───────────────────────────────────────────────────────────────
367
+
368
+ private fun beaconKey(major: Int, minor: Int) = "${major}_${minor}"
369
+
370
+ private fun proximityLabel(distance: Double) = when {
371
+ distance < 0 -> "unknown"
372
+ distance < 0.5 -> "immediate"
373
+ distance < 3.0 -> "near"
374
+ else -> "far"
375
+ }
376
+
377
+ private fun getDeviceId(): String {
378
+ val prefs = reactContext.getSharedPreferences("SpotnySDK", Context.MODE_PRIVATE)
379
+ return prefs.getString("deviceId", null) ?: run {
380
+ val id = UUID.randomUUID().toString()
381
+ prefs.edit().putString("deviceId", id).apply()
382
+ id
383
+ }
384
+ }
385
+
386
+ private fun sendEvent(name: String, payload: WritableMap) {
387
+ reactContext
388
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
389
+ .emit(name, payload)
390
+ }
391
+
392
+ private fun logFile() = File(reactContext.filesDir, "spotny_beacon_debug.log")
393
+
394
+ private fun logToFile(message: String) {
395
+ ioExecutor.execute {
396
+ try {
397
+ val ts = SimpleDateFormat("yy-MM-dd HH:mm:ss", Locale.US).format(Date())
398
+ logFile().appendText("[$ts] $message\n")
399
+ } catch (_: Exception) {}
400
+ }
401
+ }
402
+
403
+ // ── State cleanup ─────────────────────────────────────────────────────────
404
+
405
+ private fun cleanupBeacon(key: String) {
406
+ sendProximity("PROXIMITY_EXIT", key, 0.0)
407
+ activeCampaigns.remove(key)
408
+ lastProximityEventSent.remove(key)
409
+ lastProximityDistance.remove(key)
410
+ lastImpressionEventSent.remove(key)
411
+ lastCampaignFetchAttempt.remove(key)
412
+ fetchInProgress.remove(key)
413
+ proximityEventInProgress.remove(key)
414
+ impressionEventInProgress.remove(key)
415
+ Log.d(TAG, "Cleaned up state for beacon $key")
416
+ }
417
+
418
+ private fun cleanupAllState() {
419
+ // Stagger exit events to avoid firing N simultaneous network requests
420
+ val keys = activeCampaigns.keys.toList()
421
+ keys.forEachIndexed { index, key ->
422
+ android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
423
+ cleanupBeacon(key)
424
+ }, index * 300L)
425
+ }
426
+ }
427
+
428
+ // ── Backend API ───────────────────────────────────────────────────────────
429
+
430
+ private fun isTokenExpired(): Boolean {
431
+ if (sdkToken == null) return true
432
+ val expiry = sdkTokenExpiry ?: return false // no expiry field = treat as valid
433
+ return System.currentTimeMillis() >= expiry
434
+ }
435
+
436
+ private fun persistJWT() {
437
+ val prefs = reactContext.getSharedPreferences("SpotnySDK", Context.MODE_PRIVATE).edit()
438
+ sdkToken?.let { prefs.putString("jwt", it) }
439
+ sdkCredential?.let { prefs.putString("sdkCredential", it) }
440
+ apiKey?.let { prefs.putString("sdkApiKey", it) }
441
+ userId?.let { prefs.putString("sdkUserId", it) }
442
+ sdkTokenExpiry?.let { prefs.putLong("jwtExpiry", it) }
443
+ prefs.apply()
444
+ }
445
+
446
+ /** Synchronous JWT refresh — safe to call from ioExecutor thread. */
447
+ private fun refreshJWTBlocking(): Boolean {
448
+ val credential = sdkCredential ?: return false
449
+ val key = apiKey ?: return false
450
+ return try {
451
+ val conn = (URL("$backendURL/api/app/sdk/verify").openConnection() as HttpURLConnection).apply {
452
+ requestMethod = "POST"
453
+ setRequestProperty("Content-Type", "application/json")
454
+ connectTimeout = 10_000
455
+ readTimeout = 10_000
456
+ doOutput = true
457
+ }
458
+ conn.outputStream.use { it.write(buildJsonString(mapOf("token" to credential, "api_key" to key, "user_id" to (userId ?: ""))).toByteArray()) }
459
+ val status = conn.responseCode
460
+ val response = try { conn.inputStream.bufferedReader().readText() }
461
+ catch (_: Exception) { conn.errorStream?.bufferedReader()?.readText() ?: "" }
462
+ conn.disconnect()
463
+ if (status in 200..299) {
464
+ val data = (parseJsonObject(response)?.get("data") as? Map<*, *>)
465
+ val jwt = (data?.get("jwt") as? String)?.takeIf { it.isNotBlank() }
466
+ if (jwt != null) {
467
+ sdkToken = jwt
468
+ val expiresAt = (data?.get("expires_at") as? Number)?.toLong()
469
+ if (expiresAt != null) sdkTokenExpiry = expiresAt * 1000L
470
+ persistJWT()
471
+ Log.d(TAG, "JWT refreshed, expires: ${sdkTokenExpiry?.let { java.util.Date(it) } ?: "no expiry"}")
472
+ true
473
+ } else false
474
+ } else false
475
+ } catch (e: Exception) {
476
+ Log.e(TAG, "JWT refresh error: ${e.message}")
477
+ false
478
+ }
479
+ }
480
+
481
+ private fun post(
482
+ endpoint: String,
483
+ payload: Map<String, Any?>,
484
+ completion: (Int, String) -> Unit
485
+ ) {
486
+ ioExecutor.execute {
487
+ // Auto-refresh JWT before every call except verify itself
488
+ if (endpoint != "/api/app/sdk/verify" && isTokenExpired()) {
489
+ if (!refreshJWTBlocking()) {
490
+ reactContext.runOnUiQueueThread { completion(-1, "") }
491
+ return@execute
492
+ }
493
+ }
494
+ try {
495
+ val conn = (URL("$backendURL$endpoint").openConnection() as HttpURLConnection).apply {
496
+ requestMethod = "POST"
497
+ setRequestProperty("Content-Type", "application/json")
498
+ sdkToken?.let { setRequestProperty("Authorization", "Bearer $it") }
499
+ connectTimeout = 10_000
500
+ readTimeout = 10_000
501
+ doOutput = true
502
+ }
503
+ val body = buildJsonString(payload)
504
+ conn.outputStream.use { it.write(body.toByteArray()) }
505
+ val status = conn.responseCode
506
+ val response = try { conn.inputStream.bufferedReader().readText() }
507
+ catch (_: Exception) { conn.errorStream?.bufferedReader()?.readText() ?: "" }
508
+ conn.disconnect()
509
+ reactContext.runOnUiQueueThread { completion(status, response) }
510
+ } catch (e: Exception) {
511
+ Log.e(TAG, "POST $endpoint error: ${e.message}")
512
+ reactContext.runOnUiQueueThread { completion(-1, "") }
513
+ }
514
+ }
515
+ }
516
+
517
+ /** Minimal JSON serialiser (avoids adding a full JSON library dependency). */
518
+ private fun buildJsonString(map: Map<String, Any?>): String {
519
+ val sb = StringBuilder("{")
520
+ map.entries.forEachIndexed { i, (k, v) ->
521
+ if (i > 0) sb.append(",")
522
+ sb.append("\"$k\":")
523
+ when (v) {
524
+ null -> sb.append("null")
525
+ is String -> sb.append("\"${v.replace("\"", "\\\"")}\"")
526
+ is Boolean -> sb.append(v)
527
+ is Number -> sb.append(v)
528
+ else -> sb.append("\"$v\"")
529
+ }
530
+ }
531
+ sb.append("}")
532
+ return sb.toString()
533
+ }
534
+
535
+ // ── Campaign fetching ─────────────────────────────────────────────────────
536
+
537
+ private fun fetchCampaign(major: Int, minor: Int) {
538
+ val key = beaconKey(major, minor)
539
+ if (activeCampaigns.containsKey(key)) return
540
+ if (fetchInProgress[key] == true) return
541
+
542
+ val last = lastCampaignFetchAttempt[key]
543
+ if (last != null && System.currentTimeMillis() - last < campaignFetchCooldown) return
544
+
545
+ lastCampaignFetchAttempt[key] = System.currentTimeMillis()
546
+ fetchInProgress[key] = true
547
+
548
+ val payload = mutableMapOf<String, Any?>("beacon_id" to key)
549
+
550
+ post("/api/app/campaigns/beacon", payload) { status, body ->
551
+ fetchInProgress[key] = false
552
+ if (status != 200) {
553
+ Log.w(TAG, "Campaign fetch status $status for beacon $key")
554
+ return@post
555
+ }
556
+ try {
557
+ val json = parseJsonObject(body) ?: return@post
558
+ val dataObj = json["data"] as? Map<*, *> ?: return@post
559
+ val screen = dataObj["screen"] as? Map<*, *> ?: return@post
560
+ val screenId = (screen["id"] as? Number)?.toInt() ?: return@post
561
+
562
+ var campaignId: Int? = null
563
+ var inQueue = false
564
+ val campaignObj = dataObj["campaign"] as? Map<*, *>
565
+ if (campaignObj != null) {
566
+ campaignId = (campaignObj["id"] as? Number)?.toInt()
567
+ inQueue = campaignObj["inQueue"] as? Boolean ?: false
568
+ }
569
+
570
+ activeCampaigns[key] = CampaignData(
571
+ campaignId = campaignId, screenId = screenId,
572
+ sessionId = null, inQueue = inQueue, major = major, minor = minor)
573
+ Log.d(TAG, "Campaign loaded for beacon $key — screenId=$screenId")
574
+ } catch (e: Exception) {
575
+ Log.e(TAG, "Campaign JSON parse error for $key: ${e.message}")
576
+ }
577
+ }
578
+ }
579
+
580
+ // ── Tracking events ───────────────────────────────────────────────────────
581
+
582
+ private fun sendTracking(
583
+ eventType: String,
584
+ key: String,
585
+ distance: Double,
586
+ endpoint: String
587
+ ) {
588
+ val isImpression = eventType == "IMPRESSION_HEARTBEAT"
589
+ if (isImpression && impressionEventInProgress[key] == true) return
590
+ if (!isImpression && proximityEventInProgress[key] == true) return
591
+
592
+ if (isImpression) impressionEventInProgress[key] = true
593
+ else proximityEventInProgress[key] = true
594
+
595
+ val campaign = activeCampaigns[key]
596
+ if (campaign == null) {
597
+ if (isImpression) impressionEventInProgress[key] = false
598
+ else proximityEventInProgress[key] = false
599
+ return
600
+ }
601
+ if (isImpression && (campaign.campaignId == null || campaign.inQueue)) {
602
+ impressionEventInProgress[key] = false; return
603
+ }
604
+
605
+ val payload = mutableMapOf<String, Any?>(
606
+ "event_type" to eventType,
607
+ "distance" to distance,
608
+ "screen_id" to campaign.screenId
609
+ )
610
+ campaign.campaignId?.let { payload["campaign_id"] = it }
611
+ campaign.sessionId?.let { payload["session_id"] = it }
612
+
613
+ post(endpoint, payload) { status, body ->
614
+ if (status in 200..299) {
615
+ Log.d(TAG, "$eventType sent — distance ${"%.2f".format(distance)}m")
616
+ if (!isImpression && campaign.sessionId == null) {
617
+ try {
618
+ val json = parseJsonObject(body)
619
+ val sid = ((json?.get("data") as? Map<*, *>)
620
+ ?.get("event") as? Map<*, *>)
621
+ ?.get("session_id") as? String
622
+ if (sid != null) {
623
+ activeCampaigns[key] = campaign.copy(sessionId = sid)
624
+ Log.d(TAG, "session_id = $sid")
625
+ }
626
+ } catch (_: Exception) {}
627
+ }
628
+ } else if (status == 429) {
629
+ val penalty = System.currentTimeMillis() + 10_000L
630
+ if (isImpression) lastImpressionEventSent[key] = penalty
631
+ else lastProximityEventSent[key] = penalty
632
+ Log.w(TAG, "$eventType rate-limited (429)")
633
+ } else {
634
+ Log.w(TAG, "$eventType failed — status $status")
635
+ }
636
+ if (isImpression) impressionEventInProgress[key] = false
637
+ else proximityEventInProgress[key] = false
638
+ }
639
+ }
640
+
641
+ private fun sendProximity(eventType: String, key: String, distance: Double) =
642
+ sendTracking(eventType, key, distance, "/api/app/impressions/proximity")
643
+
644
+ private fun sendImpression(key: String, distance: Double) =
645
+ sendTracking("IMPRESSION_HEARTBEAT", key, distance, "/api/app/impressions/track")
646
+
647
+ // ── Minimal JSON parser (avoids org.json / GSON dependency) ──────────────
648
+ // Only handles flat and one-level-deep objects. For this SDK, that is enough.
649
+
650
+ @Suppress("UNCHECKED_CAST")
651
+ private fun parseJsonObject(json: String): Map<String, Any?>? {
652
+ return try {
653
+ org.json.JSONObject(json).toMap()
654
+ } catch (_: Exception) { null }
655
+ }
656
+
657
+ private fun org.json.JSONObject.toMap(): Map<String, Any?> {
658
+ val map = mutableMapOf<String, Any?>()
659
+ keys().forEach { key ->
660
+ map[key] = when (val v = get(key)) {
661
+ is org.json.JSONObject -> v.toMap()
662
+ is org.json.JSONArray -> v.toList()
663
+ org.json.JSONObject.NULL -> null
664
+ else -> v
665
+ }
666
+ }
667
+ return map
668
+ }
669
+
670
+ private fun org.json.JSONArray.toList(): List<Any?> {
671
+ return (0 until length()).map { i ->
672
+ when (val v = get(i)) {
673
+ is org.json.JSONObject -> v.toMap()
674
+ is org.json.JSONArray -> v.toList()
675
+ org.json.JSONObject.NULL -> null
676
+ else -> v
677
+ }
678
+ }
679
+ }
680
+
681
+ companion object {
682
+ const val NAME = NativeSpotnySdkSpec.NAME
683
+ }
684
+ }
685
+