ilabs-flir 1.0.2 → 1.0.4
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/Flir.podspec +31 -31
- package/README.md +1271 -1271
- package/android/Flir/build.gradle.kts +85 -80
- package/android/Flir/libs/flir-stubs.jar +0 -0
- package/android/Flir/src/main/AndroidManifest.xml +31 -31
- package/android/Flir/src/main/java/com/flir/thermalsdk/ErrorCode.java +13 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/ErrorCodeException.java +14 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/ThermalSdkAndroid.java +16 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/androidsdk/image/BitmapAndroid.java +20 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/image/ImageBuffer.java +11 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/image/JavaImageBuffer.java +35 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/image/Palette.java +15 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/image/PaletteManager.java +16 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/image/Point.java +11 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/image/ThermalImage.java +23 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/image/ThermalValue.java +9 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/Camera.java +26 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/CameraType.java +8 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/CommunicationInterface.java +16 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/ConnectParameters.java +16 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/Identity.java +23 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/IpSettings.java +9 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/RemoteControl.java +16 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/connectivity/ConnectionStatusListener.java +7 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/discovery/DiscoveryEventListener.java +14 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/discovery/DiscoveryFactory.java +33 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/remote/OnReceived.java +5 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/remote/OnRemoteError.java +7 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/streaming/Stream.java +8 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/streaming/ThermalStreamer.java +28 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/streaming/VisualStreamer.java +18 -0
- package/android/Flir/src/main/java/flir/android/FlirCommands.java +136 -0
- package/android/Flir/src/main/java/flir/android/FlirDownloadManager.kt +76 -75
- package/android/Flir/src/main/java/flir/android/FlirDownloadPackage.kt +16 -16
- package/android/Flir/src/main/java/flir/android/FlirFrameCache.kt +6 -6
- package/android/Flir/src/main/java/flir/android/FlirManager.kt +477 -248
- package/android/Flir/src/main/java/flir/android/FlirModule.kt +74 -74
- package/android/Flir/src/main/java/flir/android/FlirPackage.kt +19 -19
- package/android/Flir/src/main/java/flir/android/FlirSDKLoader.kt +165 -117
- package/android/Flir/src/main/java/flir/android/FlirSdkManager.java +1511 -0
- package/android/Flir/src/main/java/flir/android/FlirStatus.kt +12 -12
- package/android/Flir/src/main/java/flir/android/FlirView.kt +48 -48
- package/android/Flir/src/main/java/flir/android/FlirViewManager.kt +13 -13
- package/app.plugin.js +264 -264
- package/expo-module.config.json +5 -5
- package/ios/Flir/Framework/ThermalSDK/FLIRBattery.h +76 -76
- package/ios/Flir/Framework/ThermalSDK/FLIRCalibration.h +108 -108
- package/ios/Flir/Framework/ThermalSDK/FLIRCamera.h +156 -156
- package/ios/Flir/Framework/ThermalSDK/FLIRCameraDeviceInfo.h +53 -53
- package/ios/Flir/Framework/ThermalSDK/FLIRCameraEvent.h +132 -132
- package/ios/Flir/Framework/ThermalSDK/FLIRCameraImport.h +204 -204
- package/ios/Flir/Framework/ThermalSDK/FLIRColorDistributionSettings.h +204 -204
- package/ios/Flir/Framework/ThermalSDK/FLIRColorizer.h +82 -82
- package/ios/Flir/Framework/ThermalSDK/FLIRDiscoveredCamera.h +44 -44
- package/ios/Flir/Framework/ThermalSDK/FLIRDiscovery.h +132 -132
- package/ios/Flir/Framework/ThermalSDK/FLIRDisplaySettings.h +29 -29
- package/ios/Flir/Framework/ThermalSDK/FLIRFocus.h +70 -70
- package/ios/Flir/Framework/ThermalSDK/FLIRFusion.h +192 -192
- package/ios/Flir/Framework/ThermalSDK/FLIRFusionController.h +136 -136
- package/ios/Flir/Framework/ThermalSDK/FLIRFusionTransformation.h +35 -35
- package/ios/Flir/Framework/ThermalSDK/FLIRIdentity.h +264 -264
- package/ios/Flir/Framework/ThermalSDK/FLIRImageBase.h +196 -196
- package/ios/Flir/Framework/ThermalSDK/FLIRImageColorizer.h +26 -26
- package/ios/Flir/Framework/ThermalSDK/FLIRImageStatistics.h +61 -61
- package/ios/Flir/Framework/ThermalSDK/FLIRIsotherms.h +208 -208
- package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementArea.h +38 -38
- package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementCollection.h +147 -147
- package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementDelta.h +62 -62
- package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementDimensions.h +33 -33
- package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementEllipse.h +49 -49
- package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementLine.h +66 -66
- package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementMarker.h +69 -69
- package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementParameters.h +41 -41
- package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementRectangle.h +36 -36
- package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementReference.h +27 -27
- package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementShape.h +46 -46
- package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementSpot.h +33 -33
- package/ios/Flir/Framework/ThermalSDK/FLIRMeasurementsController.h +160 -160
- package/ios/Flir/Framework/ThermalSDK/FLIRMeterLinkSensorPoll.h +247 -247
- package/ios/Flir/Framework/ThermalSDK/FLIROverlayController.h +27 -27
- package/ios/Flir/Framework/ThermalSDK/FLIRPalette.h +60 -60
- package/ios/Flir/Framework/ThermalSDK/FLIRPaletteController.h +36 -36
- package/ios/Flir/Framework/ThermalSDK/FLIRPaletteManager.h +97 -97
- package/ios/Flir/Framework/ThermalSDK/FLIRQuantification.h +55 -55
- package/ios/Flir/Framework/ThermalSDK/FLIRRemoteControl.h +393 -393
- package/ios/Flir/Framework/ThermalSDK/FLIRRenderer.h +35 -35
- package/ios/Flir/Framework/ThermalSDK/FLIRRendererImpl.h +17 -17
- package/ios/Flir/Framework/ThermalSDK/FLIRScale.h +99 -99
- package/ios/Flir/Framework/ThermalSDK/FLIRScaleController.h +44 -44
- package/ios/Flir/Framework/ThermalSDK/FLIRStream.h +109 -109
- package/ios/Flir/Framework/ThermalSDK/FLIRStreamer.h +124 -124
- package/ios/Flir/Framework/ThermalSDK/FLIRSystem.h +40 -40
- package/ios/Flir/Framework/ThermalSDK/FLIRTemperatureRange.h +43 -43
- package/ios/Flir/Framework/ThermalSDK/FLIRThermalDelta.h +77 -77
- package/ios/Flir/Framework/ThermalSDK/FLIRThermalImage.h +331 -331
- package/ios/Flir/Framework/ThermalSDK/FLIRThermalImageFile.h +56 -56
- package/ios/Flir/Framework/ThermalSDK/FLIRThermalParameters.h +31 -31
- package/ios/Flir/Framework/ThermalSDK/FLIRThermalValue.h +92 -92
- package/ios/Flir/Framework/ThermalSDK/FLIRWirelessCameraDetails.h +88 -88
- package/ios/Flir/Framework/ThermalSDK/ThermalSDK.h +73 -73
- package/ios/Flir/SDKLoader/FlirSDKLoader.m +13 -13
- package/ios/Flir/SDKLoader/FlirSDKLoader.swift +175 -175
- package/ios/Flir/src/FlirEventEmitter.h +12 -12
- package/ios/Flir/src/FlirEventEmitter.m +33 -33
- package/ios/Flir/src/FlirModule.h +10 -10
- package/ios/Flir/src/FlirModule.m +381 -381
- package/ios/Flir/src/FlirPreviewView.h +13 -13
- package/ios/Flir/src/FlirPreviewView.m +24 -24
- package/ios/Flir/src/FlirState.h +20 -20
- package/ios/Flir/src/FlirState.m +79 -79
- package/ios/Flir/src/FlirViewManager.h +9 -9
- package/ios/Flir/src/FlirViewManager.m +16 -16
- package/package.json +60 -60
- package/react-native.config.js +14 -14
- package/scripts/copy_ios_libs.sh +32 -32
- package/scripts/create_stubs.py +174 -174
- package/scripts/download-sdk.js +62 -62
- package/scripts/prepare-binaries.sh +171 -171
- package/sdk-manifest.json +37 -30
- package/src/FlirDownload.ts +78 -78
- package/src/index.d.ts +17 -17
- package/src/index.js +7 -7
- package/src/index.ts +7 -7
- package/android/Flir/src/main/java/flir/android/CameraHandler.java +0 -194
- package/android/Flir/src/main/java/flir/android/FlirController.kt +0 -11
- package/android/Flir/src/main/java/flir/android/FrameDataHolder.java +0 -14
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
package flir.android
|
|
2
|
-
|
|
3
|
-
import com.facebook.react.bridge.Promise
|
|
4
|
-
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
|
-
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
6
|
-
import com.facebook.react.bridge.ReactMethod
|
|
7
|
-
|
|
8
|
-
class FlirModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
|
|
9
|
-
override fun getName(): String = "FlirModule"
|
|
10
|
-
|
|
11
|
-
// Simple placeholder conversion: converts an ARGB color to a pseudo-temperature value.
|
|
12
|
-
// Replace with SDK call when integrating thermalsdk APIs.
|
|
13
|
-
@ReactMethod
|
|
14
|
-
fun getTemperatureFromColor(color: Int, promise: Promise) {
|
|
15
|
-
try {
|
|
16
|
-
val r = (color shr 16) and 0xFF
|
|
17
|
-
val g = (color shr 8) and 0xFF
|
|
18
|
-
val b = color and 0xFF
|
|
19
|
-
// Luminance-like value scaled to a plausible temperature range (0°C - 400°C)
|
|
20
|
-
val lum = 0.2126 * r + 0.7152 * g + 0.0722 * b
|
|
21
|
-
val temp = 0.0 + (lum / 255.0) * 400.0
|
|
22
|
-
promise.resolve(temp)
|
|
23
|
-
} catch (e: Exception) {
|
|
24
|
-
promise.reject("ERR_FLIR_CONVERT", e)
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
@ReactMethod
|
|
29
|
-
fun getLatestFramePath(promise: Promise) {
|
|
30
|
-
try {
|
|
31
|
-
val path = FlirFrameCache.latestFramePath
|
|
32
|
-
if (path != null) promise.resolve(path) else promise.resolve(null)
|
|
33
|
-
} catch (e: Exception) {
|
|
34
|
-
promise.reject("ERR_FLIR_PATH", e)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
@ReactMethod
|
|
39
|
-
fun getTemperatureAt(x: Int, y: Int, promise: Promise) {
|
|
40
|
-
try {
|
|
41
|
-
val temp = FlirManager.getTemperatureAt(x, y)
|
|
42
|
-
if (temp != null) promise.resolve(temp) else promise.reject("ERR_NO_DATA", "No temperature data available")
|
|
43
|
-
} catch (e: Exception) {
|
|
44
|
-
promise.reject("ERR_FLIR_SAMPLE", e)
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
@ReactMethod
|
|
49
|
-
fun isEmulator(promise: Promise) {
|
|
50
|
-
try {
|
|
51
|
-
promise.resolve(FlirManager.isEmulator())
|
|
52
|
-
} catch (e: Exception) {
|
|
53
|
-
promise.reject("ERR_FLIR_EMULATOR_CHECK", e)
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
@ReactMethod
|
|
58
|
-
fun isDeviceConnected(promise: Promise) {
|
|
59
|
-
try {
|
|
60
|
-
promise.resolve(FlirManager.isDeviceConnected())
|
|
61
|
-
} catch (e: Exception) {
|
|
62
|
-
promise.reject("ERR_FLIR_DEVICE_CHECK", e)
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
@ReactMethod
|
|
67
|
-
fun getConnectedDeviceInfo(promise: Promise) {
|
|
68
|
-
try {
|
|
69
|
-
promise.resolve(FlirManager.getConnectedDeviceInfo())
|
|
70
|
-
} catch (e: Exception) {
|
|
71
|
-
promise.reject("ERR_FLIR_DEVICE_INFO", e)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
1
|
+
package flir.android
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Promise
|
|
4
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
6
|
+
import com.facebook.react.bridge.ReactMethod
|
|
7
|
+
|
|
8
|
+
class FlirModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
|
|
9
|
+
override fun getName(): String = "FlirModule"
|
|
10
|
+
|
|
11
|
+
// Simple placeholder conversion: converts an ARGB color to a pseudo-temperature value.
|
|
12
|
+
// Replace with SDK call when integrating thermalsdk APIs.
|
|
13
|
+
@ReactMethod
|
|
14
|
+
fun getTemperatureFromColor(color: Int, promise: Promise) {
|
|
15
|
+
try {
|
|
16
|
+
val r = (color shr 16) and 0xFF
|
|
17
|
+
val g = (color shr 8) and 0xFF
|
|
18
|
+
val b = color and 0xFF
|
|
19
|
+
// Luminance-like value scaled to a plausible temperature range (0°C - 400°C)
|
|
20
|
+
val lum = 0.2126 * r + 0.7152 * g + 0.0722 * b
|
|
21
|
+
val temp = 0.0 + (lum / 255.0) * 400.0
|
|
22
|
+
promise.resolve(temp)
|
|
23
|
+
} catch (e: Exception) {
|
|
24
|
+
promise.reject("ERR_FLIR_CONVERT", e)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@ReactMethod
|
|
29
|
+
fun getLatestFramePath(promise: Promise) {
|
|
30
|
+
try {
|
|
31
|
+
val path = FlirFrameCache.latestFramePath
|
|
32
|
+
if (path != null) promise.resolve(path) else promise.resolve(null)
|
|
33
|
+
} catch (e: Exception) {
|
|
34
|
+
promise.reject("ERR_FLIR_PATH", e)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@ReactMethod
|
|
39
|
+
fun getTemperatureAt(x: Int, y: Int, promise: Promise) {
|
|
40
|
+
try {
|
|
41
|
+
val temp = FlirManager.getTemperatureAt(x, y)
|
|
42
|
+
if (temp != null) promise.resolve(temp) else promise.reject("ERR_NO_DATA", "No temperature data available")
|
|
43
|
+
} catch (e: Exception) {
|
|
44
|
+
promise.reject("ERR_FLIR_SAMPLE", e)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@ReactMethod
|
|
49
|
+
fun isEmulator(promise: Promise) {
|
|
50
|
+
try {
|
|
51
|
+
promise.resolve(FlirManager.isEmulator())
|
|
52
|
+
} catch (e: Exception) {
|
|
53
|
+
promise.reject("ERR_FLIR_EMULATOR_CHECK", e)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@ReactMethod
|
|
58
|
+
fun isDeviceConnected(promise: Promise) {
|
|
59
|
+
try {
|
|
60
|
+
promise.resolve(FlirManager.isDeviceConnected())
|
|
61
|
+
} catch (e: Exception) {
|
|
62
|
+
promise.reject("ERR_FLIR_DEVICE_CHECK", e)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@ReactMethod
|
|
67
|
+
fun getConnectedDeviceInfo(promise: Promise) {
|
|
68
|
+
try {
|
|
69
|
+
promise.resolve(FlirManager.getConnectedDeviceInfo())
|
|
70
|
+
} catch (e: Exception) {
|
|
71
|
+
promise.reject("ERR_FLIR_DEVICE_INFO", e)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
package flir.android
|
|
2
|
-
|
|
3
|
-
import com.facebook.react.ReactPackage
|
|
4
|
-
import com.facebook.react.bridge.NativeModule
|
|
5
|
-
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
-
import com.facebook.react.uimanager.ViewManager
|
|
7
|
-
|
|
8
|
-
class FlirPackage : ReactPackage {
|
|
9
|
-
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
10
|
-
return listOf(
|
|
11
|
-
FlirModule(reactContext),
|
|
12
|
-
FlirDownloadManager(reactContext)
|
|
13
|
-
)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
17
|
-
return listOf(FlirViewManager())
|
|
18
|
-
}
|
|
19
|
-
}
|
|
1
|
+
package flir.android
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.ReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.uimanager.ViewManager
|
|
7
|
+
|
|
8
|
+
class FlirPackage : ReactPackage {
|
|
9
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
10
|
+
return listOf(
|
|
11
|
+
FlirModule(reactContext),
|
|
12
|
+
FlirDownloadManager(reactContext)
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
17
|
+
return listOf(FlirViewManager())
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -1,90 +1,176 @@
|
|
|
1
1
|
package flir.android
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import android.os.Build
|
|
5
|
+
import android.util.Log
|
|
6
6
|
import kotlinx.coroutines.*
|
|
7
7
|
import org.json.JSONObject
|
|
8
8
|
import java.io.File
|
|
9
9
|
import java.io.FileOutputStream
|
|
10
|
+
import java.net.HttpURLConnection
|
|
10
11
|
import java.net.URL
|
|
11
|
-
import java.security.MessageDigest
|
|
12
12
|
import java.util.zip.ZipInputStream
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* FlirSDKLoader - Downloads and manages architecture-specific FLIR SDK packages
|
|
16
|
+
*
|
|
17
|
+
* Each package contains:
|
|
18
|
+
* - classes.dex (combined DEX from thermalsdk + androidsdk)
|
|
19
|
+
* - Native .so libraries in jni/{arch}/ folder
|
|
20
|
+
*
|
|
21
|
+
* URLs are read from sdk-manifest.json in assets folder
|
|
22
|
+
*/
|
|
14
23
|
object FlirSDKLoader {
|
|
15
24
|
|
|
16
|
-
private const val
|
|
17
|
-
private var splitInstallManager: SplitInstallManager? = null
|
|
25
|
+
private const val TAG = "FlirSDKLoader"
|
|
18
26
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
// Cached manifest data
|
|
28
|
+
private var cachedManifest: SDKManifest? = null
|
|
29
|
+
|
|
30
|
+
// Manifest data classes
|
|
31
|
+
data class ArchPackage(val downloadUrl: String, val sizeBytes: Long)
|
|
32
|
+
data class SDKManifest(val version: String, val packages: Map<String, ArchPackage>)
|
|
22
33
|
|
|
23
34
|
private fun getSDKDirectory(context: Context) = File(context.filesDir, "FlirSDK")
|
|
24
35
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Load manifest from assets
|
|
38
|
+
*/
|
|
39
|
+
private fun loadManifest(context: Context): SDKManifest? {
|
|
40
|
+
if (cachedManifest != null) return cachedManifest
|
|
41
|
+
|
|
42
|
+
return try {
|
|
43
|
+
val json = context.assets.open("sdk-manifest.json").bufferedReader().readText()
|
|
44
|
+
val root = JSONObject(json)
|
|
45
|
+
val android = root.getJSONObject("android")
|
|
46
|
+
val packagesJson = android.getJSONObject("packages")
|
|
47
|
+
|
|
48
|
+
val packages = mutableMapOf<String, ArchPackage>()
|
|
49
|
+
packagesJson.keys().forEach { arch ->
|
|
50
|
+
val pkg = packagesJson.getJSONObject(arch)
|
|
51
|
+
packages[arch] = ArchPackage(
|
|
52
|
+
downloadUrl = pkg.getString("downloadUrl"),
|
|
53
|
+
sizeBytes = pkg.getLong("sizeBytes")
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
SDKManifest(
|
|
58
|
+
version = root.getString("version"),
|
|
59
|
+
packages = packages
|
|
60
|
+
).also { cachedManifest = it }
|
|
61
|
+
} catch (e: Exception) {
|
|
62
|
+
Log.e(TAG, "Failed to load manifest: ${e.message}")
|
|
63
|
+
null
|
|
29
64
|
}
|
|
30
|
-
// Check direct download - look for either file from the manifest
|
|
31
|
-
val sdkDir = getSDKDirectory(context)
|
|
32
|
-
return File(sdkDir, "thermalsdk-release.aar").exists() ||
|
|
33
|
-
File(sdkDir, "androidsdk-release.aar").exists() ||
|
|
34
|
-
File(sdkDir, "thermalsdk.aar").exists()
|
|
35
65
|
}
|
|
36
66
|
|
|
37
|
-
|
|
38
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Get the primary ABI for this device
|
|
69
|
+
*/
|
|
70
|
+
fun getDeviceArch(): String {
|
|
71
|
+
val supportedAbis = Build.SUPPORTED_ABIS
|
|
72
|
+
Log.d(TAG, "Device supported ABIs: ${supportedAbis.joinToString()}")
|
|
73
|
+
|
|
74
|
+
val knownArchs = setOf("arm64-v8a", "armeabi-v7a", "x86_64")
|
|
75
|
+
for (abi in supportedAbis) {
|
|
76
|
+
if (abi in knownArchs) {
|
|
77
|
+
Log.d(TAG, "Selected ABI: $abi")
|
|
78
|
+
return abi
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return "arm64-v8a"
|
|
39
82
|
}
|
|
40
83
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
val
|
|
47
|
-
onError("SplitInstallManager not initialized")
|
|
48
|
-
return
|
|
49
|
-
}
|
|
84
|
+
/**
|
|
85
|
+
* Check if SDK is already downloaded
|
|
86
|
+
*/
|
|
87
|
+
fun isSDKAvailable(context: Context): Boolean {
|
|
88
|
+
val sdkDir = getSDKDirectory(context)
|
|
89
|
+
val arch = getDeviceArch()
|
|
50
90
|
|
|
51
|
-
val
|
|
52
|
-
|
|
53
|
-
.build()
|
|
91
|
+
val dexFile = File(sdkDir, "classes.dex")
|
|
92
|
+
val soDir = File(sdkDir, "jni/$arch")
|
|
54
93
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
SplitInstallSessionStatus.DOWNLOADING -> {
|
|
58
|
-
val progress = state.bytesDownloaded().toFloat() / state.totalBytesToDownload()
|
|
59
|
-
onProgress(progress)
|
|
60
|
-
}
|
|
61
|
-
SplitInstallSessionStatus.INSTALLED -> onComplete()
|
|
62
|
-
SplitInstallSessionStatus.FAILED -> onError("Install failed: ${state.errorCode()}")
|
|
63
|
-
else -> {}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
94
|
+
val hasDex = dexFile.exists() && dexFile.length() > 0
|
|
95
|
+
val hasSo = soDir.exists() && soDir.listFiles()?.isNotEmpty() == true
|
|
66
96
|
|
|
67
|
-
|
|
97
|
+
Log.d(TAG, "SDK available: dex=$hasDex, so=$hasSo")
|
|
98
|
+
return hasDex && hasSo
|
|
68
99
|
}
|
|
69
100
|
|
|
70
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Get path to the DEX file
|
|
103
|
+
*/
|
|
104
|
+
fun getDexPath(context: Context): File? {
|
|
105
|
+
val dexFile = File(getSDKDirectory(context), "classes.dex")
|
|
106
|
+
return if (dexFile.exists()) dexFile else null
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get path to native libraries directory
|
|
111
|
+
*/
|
|
112
|
+
fun getNativeLibDir(context: Context): File? {
|
|
113
|
+
val arch = getDeviceArch()
|
|
114
|
+
val libDir = File(getSDKDirectory(context), "jni/$arch")
|
|
115
|
+
return if (libDir.exists()) libDir else null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get estimated download size from manifest
|
|
120
|
+
*/
|
|
121
|
+
fun getDownloadSize(context: Context): Long {
|
|
122
|
+
val manifest = loadManifest(context) ?: return 15_000_000L
|
|
123
|
+
val arch = getDeviceArch()
|
|
124
|
+
return manifest.packages[arch]?.sizeBytes ?: 15_000_000L
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Download the SDK package for this device's architecture
|
|
129
|
+
*/
|
|
130
|
+
suspend fun downloadSDK(
|
|
71
131
|
context: Context,
|
|
72
132
|
onProgress: (downloaded: Long, total: Long) -> Unit
|
|
73
133
|
): Result<Unit> = withContext(Dispatchers.IO) {
|
|
74
134
|
try {
|
|
75
|
-
val
|
|
76
|
-
|
|
77
|
-
|
|
135
|
+
val arch = getDeviceArch()
|
|
136
|
+
val manifest = loadManifest(context)
|
|
137
|
+
?: return@withContext Result.failure(Exception("Failed to load manifest"))
|
|
138
|
+
|
|
139
|
+
val archPackage = manifest.packages[arch]
|
|
140
|
+
?: return@withContext Result.failure(Exception("No package for architecture: $arch"))
|
|
78
141
|
|
|
79
|
-
val downloadUrl =
|
|
80
|
-
|
|
81
|
-
val totalSize = manifest.android.directDownload.sizeBytes
|
|
142
|
+
val downloadUrl = archPackage.downloadUrl
|
|
143
|
+
Log.i(TAG, "Downloading SDK for $arch from $downloadUrl")
|
|
82
144
|
|
|
83
145
|
val sdkDir = getSDKDirectory(context).apply { mkdirs() }
|
|
84
|
-
val zipFile = File(context.cacheDir, "flir-sdk.zip")
|
|
146
|
+
val zipFile = File(context.cacheDir, "flir-sdk-$arch.zip")
|
|
85
147
|
|
|
86
|
-
// Download
|
|
87
|
-
URL(downloadUrl).
|
|
148
|
+
// Download with redirect handling (GitHub uses 302 redirects)
|
|
149
|
+
var connection = URL(downloadUrl).openConnection() as HttpURLConnection
|
|
150
|
+
connection.instanceFollowRedirects = true
|
|
151
|
+
connection.connectTimeout = 30000
|
|
152
|
+
connection.readTimeout = 60000
|
|
153
|
+
|
|
154
|
+
// Handle redirects manually for cross-protocol redirects
|
|
155
|
+
var redirectCount = 0
|
|
156
|
+
while (connection.responseCode in 301..302 && redirectCount < 5) {
|
|
157
|
+
val redirectUrl = connection.getHeaderField("Location")
|
|
158
|
+
Log.d(TAG, "Redirect to: $redirectUrl")
|
|
159
|
+
connection.disconnect()
|
|
160
|
+
connection = URL(redirectUrl).openConnection() as HttpURLConnection
|
|
161
|
+
connection.instanceFollowRedirects = true
|
|
162
|
+
redirectCount++
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (connection.responseCode != HttpURLConnection.HTTP_OK) {
|
|
166
|
+
return@withContext Result.failure(Exception("HTTP error ${connection.responseCode}"))
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
val totalSize = connection.contentLengthLong.let {
|
|
170
|
+
if (it > 0) it else archPackage.sizeBytes
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
connection.inputStream.use { input ->
|
|
88
174
|
FileOutputStream(zipFile).use { output ->
|
|
89
175
|
val buffer = ByteArray(8192)
|
|
90
176
|
var totalRead = 0L
|
|
@@ -99,97 +185,59 @@ object FlirSDKLoader {
|
|
|
99
185
|
}
|
|
100
186
|
}
|
|
101
187
|
}
|
|
188
|
+
connection.disconnect()
|
|
102
189
|
|
|
103
|
-
|
|
104
|
-
val actualHash = sha256(zipFile)
|
|
105
|
-
if (actualHash != expectedHash) {
|
|
106
|
-
zipFile.delete()
|
|
107
|
-
return@withContext Result.failure(SecurityException("Checksum mismatch"))
|
|
108
|
-
}
|
|
190
|
+
Log.i(TAG, "Download complete: ${zipFile.length()} bytes")
|
|
109
191
|
|
|
110
192
|
// Extract
|
|
111
193
|
unzip(zipFile, sdkDir)
|
|
112
194
|
zipFile.delete()
|
|
113
195
|
|
|
196
|
+
// Verify
|
|
197
|
+
val dexFile = File(sdkDir, "classes.dex")
|
|
198
|
+
val soDir = File(sdkDir, "jni/$arch")
|
|
199
|
+
|
|
200
|
+
if (!dexFile.exists()) {
|
|
201
|
+
return@withContext Result.failure(Exception("DEX file not extracted"))
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
dexFile.setReadOnly()
|
|
205
|
+
Log.i(TAG, "SDK installed: dex=${dexFile.length()} bytes, libs=${soDir.listFiles()?.size ?: 0} files")
|
|
206
|
+
|
|
114
207
|
Result.success(Unit)
|
|
115
208
|
} catch (e: Exception) {
|
|
209
|
+
Log.e(TAG, "Download failed", e)
|
|
116
210
|
Result.failure(e)
|
|
117
211
|
}
|
|
118
212
|
}
|
|
119
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Delete downloaded SDK
|
|
216
|
+
*/
|
|
120
217
|
fun deleteSDK(context: Context): Boolean {
|
|
121
|
-
splitInstallManager?.deferredUninstall(listOf(FEATURE_MODULE))
|
|
122
218
|
return getSDKDirectory(context).deleteRecursively()
|
|
123
219
|
}
|
|
124
220
|
|
|
125
|
-
private fun loadManifest(context: Context): SDKManifest? {
|
|
126
|
-
return try {
|
|
127
|
-
val json = context.assets.open("sdk-manifest.json").bufferedReader().readText()
|
|
128
|
-
SDKManifest.fromJson(json)
|
|
129
|
-
} catch (e: Exception) { null }
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
private fun sha256(file: File): String {
|
|
133
|
-
val digest = MessageDigest.getInstance("SHA-256")
|
|
134
|
-
file.inputStream().use { input ->
|
|
135
|
-
val buffer = ByteArray(8192)
|
|
136
|
-
var bytesRead: Int
|
|
137
|
-
while (input.read(buffer).also { bytesRead = it } != -1) {
|
|
138
|
-
digest.update(buffer, 0, bytesRead)
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return digest.digest().joinToString("") { "%02x".format(it) }
|
|
142
|
-
}
|
|
143
|
-
|
|
144
221
|
private fun unzip(source: File, destination: File) {
|
|
222
|
+
Log.d(TAG, "Extracting ${source.name} to ${destination.absolutePath}")
|
|
223
|
+
|
|
145
224
|
ZipInputStream(source.inputStream()).use { zip ->
|
|
146
225
|
var entry = zip.nextEntry
|
|
147
226
|
while (entry != null) {
|
|
148
227
|
val file = File(destination, entry.name)
|
|
228
|
+
Log.d(TAG, " Extracting: ${entry.name}")
|
|
229
|
+
|
|
149
230
|
if (entry.isDirectory) {
|
|
150
231
|
file.mkdirs()
|
|
151
232
|
} else {
|
|
152
233
|
file.parentFile?.mkdirs()
|
|
153
|
-
file.outputStream().use {
|
|
234
|
+
file.outputStream().use { output ->
|
|
235
|
+
zip.copyTo(output)
|
|
236
|
+
}
|
|
154
237
|
}
|
|
238
|
+
zip.closeEntry()
|
|
155
239
|
entry = zip.nextEntry
|
|
156
240
|
}
|
|
157
241
|
}
|
|
158
242
|
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
data class SDKManifest(
|
|
162
|
-
val version: String,
|
|
163
|
-
val android: AndroidManifest
|
|
164
|
-
) {
|
|
165
|
-
data class AndroidManifest(
|
|
166
|
-
val playFeatureModule: String,
|
|
167
|
-
val directDownload: DirectDownload
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
data class DirectDownload(
|
|
171
|
-
val downloadUrl: String,
|
|
172
|
-
val sha256: String,
|
|
173
|
-
val sizeBytes: Long
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
companion object {
|
|
177
|
-
fun fromJson(json: String): SDKManifest {
|
|
178
|
-
val root = JSONObject(json)
|
|
179
|
-
val android = root.getJSONObject("android")
|
|
180
|
-
val direct = android.getJSONObject("directDownload")
|
|
181
|
-
|
|
182
|
-
return SDKManifest(
|
|
183
|
-
version = root.getString("version"),
|
|
184
|
-
android = AndroidManifest(
|
|
185
|
-
playFeatureModule = android.getString("playFeatureModule"),
|
|
186
|
-
directDownload = DirectDownload(
|
|
187
|
-
downloadUrl = direct.getString("downloadUrl"),
|
|
188
|
-
sha256 = direct.getString("sha256"),
|
|
189
|
-
sizeBytes = direct.getLong("sizeBytes")
|
|
190
|
-
)
|
|
191
|
-
)
|
|
192
|
-
)
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
243
|
+
}
|