ilabs-flir 1.0.3 → 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/android/Flir/libs/flir-stubs.jar +0 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/ErrorCode.java +13 -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/live/Camera.java +26 -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/RemoteControl.java +16 -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/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 +40 -15
- package/android/Flir/src/main/java/flir/android/FlirDownloadManager.kt +6 -5
- package/android/Flir/src/main/java/flir/android/FlirManager.kt +265 -42
- package/android/Flir/src/main/java/flir/android/FlirSDKLoader.kt +242 -194
- package/android/Flir/src/main/java/flir/android/FlirSdkManager.java +1376 -755
- package/package.json +1 -1
- package/sdk-manifest.json +14 -7
- package/android/Flir/src/main/java/flir/android/CameraHandler.java +0 -224
- package/android/Flir/src/main/java/flir/android/FlirConnectionManager.java +0 -354
- package/android/Flir/src/main/java/flir/android/FlirController.kt +0 -11
- package/android/Flir/src/main/java/flir/android/FlirDiscoveryManager.java +0 -236
- package/android/Flir/src/main/java/flir/android/FrameDataHolder.java +0 -14
|
@@ -1,195 +1,243 @@
|
|
|
1
|
-
package flir.android
|
|
2
|
-
|
|
3
|
-
import android.content.Context
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import kotlinx.coroutines.*
|
|
7
|
-
import org.json.JSONObject
|
|
8
|
-
import java.io.File
|
|
9
|
-
import java.io.FileOutputStream
|
|
10
|
-
import java.net.
|
|
11
|
-
import java.
|
|
12
|
-
import java.util.zip.ZipInputStream
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
val
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
1
|
+
package flir.android
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.os.Build
|
|
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
|
+
|
|
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
|
+
*/
|
|
23
|
+
object FlirSDKLoader {
|
|
24
|
+
|
|
25
|
+
private const val TAG = "FlirSDKLoader"
|
|
26
|
+
|
|
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
|
+
/**
|
|
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"
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Check if SDK is already downloaded
|
|
86
|
+
*/
|
|
87
|
+
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
|
|
99
|
+
}
|
|
100
|
+
|
|
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(
|
|
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
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Delete downloaded SDK
|
|
216
|
+
*/
|
|
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
|
+
}
|
|
242
|
+
}
|
|
195
243
|
}
|