capacitor-plugin-camera-forked 3.0.12 → 3.0.13
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.
|
@@ -170,6 +170,7 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
170
170
|
|
|
171
171
|
imageAnalysis = imageAnalysisBuilder.build();
|
|
172
172
|
|
|
173
|
+
// Configure image analysis for better focus performance
|
|
173
174
|
imageAnalysis.setAnalyzer(exec, new ImageAnalysis.Analyzer() {
|
|
174
175
|
@Override
|
|
175
176
|
public void analyze(@NonNull ImageProxy image) {
|
|
@@ -297,6 +298,79 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
297
298
|
return null;
|
|
298
299
|
}
|
|
299
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Initialize responsive auto-focus on camera start
|
|
303
|
+
*/
|
|
304
|
+
private void initializeResponsiveAutoFocus() {
|
|
305
|
+
if (camera != null && previewView != null) {
|
|
306
|
+
try {
|
|
307
|
+
// Set initial focus to center for faster startup
|
|
308
|
+
MeteringPointFactory factory = previewView.getMeteringPointFactory();
|
|
309
|
+
float centerX = previewView.getWidth() / 2.0f;
|
|
310
|
+
float centerY = previewView.getHeight() / 2.0f;
|
|
311
|
+
MeteringPoint centerPoint = factory.createPoint(centerX, centerY);
|
|
312
|
+
|
|
313
|
+
// Create responsive auto-focus action with longer duration for better stability
|
|
314
|
+
FocusMeteringAction initialFocus = new FocusMeteringAction.Builder(centerPoint)
|
|
315
|
+
.setAutoCancelDuration(5, TimeUnit.SECONDS) // Increased from 1 to 5 seconds
|
|
316
|
+
.build();
|
|
317
|
+
|
|
318
|
+
camera.getCameraControl().startFocusAndMetering(initialFocus);
|
|
319
|
+
|
|
320
|
+
// Enable continuous auto-focus by starting a background focus monitoring
|
|
321
|
+
startContinuousAutoFocus();
|
|
322
|
+
|
|
323
|
+
Log.d("Camera", "Initialized responsive auto-focus with continuous monitoring");
|
|
324
|
+
} catch (Exception e) {
|
|
325
|
+
Log.e("Camera", "Failed to initialize responsive auto-focus: " + e.getMessage());
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Start continuous auto-focus monitoring for better focus stability
|
|
332
|
+
*/
|
|
333
|
+
private void startContinuousAutoFocus() {
|
|
334
|
+
if (camera != null && previewView != null) {
|
|
335
|
+
// Use a separate executor for continuous focus to avoid blocking
|
|
336
|
+
ExecutorService focusExecutor = Executors.newSingleThreadExecutor();
|
|
337
|
+
|
|
338
|
+
focusExecutor.execute(new Runnable() {
|
|
339
|
+
@Override
|
|
340
|
+
public void run() {
|
|
341
|
+
try {
|
|
342
|
+
while (camera != null && camera.getCameraInfo().getCameraState().getValue().getType() == CameraState.Type.OPEN) {
|
|
343
|
+
Thread.sleep(2000); // Check every 2 seconds
|
|
344
|
+
|
|
345
|
+
getActivity().runOnUiThread(new Runnable() {
|
|
346
|
+
@Override
|
|
347
|
+
public void run() {
|
|
348
|
+
try {
|
|
349
|
+
// Trigger auto-focus at center to maintain continuous focus
|
|
350
|
+
MeteringPointFactory factory = previewView.getMeteringPointFactory();
|
|
351
|
+
float centerX = previewView.getWidth() / 2.0f;
|
|
352
|
+
float centerY = previewView.getHeight() / 2.0f;
|
|
353
|
+
MeteringPoint centerPoint = factory.createPoint(centerX, centerY);
|
|
354
|
+
|
|
355
|
+
FocusMeteringAction continuousAction = new FocusMeteringAction.Builder(centerPoint)
|
|
356
|
+
.setAutoCancelDuration(3, TimeUnit.SECONDS)
|
|
357
|
+
.build();
|
|
358
|
+
|
|
359
|
+
camera.getCameraControl().startFocusAndMetering(continuousAction);
|
|
360
|
+
} catch (Exception e) {
|
|
361
|
+
Log.d("Camera", "Continuous focus update failed: " + e.getMessage());
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
} catch (InterruptedException e) {
|
|
367
|
+
Log.d("Camera", "Continuous auto-focus stopped");
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
300
374
|
@PluginMethod
|
|
301
375
|
public void startCamera(PluginCall call) {
|
|
302
376
|
getActivity().runOnUiThread(new Runnable() {
|
|
@@ -305,6 +379,10 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
305
379
|
camera = cameraProvider.bindToLifecycle((LifecycleOwner) getContext(), cameraSelector, useCaseGroup);
|
|
306
380
|
previewView.setVisibility(View.VISIBLE);
|
|
307
381
|
makeWebViewTransparent();
|
|
382
|
+
|
|
383
|
+
// Initialize responsive auto-focus for better performance
|
|
384
|
+
initializeResponsiveAutoFocus();
|
|
385
|
+
|
|
308
386
|
triggerOnPlayed();
|
|
309
387
|
call.resolve();
|
|
310
388
|
} catch (Exception e) {
|
|
@@ -408,6 +486,9 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
408
486
|
@Override
|
|
409
487
|
public void run() {
|
|
410
488
|
try {
|
|
489
|
+
// Cancel any existing focus operations for faster transitions
|
|
490
|
+
camera.getCameraControl().cancelFocusAndMetering();
|
|
491
|
+
|
|
411
492
|
// Use PreviewView's built-in MeteringPointFactory for proper coordinate transformation
|
|
412
493
|
MeteringPointFactory factory = previewView.getMeteringPointFactory();
|
|
413
494
|
|
|
@@ -417,24 +498,25 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
417
498
|
|
|
418
499
|
MeteringPoint focusPoint = factory.createPoint(previewX, previewY);
|
|
419
500
|
|
|
420
|
-
// Get configurable options
|
|
501
|
+
// Get configurable options with improved defaults for better focus stability
|
|
421
502
|
boolean includeExposure = Boolean.TRUE.equals(call.getBoolean("includeExposure", true));
|
|
422
503
|
Integer autoCancelDurationParam = call.getInt("autoCancelDurationSeconds");
|
|
423
|
-
|
|
504
|
+
// Increased to 4 seconds for better focus stability and manual control
|
|
505
|
+
int autoCancelDuration = autoCancelDurationParam != null ? autoCancelDurationParam : 4;
|
|
424
506
|
|
|
425
|
-
// Build the focus and metering action
|
|
426
|
-
FocusMeteringAction.Builder builder
|
|
507
|
+
// Build the focus and metering action with optimized settings
|
|
508
|
+
FocusMeteringAction.Builder builder;
|
|
427
509
|
|
|
428
|
-
// Set auto-focus flags
|
|
510
|
+
// Set auto-focus flags with optimized configuration
|
|
429
511
|
if (includeExposure) {
|
|
430
512
|
// Use both AF and AE flags for the same point
|
|
431
513
|
builder = new FocusMeteringAction.Builder(focusPoint, FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE);
|
|
432
514
|
} else {
|
|
433
|
-
// Use only AF flag
|
|
515
|
+
// Use only AF flag for faster focus-only operations
|
|
434
516
|
builder = new FocusMeteringAction.Builder(focusPoint, FocusMeteringAction.FLAG_AF);
|
|
435
517
|
}
|
|
436
518
|
|
|
437
|
-
// Set auto-cancel duration
|
|
519
|
+
// Set shorter auto-cancel duration for responsive focus
|
|
438
520
|
builder.setAutoCancelDuration(autoCancelDuration, TimeUnit.SECONDS);
|
|
439
521
|
|
|
440
522
|
FocusMeteringAction action = builder.build();
|
|
@@ -442,7 +524,7 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
442
524
|
// Start focus and metering with result callback
|
|
443
525
|
ListenableFuture<FocusMeteringResult> future = camera.getCameraControl().startFocusAndMetering(action);
|
|
444
526
|
|
|
445
|
-
// Add callback to handle the result
|
|
527
|
+
// Add callback to handle the result with enhanced focus maintenance
|
|
446
528
|
future.addListener(new Runnable() {
|
|
447
529
|
@Override
|
|
448
530
|
public void run() {
|
|
@@ -461,6 +543,11 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
461
543
|
response.put("x", x);
|
|
462
544
|
response.put("y", y);
|
|
463
545
|
|
|
546
|
+
// If manual focus was successful, maintain it with a follow-up action
|
|
547
|
+
if (result.isFocusSuccessful()) {
|
|
548
|
+
maintainFocusAtPoint(previewX, previewY);
|
|
549
|
+
}
|
|
550
|
+
|
|
464
551
|
call.resolve(response);
|
|
465
552
|
} catch (Exception e) {
|
|
466
553
|
Log.e("Camera", "Focus operation failed", e);
|
|
@@ -477,6 +564,51 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
477
564
|
});
|
|
478
565
|
}
|
|
479
566
|
|
|
567
|
+
/**
|
|
568
|
+
* Maintain focus at a specific point with repeated focus actions for stability
|
|
569
|
+
*/
|
|
570
|
+
private void maintainFocusAtPoint(float previewX, float previewY) {
|
|
571
|
+
if (camera == null || previewView == null) return;
|
|
572
|
+
|
|
573
|
+
// Use a separate executor for focus maintenance
|
|
574
|
+
ExecutorService focusMaintainExecutor = Executors.newSingleThreadExecutor();
|
|
575
|
+
|
|
576
|
+
focusMaintainExecutor.execute(new Runnable() {
|
|
577
|
+
@Override
|
|
578
|
+
public void run() {
|
|
579
|
+
try {
|
|
580
|
+
// Maintain focus for 10 seconds with periodic refocus
|
|
581
|
+
for (int i = 0; i < 5; i++) {
|
|
582
|
+
Thread.sleep(2000); // Wait 2 seconds between focus actions
|
|
583
|
+
|
|
584
|
+
getActivity().runOnUiThread(new Runnable() {
|
|
585
|
+
@Override
|
|
586
|
+
public void run() {
|
|
587
|
+
try {
|
|
588
|
+
if (camera != null && camera.getCameraInfo().getCameraState().getValue().getType() == CameraState.Type.OPEN) {
|
|
589
|
+
MeteringPointFactory factory = previewView.getMeteringPointFactory();
|
|
590
|
+
MeteringPoint maintainPoint = factory.createPoint(previewX, previewY);
|
|
591
|
+
|
|
592
|
+
FocusMeteringAction maintainAction = new FocusMeteringAction.Builder(maintainPoint)
|
|
593
|
+
.setAutoCancelDuration(3, TimeUnit.SECONDS)
|
|
594
|
+
.build();
|
|
595
|
+
|
|
596
|
+
camera.getCameraControl().startFocusAndMetering(maintainAction);
|
|
597
|
+
Log.d("Camera", "Maintaining focus at tapped point");
|
|
598
|
+
}
|
|
599
|
+
} catch (Exception e) {
|
|
600
|
+
Log.d("Camera", "Focus maintenance failed: " + e.getMessage());
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
} catch (InterruptedException e) {
|
|
606
|
+
Log.d("Camera", "Focus maintenance interrupted");
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
|
|
480
612
|
@PluginMethod
|
|
481
613
|
public void setAutoFocusMode(PluginCall call) {
|
|
482
614
|
if (camera == null) {
|
|
@@ -488,12 +620,17 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
488
620
|
@Override
|
|
489
621
|
public void run() {
|
|
490
622
|
try {
|
|
491
|
-
String mode = call.getString("mode", "
|
|
623
|
+
String mode = call.getString("mode", "continuous");
|
|
624
|
+
boolean enableFastTransitions = call.getBoolean("enableFastTransitions", true);
|
|
625
|
+
boolean enableAdaptiveFocus = call.getBoolean("enableAdaptiveFocus", true);
|
|
492
626
|
|
|
493
627
|
switch (mode.toLowerCase()) {
|
|
494
628
|
case "auto":
|
|
495
|
-
// Default auto-focus behavior
|
|
496
|
-
|
|
629
|
+
// Default auto-focus behavior with optimization
|
|
630
|
+
if (enableFastTransitions) {
|
|
631
|
+
// Reset focus to enable faster transitions
|
|
632
|
+
camera.getCameraControl().cancelFocusAndMetering();
|
|
633
|
+
}
|
|
497
634
|
break;
|
|
498
635
|
case "manual":
|
|
499
636
|
// For manual focus, we would need to disable auto-focus
|
|
@@ -501,7 +638,35 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
501
638
|
Log.w("Camera", "Manual focus mode not fully supported in CameraX");
|
|
502
639
|
break;
|
|
503
640
|
case "continuous":
|
|
504
|
-
//
|
|
641
|
+
// Enhanced continuous focus mode with adaptive focusing
|
|
642
|
+
if (enableFastTransitions) {
|
|
643
|
+
// Cancel and restart for faster focus transitions
|
|
644
|
+
camera.getCameraControl().cancelFocusAndMetering();
|
|
645
|
+
}
|
|
646
|
+
if (enableAdaptiveFocus) {
|
|
647
|
+
// Start adaptive continuous focus for better near/far transitions
|
|
648
|
+
startAdaptiveContinuousFocus();
|
|
649
|
+
}
|
|
650
|
+
break;
|
|
651
|
+
case "macro":
|
|
652
|
+
// Optimized for close-up focusing
|
|
653
|
+
Log.i("Camera", "Macro focus mode - optimized for close distances");
|
|
654
|
+
break;
|
|
655
|
+
case "infinity":
|
|
656
|
+
// Optimized for far distance focusing
|
|
657
|
+
// Focus at center point with infinity bias
|
|
658
|
+
if (previewView != null) {
|
|
659
|
+
MeteringPointFactory factory = previewView.getMeteringPointFactory();
|
|
660
|
+
float centerX = previewView.getWidth() / 2.0f;
|
|
661
|
+
float centerY = previewView.getHeight() / 2.0f;
|
|
662
|
+
MeteringPoint centerPoint = factory.createPoint(centerX, centerY);
|
|
663
|
+
|
|
664
|
+
FocusMeteringAction infinityAction = new FocusMeteringAction.Builder(centerPoint)
|
|
665
|
+
.setAutoCancelDuration(1, TimeUnit.SECONDS)
|
|
666
|
+
.build();
|
|
667
|
+
|
|
668
|
+
camera.getCameraControl().startFocusAndMetering(infinityAction);
|
|
669
|
+
}
|
|
505
670
|
break;
|
|
506
671
|
default:
|
|
507
672
|
call.reject("Unsupported focus mode: " + mode);
|
|
@@ -511,6 +676,8 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
511
676
|
JSObject result = new JSObject();
|
|
512
677
|
result.put("success", true);
|
|
513
678
|
result.put("mode", mode);
|
|
679
|
+
result.put("enableFastTransitions", enableFastTransitions);
|
|
680
|
+
result.put("enableAdaptiveFocus", enableAdaptiveFocus);
|
|
514
681
|
call.resolve(result);
|
|
515
682
|
} catch (Exception e) {
|
|
516
683
|
call.reject("Error setting auto focus mode: " + e.getMessage());
|
|
@@ -519,6 +686,70 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
519
686
|
});
|
|
520
687
|
}
|
|
521
688
|
|
|
689
|
+
/**
|
|
690
|
+
* Start adaptive continuous focus for better handling of near/far object transitions
|
|
691
|
+
*/
|
|
692
|
+
private void startAdaptiveContinuousFocus() {
|
|
693
|
+
if (camera == null || previewView == null) return;
|
|
694
|
+
|
|
695
|
+
ExecutorService adaptiveFocusExecutor = Executors.newSingleThreadExecutor();
|
|
696
|
+
|
|
697
|
+
adaptiveFocusExecutor.execute(new Runnable() {
|
|
698
|
+
@Override
|
|
699
|
+
public void run() {
|
|
700
|
+
try {
|
|
701
|
+
// Create multiple focus points for better scene coverage
|
|
702
|
+
float[] focusPoints = {
|
|
703
|
+
0.3f, 0.3f, // Top-left quadrant
|
|
704
|
+
0.7f, 0.3f, // Top-right quadrant
|
|
705
|
+
0.5f, 0.5f, // Center
|
|
706
|
+
0.3f, 0.7f, // Bottom-left quadrant
|
|
707
|
+
0.7f, 0.7f // Bottom-right quadrant
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
int pointIndex = 0;
|
|
711
|
+
|
|
712
|
+
while (camera != null && camera.getCameraInfo().getCameraState().getValue().getType() == CameraState.Type.OPEN) {
|
|
713
|
+
Thread.sleep(1500); // Focus check every 1.5 seconds
|
|
714
|
+
|
|
715
|
+
final int currentPointIndex = pointIndex;
|
|
716
|
+
|
|
717
|
+
getActivity().runOnUiThread(new Runnable() {
|
|
718
|
+
@Override
|
|
719
|
+
public void run() {
|
|
720
|
+
try {
|
|
721
|
+
if (camera != null && previewView != null) {
|
|
722
|
+
MeteringPointFactory factory = previewView.getMeteringPointFactory();
|
|
723
|
+
|
|
724
|
+
// Use current focus point from the array
|
|
725
|
+
float x = focusPoints[currentPointIndex * 2] * previewView.getWidth();
|
|
726
|
+
float y = focusPoints[currentPointIndex * 2 + 1] * previewView.getHeight();
|
|
727
|
+
|
|
728
|
+
MeteringPoint adaptivePoint = factory.createPoint(x, y);
|
|
729
|
+
|
|
730
|
+
FocusMeteringAction adaptiveAction = new FocusMeteringAction.Builder(adaptivePoint)
|
|
731
|
+
.setAutoCancelDuration(2, TimeUnit.SECONDS)
|
|
732
|
+
.build();
|
|
733
|
+
|
|
734
|
+
camera.getCameraControl().startFocusAndMetering(adaptiveAction);
|
|
735
|
+
Log.d("Camera", "Adaptive focus at point: " + (currentPointIndex + 1));
|
|
736
|
+
}
|
|
737
|
+
} catch (Exception e) {
|
|
738
|
+
Log.d("Camera", "Adaptive focus failed: " + e.getMessage());
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
// Cycle through focus points
|
|
744
|
+
pointIndex = (pointIndex + 1) % (focusPoints.length / 2);
|
|
745
|
+
}
|
|
746
|
+
} catch (InterruptedException e) {
|
|
747
|
+
Log.d("Camera", "Adaptive continuous focus stopped");
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
|
|
522
753
|
@PluginMethod
|
|
523
754
|
public void resetFocus(PluginCall call) {
|
|
524
755
|
if (camera == null) {
|
|
@@ -533,8 +764,28 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
533
764
|
// Cancel any ongoing focus operations
|
|
534
765
|
camera.getCameraControl().cancelFocusAndMetering();
|
|
535
766
|
|
|
767
|
+
// Restart enhanced continuous autofocus for better stability
|
|
768
|
+
boolean restartContinuous = call.getBoolean("restartContinuous", true);
|
|
769
|
+
if (restartContinuous && previewView != null) {
|
|
770
|
+
// Set focus to center of screen to restart continuous AF with improved settings
|
|
771
|
+
MeteringPointFactory factory = previewView.getMeteringPointFactory();
|
|
772
|
+
float centerX = previewView.getWidth() / 2.0f;
|
|
773
|
+
float centerY = previewView.getHeight() / 2.0f;
|
|
774
|
+
MeteringPoint centerPoint = factory.createPoint(centerX, centerY);
|
|
775
|
+
|
|
776
|
+
FocusMeteringAction restartAction = new FocusMeteringAction.Builder(centerPoint)
|
|
777
|
+
.setAutoCancelDuration(5, TimeUnit.SECONDS) // Increased duration for stability
|
|
778
|
+
.build();
|
|
779
|
+
|
|
780
|
+
camera.getCameraControl().startFocusAndMetering(restartAction);
|
|
781
|
+
|
|
782
|
+
// Restart the continuous auto-focus monitoring
|
|
783
|
+
startContinuousAutoFocus();
|
|
784
|
+
}
|
|
785
|
+
|
|
536
786
|
JSObject result = new JSObject();
|
|
537
787
|
result.put("success", true);
|
|
788
|
+
result.put("restartedContinuous", restartContinuous);
|
|
538
789
|
call.resolve(result);
|
|
539
790
|
} catch (Exception e) {
|
|
540
791
|
call.reject("Error resetting focus: " + e.getMessage());
|
package/package.json
CHANGED