@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.
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraPreview.java +25 -20
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java +5 -4
- package/ios/Sources/CapgoCameraPreviewPlugin/CameraController.swift +51 -1
- package/ios/Sources/CapgoCameraPreviewPlugin/Plugin.swift +1 -1
- package/package.json +1 -1
|
@@ -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 (
|
|
2255
|
-
FocusMeteringAction action = new FocusMeteringAction.Builder(
|
|
2256
|
-
|
|
2257
|
-
.
|
|
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.
|
|
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] = [
|