@capgo/camera-preview 8.1.3 → 8.1.5

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.
@@ -1520,6 +1520,31 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
1520
1520
 
1521
1521
  @Override
1522
1522
  public void onCameraStarted(int width, int height, int x, int y) {
1523
+ // Always transition window and WebView backgrounds to transparent when the camera starts,
1524
+ // regardless of whether there is a pending JS call. This is critical for the
1525
+ // background/foreground resume cycle: on resume, handleOnResume() sets backgrounds to
1526
+ // black (to prevent flicker) and then restarts the camera session, but
1527
+ // cameraStartCallbackId is null at that point. Without this unconditional block the
1528
+ // window and WebView stay black after every background/foreground transition.
1529
+ // Both backgrounds are set together in the same UI thread operation to avoid race
1530
+ // conditions and compositor layering issues.
1531
+ if (isToBackMode()) {
1532
+ getBridge()
1533
+ .getActivity()
1534
+ .runOnUiThread(() -> {
1535
+ try {
1536
+ // Set window background to transparent
1537
+ getBridge().getActivity().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
1538
+ // Set webview background to almost-transparent for MIUI compatibility
1539
+ // Use alpha=1 instead of 0 to work around MIUI/Xiaomi rendering issues
1540
+ // where Color.TRANSPARENT (alpha=0) is not rendered correctly
1541
+ getBridge().getWebView().setBackgroundColor(Color.argb(1, 255, 255, 255));
1542
+ } catch (Exception e) {
1543
+ Log.w(TAG, "Failed to set backgrounds to transparent", e);
1544
+ }
1545
+ });
1546
+ }
1547
+
1523
1548
  PluginCall call = bridge.getSavedCall(cameraStartCallbackId);
1524
1549
  if (call != null) {
1525
1550
  // Convert pixel values back to logical units
@@ -1650,26 +1675,6 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
1650
1675
  ")"
1651
1676
  );
1652
1677
 
1653
- // Transition window and webview backgrounds to transparent now that camera is ready
1654
- // This prevents flickering during camera initialization
1655
- // Both are set together in the same UI thread operation to avoid race conditions
1656
- if (isToBackMode()) {
1657
- getBridge()
1658
- .getActivity()
1659
- .runOnUiThread(() -> {
1660
- try {
1661
- // Set window background to transparent
1662
- getBridge().getActivity().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
1663
- // Set webview background to almost-transparent for MIUI compatibility
1664
- // Use alpha=1 instead of 0 to work around MIUI/Xiaomi rendering issues
1665
- // where Color.TRANSPARENT (alpha=0) is not rendered correctly
1666
- getBridge().getWebView().setBackgroundColor(Color.argb(1, 255, 255, 255));
1667
- } catch (Exception e) {
1668
- Log.w(TAG, "Failed to set backgrounds to transparent", e);
1669
- }
1670
- });
1671
- }
1672
-
1673
1678
  call.resolve(result);
1674
1679
  bridge.releaseCall(call);
1675
1680
  cameraStartCallbackId = null; // Prevent re-use
@@ -2251,10 +2251,11 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
2251
2251
  MeteringPointFactory factory = previewView.getMeteringPointFactory();
2252
2252
  MeteringPoint point = factory.createPoint(x * viewWidth, y * viewHeight);
2253
2253
 
2254
- // Create focus and metering action (persistent, no auto-cancel) to match iOS behavior
2255
- FocusMeteringAction action = new FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE)
2256
- .disableAutoCancel()
2257
- .build();
2254
+ // Create focus and metering action (resets after time to allow for auto focusing on movement later)
2255
+ FocusMeteringAction action = new FocusMeteringAction.Builder(
2256
+ point,
2257
+ FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE
2258
+ ).build();
2258
2259
 
2259
2260
  if (IsOperationRunning("setFocus")) {
2260
2261
  Log.d(TAG, "setFocus: Ignored because stop is pending");
@@ -56,6 +56,43 @@ class CameraController: NSObject {
56
56
  }
57
57
  }
58
58
 
