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.
- package/LICENSE +20 -0
- package/README.md +589 -0
- package/SpotnySdk.podspec +35 -0
- package/android/build.gradle +70 -0
- package/android/src/main/AndroidManifest.xml +33 -0
- package/android/src/main/java/com/spotnysdk/SpotnySdkModule.kt +685 -0
- package/android/src/main/java/com/spotnysdk/SpotnySdkPackage.kt +31 -0
- package/ios/Frameworks/CBORCoding.xcframework/Info.plist +44 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64/CBORCoding.framework/CBORCoding +0 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64/CBORCoding.framework/Headers/CBORCoding-Swift.h +317 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64/CBORCoding.framework/Info.plist +0 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64/CBORCoding.framework/Modules/CBORCoding.swiftmodule/arm64-apple-ios.abi.json +7021 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64/CBORCoding.framework/Modules/CBORCoding.swiftmodule/arm64-apple-ios.private.swiftinterface +193 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64/CBORCoding.framework/Modules/CBORCoding.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64/CBORCoding.framework/Modules/CBORCoding.swiftmodule/arm64-apple-ios.swiftinterface +193 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64/CBORCoding.framework/Modules/module.modulemap +4 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/CBORCoding +0 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Headers/CBORCoding-Swift.h +630 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Info.plist +0 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/CBORCoding.swiftmodule/arm64-apple-ios-simulator.abi.json +7021 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/CBORCoding.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +193 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/CBORCoding.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/CBORCoding.swiftmodule/arm64-apple-ios-simulator.swiftinterface +193 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/CBORCoding.swiftmodule/x86_64-apple-ios-simulator.abi.json +7021 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/CBORCoding.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +193 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/CBORCoding.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/CBORCoding.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +193 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/Modules/module.modulemap +4 -0
- package/ios/Frameworks/CBORCoding.xcframework/ios-arm64_x86_64-simulator/CBORCoding.framework/_CodeSignature/CodeResources +267 -0
- package/ios/Frameworks/Half.xcframework/Info.plist +44 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Half +0 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Headers/Half-Swift.h +317 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Headers/half.h +95 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Info.plist +0 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Modules/Half.swiftmodule/arm64-apple-ios.abi.json +4942 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Modules/Half.swiftmodule/arm64-apple-ios.private.swiftinterface +650 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Modules/Half.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Modules/Half.swiftmodule/arm64-apple-ios.swiftinterface +650 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64/Half.framework/Modules/module.modulemap +11 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Half +0 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Headers/Half-Swift.h +630 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Headers/half.h +95 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Info.plist +0 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/Half.swiftmodule/arm64-apple-ios-simulator.abi.json +4942 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/Half.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +650 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/Half.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/Half.swiftmodule/arm64-apple-ios-simulator.swiftinterface +650 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/Half.swiftmodule/x86_64-apple-ios-simulator.abi.json +4973 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/Half.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +660 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/Half.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/Half.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +660 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/Modules/module.modulemap +11 -0
- package/ios/Frameworks/Half.xcframework/ios-arm64_x86_64-simulator/Half.framework/_CodeSignature/CodeResources +245 -0
- package/ios/Frameworks/KontaktSDK.xcframework/Info.plist +44 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Base.lproj/Localizable.strings +0 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/CLBeacon+Kontakt.h +37 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKAction.h +90 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKActionContent.h +80 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKBeaconManager.h +326 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKBeaconRegion.h +102 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKCloudClient.h +241 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKCloudClientJSONResponseSerializer.h +20 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKCloudClientResponseSerializer.h +35 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKCloudClientSessionManager.h +207 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKCloudDefinitions.h +13 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKCloudModel.h +36 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDevice.h +179 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceCCOperationDelegate.h +13 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceConfiguration.h +329 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceConfigurationGPIO.h +24 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceConnection.h +379 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceConnectionOperation.h +65 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceDataLoggerConnection.h +56 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceDataLoggerReading.h +49 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceDefinitions.h +853 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceGATTDefinitions.h +106 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceGATTOperation.h +68 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceGatewayConfigurationType.h +43 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceGatewayConnection.h +101 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceGatewayDiagnostic.h +61 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceGatewayWiFiNetwork.h +76 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceKontaktRecognitionBox.h +26 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceListenOperation.h +20 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceNotifyOperation.h +34 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDevicePowerSaving.h +86 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDeviceWriteOperation.h +51 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKDevicesManager.h +182 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKEddystone.h +101 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKEddystoneFrame.h +47 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKEddystoneManager.h +160 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKEddystoneRegion.h +85 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKEddystoneTLM.h +49 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKEddystoneUID.h +55 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKEddystoneURL.h +33 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKEddystoneURLValueTransformer.h +13 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKFirmware.h +98 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKGroupOperation.h +100 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKKontaktResponse.h +85 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKManager.h +121 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKNearbyDevice.h +135 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKNearbyDeviceTelemetry.h +513 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKOperation.h +116 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKPersonPosition.h +22 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKSecureBeaconRegion.h +86 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKSecureEddystoneRegion.h +59 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKTrigger.h +94 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKTriggerContext.h +79 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KTKVenue.h +105 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/Kontakt.h +187 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KontaktSDK-Swift.h +668 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/KontaktSDK.h +43 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/NSData+Kontakt.h +115 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/NSError+Kontakt.h +26 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/NSString+Kontakt.h +25 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Headers/NSURLRequest+Kontakt.h +44 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Info.plist +0 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/KontaktSDK +0 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/arm64-apple-ios.abi.json +14106 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +428 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/arm64-apple-ios.swiftinterface +428 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/Modules/module.modulemap +11 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64/KontaktSDK.framework/strip-frameworks.sh +72 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Base.lproj/Localizable.strings +0 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/CLBeacon+Kontakt.h +37 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKAction.h +90 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKActionContent.h +80 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKBeaconManager.h +326 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKBeaconRegion.h +102 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKCloudClient.h +241 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKCloudClientJSONResponseSerializer.h +20 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKCloudClientResponseSerializer.h +35 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKCloudClientSessionManager.h +207 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKCloudDefinitions.h +13 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKCloudModel.h +36 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDevice.h +179 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceCCOperationDelegate.h +13 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceConfiguration.h +329 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceConfigurationGPIO.h +24 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceConnection.h +379 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceConnectionOperation.h +65 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceDataLoggerConnection.h +56 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceDataLoggerReading.h +49 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceDefinitions.h +853 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceGATTDefinitions.h +106 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceGATTOperation.h +68 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceGatewayConfigurationType.h +43 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceGatewayConnection.h +101 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceGatewayDiagnostic.h +61 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceGatewayWiFiNetwork.h +76 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceKontaktRecognitionBox.h +26 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceListenOperation.h +20 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceNotifyOperation.h +34 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDevicePowerSaving.h +86 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDeviceWriteOperation.h +51 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKDevicesManager.h +182 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKEddystone.h +101 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKEddystoneFrame.h +47 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKEddystoneManager.h +160 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKEddystoneRegion.h +85 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKEddystoneTLM.h +49 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKEddystoneUID.h +55 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKEddystoneURL.h +33 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKEddystoneURLValueTransformer.h +13 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKFirmware.h +98 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKGroupOperation.h +100 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKKontaktResponse.h +85 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKManager.h +121 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKNearbyDevice.h +135 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKNearbyDeviceTelemetry.h +513 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKOperation.h +116 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKPersonPosition.h +22 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKSecureBeaconRegion.h +86 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKSecureEddystoneRegion.h +59 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKTrigger.h +94 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKTriggerContext.h +79 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KTKVenue.h +105 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/Kontakt.h +187 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KontaktSDK-Swift.h +1332 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/KontaktSDK.h +43 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/NSData+Kontakt.h +115 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/NSError+Kontakt.h +26 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/NSString+Kontakt.h +25 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Headers/NSURLRequest+Kontakt.h +44 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Info.plist +0 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/KontaktSDK +0 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +14106 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +428 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +428 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +14106 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +428 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/KontaktSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +428 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/Modules/module.modulemap +11 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/_CodeSignature/CodeResources +894 -0
- package/ios/Frameworks/KontaktSDK.xcframework/ios-arm64_x86_64-simulator/KontaktSDK.framework/strip-frameworks.sh +72 -0
- package/ios/SpotnyBeaconScanner.swift +938 -0
- package/ios/SpotnySdk.h +10 -0
- package/ios/SpotnySdk.mm +127 -0
- package/lib/module/NativeSpotnySdk.js +5 -0
- package/lib/module/NativeSpotnySdk.js.map +1 -0
- package/lib/module/index.js +96 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeSpotnySdk.d.ts +18 -0
- package/lib/typescript/src/NativeSpotnySdk.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +81 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +169 -0
- package/src/NativeSpotnySdk.ts +29 -0
- 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
|
+
|