ilabs-flir 2.0.4 → 2.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.
- package/Flir.podspec +139 -139
- package/README.md +1066 -1066
- package/android/Flir/build.gradle.kts +72 -72
- package/android/Flir/src/main/AndroidManifest.xml +45 -45
- package/android/Flir/src/main/java/flir/android/FlirCommands.java +136 -136
- package/android/Flir/src/main/java/flir/android/FlirFrameCache.kt +6 -6
- package/android/Flir/src/main/java/flir/android/FlirManager.kt +476 -476
- package/android/Flir/src/main/java/flir/android/FlirModule.kt +257 -257
- package/android/Flir/src/main/java/flir/android/FlirPackage.kt +18 -18
- package/android/Flir/src/main/java/flir/android/FlirSDKLoader.kt +74 -74
- package/android/Flir/src/main/java/flir/android/FlirSdkManager.java +583 -583
- 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 +381 -381
- package/expo-module.config.json +5 -5
- package/ios/Flir/src/Flir-Bridging-Header.h +34 -34
- package/ios/Flir/src/FlirEventEmitter.h +25 -25
- package/ios/Flir/src/FlirEventEmitter.m +63 -63
- package/ios/Flir/src/FlirManager.swift +599 -599
- package/ios/Flir/src/FlirModule.h +17 -17
- package/ios/Flir/src/FlirModule.m +713 -713
- package/ios/Flir/src/FlirPreviewView.h +13 -13
- package/ios/Flir/src/FlirPreviewView.m +171 -171
- package/ios/Flir/src/FlirState.h +68 -68
- package/ios/Flir/src/FlirState.m +135 -135
- package/ios/Flir/src/FlirViewManager.h +16 -16
- package/ios/Flir/src/FlirViewManager.m +27 -27
- package/package.json +70 -71
- package/react-native.config.js +14 -14
- package/scripts/fetch-binaries.js +103 -17
- package/sdk-manifest.json +50 -50
- package/src/index.d.ts +63 -63
- package/src/index.js +7 -7
- package/src/index.ts +6 -6
|
@@ -1,476 +1,476 @@
|
|
|
1
|
-
package flir.android
|
|
2
|
-
|
|
3
|
-
import android.content.Context
|
|
4
|
-
import android.graphics.Bitmap
|
|
5
|
-
import android.util.Log
|
|
6
|
-
import com.facebook.react.bridge.Arguments
|
|
7
|
-
import com.facebook.react.bridge.ReactApplicationContext
|
|
8
|
-
import com.facebook.react.bridge.ReactContext
|
|
9
|
-
import com.facebook.react.bridge.WritableArray
|
|
10
|
-
import com.facebook.react.bridge.WritableMap
|
|
11
|
-
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
12
|
-
import com.facebook.react.uimanager.ThemedReactContext
|
|
13
|
-
import com.flir.thermalsdk.live.Identity
|
|
14
|
-
import java.io.File
|
|
15
|
-
import java.io.FileOutputStream
|
|
16
|
-
import java.util.concurrent.atomic.AtomicLong
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Simplified FlirManager - bridge between React Native and FlirSdkManager
|
|
20
|
-
* No filtering - returns ALL discovered devices (USB, Network, Emulator)
|
|
21
|
-
* Let React Native handle any filtering logic
|
|
22
|
-
*/
|
|
23
|
-
object FlirManager {
|
|
24
|
-
private const val TAG = "FlirManager"
|
|
25
|
-
|
|
26
|
-
private var sdkManager: FlirSdkManager? = null
|
|
27
|
-
private var reactContext: ReactContext? = null
|
|
28
|
-
private var appContext: Context? = null
|
|
29
|
-
|
|
30
|
-
// Frame rate limiting
|
|
31
|
-
private val lastEmitMs = AtomicLong(0)
|
|
32
|
-
private val minEmitIntervalMs = 100L // ~10 fps max for RN events
|
|
33
|
-
|
|
34
|
-
// State
|
|
35
|
-
private var isInitialized = false
|
|
36
|
-
private var isScanning = false
|
|
37
|
-
private var isConnected = false
|
|
38
|
-
private var isStreaming = false
|
|
39
|
-
private var connectedDeviceId: String? = null
|
|
40
|
-
private var connectedDeviceName: String? = null
|
|
41
|
-
|
|
42
|
-
// Latest bitmap for texture updates
|
|
43
|
-
private var latestBitmap: Bitmap? = null
|
|
44
|
-
|
|
45
|
-
// Callbacks
|
|
46
|
-
interface TextureUpdateCallback {
|
|
47
|
-
fun onTextureUpdate(bitmap: Bitmap, textureUnit: Int)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
interface TemperatureCallback {
|
|
51
|
-
fun onTemperatureData(temperature: Double, x: Int, y: Int)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
private var textureCallback: TextureUpdateCallback? = null
|
|
55
|
-
private var temperatureCallback: TemperatureCallback? = null
|
|
56
|
-
|
|
57
|
-
fun setTextureCallback(callback: TextureUpdateCallback?) {
|
|
58
|
-
textureCallback = callback
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
fun setTemperatureCallback(callback: TemperatureCallback?) {
|
|
62
|
-
temperatureCallback = callback
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
fun getLatestBitmap(): Bitmap? = latestBitmap
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Initialize the FLIR SDK
|
|
69
|
-
*/
|
|
70
|
-
fun init(context: Context) {
|
|
71
|
-
// Store react context for event emission if it's a React context
|
|
72
|
-
// Always update if we get a valid ReactContext (in case previous was stale)
|
|
73
|
-
if (context is ReactContext) {
|
|
74
|
-
Log.d(TAG, "Storing ReactContext for event emission: ${context.javaClass.simpleName}")
|
|
75
|
-
reactContext = context
|
|
76
|
-
} else {
|
|
77
|
-
Log.d(TAG, "Context is not ReactContext: ${context.javaClass.simpleName}")
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (isInitialized) {
|
|
81
|
-
Log.d(TAG, "Already initialized")
|
|
82
|
-
return
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
appContext = context.applicationContext
|
|
86
|
-
|
|
87
|
-
sdkManager = FlirSdkManager.getInstance(context)
|
|
88
|
-
sdkManager?.setListener(sdkListener)
|
|
89
|
-
sdkManager?.initialize()
|
|
90
|
-
|
|
91
|
-
isInitialized = true
|
|
92
|
-
Log.i(TAG, "FlirManager initialized")
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Start scanning for devices (USB, Network, Emulator - ALL types)
|
|
97
|
-
*/
|
|
98
|
-
fun startDiscovery(retry: Boolean = false) {
|
|
99
|
-
Log.i(TAG, "startDiscovery(retry=$retry)")
|
|
100
|
-
|
|
101
|
-
if (!isInitialized && appContext != null) {
|
|
102
|
-
init(appContext!!)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (isScanning && !retry) {
|
|
106
|
-
Log.d(TAG, "Already scanning")
|
|
107
|
-
return
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
isScanning = true
|
|
111
|
-
emitDeviceState("discovering")
|
|
112
|
-
sdkManager?.scan()
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Start discovery with React context
|
|
117
|
-
*/
|
|
118
|
-
fun startDiscoveryAndConnect(context: ThemedReactContext, isEmuMode: Boolean = false) {
|
|
119
|
-
reactContext = context
|
|
120
|
-
startDiscovery(retry = false)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Stop scanning
|
|
125
|
-
*/
|
|
126
|
-
fun stopDiscovery() {
|
|
127
|
-
Log.i(TAG, "stopDiscovery")
|
|
128
|
-
sdkManager?.stop()
|
|
129
|
-
isScanning = false
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Connect to a device by ID
|
|
134
|
-
*/
|
|
135
|
-
fun connectToDevice(deviceId: String) {
|
|
136
|
-
Log.i(TAG, "connectToDevice: $deviceId")
|
|
137
|
-
|
|
138
|
-
val devices = sdkManager?.discoveredDevices ?: emptyList()
|
|
139
|
-
val identity = devices.find { it.deviceId == deviceId }
|
|
140
|
-
|
|
141
|
-
if (identity != null) {
|
|
142
|
-
sdkManager?.connect(identity)
|
|
143
|
-
} else {
|
|
144
|
-
Log.e(TAG, "Device not found: $deviceId")
|
|
145
|
-
emitError("Device not found: $deviceId")
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Switch to a different device
|
|
151
|
-
*/
|
|
152
|
-
fun switchToDevice(deviceId: String) {
|
|
153
|
-
if (deviceId == connectedDeviceId) {
|
|
154
|
-
Log.d(TAG, "Already connected to: $deviceId")
|
|
155
|
-
return
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Disconnect current and connect new
|
|
159
|
-
if (isConnected) {
|
|
160
|
-
sdkManager?.disconnect()
|
|
161
|
-
}
|
|
162
|
-
connectToDevice(deviceId)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Start streaming from connected device
|
|
167
|
-
*/
|
|
168
|
-
fun startStream() {
|
|
169
|
-
Log.i(TAG, "startStream")
|
|
170
|
-
sdkManager?.startStream()
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Stop streaming
|
|
175
|
-
*/
|
|
176
|
-
fun stopStream() {
|
|
177
|
-
Log.i(TAG, "stopStream")
|
|
178
|
-
sdkManager?.stopStream()
|
|
179
|
-
isStreaming = false
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Disconnect from current device
|
|
184
|
-
*/
|
|
185
|
-
fun disconnect() {
|
|
186
|
-
Log.i(TAG, "disconnect")
|
|
187
|
-
sdkManager?.disconnect()
|
|
188
|
-
isConnected = false
|
|
189
|
-
isStreaming = false
|
|
190
|
-
connectedDeviceId = null
|
|
191
|
-
connectedDeviceName = null
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Stop everything
|
|
196
|
-
*/
|
|
197
|
-
fun stop() {
|
|
198
|
-
Log.i(TAG, "stop")
|
|
199
|
-
stopStream()
|
|
200
|
-
disconnect()
|
|
201
|
-
stopDiscovery()
|
|
202
|
-
latestBitmap = null
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Get temperature at point in image coordinates
|
|
207
|
-
*/
|
|
208
|
-
fun getTemperatureAt(x: Int, y: Int): Double? {
|
|
209
|
-
return sdkManager?.getTemperatureAt(x, y)?.takeIf { !it.isNaN() }
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Get temperature at normalized coordinates (0.0 to 1.0)
|
|
214
|
-
*/
|
|
215
|
-
fun getTemperatureAtNormalized(normalizedX: Double, normalizedY: Double): Double? {
|
|
216
|
-
return sdkManager?.getTemperatureAtNormalized(normalizedX, normalizedY)?.takeIf { !it.isNaN() }
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Alias for getTemperatureAt
|
|
221
|
-
*/
|
|
222
|
-
fun getTemperatureAtPoint(x: Int, y: Int): Double? = getTemperatureAt(x, y)
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Set palette
|
|
226
|
-
*/
|
|
227
|
-
fun setPalette(name: String) {
|
|
228
|
-
Log.d(TAG, "setPalette: $name")
|
|
229
|
-
sdkManager?.setPalette(name)
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Get available palettes
|
|
234
|
-
*/
|
|
235
|
-
fun getAvailablePalettes(): List<String> {
|
|
236
|
-
return sdkManager?.availablePalettes ?: emptyList()
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Get list of discovered devices
|
|
241
|
-
*/
|
|
242
|
-
fun getDiscoveredDevices(): List<Identity> {
|
|
243
|
-
return sdkManager?.discoveredDevices ?: emptyList()
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Check states
|
|
248
|
-
*/
|
|
249
|
-
fun isConnected(): Boolean = isConnected
|
|
250
|
-
fun isStreaming(): Boolean = isStreaming
|
|
251
|
-
fun isEmulator(): Boolean = connectedDeviceName?.contains("EMULAT", ignoreCase = true) == true
|
|
252
|
-
fun isDeviceConnected(): Boolean = isConnected
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Get connected device info
|
|
256
|
-
*/
|
|
257
|
-
fun getConnectedDeviceInfo(): String {
|
|
258
|
-
return connectedDeviceName ?: "Not connected"
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Get latest frame as file path (for RN)
|
|
263
|
-
*/
|
|
264
|
-
fun getLatestFramePath(): String? {
|
|
265
|
-
val bitmap = latestBitmap ?: return null
|
|
266
|
-
return try {
|
|
267
|
-
val file = File.createTempFile("flir_frame_", ".jpg")
|
|
268
|
-
FileOutputStream(file).use { out ->
|
|
269
|
-
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out)
|
|
270
|
-
}
|
|
271
|
-
file.absolutePath
|
|
272
|
-
} catch (t: Throwable) {
|
|
273
|
-
Log.e(TAG, "Failed to save frame", t)
|
|
274
|
-
null
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// SDK Listener
|
|
279
|
-
private val sdkListener = object : FlirSdkManager.Listener {
|
|
280
|
-
override fun onDeviceFound(identity: Identity) {
|
|
281
|
-
Log.i(TAG, "Device found: ${identity.deviceId}")
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
override fun onDeviceListUpdated(devices: List<Identity>) {
|
|
285
|
-
Log.i(TAG, "Devices updated: ${devices.size} found")
|
|
286
|
-
devices.forEach {
|
|
287
|
-
Log.d(TAG, " - ${it.deviceId} (${it.communicationInterface})")
|
|
288
|
-
}
|
|
289
|
-
emitDevicesFound(devices)
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
override fun onConnected(identity: Identity?) {
|
|
293
|
-
Log.i(TAG, "Connected to: ${identity?.deviceId}")
|
|
294
|
-
isConnected = true
|
|
295
|
-
connectedDeviceId = identity?.deviceId
|
|
296
|
-
connectedDeviceName = identity?.deviceId
|
|
297
|
-
emitDeviceState("connected")
|
|
298
|
-
|
|
299
|
-
// Auto-start streaming when connected
|
|
300
|
-
startStream()
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
override fun onDisconnected() {
|
|
304
|
-
Log.i(TAG, "Disconnected")
|
|
305
|
-
isConnected = false
|
|
306
|
-
isStreaming = false
|
|
307
|
-
connectedDeviceId = null
|
|
308
|
-
connectedDeviceName = null
|
|
309
|
-
emitDeviceState("disconnected")
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
override fun onFrame(bitmap: Bitmap) {
|
|
313
|
-
if (bitmap.isRecycled || bitmap.width <= 0 || bitmap.height <= 0) {
|
|
314
|
-
return
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
latestBitmap = bitmap
|
|
318
|
-
isStreaming = true
|
|
319
|
-
|
|
320
|
-
// Notify texture callback (for GL rendering)
|
|
321
|
-
if (textureCallback != null) {
|
|
322
|
-
textureCallback?.onTextureUpdate(bitmap, 0)
|
|
323
|
-
} else {
|
|
324
|
-
// Log only occasionally to avoid spam
|
|
325
|
-
if (System.currentTimeMillis() % 5000 < 100) {
|
|
326
|
-
Log.w(TAG, "⚠️ Frame received but textureCallback is null - texture won't update!")
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// Rate-limited RN event
|
|
331
|
-
emitFrameToReactNative(bitmap)
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
override fun onError(message: String) {
|
|
335
|
-
Log.e(TAG, "Error: $message")
|
|
336
|
-
emitError(message)
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// React Native event emitters
|
|
341
|
-
private fun emitFrameToReactNative(bitmap: Bitmap) {
|
|
342
|
-
val now = System.currentTimeMillis()
|
|
343
|
-
if (now - lastEmitMs.get() < minEmitIntervalMs) return
|
|
344
|
-
lastEmitMs.set(now)
|
|
345
|
-
|
|
346
|
-
val ctx = reactContext ?: return
|
|
347
|
-
try {
|
|
348
|
-
val params = Arguments.createMap().apply {
|
|
349
|
-
putInt("width", bitmap.width)
|
|
350
|
-
putInt("height", bitmap.height)
|
|
351
|
-
putDouble("timestamp", now.toDouble())
|
|
352
|
-
}
|
|
353
|
-
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
354
|
-
.emit("FlirFrameReceived", params)
|
|
355
|
-
} catch (e: Exception) {
|
|
356
|
-
// Ignore
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
private fun emitDeviceState(state: String) {
|
|
361
|
-
val ctx = reactContext
|
|
362
|
-
if (ctx == null) {
|
|
363
|
-
Log.e(TAG, "Cannot emit FlirDeviceConnected($state) - reactContext is null!")
|
|
364
|
-
return
|
|
365
|
-
}
|
|
366
|
-
Log.d(TAG, "Emitting FlirDeviceConnected: $state")
|
|
367
|
-
try {
|
|
368
|
-
val params = Arguments.createMap().apply {
|
|
369
|
-
putString("state", state)
|
|
370
|
-
putBoolean("isConnected", isConnected)
|
|
371
|
-
putBoolean("isStreaming", isStreaming)
|
|
372
|
-
putBoolean("isEmulator", isEmulator())
|
|
373
|
-
connectedDeviceName?.let { putString("deviceName", it) }
|
|
374
|
-
connectedDeviceId?.let { putString("deviceId", it) }
|
|
375
|
-
}
|
|
376
|
-
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
377
|
-
.emit("FlirDeviceConnected", params)
|
|
378
|
-
} catch (e: Exception) {
|
|
379
|
-
Log.e(TAG, "Failed to emit device state", e)
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
private fun emitDevicesFound(devices: List<Identity>) {
|
|
384
|
-
val ctx = reactContext
|
|
385
|
-
if (ctx == null) {
|
|
386
|
-
Log.e(TAG, "Cannot emit FlirDevicesFound - reactContext is null!")
|
|
387
|
-
return
|
|
388
|
-
}
|
|
389
|
-
Log.d(TAG, "Emitting FlirDevicesFound with ${devices.size} devices")
|
|
390
|
-
try {
|
|
391
|
-
val params = Arguments.createMap()
|
|
392
|
-
val devicesArray: WritableArray = Arguments.createArray()
|
|
393
|
-
|
|
394
|
-
devices.forEach { identity ->
|
|
395
|
-
val deviceMap: WritableMap = Arguments.createMap().apply {
|
|
396
|
-
putString("id", identity.deviceId)
|
|
397
|
-
putString("name", identity.deviceId)
|
|
398
|
-
putString("communicationType", identity.communicationInterface.name)
|
|
399
|
-
putBoolean("isEmulator", identity.communicationInterface.name == "EMULATOR")
|
|
400
|
-
}
|
|
401
|
-
devicesArray.pushMap(deviceMap)
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
params.putArray("devices", devicesArray)
|
|
405
|
-
params.putInt("count", devices.size)
|
|
406
|
-
|
|
407
|
-
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
408
|
-
.emit("FlirDevicesFound", params)
|
|
409
|
-
Log.d(TAG, "Successfully emitted FlirDevicesFound")
|
|
410
|
-
} catch (e: Exception) {
|
|
411
|
-
Log.e(TAG, "Failed to emit devices found", e)
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
private fun emitError(message: String) {
|
|
416
|
-
val ctx = reactContext ?: return
|
|
417
|
-
try {
|
|
418
|
-
val params = Arguments.createMap().apply {
|
|
419
|
-
putString("error", message)
|
|
420
|
-
}
|
|
421
|
-
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
422
|
-
.emit("FlirError", params)
|
|
423
|
-
} catch (e: Exception) {
|
|
424
|
-
Log.e(TAG, "Failed to emit error", e)
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// Legacy compatibility
|
|
429
|
-
@JvmStatic
|
|
430
|
-
fun getInstance(): FlirManager = this
|
|
431
|
-
|
|
432
|
-
interface DiscoveryCallback {
|
|
433
|
-
fun onDeviceFound(deviceName: String)
|
|
434
|
-
fun onDiscoveryTimeout()
|
|
435
|
-
fun onEmulatorEnabled()
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
private var discoveryCallback: DiscoveryCallback? = null
|
|
439
|
-
|
|
440
|
-
fun setDiscoveryCallback(callback: DiscoveryCallback?) {
|
|
441
|
-
discoveryCallback = callback
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// Legacy methods - no-ops or simple forwards
|
|
445
|
-
fun setEmulatorMode(enabled: Boolean) {
|
|
446
|
-
Log.d(TAG, "setEmulatorMode($enabled) - legacy, use startDiscovery() instead")
|
|
447
|
-
if (enabled) {
|
|
448
|
-
startDiscovery(retry = true)
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
fun enableEmulatorMode() = setEmulatorMode(true)
|
|
453
|
-
|
|
454
|
-
fun forceEmulatorMode(type: String = "FLIR_ONE_EDGE") {
|
|
455
|
-
Log.d(TAG, "forceEmulatorMode($type) - legacy, use startDiscovery() instead")
|
|
456
|
-
startDiscovery(retry = true)
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
fun setPreferredEmulatorType(type: String) {
|
|
460
|
-
Log.d(TAG, "setPreferredEmulatorType($type) - legacy, no longer used")
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
fun updateAcol(value: Float) {
|
|
464
|
-
// No-op - not used in simplified version
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Cleanup
|
|
469
|
-
*/
|
|
470
|
-
fun destroy() {
|
|
471
|
-
stop()
|
|
472
|
-
sdkManager?.destroy()
|
|
473
|
-
sdkManager = null
|
|
474
|
-
isInitialized = false
|
|
475
|
-
}
|
|
476
|
-
}
|
|
1
|
+
package flir.android
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Bitmap
|
|
5
|
+
import android.util.Log
|
|
6
|
+
import com.facebook.react.bridge.Arguments
|
|
7
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
8
|
+
import com.facebook.react.bridge.ReactContext
|
|
9
|
+
import com.facebook.react.bridge.WritableArray
|
|
10
|
+
import com.facebook.react.bridge.WritableMap
|
|
11
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
12
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
13
|
+
import com.flir.thermalsdk.live.Identity
|
|
14
|
+
import java.io.File
|
|
15
|
+
import java.io.FileOutputStream
|
|
16
|
+
import java.util.concurrent.atomic.AtomicLong
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Simplified FlirManager - bridge between React Native and FlirSdkManager
|
|
20
|
+
* No filtering - returns ALL discovered devices (USB, Network, Emulator)
|
|
21
|
+
* Let React Native handle any filtering logic
|
|
22
|
+
*/
|
|
23
|
+
object FlirManager {
|
|
24
|
+
private const val TAG = "FlirManager"
|
|
25
|
+
|
|
26
|
+
private var sdkManager: FlirSdkManager? = null
|
|
27
|
+
private var reactContext: ReactContext? = null
|
|
28
|
+
private var appContext: Context? = null
|
|
29
|
+
|
|
30
|
+
// Frame rate limiting
|
|
31
|
+
private val lastEmitMs = AtomicLong(0)
|
|
32
|
+
private val minEmitIntervalMs = 100L // ~10 fps max for RN events
|
|
33
|
+
|
|
34
|
+
// State
|
|
35
|
+
private var isInitialized = false
|
|
36
|
+
private var isScanning = false
|
|
37
|
+
private var isConnected = false
|
|
38
|
+
private var isStreaming = false
|
|
39
|
+
private var connectedDeviceId: String? = null
|
|
40
|
+
private var connectedDeviceName: String? = null
|
|
41
|
+
|
|
42
|
+
// Latest bitmap for texture updates
|
|
43
|
+
private var latestBitmap: Bitmap? = null
|
|
44
|
+
|
|
45
|
+
// Callbacks
|
|
46
|
+
interface TextureUpdateCallback {
|
|
47
|
+
fun onTextureUpdate(bitmap: Bitmap, textureUnit: Int)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface TemperatureCallback {
|
|
51
|
+
fun onTemperatureData(temperature: Double, x: Int, y: Int)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private var textureCallback: TextureUpdateCallback? = null
|
|
55
|
+
private var temperatureCallback: TemperatureCallback? = null
|
|
56
|
+
|
|
57
|
+
fun setTextureCallback(callback: TextureUpdateCallback?) {
|
|
58
|
+
textureCallback = callback
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fun setTemperatureCallback(callback: TemperatureCallback?) {
|
|
62
|
+
temperatureCallback = callback
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fun getLatestBitmap(): Bitmap? = latestBitmap
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Initialize the FLIR SDK
|
|
69
|
+
*/
|
|
70
|
+
fun init(context: Context) {
|
|
71
|
+
// Store react context for event emission if it's a React context
|
|
72
|
+
// Always update if we get a valid ReactContext (in case previous was stale)
|
|
73
|
+
if (context is ReactContext) {
|
|
74
|
+
Log.d(TAG, "Storing ReactContext for event emission: ${context.javaClass.simpleName}")
|
|
75
|
+
reactContext = context
|
|
76
|
+
} else {
|
|
77
|
+
Log.d(TAG, "Context is not ReactContext: ${context.javaClass.simpleName}")
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (isInitialized) {
|
|
81
|
+
Log.d(TAG, "Already initialized")
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
appContext = context.applicationContext
|
|
86
|
+
|
|
87
|
+
sdkManager = FlirSdkManager.getInstance(context)
|
|
88
|
+
sdkManager?.setListener(sdkListener)
|
|
89
|
+
sdkManager?.initialize()
|
|
90
|
+
|
|
91
|
+
isInitialized = true
|
|
92
|
+
Log.i(TAG, "FlirManager initialized")
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Start scanning for devices (USB, Network, Emulator - ALL types)
|
|
97
|
+
*/
|
|
98
|
+
fun startDiscovery(retry: Boolean = false) {
|
|
99
|
+
Log.i(TAG, "startDiscovery(retry=$retry)")
|
|
100
|
+
|
|
101
|
+
if (!isInitialized && appContext != null) {
|
|
102
|
+
init(appContext!!)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (isScanning && !retry) {
|
|
106
|
+
Log.d(TAG, "Already scanning")
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
isScanning = true
|
|
111
|
+
emitDeviceState("discovering")
|
|
112
|
+
sdkManager?.scan()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Start discovery with React context
|
|
117
|
+
*/
|
|
118
|
+
fun startDiscoveryAndConnect(context: ThemedReactContext, isEmuMode: Boolean = false) {
|
|
119
|
+
reactContext = context
|
|
120
|
+
startDiscovery(retry = false)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Stop scanning
|
|
125
|
+
*/
|
|
126
|
+
fun stopDiscovery() {
|
|
127
|
+
Log.i(TAG, "stopDiscovery")
|
|
128
|
+
sdkManager?.stop()
|
|
129
|
+
isScanning = false
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Connect to a device by ID
|
|
134
|
+
*/
|
|
135
|
+
fun connectToDevice(deviceId: String) {
|
|
136
|
+
Log.i(TAG, "connectToDevice: $deviceId")
|
|
137
|
+
|
|
138
|
+
val devices = sdkManager?.discoveredDevices ?: emptyList()
|
|
139
|
+
val identity = devices.find { it.deviceId == deviceId }
|
|
140
|
+
|
|
141
|
+
if (identity != null) {
|
|
142
|
+
sdkManager?.connect(identity)
|
|
143
|
+
} else {
|
|
144
|
+
Log.e(TAG, "Device not found: $deviceId")
|
|
145
|
+
emitError("Device not found: $deviceId")
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Switch to a different device
|
|
151
|
+
*/
|
|
152
|
+
fun switchToDevice(deviceId: String) {
|
|
153
|
+
if (deviceId == connectedDeviceId) {
|
|
154
|
+
Log.d(TAG, "Already connected to: $deviceId")
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Disconnect current and connect new
|
|
159
|
+
if (isConnected) {
|
|
160
|
+
sdkManager?.disconnect()
|
|
161
|
+
}
|
|
162
|
+
connectToDevice(deviceId)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Start streaming from connected device
|
|
167
|
+
*/
|
|
168
|
+
fun startStream() {
|
|
169
|
+
Log.i(TAG, "startStream")
|
|
170
|
+
sdkManager?.startStream()
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Stop streaming
|
|
175
|
+
*/
|
|
176
|
+
fun stopStream() {
|
|
177
|
+
Log.i(TAG, "stopStream")
|
|
178
|
+
sdkManager?.stopStream()
|
|
179
|
+
isStreaming = false
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Disconnect from current device
|
|
184
|
+
*/
|
|
185
|
+
fun disconnect() {
|
|
186
|
+
Log.i(TAG, "disconnect")
|
|
187
|
+
sdkManager?.disconnect()
|
|
188
|
+
isConnected = false
|
|
189
|
+
isStreaming = false
|
|
190
|
+
connectedDeviceId = null
|
|
191
|
+
connectedDeviceName = null
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Stop everything
|
|
196
|
+
*/
|
|
197
|
+
fun stop() {
|
|
198
|
+
Log.i(TAG, "stop")
|
|
199
|
+
stopStream()
|
|
200
|
+
disconnect()
|
|
201
|
+
stopDiscovery()
|
|
202
|
+
latestBitmap = null
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Get temperature at point in image coordinates
|
|
207
|
+
*/
|
|
208
|
+
fun getTemperatureAt(x: Int, y: Int): Double? {
|
|
209
|
+
return sdkManager?.getTemperatureAt(x, y)?.takeIf { !it.isNaN() }
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get temperature at normalized coordinates (0.0 to 1.0)
|
|
214
|
+
*/
|
|
215
|
+
fun getTemperatureAtNormalized(normalizedX: Double, normalizedY: Double): Double? {
|
|
216
|
+
return sdkManager?.getTemperatureAtNormalized(normalizedX, normalizedY)?.takeIf { !it.isNaN() }
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Alias for getTemperatureAt
|
|
221
|
+
*/
|
|
222
|
+
fun getTemperatureAtPoint(x: Int, y: Int): Double? = getTemperatureAt(x, y)
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Set palette
|
|
226
|
+
*/
|
|
227
|
+
fun setPalette(name: String) {
|
|
228
|
+
Log.d(TAG, "setPalette: $name")
|
|
229
|
+
sdkManager?.setPalette(name)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Get available palettes
|
|
234
|
+
*/
|
|
235
|
+
fun getAvailablePalettes(): List<String> {
|
|
236
|
+
return sdkManager?.availablePalettes ?: emptyList()
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Get list of discovered devices
|
|
241
|
+
*/
|
|
242
|
+
fun getDiscoveredDevices(): List<Identity> {
|
|
243
|
+
return sdkManager?.discoveredDevices ?: emptyList()
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Check states
|
|
248
|
+
*/
|
|
249
|
+
fun isConnected(): Boolean = isConnected
|
|
250
|
+
fun isStreaming(): Boolean = isStreaming
|
|
251
|
+
fun isEmulator(): Boolean = connectedDeviceName?.contains("EMULAT", ignoreCase = true) == true
|
|
252
|
+
fun isDeviceConnected(): Boolean = isConnected
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Get connected device info
|
|
256
|
+
*/
|
|
257
|
+
fun getConnectedDeviceInfo(): String {
|
|
258
|
+
return connectedDeviceName ?: "Not connected"
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Get latest frame as file path (for RN)
|
|
263
|
+
*/
|
|
264
|
+
fun getLatestFramePath(): String? {
|
|
265
|
+
val bitmap = latestBitmap ?: return null
|
|
266
|
+
return try {
|
|
267
|
+
val file = File.createTempFile("flir_frame_", ".jpg")
|
|
268
|
+
FileOutputStream(file).use { out ->
|
|
269
|
+
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out)
|
|
270
|
+
}
|
|
271
|
+
file.absolutePath
|
|
272
|
+
} catch (t: Throwable) {
|
|
273
|
+
Log.e(TAG, "Failed to save frame", t)
|
|
274
|
+
null
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// SDK Listener
|
|
279
|
+
private val sdkListener = object : FlirSdkManager.Listener {
|
|
280
|
+
override fun onDeviceFound(identity: Identity) {
|
|
281
|
+
Log.i(TAG, "Device found: ${identity.deviceId}")
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
override fun onDeviceListUpdated(devices: List<Identity>) {
|
|
285
|
+
Log.i(TAG, "Devices updated: ${devices.size} found")
|
|
286
|
+
devices.forEach {
|
|
287
|
+
Log.d(TAG, " - ${it.deviceId} (${it.communicationInterface})")
|
|
288
|
+
}
|
|
289
|
+
emitDevicesFound(devices)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
override fun onConnected(identity: Identity?) {
|
|
293
|
+
Log.i(TAG, "Connected to: ${identity?.deviceId}")
|
|
294
|
+
isConnected = true
|
|
295
|
+
connectedDeviceId = identity?.deviceId
|
|
296
|
+
connectedDeviceName = identity?.deviceId
|
|
297
|
+
emitDeviceState("connected")
|
|
298
|
+
|
|
299
|
+
// Auto-start streaming when connected
|
|
300
|
+
startStream()
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
override fun onDisconnected() {
|
|
304
|
+
Log.i(TAG, "Disconnected")
|
|
305
|
+
isConnected = false
|
|
306
|
+
isStreaming = false
|
|
307
|
+
connectedDeviceId = null
|
|
308
|
+
connectedDeviceName = null
|
|
309
|
+
emitDeviceState("disconnected")
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
override fun onFrame(bitmap: Bitmap) {
|
|
313
|
+
if (bitmap.isRecycled || bitmap.width <= 0 || bitmap.height <= 0) {
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
latestBitmap = bitmap
|
|
318
|
+
isStreaming = true
|
|
319
|
+
|
|
320
|
+
// Notify texture callback (for GL rendering)
|
|
321
|
+
if (textureCallback != null) {
|
|
322
|
+
textureCallback?.onTextureUpdate(bitmap, 0)
|
|
323
|
+
} else {
|
|
324
|
+
// Log only occasionally to avoid spam
|
|
325
|
+
if (System.currentTimeMillis() % 5000 < 100) {
|
|
326
|
+
Log.w(TAG, "⚠️ Frame received but textureCallback is null - texture won't update!")
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Rate-limited RN event
|
|
331
|
+
emitFrameToReactNative(bitmap)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
override fun onError(message: String) {
|
|
335
|
+
Log.e(TAG, "Error: $message")
|
|
336
|
+
emitError(message)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// React Native event emitters
|
|
341
|
+
private fun emitFrameToReactNative(bitmap: Bitmap) {
|
|
342
|
+
val now = System.currentTimeMillis()
|
|
343
|
+
if (now - lastEmitMs.get() < minEmitIntervalMs) return
|
|
344
|
+
lastEmitMs.set(now)
|
|
345
|
+
|
|
346
|
+
val ctx = reactContext ?: return
|
|
347
|
+
try {
|
|
348
|
+
val params = Arguments.createMap().apply {
|
|
349
|
+
putInt("width", bitmap.width)
|
|
350
|
+
putInt("height", bitmap.height)
|
|
351
|
+
putDouble("timestamp", now.toDouble())
|
|
352
|
+
}
|
|
353
|
+
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
354
|
+
.emit("FlirFrameReceived", params)
|
|
355
|
+
} catch (e: Exception) {
|
|
356
|
+
// Ignore
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
private fun emitDeviceState(state: String) {
|
|
361
|
+
val ctx = reactContext
|
|
362
|
+
if (ctx == null) {
|
|
363
|
+
Log.e(TAG, "Cannot emit FlirDeviceConnected($state) - reactContext is null!")
|
|
364
|
+
return
|
|
365
|
+
}
|
|
366
|
+
Log.d(TAG, "Emitting FlirDeviceConnected: $state")
|
|
367
|
+
try {
|
|
368
|
+
val params = Arguments.createMap().apply {
|
|
369
|
+
putString("state", state)
|
|
370
|
+
putBoolean("isConnected", isConnected)
|
|
371
|
+
putBoolean("isStreaming", isStreaming)
|
|
372
|
+
putBoolean("isEmulator", isEmulator())
|
|
373
|
+
connectedDeviceName?.let { putString("deviceName", it) }
|
|
374
|
+
connectedDeviceId?.let { putString("deviceId", it) }
|
|
375
|
+
}
|
|
376
|
+
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
377
|
+
.emit("FlirDeviceConnected", params)
|
|
378
|
+
} catch (e: Exception) {
|
|
379
|
+
Log.e(TAG, "Failed to emit device state", e)
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
private fun emitDevicesFound(devices: List<Identity>) {
|
|
384
|
+
val ctx = reactContext
|
|
385
|
+
if (ctx == null) {
|
|
386
|
+
Log.e(TAG, "Cannot emit FlirDevicesFound - reactContext is null!")
|
|
387
|
+
return
|
|
388
|
+
}
|
|
389
|
+
Log.d(TAG, "Emitting FlirDevicesFound with ${devices.size} devices")
|
|
390
|
+
try {
|
|
391
|
+
val params = Arguments.createMap()
|
|
392
|
+
val devicesArray: WritableArray = Arguments.createArray()
|
|
393
|
+
|
|
394
|
+
devices.forEach { identity ->
|
|
395
|
+
val deviceMap: WritableMap = Arguments.createMap().apply {
|
|
396
|
+
putString("id", identity.deviceId)
|
|
397
|
+
putString("name", identity.deviceId)
|
|
398
|
+
putString("communicationType", identity.communicationInterface.name)
|
|
399
|
+
putBoolean("isEmulator", identity.communicationInterface.name == "EMULATOR")
|
|
400
|
+
}
|
|
401
|
+
devicesArray.pushMap(deviceMap)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
params.putArray("devices", devicesArray)
|
|
405
|
+
params.putInt("count", devices.size)
|
|
406
|
+
|
|
407
|
+
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
408
|
+
.emit("FlirDevicesFound", params)
|
|
409
|
+
Log.d(TAG, "Successfully emitted FlirDevicesFound")
|
|
410
|
+
} catch (e: Exception) {
|
|
411
|
+
Log.e(TAG, "Failed to emit devices found", e)
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
private fun emitError(message: String) {
|
|
416
|
+
val ctx = reactContext ?: return
|
|
417
|
+
try {
|
|
418
|
+
val params = Arguments.createMap().apply {
|
|
419
|
+
putString("error", message)
|
|
420
|
+
}
|
|
421
|
+
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
422
|
+
.emit("FlirError", params)
|
|
423
|
+
} catch (e: Exception) {
|
|
424
|
+
Log.e(TAG, "Failed to emit error", e)
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Legacy compatibility
|
|
429
|
+
@JvmStatic
|
|
430
|
+
fun getInstance(): FlirManager = this
|
|
431
|
+
|
|
432
|
+
interface DiscoveryCallback {
|
|
433
|
+
fun onDeviceFound(deviceName: String)
|
|
434
|
+
fun onDiscoveryTimeout()
|
|
435
|
+
fun onEmulatorEnabled()
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
private var discoveryCallback: DiscoveryCallback? = null
|
|
439
|
+
|
|
440
|
+
fun setDiscoveryCallback(callback: DiscoveryCallback?) {
|
|
441
|
+
discoveryCallback = callback
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Legacy methods - no-ops or simple forwards
|
|
445
|
+
fun setEmulatorMode(enabled: Boolean) {
|
|
446
|
+
Log.d(TAG, "setEmulatorMode($enabled) - legacy, use startDiscovery() instead")
|
|
447
|
+
if (enabled) {
|
|
448
|
+
startDiscovery(retry = true)
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
fun enableEmulatorMode() = setEmulatorMode(true)
|
|
453
|
+
|
|
454
|
+
fun forceEmulatorMode(type: String = "FLIR_ONE_EDGE") {
|
|
455
|
+
Log.d(TAG, "forceEmulatorMode($type) - legacy, use startDiscovery() instead")
|
|
456
|
+
startDiscovery(retry = true)
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
fun setPreferredEmulatorType(type: String) {
|
|
460
|
+
Log.d(TAG, "setPreferredEmulatorType($type) - legacy, no longer used")
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
fun updateAcol(value: Float) {
|
|
464
|
+
// No-op - not used in simplified version
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Cleanup
|
|
469
|
+
*/
|
|
470
|
+
fun destroy() {
|
|
471
|
+
stop()
|
|
472
|
+
sdkManager?.destroy()
|
|
473
|
+
sdkManager = null
|
|
474
|
+
isInitialized = false
|
|
475
|
+
}
|
|
476
|
+
}
|