59
+ // Continuous focus with significant movement if focus was locked from setFocus earlier
60
+ @objc private func subjectAreaDidChange(notification: NSNotification) {
61
+ guard let device = self.currentCameraPosition == .rear ? rearCamera : frontCamera else { return }
62
+
63
+ do {
64
+ try device.lockForConfiguration()
65
+ defer { device.unlockForConfiguration() }
66
+
67
+ // Reset Focus to the center and make it continuous
68
+ if device.isFocusModeSupported(.continuousAutoFocus) {
69
+ device.focusMode = .continuousAutoFocus
70
+ if device.isFocusPointOfInterestSupported {
71
+ device.focusPointOfInterest = CGPoint(x: 0.5, y: 0.5)
72
+ }
73
+ }
74
+
75
+ // 2. Reset Exposure to the center ONLY if it is not explicitly locked
76
+ if device.exposureMode != .locked {
77
+ if device.isExposureModeSupported(.continuousAutoExposure) {
78
+ device.exposureMode = .continuousAutoExposure
79
+ if device.isExposurePointOfInterestSupported {
80
+ device.exposurePointOfInterest = CGPoint(x: 0.5, y: 0.5)
81
+ }
82
+ device.setExposureTargetBias(0.0) { _ in }
83
+ }
84
+ }
85
+
86
+ // 3. Turn off monitoring until the user taps to focus again
87
+ device.isSubjectAreaChangeMonitoringEnabled = false
88
+
89
+ print("[CameraPreview] Phone moved: Reset focus. Exposure reset skipped if locked.")
90
+
91
+ } catch {
92
+ print("[CameraPreview] Failed to reset focus after subject area change: \(error)")
93
+ }
94
+ }
95
+
59
96
  var captureSession: AVCaptureSession?
60
97
  var disableFocusIndicator: Bool = false
61
98
 
@@ -446,6 +483,10 @@ extension CameraController {
446
483
  // Set initial zoom
447
484
  self.setInitialZoom(level: initialZoomLevel)
448
485
 
486
+ // Set up listener for change in subject area of camera feed
487
+ NotificationCenter.default.removeObserver(self, name: .AVCaptureDeviceSubjectAreaDidChange, object: nil)
488
+ NotificationCenter.default.addObserver(self, selector: #selector(self.subjectAreaDidChange), name: .AVCaptureDeviceSubjectAreaDidChange, object: nil)
489
+
449
490
  // Start the session - all outputs are already configured
450
491
  captureSession.startRunning()
451
492
 
@@ -1827,6 +1868,9 @@ extension CameraController {
1827
1868
  }
1828
1869
  }
1829
1870
 
1871
+ // Turn on subject area monitor for switch to continuous focus if needed
1872
+ device.isSubjectAreaChangeMonitoringEnabled = true
1873
+
1830
1874
  device.unlockForConfiguration()
1831
1875
  } catch {
1832
1876
  throw CameraControllerError.unknown
@@ -1999,6 +2043,8 @@ extension CameraController {
1999
2043
  captureSession.inputs.forEach { captureSession.removeInput($0) }
2000
2044
  captureSession.outputs.forEach { captureSession.removeOutput($0) }
2001
2045
  }
2046
+ // Remove listener for subject area change
2047
+ NotificationCenter.default.removeObserver(self, name: .AVCaptureDeviceSubjectAreaDidChange, object: nil)
2002
2048
 
2003
2049
  self.motionManager.stopAccelerometerUpdates()
2004
2050
  self.previewLayer?.removeFromSuperlayer()
@@ -2240,7 +2286,7 @@ extension CameraController {
2240
2286
  if connection.isEnabled == false { connection.isEnabled = true }
2241
2287
  // Goes off accelerometer now
2242
2288
  connection.videoOrientation = self.getPhysicalOrientation()
2243
-
2289
+
2244
2290
  // Front camera: mirror the recorded video so it looks natural (selfie style).
2245
2291
  if self.currentCameraPosition == .front, connection.isVideoMirroringSupported {
2246
2292
  connection.isVideoMirrored = true
@@ -2317,6 +2363,10 @@ extension CameraController: UIGestureRecognizerDelegate {
2317
2363
  device.setExposureTargetBias(0.0) { _ in }
2318
2364
  }
2319
2365
  }
2366
+
2367
+ // Turn on subject area monitor for switch to continuous focus if needed
2368
+ device.isSubjectAreaChangeMonitoringEnabled = true
2369
+
2320
2370
  } catch {
2321
2371
  debugPrint(error)
2322
2372
  }
@@ -34,7 +34,7 @@ extension UIWindow {
34
34
  */
35
35
  @objc(CameraPreview)
36
36
  public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelegate {
37
- private let pluginVersion: String = "8.1.3"
37
+ private let pluginVersion: String = "8.1.5"
38
38
  public let identifier = "CameraPreviewPlugin"
39
39
  public let jsName = "CameraPreview"
40
40
  public let pluginMethods: [CAPPluginMethod] = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/camera-preview",
3
- "version": "8.1.3",
3
+ "version": "8.1.5",
4
4
  "description": "Camera preview",
5
5
  "license": "MPL-2.0",
6
6
  "repository": {