ilabs-flir 1.0.4 → 1.0.6

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 (38) hide show
  1. package/android/Flir/build.gradle.kts +14 -27
  2. package/android/Flir/libs/androidsdk-release.aar +0 -0
  3. package/android/Flir/libs/thermalsdk-release.aar +0 -0
  4. package/android/Flir/src/main/AndroidManifest.xml +14 -0
  5. package/android/Flir/src/main/java/flir/android/FlirDownloadManager.kt +23 -44
  6. package/android/Flir/src/main/java/flir/android/FlirManager.kt +312 -313
  7. package/android/Flir/src/main/java/flir/android/FlirModule.kt +183 -0
  8. package/android/Flir/src/main/java/flir/android/FlirSDKLoader.kt +35 -194
  9. package/android/Flir/src/main/java/flir/android/FlirSdkManager.java +409 -1339
  10. package/package.json +1 -1
  11. package/src/index.d.ts +60 -1
  12. package/android/Flir/libs/flir-stubs.jar +0 -0
  13. package/android/Flir/src/main/java/com/flir/thermalsdk/ErrorCode.java +0 -13
  14. package/android/Flir/src/main/java/com/flir/thermalsdk/ErrorCodeException.java +0 -14
  15. package/android/Flir/src/main/java/com/flir/thermalsdk/ThermalSdkAndroid.java +0 -16
  16. package/android/Flir/src/main/java/com/flir/thermalsdk/androidsdk/image/BitmapAndroid.java +0 -20
  17. package/android/Flir/src/main/java/com/flir/thermalsdk/image/ImageBuffer.java +0 -11
  18. package/android/Flir/src/main/java/com/flir/thermalsdk/image/JavaImageBuffer.java +0 -35
  19. package/android/Flir/src/main/java/com/flir/thermalsdk/image/Palette.java +0 -15
  20. package/android/Flir/src/main/java/com/flir/thermalsdk/image/PaletteManager.java +0 -16
  21. package/android/Flir/src/main/java/com/flir/thermalsdk/image/Point.java +0 -11
  22. package/android/Flir/src/main/java/com/flir/thermalsdk/image/ThermalImage.java +0 -23
  23. package/android/Flir/src/main/java/com/flir/thermalsdk/image/ThermalValue.java +0 -9
  24. package/android/Flir/src/main/java/com/flir/thermalsdk/live/Camera.java +0 -26
  25. package/android/Flir/src/main/java/com/flir/thermalsdk/live/CameraType.java +0 -8
  26. package/android/Flir/src/main/java/com/flir/thermalsdk/live/CommunicationInterface.java +0 -16
  27. package/android/Flir/src/main/java/com/flir/thermalsdk/live/ConnectParameters.java +0 -16
  28. package/android/Flir/src/main/java/com/flir/thermalsdk/live/Identity.java +0 -23
  29. package/android/Flir/src/main/java/com/flir/thermalsdk/live/IpSettings.java +0 -9
  30. package/android/Flir/src/main/java/com/flir/thermalsdk/live/RemoteControl.java +0 -16
  31. package/android/Flir/src/main/java/com/flir/thermalsdk/live/connectivity/ConnectionStatusListener.java +0 -7
  32. package/android/Flir/src/main/java/com/flir/thermalsdk/live/discovery/DiscoveryEventListener.java +0 -14
  33. package/android/Flir/src/main/java/com/flir/thermalsdk/live/discovery/DiscoveryFactory.java +0 -33
  34. package/android/Flir/src/main/java/com/flir/thermalsdk/live/remote/OnReceived.java +0 -5
  35. package/android/Flir/src/main/java/com/flir/thermalsdk/live/remote/OnRemoteError.java +0 -7
  36. package/android/Flir/src/main/java/com/flir/thermalsdk/live/streaming/Stream.java +0 -8
  37. package/android/Flir/src/main/java/com/flir/thermalsdk/live/streaming/ThermalStreamer.java +0 -28
  38. package/android/Flir/src/main/java/com/flir/thermalsdk/live/streaming/VisualStreamer.java +0 -18
