ilabs-flir 2.1.31 → 2.1.33

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.
@@ -37,25 +37,28 @@ import java.util.concurrent.Executors;
37
37
  */
38
38
  public class FlirSdkManager {
39
39
  private static final String TAG = "FlirSdkManager";
40
-
40
+
41
41
  // Singleton instance
42
42
  private static FlirSdkManager instance;
43
-
43
+
44
44
  // Core components
45
45
  private final Context context;
46
- // Use bounded thread pool to prevent thread explosion during rapid frame processing
46
+ // Use bounded thread pool to prevent thread explosion during rapid frame
47
+ // processing
47
48
  private final Executor executor = Executors.newFixedThreadPool(2);
48
49
  // Single-threaded executor for frame processing to ensure ordered processing
49
50
  private final Executor frameExecutor = Executors.newSingleThreadExecutor();
50
- // Battery poller scheduler - polls battery level & charging state periodically if supported
51
- private final java.util.concurrent.ScheduledExecutorService batteryPoller = java.util.concurrent.Executors.newSingleThreadScheduledExecutor();
51
+ // Battery poller scheduler - polls battery level & charging state periodically
52
+ // if supported
53
+ private final java.util.concurrent.ScheduledExecutorService batteryPoller = java.util.concurrent.Executors
54
+ .newSingleThreadScheduledExecutor();
52
55
  private volatile int lastPolledBatteryLevel = -1;
53
56
  private volatile boolean lastPolledCharging = false;
54
57
  // Frame processing guard - skip frames if still processing previous one
55
58
  private volatile boolean isProcessingFrame = false;
56
59
  private long lastFrameProcessedMs = 0;
57
60
  private static final long MIN_FRAME_INTERVAL_MS = 50; // Max ~20 FPS frame processing
58
-
61
+
59
62
  // State
60
63
  private boolean isInitialized = false;
61
64
  private boolean isScanning = false;
@@ -63,30 +66,37 @@ public class FlirSdkManager {
63
66
  private ThermalStreamer streamer;
64
67
  private Stream activeStream;
65
68
  private final List<Identity> discoveredDevices = Collections.synchronizedList(new ArrayList<>());
66
- // When true, prefer getting SDK-provided rotated frames instead of rotating ourselves
69
+ // When true, prefer getting SDK-provided rotated frames instead of rotating
70
+ // ourselves
67
71
  private volatile boolean preferSdkRotation = false;
68
-
72
+
69
73
  // Listener
70
74
  private Listener listener;
71
-
75
+
72
76
  /**
73
77
  * Listener interface for SDK events
74
78
  */
75
79
  public interface Listener {
76
80
  void onDeviceFound(Identity identity);
81
+
77
82
  void onDeviceListUpdated(List<Identity> devices);
83
+
78
84
  void onConnected(Identity identity);
85
+
79
86
  void onDisconnected();
87
+
80
88
  void onFrame(Bitmap bitmap);
89
+
81
90
  void onError(String message);
91
+
82
92
  void onBatteryUpdated(int level, boolean isCharging);
83
93
  }
84
-
94
+
85
95
  // Private constructor for singleton
86
96
  private FlirSdkManager(Context context) {
87
97
  this.context = context.getApplicationContext();
88
98
  }
89
-
99
+
90
100
  /**
91
101
  * Get singleton instance
92
102
  */
@@ -96,7 +106,7 @@ public class FlirSdkManager {
96
106
  }
97
107
  return instance;
98
108
  }
99
-
109
+
100
110
  /**
101
111
  * Set listener for SDK events
102
112
  */
@@ -109,16 +119,25 @@ public class FlirSdkManager {
109
119
  // Try to ask SDK streamer to provide rotated images if possible
110
120
  if (streamer != null) {
111
121
  try {
112
- // Try common method names via reflection to avoid hard dependency on exact API signature
122
+ // Try common method names via reflection to avoid hard dependency on exact API
123
+ // signature
113
124
  Object obj = streamer;
114
125
  java.lang.reflect.Method m = null;
115
- try { m = obj.getClass().getMethod("setImageRotation", int.class); } catch (Throwable ignored) {}
126
+ try {
127
+ m = obj.getClass().getMethod("setImageRotation", int.class);
128
+ } catch (Throwable ignored) {
129
+ }
116
130
  if (m == null) {
117
- try { m = obj.getClass().getMethod("setRotation", int.class); } catch (Throwable ignored) {}
131
+ try {
132
+ m = obj.getClass().getMethod("setRotation", int.class);
133
+ } catch (Throwable ignored) {
134
+ }
118
135
  }
119
136
  if (m != null) {
120
- // If caller asked SDK to rotate, choose 0 = 'auto' or prefer flag; here we request SDK to respect device orientation
121
- int degrees = prefer ? 0 : 0; // SDK-specific - for now, 0 requests orientation-respected frames if method interprets so
137
+ // If caller asked SDK to rotate, choose 0 = 'auto' or prefer flag; here we
138
+ // request SDK to respect device orientation
139
+ int degrees = prefer ? 0 : 0; // SDK-specific - for now, 0 requests orientation-respected frames if
140
+ // method interprets so
122
141
  m.invoke(obj, degrees);
123
142
  Log.d(TAG, "setPreferSdkRotation: requested SDK rotation via reflection");
124
143
  } else {
@@ -130,8 +149,10 @@ public class FlirSdkManager {
130
149
  }
131
150
  }
132
151
 
133
- public boolean isPreferSdkRotation() { return preferSdkRotation; }
134
-
152
+ public boolean isPreferSdkRotation() {
153
+ return preferSdkRotation;
154
+ }
155
+
135
156
  /**
136
157
  * Initialize the FLIR Thermal SDK
137
158
  */
@@ -140,8 +161,18 @@ public class FlirSdkManager {
140
161
  Log.d(TAG, "Already initialized");
141
162
  return;
142
163
  }
143
-
164
+
144
165
  try {
166
+ // Explicitly load native library to ensure it's available and initialized
167
+ // This can help resolve issues where the automatic loading fails or happens out
168
+ // of order
169
+ try {
170
+ System.loadLibrary("atlas_native");
171
+ Log.d(TAG, "Manually loaded atlas_native library");
172
+ } catch (Throwable t) {
173
+ Log.w(TAG, "Manual load of atlas_native failed: " + t.getMessage());
174
+ }
175
+
145
176
  ThermalSdkAndroid.init(context);
146
177
  isInitialized = true;
147
178
  Log.d(TAG, "SDK initialized successfully");
@@ -150,14 +181,14 @@ public class FlirSdkManager {
150
181
  notifyError("SDK initialization failed: " + e.getMessage());
151
182
  }
152
183
  }
153
-
184
+
154
185
  /**
155
186
  * Check if SDK is initialized
156
187
  */
157
188
  public boolean isInitialized() {
158
189
  return isInitialized;
159
190
  }
160
-
191
+
161
192
  /**
162
193
  * Start scanning for all device types (USB, Network, Emulator)
163
194
  * Returns ALL devices - no filtering
@@ -168,31 +199,30 @@ public class FlirSdkManager {
168
199
  notifyError("SDK not initialized");
169
200
  return;
170
201
  }
171
-
202
+
172
203
  if (isScanning) {
173
204
  Log.d(TAG, "Already scanning");
174
205
  return;
175
206
  }
176
-
207
+
177
208
  isScanning = true;
178
209
  discoveredDevices.clear();
179
-
210
+
180
211
  Log.d(TAG, "Starting discovery for EMULATOR, NETWORK, USB...");
181
-
212
+
182
213
  try {
183
214
  DiscoveryFactory.getInstance().scan(
184
- discoveryListener,
185
- CommunicationInterface.EMULATOR,
186
- CommunicationInterface.NETWORK,
187
- CommunicationInterface.USB
188
- );
215
+ discoveryListener,
216
+ CommunicationInterface.EMULATOR,
217
+ CommunicationInterface.NETWORK,
218
+ CommunicationInterface.USB);
189
219
  } catch (Exception e) {
190
220
  Log.e(TAG, "Failed to start scan", e);
191
221
  isScanning = false;
192
222
  notifyError("Scan failed: " + e.getMessage());
193
223
  }
194
224
  }
195
-
225
+
196
226
  /**
197
227
  * Stop scanning for devices
198
228
  */
@@ -200,28 +230,27 @@ public class FlirSdkManager {
200
230
  if (!isScanning) {
201
231
  return;
202
232
  }
203
-
233
+
204
234
  try {
205
235
  DiscoveryFactory.getInstance().stop(
206
- CommunicationInterface.EMULATOR,
207
- CommunicationInterface.NETWORK,
208
- CommunicationInterface.USB
209
- );
236
+ CommunicationInterface.EMULATOR,
237
+ CommunicationInterface.NETWORK,
238
+ CommunicationInterface.USB);
210
239
  } catch (Exception e) {
211
240
  Log.e(TAG, "Failed to stop scan", e);
212
241
  }
213
-
242
+
214
243
  isScanning = false;
215
244
  Log.d(TAG, "Discovery stopped");
216
245
  }
217
-
246
+
218
247
  /**
219
248
  * Get list of discovered devices
220
249
  */
221
250
  public List<Identity> getDiscoveredDevices() {
222
251
  return new ArrayList<>(discoveredDevices);
223
252
  }
224
-
253
+
225
254
  /**
226
255
  * Connect to a device
227
256
  */
@@ -230,22 +259,22 @@ public class FlirSdkManager {
230
259
  notifyError("Invalid identity");
231
260
  return;
232
261
  }
233
-
262
+
234
263
  // Disconnect if already connected
235
264
  if (camera != null) {
236
265
  disconnect();
237
266
  }
238
-
267
+
239
268
  Log.d(TAG, "Connecting to: " + identity.deviceId);
240
-
269
+
241
270
  // Run connection on background thread since it's blocking
242
271
  executor.execute(() -> {
243
272
  try {
244
273
  camera = new Camera();
245
274
  camera.connect(identity, connectionStatusListener, new ConnectParameters());
246
-
275
+
247
276
  Log.d(TAG, "Connected to camera");
248
-
277
+
249
278
  if (listener != null) {
250
279
  listener.onConnected(identity);
251
280
  }
@@ -258,13 +287,13 @@ public class FlirSdkManager {
258
287
  }
259
288
  });
260
289
  }
261
-
290
+
262
291
  /**
263
292
  * Disconnect from current device
264
293
  */
265
294
  public void disconnect() {
266
295
  stopStream();
267
-
296
+
268
297
  if (camera != null) {
269
298
  try {
270
299
  camera.disconnect();
@@ -275,21 +304,21 @@ public class FlirSdkManager {
275
304
  }
276
305
  // stop battery poller
277
306
  stopBatteryPoller();
278
-
307
+
279
308
  if (listener != null) {
280
309
  listener.onDisconnected();
281
310
  }
282
-
311
+
283
312
  Log.d(TAG, "Disconnected");
284
313
  }
285
-
314
+
286
315
  /**
287
316
  * Check if connected
288
317
  */
289
318
  public boolean isConnected() {
290
319
  return camera != null;
291
320
  }
292
-
321
+
293
322
  /**
294
323
  * Start streaming from connected device
295
324
  */
@@ -298,7 +327,7 @@ public class FlirSdkManager {
298
327
  notifyError("Not connected");
299
328
  return;
300
329
  }
301
-
330
+
302
331
  executor.execute(() -> {
303
332
  try {
304
333
  // Get available streams
@@ -307,7 +336,7 @@ public class FlirSdkManager {
307
336
  notifyError("No streams available");
308
337
  return;
309
338
  }
310
-
339
+
311
340
  // Find thermal stream
312
341
  Stream thermalStream = null;
313
342
  for (Stream stream : streams) {
@@ -316,68 +345,67 @@ public class FlirSdkManager {
316
345
  break;
317
346
  }
318
347
  }
319
-
348
+
320
349
  if (thermalStream == null) {
321
350
  thermalStream = streams.get(0);
322
351
  }
323
-
352
+
324
353
  activeStream = thermalStream;
325
354
  streamer = new ThermalStreamer(thermalStream);
326
-
355
+
327
356
  // Start receiving frames using OnReceived and OnRemoteError
328
357
  thermalStream.start(
329
- (OnReceived<Void>) v -> {
330
- // FRAME DROP GUARD: Skip frame if still processing previous one
331
- // This prevents thread buildup and ensures smooth frame flow
332
- long now = System.currentTimeMillis();
333
- if (isProcessingFrame || (now - lastFrameProcessedMs < MIN_FRAME_INTERVAL_MS)) {
334
- // Drop frame - processing is behind or too soon since last frame
335
- return;
336
- }
337
-
338
- // Mark processing start before queuing task
339
- isProcessingFrame = true;
340
-
341
- // Use single-threaded frameExecutor to ensure ordered frame processing
342
- frameExecutor.execute(() -> {
343
- try {
344
- if (streamer != null) {
345
- streamer.update();
346
-
347
- // Get ImageBuffer and convert to Bitmap
348
- ImageBuffer imageBuffer = streamer.getImage();
349
- if (imageBuffer != null && listener != null) {
350
- BitmapAndroid bitmapAndroid = BitmapAndroid.createBitmap(imageBuffer);
351
- Bitmap bitmap = bitmapAndroid.getBitMap();
352
- if (bitmap != null) {
353
- listener.onFrame(bitmap);
358
+ (OnReceived<Void>) v -> {
359
+ // FRAME DROP GUARD: Skip frame if still processing previous one
360
+ // This prevents thread buildup and ensures smooth frame flow
361
+ long now = System.currentTimeMillis();
362
+ if (isProcessingFrame || (now - lastFrameProcessedMs < MIN_FRAME_INTERVAL_MS)) {
363
+ // Drop frame - processing is behind or too soon since last frame
364
+ return;
365
+ }
366
+
367
+ // Mark processing start before queuing task
368
+ isProcessingFrame = true;
369
+
370
+ // Use single-threaded frameExecutor to ensure ordered frame processing
371
+ frameExecutor.execute(() -> {
372
+ try {
373
+ if (streamer != null) {
374
+ streamer.update();
375
+
376
+ // Get ImageBuffer and convert to Bitmap
377
+ ImageBuffer imageBuffer = streamer.getImage();
378
+ if (imageBuffer != null && listener != null) {
379
+ BitmapAndroid bitmapAndroid = BitmapAndroid.createBitmap(imageBuffer);
380
+ Bitmap bitmap = bitmapAndroid.getBitMap();
381
+ if (bitmap != null) {
382
+ listener.onFrame(bitmap);
383
+ }
354
384
  }
355
385
  }
386
+ } catch (Exception e) {
387
+ Log.e(TAG, "Error processing frame", e);
388
+ } finally {
389
+ // Reset frame processing guard to allow next frame
390
+ lastFrameProcessedMs = System.currentTimeMillis();
391
+ isProcessingFrame = false;
356
392
  }
357
- } catch (Exception e) {
358
- Log.e(TAG, "Error processing frame", e);
359
- } finally {
360
- // Reset frame processing guard to allow next frame
361
- lastFrameProcessedMs = System.currentTimeMillis();
362
- isProcessingFrame = false;
363
- }
393
+ });
394
+ },
395
+ error -> {
396
+ Log.e(TAG, "Stream error: " + error);
397
+ notifyError("Stream error: " + error);
364
398
  });
365
- },
366
- error -> {
367
- Log.e(TAG, "Stream error: " + error);
368
- notifyError("Stream error: " + error);
369
- }
370
- );
371
-
399
+
372
400
  Log.d(TAG, "Streaming started");
373
-
401
+
374
402
  } catch (Exception e) {
375
403
  Log.e(TAG, "Failed to start stream", e);
376
404
  notifyError("Stream failed: " + e.getMessage());
377
405
  }
378
406
  });
379
407
  }
380
-
408
+
381
409
  /**
382
410
  * Stop streaming
383
411
  */
@@ -390,19 +418,20 @@ public class FlirSdkManager {
390
418
  }
391
419
  activeStream = null;
392
420
  }
393
-
421
+
394
422
  streamer = null;
395
-
423
+
396
424
  // Reset frame processing state
397
425
  isProcessingFrame = false;
398
426
  lastFrameProcessedMs = 0;
399
-
427
+
400
428
  Log.d(TAG, "Streaming stopped");
401
429
  }
402
-
430
+
403
431
  /**
404
432
  * Get temperature at a specific point in the image
405
433
  * Queries the SDK directly - simple and no locks needed
434
+ *
406
435
  * @param x X coordinate (0 to image width-1)
407
436
  * @param y Y coordinate (0 to image height-1)
408
437
  * @return Temperature in Celsius, or Double.NaN if not available
@@ -411,17 +440,17 @@ public class FlirSdkManager {
411
440
  if (streamer == null) {
412
441
  return Double.NaN;
413
442
  }
414
-
415
- final double[] result = {Double.NaN};
443
+
444
+ final double[] result = { Double.NaN };
416
445
  try {
417
446
  streamer.withThermalImage(thermalImage -> {
418
447
  try {
419
448
  int imgWidth = thermalImage.getWidth();
420
449
  int imgHeight = thermalImage.getHeight();
421
-
450
+
422
451
  int clampedX = Math.max(0, Math.min(imgWidth - 1, x));
423
452
  int clampedY = Math.max(0, Math.min(imgHeight - 1, y));
424
-
453
+
425
454
  ThermalValue value = thermalImage.getValueAt(new Point(clampedX, clampedY));
426
455
  if (value != null) {
427
456
  result[0] = value.asCelsius().value;
@@ -433,12 +462,13 @@ public class FlirSdkManager {
433
462
  } catch (Exception e) {
434
463
  Log.w(TAG, "Temperature query failed", e);
435
464
  }
436
-
465
+
437
466
  return result[0];
438
467
  }
439
-
468
+
440
469
  /**
441
470
  * Get temperature at normalized coordinates (0.0 to 1.0)
471
+ *
442
472
  * @param normalizedX X coordinate (0.0 to 1.0)
443
473
  * @param normalizedY Y coordinate (0.0 to 1.0)
444
474
  * @return Temperature in Celsius, or Double.NaN if not available
@@ -447,20 +477,20 @@ public class FlirSdkManager {
447
477
  if (streamer == null) {
448
478
  return Double.NaN;
449
479
  }
450
-
451
- final double[] result = {Double.NaN};
480
+
481
+ final double[] result = { Double.NaN };
452
482
  try {
453
483
  streamer.withThermalImage(thermalImage -> {
454
484
  try {
455
485
  int width = thermalImage.getWidth();
456
486
  int height = thermalImage.getHeight();
457
-
487
+
458
488
  int x = (int) (normalizedX * (width - 1));
459
489
  int y = (int) (normalizedY * (height - 1));
460
-
490
+
461
491
  x = Math.max(0, Math.min(width - 1, x));
462
492
  y = Math.max(0, Math.min(height - 1, y));
463
-
493
+
464
494
  ThermalValue value = thermalImage.getValueAt(new Point(x, y));
465
495
  if (value != null) {
466
496
  result[0] = value.asCelsius().value;
@@ -472,10 +502,10 @@ public class FlirSdkManager {
472
502
  } catch (Exception e) {
473
503
  Log.w(TAG, "Temperature query failed", e);
474
504
  }
475
-
505
+
476
506
  return result[0];
477
507
  }
478
-
508
+
479
509
  /**
480
510
  * Set palette for thermal image rendering
481
511
  */
@@ -484,7 +514,7 @@ public class FlirSdkManager {
484
514
  Log.w(TAG, "No active streamer");
485
515
  return;
486
516
  }
487
-
517
+
488
518
  executor.execute(() -> {
489
519
  try {
490
520
  Palette palette = findPalette(paletteName);
@@ -499,7 +529,7 @@ public class FlirSdkManager {
499
529
  }
500
530
  });
501
531
  }
502
-
532
+
503
533
  /**
504
534
  * Get list of available palettes
505
535
  */
@@ -517,18 +547,22 @@ public class FlirSdkManager {
517
547
  }
518
548
 
519
549
  /**
520
- * Best-effort: Fetch battery level from connected camera if SDK exposes battery APIs
550
+ * Best-effort: Fetch battery level from connected camera if SDK exposes battery
551
+ * APIs
521
552
  * Returns -1 if unavailable
522
553
  */
523
554
  public int getBatteryLevel() {
524
- if (camera == null) return -1;
555
+ if (camera == null)
556
+ return -1;
525
557
  try {
526
558
  // Common SDK methods to try
527
559
  try {
528
560
  java.lang.reflect.Method m = camera.getClass().getMethod("getBatteryLevel");
529
561
  Object r = m.invoke(camera);
530
- if (r instanceof Number) return ((Number) r).intValue();
531
- } catch (Throwable ignored) {}
562
+ if (r instanceof Number)
563
+ return ((Number) r).intValue();
564
+ } catch (Throwable ignored) {
565
+ }
532
566
 
533
567
  try {
534
568
  java.lang.reflect.Method m = camera.getClass().getMethod("getBattery");
@@ -537,10 +571,13 @@ public class FlirSdkManager {
537
571
  try {
538
572
  java.lang.reflect.Method levelMethod = batt.getClass().getMethod("getLevel");
539
573
  Object lv = levelMethod.invoke(batt);
540
- if (lv instanceof Number) return ((Number) lv).intValue();
541
- } catch (Throwable ignored) {}
574
+ if (lv instanceof Number)
575
+ return ((Number) lv).intValue();
576
+ } catch (Throwable ignored) {
577
+ }
542
578
  }
543
- } catch (Throwable ignored) {}
579
+ } catch (Throwable ignored) {
580
+ }
544
581
  } catch (Throwable t) {
545
582
  Log.w(TAG, "Error querying battery level", t);
546
583
  }
@@ -552,13 +589,16 @@ public class FlirSdkManager {
552
589
  * Returns false if unknown
553
590
  */
554
591
  public boolean isBatteryCharging() {
555
- if (camera == null) return false;
592
+ if (camera == null)
593
+ return false;
556
594
  try {
557
595
  try {
558
596
  java.lang.reflect.Method m = camera.getClass().getMethod("isCharging");
559
597
  Object r = m.invoke(camera);
560
- if (r instanceof Boolean) return (Boolean) r;
561
- } catch (Throwable ignored) {}
598
+ if (r instanceof Boolean)
599
+ return (Boolean) r;
600
+ } catch (Throwable ignored) {
601
+ }
562
602
 
563
603
  try {
564
604
  java.lang.reflect.Method m = camera.getClass().getMethod("getBattery");
@@ -567,16 +607,19 @@ public class FlirSdkManager {
567
607
  try {
568
608
  java.lang.reflect.Method isCh = batt.getClass().getMethod("isCharging");
569
609
  Object cv = isCh.invoke(batt);
570
- if (cv instanceof Boolean) return (Boolean) cv;
571
- } catch (Throwable ignored) {}
610
+ if (cv instanceof Boolean)
611
+ return (Boolean) cv;
612
+ } catch (Throwable ignored) {
613
+ }
572
614
  }
573
- } catch (Throwable ignored) {}
615
+ } catch (Throwable ignored) {
616
+ }
574
617
  } catch (Throwable t) {
575
618
  Log.w(TAG, "Error querying battery charging state", t);
576
619
  }
577
620
  return false;
578
621
  }
579
-
622
+
580
623
  // Find palette by name
581
624
  private Palette findPalette(String name) {
582
625
  try {
@@ -595,15 +638,15 @@ public class FlirSdkManager {
595
638
  }
596
639
  return null;
597
640
  }
598
-
641
+
599
642
  // Discovery listener - no filtering, returns all devices
600
643
  private final DiscoveryEventListener discoveryListener = new DiscoveryEventListener() {
601
644
  @Override
602
645
  public void onCameraFound(DiscoveredCamera discoveredCamera) {
603
646
  Identity identity = discoveredCamera.getIdentity();
604
- Log.d(TAG, "Device found: " + identity.deviceId +
605
- " type=" + identity.communicationInterface);
606
-
647
+ Log.d(TAG, "Device found: " + identity.deviceId +
648
+ " type=" + identity.communicationInterface);
649
+
607
650
  // Add to list if not already present
608
651
  synchronized (discoveredDevices) {
609
652
  boolean exists = false;
@@ -617,58 +660,58 @@ public class FlirSdkManager {
617
660
  discoveredDevices.add(identity);
618
661
  }
619
662
  }
620
-
663
+
621
664
  if (listener != null) {
622
665
  listener.onDeviceFound(identity);
623
666
  listener.onDeviceListUpdated(new ArrayList<>(discoveredDevices));
624
667
  }
625
668
  }
626
-
669
+
627
670
  @Override
628
671
  public void onCameraLost(Identity identity) {
629
672
  Log.d(TAG, "Device lost: " + identity.deviceId);
630
-
673
+
631
674
  synchronized (discoveredDevices) {
632
675
  discoveredDevices.removeIf(d -> d.deviceId.equals(identity.deviceId));
633
676
  }
634
-
677
+
635
678
  if (listener != null) {
636
679
  listener.onDeviceListUpdated(new ArrayList<>(discoveredDevices));
637
680
  }
638
681
  }
639
-
682
+
640
683
  @Override
641
684
  public void onDiscoveryError(CommunicationInterface iface, ErrorCode error) {
642
685
  Log.e(TAG, "Discovery error: " + iface + " - " + error);
643
686
  notifyError("Discovery error: " + error);
644
687
  }
645
-
688
+
646
689
  @Override
647
690
  public void onDiscoveryFinished(CommunicationInterface iface) {
648
691
  Log.d(TAG, "Discovery finished for: " + iface);
649
692
  }
650
693
  };
651
-
694
+
652
695
  // Connection status listener
653
696
  private final ConnectionStatusListener connectionStatusListener = new ConnectionStatusListener() {
654
697
  @Override
655
698
  public void onDisconnected(ErrorCode error) {
656
699
  Log.d(TAG, "Disconnected: " + (error != null ? error : "clean"));
657
700
  camera = null;
658
-
701
+
659
702
  if (listener != null) {
660
703
  listener.onDisconnected();
661
704
  }
662
705
  }
663
706
  };
664
-
707
+
665
708
  // Helper to notify errors
666
709
  private void notifyError(String message) {
667
710
  if (listener != null) {
668
711
  listener.onError(message);
669
712
  }
670
713
  }
671
-
714
+
672
715
  /**
673
716
  * Cleanup resources
674
717
  */
@@ -682,12 +725,14 @@ public class FlirSdkManager {
682
725
  }
683
726
 
684
727
  /**
685
- * Start a background poller to periodically check battery state and notify listener
728
+ * Start a background poller to periodically check battery state and notify
729
+ * listener
686
730
  */
687
731
  private void startBatteryPoller() {
688
732
  try {
689
733
  batteryPoller.scheduleAtFixedRate(() -> {
690
- if (camera == null) return;
734
+ if (camera == null)
735
+ return;
691
736
  try {
692
737
  int level = getBatteryLevel();
693
738
  boolean charging = isBatteryCharging();
@@ -695,7 +740,10 @@ public class FlirSdkManager {
695
740
  lastPolledBatteryLevel = level;
696
741
  lastPolledCharging = charging;
697
742
  if (listener != null) {
698
- try { listener.onBatteryUpdated(level, charging);} catch (Throwable t) {}
743
+ try {
744
+ listener.onBatteryUpdated(level, charging);
745
+ } catch (Throwable t) {
746
+ }
699
747
  }
700
748
  }
701
749
  } catch (Throwable t) {
@@ -713,6 +761,7 @@ public class FlirSdkManager {
713
761
  private void stopBatteryPoller() {
714
762
  try {
715
763
  batteryPoller.shutdownNow();
716
- } catch (Throwable ignored) {}
764
+ } catch (Throwable ignored) {
765
+ }
717
766
  }
718
767
  }