@@ -1,12 +1,35 @@
1
1
  package flir.android
2
2
 
3
+ import android.util.Log
3
4
  import com.facebook.react.bridge.Promise
4
5
  import com.facebook.react.bridge.ReactApplicationContext
5
6
  import com.facebook.react.bridge.ReactContextBaseJavaModule
6
7
  import com.facebook.react.bridge.ReactMethod
8
+ import com.facebook.react.modules.core.DeviceEventManagerModule
7
9
 
8
10
  class FlirModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
11
+
12
+ companion object {
13
+ private const val TAG = "FlirModule"
14
+ }
15
+
9
16
  override fun getName(): String = "FlirModule"
17
+
18
+ // Required for RN event emitter support
19
+ private var listenerCount = 0
20
+
21
+ @ReactMethod
22
+ fun addListener(eventName: String) {
23
+ listenerCount++
24
+ Log.d(TAG, "addListener: $eventName (count: $listenerCount)")
25
+ }
26
+
27
+ @ReactMethod
28
+ fun removeListeners(count: Int) {
29
+ listenerCount -= count
30
+ if (listenerCount < 0) listenerCount = 0
31
+ Log.d(TAG, "removeListeners: $count (remaining: $listenerCount)")
32
+ }
10
33
 
11
34
  // Simple placeholder conversion: converts an ARGB color to a pseudo-temperature value.
12
35
  // Replace with SDK call when integrating thermalsdk APIs.
@@ -71,4 +94,164 @@ class FlirModule(private val reactContext: ReactApplicationContext) : ReactConte
71
94
  promise.reject("ERR_FLIR_DEVICE_INFO", e)
72
95
  }
73
96
  }
97
+
98
+ @ReactMethod
99
+ fun isSDKDownloaded(promise: Promise) {
100
+ try {
101
+ val available = FlirSDKLoader.isSDKAvailable(reactContext)
102
+ promise.resolve(available)
103
+ } catch (e: Exception) {
104
+ promise.reject("ERR_FLIR_SDK_CHECK", e)
105
+ }
106
+ }
107
+
108
+ @ReactMethod
109
+ fun getSDKStatus(promise: Promise) {
110
+ try {
111
+ val available = FlirSDKLoader.isSDKAvailable(reactContext)
112
+ val arch = FlirSDKLoader.getDeviceArch()
113
+ val dexPath = FlirSDKLoader.getDexPath(reactContext)
114
+ val nativeLibDir = FlirSDKLoader.getNativeLibDir(reactContext)
115
+
116
+ val result = com.facebook.react.bridge.Arguments.createMap()
117
+ result.putBoolean("available", available)
118
+ result.putString("arch", arch)
119
+ result.putString("dexPath", dexPath?.absolutePath ?: "not downloaded")
120
+ result.putString("nativeLibPath", nativeLibDir?.absolutePath ?: "not downloaded")
121
+ result.putBoolean("dexExists", dexPath?.exists() == true)
122
+ result.putBoolean("nativeLibsExist", nativeLibDir?.exists() == true)
123
+
124
+ promise.resolve(result)
125
+ } catch (e: Exception) {
126
+ promise.reject("ERR_FLIR_SDK_STATUS", e)
127
+ }
128
+ }
129
+
130
+ @ReactMethod
131
+ fun getDiscoveredDevices(promise: Promise) {
132
+ try {
133
+ val devices = FlirManager.getDiscoveredDevices()
134
+ val result = com.facebook.react.bridge.Arguments.createArray()
135
+
136
+ devices.forEach { identity ->
137
+ val deviceMap = com.facebook.react.bridge.Arguments.createMap()
138
+ deviceMap.putString("id", identity.deviceId)
139
+ deviceMap.putString("name", identity.deviceId)
140
+ deviceMap.putString("communicationType", identity.communicationInterface.name)
141
+ deviceMap.putBoolean("isEmulator", identity.communicationInterface.name == "EMULATOR")
142
+ result.pushMap(deviceMap)
143
+ }
144
+
145
+ promise.resolve(result)
146
+ } catch (e: Exception) {
147
+ promise.reject("ERR_FLIR_DEVICES", e)
148
+ }
149
+ }
150
+
151
+ @ReactMethod
152
+ fun startEmulator(emulatorType: String, promise: Promise) {
153
+ try {
154
+ // Ensure SDK is initialized with context before starting discovery
155
+ FlirManager.init(reactContext)
156
+ // With simplified API, just start discovery - emulators are discovered like any device
157
+ FlirManager.startDiscovery(true)
158
+ promise.resolve(true)
159
+ } catch (e: Exception) {
160
+ promise.reject("ERR_FLIR_EMULATOR", e)
161
+ }
162
+ }
163
+
164
+ @ReactMethod
165
+ fun connectToDevice(deviceId: String, promise: Promise) {
166
+ try {
167
+ // Ensure SDK is initialized with context before connecting
168
+ FlirManager.init(reactContext)
169
+ FlirManager.connectToDevice(deviceId)
170
+ promise.resolve(true)
171
+ } catch (e: Exception) {
172
+ promise.reject("ERR_FLIR_CONNECT", e)
173
+ }
174
+ }
175
+
176
+ @ReactMethod
177
+ fun startDiscovery(promise: Promise) {
178
+ try {
179
+ // Ensure SDK is initialized with context before starting discovery
180
+ FlirManager.init(reactContext)
181
+ FlirManager.startDiscovery(true)
182
+ promise.resolve(true)
183
+ } catch (e: Exception) {
184
+ promise.reject("ERR_FLIR_DISCOVERY", e)
185
+ }
186
+ }
187
+
188
+ @ReactMethod
189
+ fun stopDiscovery(promise: Promise) {
190
+ try {
191
+ FlirManager.stopDiscovery()
192
+ promise.resolve(true)
193
+ } catch (e: Exception) {
194
+ promise.reject("ERR_FLIR_STOP_DISCOVERY", e)
195
+ }
196
+ }
197
+
198
+ @ReactMethod
199
+ fun stopFlir(promise: Promise) {
200
+ try {
201
+ FlirManager.stop()
202
+ promise.resolve(true)
203
+ } catch (e: Exception) {
204
+ promise.reject("ERR_FLIR_STOP", e)
205
+ }
206
+ }
207
+
208
+ @ReactMethod
209
+ fun initializeSDK(promise: Promise) {
210
+ try {
211
+ FlirManager.init(reactContext)
212
+
213
+ val result = com.facebook.react.bridge.Arguments.createMap()
214
+ result.putBoolean("initialized", true)
215
+ result.putString("message", "SDK initialized successfully")
216
+ promise.resolve(result)
217
+ } catch (e: Exception) {
218
+ val result = com.facebook.react.bridge.Arguments.createMap()
219
+ result.putBoolean("initialized", false)
220
+ result.putString("error", e.message ?: "Unknown error")
221
+ result.putString("errorType", e.javaClass.simpleName)
222
+ promise.resolve(result)
223
+ }
224
+ }
225
+
226
+ @ReactMethod
227
+ fun getDebugInfo(promise: Promise) {
228
+ try {
229
+ val result = com.facebook.react.bridge.Arguments.createMap()
230
+
231
+ // SDK availability
232
+ result.putBoolean("sdkAvailable", FlirSDKLoader.isSDKAvailable(reactContext))
233
+ result.putString("arch", FlirSDKLoader.getDeviceArch())
234
+
235
+ // Check if FLIR SDK classes are loadable
236
+ val classesLoaded = try {
237
+ Class.forName("com.flir.thermalsdk.androidsdk.ThermalSdkAndroid")
238
+ Class.forName("com.flir.thermalsdk.live.discovery.DiscoveryFactory")
239
+ true
240
+ } catch (e: ClassNotFoundException) {
241
+ false
242
+ }
243
+ result.putBoolean("sdkClassesLoaded", classesLoaded)
244
+
245
+ // Discovery state
246
+ val devices = FlirManager.getDiscoveredDevices()
247
+ result.putInt("discoveredDeviceCount", devices.size)
248
+ result.putBoolean("isConnected", FlirManager.isConnected())
249
+ result.putBoolean("isStreaming", FlirManager.isStreaming())
250
+ result.putString("connectedDevice", FlirManager.getConnectedDeviceInfo())
251
+
252
+ promise.resolve(result)
253
+ } catch (e: Exception) {
254
+ promise.reject("ERR_DEBUG_INFO", e)
255
+ }
256
+ }
74
257
  }
@@ -3,67 +3,18 @@ package flir.android
3
3
  import android.content.Context
4
4
  import android.os.Build
5
5
  import android.util.Log
6
- import kotlinx.coroutines.*
7
- import org.json.JSONObject
8
- import java.io.File
9
- import java.io.FileOutputStream
10
- import java.net.HttpURLConnection
11
- import java.net.URL
12
- import java.util.zip.ZipInputStream
13
6
 
14
7
  /**
15
- * FlirSDKLoader - Downloads and manages architecture-specific FLIR SDK packages
8
+ * FlirSDKLoader - SDK availability checker
16
9
  *
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
10
+ * Since the SDK is now bundled via AAR files, this class simply reports
11
+ * that the SDK is always available. The AAR files include native .so libraries
12
+ * for all supported architectures, which Android handles automatically.
22
13
  */
23
14
  object FlirSDKLoader {
24
15
 
25
16
  private const val TAG = "FlirSDKLoader"
26
17
 
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>)
33
-
34
- private fun getSDKDirectory(context: Context) = File(context.filesDir, "FlirSDK")
35
-
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
64
- }
65
- }
66
-
67
18
  /**
68
19
  * Get the primary ABI for this device
69
20
  */
@@ -71,7 +22,7 @@ object FlirSDKLoader {
71
22
  val supportedAbis = Build.SUPPORTED_ABIS
72
23
  Log.d(TAG, "Device supported ABIs: ${supportedAbis.joinToString()}")
73
24
 
74
- val knownArchs = setOf("arm64-v8a", "armeabi-v7a", "x86_64")
25
+ val knownArchs = setOf("arm64-v8a", "armeabi-v7a", "x86_64", "x86")
75
26
  for (abi in supportedAbis) {
76
27
  if (abi in knownArchs) {
77
28
  Log.d(TAG, "Selected ABI: $abi")
@@ -82,162 +33,52 @@ object FlirSDKLoader {
82
33
  }
83
34
 
84
35
  /**
85
- * Check if SDK is already downloaded
36
+ * Check if SDK is available - always true since bundled in AAR
86
37
  */
87
38
  fun isSDKAvailable(context: Context): Boolean {
88
- val sdkDir = getSDKDirectory(context)
89
- val arch = getDeviceArch()
90
-
91
- val dexFile = File(sdkDir, "classes.dex")
92
- val soDir = File(sdkDir, "jni/$arch")
93
-
94
- val hasDex = dexFile.exists() && dexFile.length() > 0
95
- val hasSo = soDir.exists() && soDir.listFiles()?.isNotEmpty() == true
96
-
97
- Log.d(TAG, "SDK available: dex=$hasDex, so=$hasSo")
98
- return hasDex && hasSo
39
+ Log.d(TAG, "SDK is bundled in AAR - always available")
40
+ return true
99
41
  }
100
42
 
101
43
  /**
102
- * Get path to the DEX file
44
+ * No download needed - SDK is bundled
103
45
  */
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
46
+ suspend fun downloadSDK(
47
+ context: Context,
48
+ onProgress: ((progress: Int, bytesDownloaded: Long, totalBytes: Long) -> Unit)? = null
49
+ ): Boolean {
50
+ Log.d(TAG, "SDK is bundled - no download needed")
51
+ onProgress?.invoke(100, 0, 0)
52
+ return true
116
53
  }
117
54
 
118
55
  /**
119
- * Get estimated download size from manifest
56
+ * Get SDK status
120
57
  */
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
58
+ fun getSDKStatus(context: Context): Map<String, Any> {
59
+ return mapOf(
60
+ "available" to true,
61
+ "bundled" to true,
62
+ "arch" to getDeviceArch(),
63
+ "version" to "4.16.0" // SDK version from AAR
64
+ )
125
65
  }
126
66
 
127
67
  /**
128
- * Download the SDK package for this device's architecture
68
+ * Get DEX path - not applicable when bundled via AAR
69
+ * Returns null since SDK is bundled in AAR, no separate DEX needed
129
70
  */
130
- suspend fun downloadSDK(
131
- context: Context,
132
- onProgress: (downloaded: Long, total: Long) -> Unit
133
- ): Result<Unit> = withContext(Dispatchers.IO) {
134
- try {
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"))
141
-
142
- val downloadUrl = archPackage.downloadUrl
143
- Log.i(TAG, "Downloading SDK for $arch from $downloadUrl")
144
-
145
- val sdkDir = getSDKDirectory(context).apply { mkdirs() }
146
- val zipFile = File(context.cacheDir, "flir-sdk-$arch.zip")
147
-
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 ->
174
- FileOutputStream(zipFile).use { output ->
175
- val buffer = ByteArray(8192)
176
- var totalRead = 0L
177
- var bytesRead: Int
178
-
179
- while (input.read(buffer).also { bytesRead = it } != -1) {
180
- output.write(buffer, 0, bytesRead)
181
- totalRead += bytesRead
182
- withContext(Dispatchers.Main) {
183
- onProgress(totalRead, totalSize)
184
- }
185
- }
186
- }
187
- }
188
- connection.disconnect()
189
-
190
- Log.i(TAG, "Download complete: ${zipFile.length()} bytes")
191
-
192
- // Extract
193
- unzip(zipFile, sdkDir)
194
- zipFile.delete()
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
-
207
- Result.success(Unit)
208
- } catch (e: Exception) {
209
- Log.e(TAG, "Download failed", e)
210
- Result.failure(e)
211
- }
71
+ fun getDexPath(context: Context): java.io.File? {
72
+ // SDK is bundled in AAR - no separate DEX file
73
+ return null
212
74
  }
213
75
 
214
76
  /**
215
- * Delete downloaded SDK
77
+ * Get native library directory - not applicable when bundled via AAR
78
+ * Returns null since native libs are included in AAR and handled by Android
216
79
  */
217
- fun deleteSDK(context: Context): Boolean {
218
- return getSDKDirectory(context).deleteRecursively()
219
- }
220
-
221
- private fun unzip(source: File, destination: File) {
222
- Log.d(TAG, "Extracting ${source.name} to ${destination.absolutePath}")
223
-
224
- ZipInputStream(source.inputStream()).use { zip ->
225
- var entry = zip.nextEntry
226
- while (entry != null) {
227
- val file = File(destination, entry.name)
228
- Log.d(TAG, " Extracting: ${entry.name}")
229
-
230
- if (entry.isDirectory) {
231
- file.mkdirs()
232
- } else {
233
- file.parentFile?.mkdirs()
234
- file.outputStream().use { output ->
235
- zip.copyTo(output)
236
- }
237
- }
238
- zip.closeEntry()
239
- entry = zip.nextEntry
240
- }
241
- }
80
+ fun getNativeLibDir(context: Context): java.io.File? {
81
+ // SDK native libs are bundled in AAR and extracted automatically by Android
82
+ return null
242
83
  }
243
- }
84
+ }