@capgo/camera-preview 8.4.3 → 8.4.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.
|
@@ -6,7 +6,11 @@ import static android.Manifest.permission.RECORD_AUDIO;
|
|
|
6
6
|
import android.Manifest;
|
|
7
7
|
import android.annotation.SuppressLint;
|
|
8
8
|
import android.app.Activity;
|
|
9
|
+
import android.app.KeyguardManager;
|
|
10
|
+
import android.content.BroadcastReceiver;
|
|
11
|
+
import android.content.Context;
|
|
9
12
|
import android.content.Intent;
|
|
13
|
+
import android.content.IntentFilter;
|
|
10
14
|
import android.content.pm.ActivityInfo;
|
|
11
15
|
import android.content.pm.PackageManager;
|
|
12
16
|
import android.content.res.Configuration;
|
|
@@ -69,12 +73,19 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
69
73
|
|
|
70
74
|
private final String pluginVersion = "";
|
|
71
75
|
|
|
76
|
+
@Override
|
|
77
|
+
public void load() {
|
|
78
|
+
registerScreenLockReceiver();
|
|
79
|
+
}
|
|
80
|
+
|
|
72
81
|
@Override
|
|
73
82
|
protected void handleOnPause() {
|
|
74
83
|
super.handleOnPause();
|
|
84
|
+
activityPaused = true;
|
|
75
85
|
if (cameraXView != null && cameraXView.isRunning()) {
|
|
76
86
|
// Store the current configuration before stopping
|
|
77
87
|
lastSessionConfig = cameraXView.getSessionConfig();
|
|
88
|
+
requestBarcodeScannerRestartAfterCameraResume();
|
|
78
89
|
cameraXView.stopSession();
|
|
79
90
|
}
|
|
80
91
|
}
|
|
@@ -82,6 +93,8 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
82
93
|
@Override
|
|
83
94
|
protected void handleOnResume() {
|
|
84
95
|
super.handleOnResume();
|
|
96
|
+
registerScreenLockReceiver();
|
|
97
|
+
activityPaused = false;
|
|
85
98
|
if (lastSessionConfig != null) {
|
|
86
99
|
// Recreate camera with last known configuration
|
|
87
100
|
if (cameraXView == null || !cameraXView.isRunning() || cameraXView.isStopping()) {
|
|
@@ -97,6 +110,8 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
97
110
|
} else {
|
|
98
111
|
toBackVisualStateActive = false;
|
|
99
112
|
}
|
|
113
|
+
requestBarcodeScannerRestartAfterCameraResume();
|
|
114
|
+
cameraRestartAfterResumeInProgress = true;
|
|
100
115
|
cameraXView.startSession(lastSessionConfig);
|
|
101
116
|
}
|
|
102
117
|
}
|
|
@@ -109,10 +124,12 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
109
124
|
cameraXView = null;
|
|
110
125
|
}
|
|
111
126
|
lastSessionConfig = null;
|
|
127
|
+
clearActiveBarcodeScanner();
|
|
112
128
|
toBackVisualStateActive = false;
|
|
113
129
|
restoreOriginalWindowBackground(getBridge().getActivity());
|
|
114
130
|
restoreWebViewVisualState();
|
|
115
131
|
restoreSystemUiForToBackMode(getBridge().getActivity());
|
|
132
|
+
unregisterScreenLockReceiver();
|
|
116
133
|
}
|
|
117
134
|
|
|
118
135
|
private CameraSessionConfiguration lastSessionConfig;
|
|
@@ -155,6 +172,26 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
155
172
|
private boolean pendingStartBarcodeScanner = false;
|
|
156
173
|
private List<String> pendingStartBarcodeFormats = new ArrayList<>();
|
|
157
174
|
private int pendingStartBarcodeDetectionInterval = 500;
|
|
175
|
+
private final Object activeBarcodeScannerLock = new Object();
|
|
176
|
+
private boolean activeBarcodeScanner = false;
|
|
177
|
+
private List<String> activeBarcodeFormats = new ArrayList<>();
|
|
178
|
+
private int activeBarcodeDetectionInterval = 500;
|
|
179
|
+
private boolean restartBarcodeScannerAfterCameraResume = false;
|
|
180
|
+
private boolean screenLocked = false;
|
|
181
|
+
private boolean activityPaused = false;
|
|
182
|
+
private boolean cameraRestartAfterResumeInProgress = false;
|
|
183
|
+
private BroadcastReceiver screenLockReceiver;
|
|
184
|
+
|
|
185
|
+
private static final class BarcodeScannerRequest {
|
|
186
|
+
|
|
187
|
+
private final List<String> formats;
|
|
188
|
+
private final int detectionInterval;
|
|
189
|
+
|
|
190
|
+
private BarcodeScannerRequest(List<String> formats, int detectionInterval) {
|
|
191
|
+
this.formats = formats;
|
|
192
|
+
this.detectionInterval = detectionInterval;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
158
195
|
|
|
159
196
|
@PluginMethod
|
|
160
197
|
public void getExposureModes(PluginCall call) {
|
|
@@ -426,6 +463,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
426
463
|
new CameraXView.BarcodeScannerStartCallback() {
|
|
427
464
|
@Override
|
|
428
465
|
public void onStarted() {
|
|
466
|
+
markActiveBarcodeScanner(formats, detectionInterval != null ? detectionInterval : 500);
|
|
429
467
|
call.resolve();
|
|
430
468
|
}
|
|
431
469
|
|
|
@@ -439,6 +477,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
439
477
|
|
|
440
478
|
@PluginMethod
|
|
441
479
|
public void stopBarcodeScanner(PluginCall call) {
|
|
480
|
+
clearActiveBarcodeScanner();
|
|
442
481
|
if (cameraXView != null) {
|
|
443
482
|
cameraXView.stopBarcodeScanner();
|
|
444
483
|
}
|
|
@@ -500,6 +539,176 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
500
539
|
pendingStartBarcodeDetectionInterval = 500;
|
|
501
540
|
}
|
|
502
541
|
|
|
542
|
+
private void markActiveBarcodeScanner(List<String> formats, int detectionInterval) {
|
|
543
|
+
synchronized (activeBarcodeScannerLock) {
|
|
544
|
+
activeBarcodeScanner = true;
|
|
545
|
+
activeBarcodeFormats = new ArrayList<>(formats);
|
|
546
|
+
activeBarcodeDetectionInterval = detectionInterval;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
private void clearActiveBarcodeScanner() {
|
|
551
|
+
synchronized (activeBarcodeScannerLock) {
|
|
552
|
+
activeBarcodeScanner = false;
|
|
553
|
+
activeBarcodeFormats = new ArrayList<>();
|
|
554
|
+
activeBarcodeDetectionInterval = 500;
|
|
555
|
+
restartBarcodeScannerAfterCameraResume = false;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
private void requestBarcodeScannerRestartAfterCameraResume() {
|
|
560
|
+
synchronized (activeBarcodeScannerLock) {
|
|
561
|
+
if (activeBarcodeScanner) {
|
|
562
|
+
restartBarcodeScannerAfterCameraResume = true;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
private BarcodeScannerRequest consumeBarcodeScannerRestartRequest() {
|
|
568
|
+
synchronized (activeBarcodeScannerLock) {
|
|
569
|
+
if (!activeBarcodeScanner || !restartBarcodeScannerAfterCameraResume) {
|
|
570
|
+
return null;
|
|
571
|
+
}
|
|
572
|
+
restartBarcodeScannerAfterCameraResume = false;
|
|
573
|
+
return new BarcodeScannerRequest(new ArrayList<>(activeBarcodeFormats), activeBarcodeDetectionInterval);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
private void restoreBarcodeScannerRestartRequest(BarcodeScannerRequest request) {
|
|
578
|
+
if (request == null) {
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
synchronized (activeBarcodeScannerLock) {
|
|
582
|
+
if (activeBarcodeScanner) {
|
|
583
|
+
restartBarcodeScannerAfterCameraResume = true;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
private void restartBarcodeScannerAfterCameraResumeIfNeeded() {
|
|
589
|
+
if (screenLocked || isDeviceLocked()) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
BarcodeScannerRequest request = consumeBarcodeScannerRestartRequest();
|
|
598
|
+
if (request == null) {
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
cameraXView.startBarcodeScanner(
|
|
603
|
+
request.formats,
|
|
604
|
+
request.detectionInterval,
|
|
605
|
+
new CameraXView.BarcodeScannerStartCallback() {
|
|
606
|
+
@Override
|
|
607
|
+
public void onStarted() {
|
|
608
|
+
markActiveBarcodeScanner(request.formats, request.detectionInterval);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
@Override
|
|
612
|
+
public void onError(String message) {
|
|
613
|
+
restoreBarcodeScannerRestartRequest(request);
|
|
614
|
+
onBarcodeScanError("Failed to restart barcode scanner after unlock: " + message);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
private void registerScreenLockReceiver() {
|
|
621
|
+
if (screenLockReceiver != null) {
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
screenLockReceiver = new BroadcastReceiver() {
|
|
626
|
+
@Override
|
|
627
|
+
public void onReceive(Context context, Intent intent) {
|
|
628
|
+
String action = intent != null ? intent.getAction() : null;
|
|
629
|
+
if (Intent.ACTION_SCREEN_OFF.equals(action)) {
|
|
630
|
+
handleScreenOff();
|
|
631
|
+
} else if (Intent.ACTION_SCREEN_ON.equals(action)) {
|
|
632
|
+
handleScreenOn();
|
|
633
|
+
} else if (Intent.ACTION_USER_PRESENT.equals(action)) {
|
|
634
|
+
handleScreenUnlocked();
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
IntentFilter filter = new IntentFilter();
|
|
640
|
+
filter.addAction(Intent.ACTION_SCREEN_OFF);
|
|
641
|
+
filter.addAction(Intent.ACTION_SCREEN_ON);
|
|
642
|
+
filter.addAction(Intent.ACTION_USER_PRESENT);
|
|
643
|
+
|
|
644
|
+
Context context = getContext();
|
|
645
|
+
if (context == null) {
|
|
646
|
+
screenLockReceiver = null;
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
650
|
+
context.registerReceiver(screenLockReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
|
|
651
|
+
} else {
|
|
652
|
+
context.registerReceiver(screenLockReceiver, filter);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
private void unregisterScreenLockReceiver() {
|
|
657
|
+
if (screenLockReceiver == null) {
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
try {
|
|
661
|
+
Context context = getContext();
|
|
662
|
+
if (context != null) {
|
|
663
|
+
context.unregisterReceiver(screenLockReceiver);
|
|
664
|
+
}
|
|
665
|
+
} catch (IllegalArgumentException ignored) {
|
|
666
|
+
// Receiver may already be unregistered by the host activity.
|
|
667
|
+
} finally {
|
|
668
|
+
screenLockReceiver = null;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
private boolean isDeviceLocked() {
|
|
673
|
+
Context context = getContext();
|
|
674
|
+
KeyguardManager keyguardManager = context != null ? (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE) : null;
|
|
675
|
+
return keyguardManager != null && keyguardManager.isKeyguardLocked();
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
private void handleScreenOff() {
|
|
679
|
+
screenLocked = true;
|
|
680
|
+
requestBarcodeScannerRestartAfterCameraResume();
|
|
681
|
+
if (cameraXView != null && cameraXView.isRunning() && !cameraXView.isStopping()) {
|
|
682
|
+
lastSessionConfig = cameraXView.getSessionConfig();
|
|
683
|
+
cameraXView.stopSession();
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
private void handleScreenOn() {
|
|
688
|
+
screenLocked = isDeviceLocked();
|
|
689
|
+
if (!screenLocked) {
|
|
690
|
+
restartBarcodeScannerAfterCameraResumeIfNeeded();
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
private void handleScreenUnlocked() {
|
|
695
|
+
screenLocked = false;
|
|
696
|
+
if (
|
|
697
|
+
!activityPaused &&
|
|
698
|
+
!cameraRestartAfterResumeInProgress &&
|
|
699
|
+
lastSessionConfig != null &&
|
|
700
|
+
(cameraXView == null || cameraXView.isStopping())
|
|
701
|
+
) {
|
|
702
|
+
cameraXView = new CameraXView(getContext(), getBridge().getWebView());
|
|
703
|
+
cameraXView.setListener(this);
|
|
704
|
+
requestBarcodeScannerRestartAfterCameraResume();
|
|
705
|
+
cameraRestartAfterResumeInProgress = true;
|
|
706
|
+
cameraXView.startSession(lastSessionConfig);
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
restartBarcodeScannerAfterCameraResumeIfNeeded();
|
|
710
|
+
}
|
|
711
|
+
|
|
503
712
|
@PluginMethod
|
|
504
713
|
public void stop(final PluginCall call) {
|
|
505
714
|
boolean force = Boolean.TRUE.equals(call.getBoolean("force", false));
|
|
@@ -532,6 +741,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
532
741
|
}
|
|
533
742
|
// Manual stops should not trigger automatic resume with stale config
|
|
534
743
|
lastSessionConfig = null;
|
|
744
|
+
clearActiveBarcodeScanner();
|
|
535
745
|
toBackVisualStateActive = false;
|
|
536
746
|
restoreOriginalWindowBackground(getBridge().getActivity());
|
|
537
747
|
restoreWebViewVisualState();
|
|
@@ -1340,6 +1550,9 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1340
1550
|
config.setEnablePhysicalDeviceSelection(enablePhysicalDeviceSelection);
|
|
1341
1551
|
config.setBarcodeScannerEnabled(barcodeScannerOptions != null);
|
|
1342
1552
|
setPendingStartBarcodeScanner(barcodeScannerOptions);
|
|
1553
|
+
if (barcodeScannerOptions == null) {
|
|
1554
|
+
clearActiveBarcodeScanner();
|
|
1555
|
+
}
|
|
1343
1556
|
|
|
1344
1557
|
bridge.saveCall(call);
|
|
1345
1558
|
cameraStartCallbackId = call.getCallbackId();
|
|
@@ -1627,6 +1840,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1627
1840
|
Log.d(TAG, "onCameraStopped: ignoring callback from stale instance");
|
|
1628
1841
|
return;
|
|
1629
1842
|
}
|
|
1843
|
+
cameraRestartAfterResumeInProgress = false;
|
|
1630
1844
|
// Ensure reference is cleared once the originating CameraXView has fully stopped
|
|
1631
1845
|
if (source != null && cameraXView == source) {
|
|
1632
1846
|
cameraXView = null;
|
|
@@ -1925,6 +2139,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1925
2139
|
|
|
1926
2140
|
@Override
|
|
1927
2141
|
public void onCameraStarted(int width, int height, int x, int y) {
|
|
2142
|
+
cameraRestartAfterResumeInProgress = false;
|
|
1928
2143
|
// Always transition window and WebView backgrounds to transparent when the camera starts,
|
|
1929
2144
|
// regardless of whether there is a pending JS call. This is critical for the
|
|
1930
2145
|
// background/foreground resume cycle: on resume, handleOnResume() sets backgrounds to
|
|
@@ -2085,6 +2300,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
2085
2300
|
new CameraXView.BarcodeScannerStartCallback() {
|
|
2086
2301
|
@Override
|
|
2087
2302
|
public void onStarted() {
|
|
2303
|
+
markActiveBarcodeScanner(formats, detectionInterval);
|
|
2088
2304
|
resolveCameraStartCall(call, result);
|
|
2089
2305
|
}
|
|
2090
2306
|
|
|
@@ -2099,6 +2315,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
2099
2315
|
|
|
2100
2316
|
resolveCameraStartCall(call, result);
|
|
2101
2317
|
}
|
|
2318
|
+
restartBarcodeScannerAfterCameraResumeIfNeeded();
|
|
2102
2319
|
}
|
|
2103
2320
|
|
|
2104
2321
|
private void resolveCameraStartCall(PluginCall call, JSObject result) {
|
|
@@ -2161,6 +2378,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
2161
2378
|
Log.d(TAG, "onCameraStartError: ignoring callback from stale instance");
|
|
2162
2379
|
return;
|
|
2163
2380
|
}
|
|
2381
|
+
cameraRestartAfterResumeInProgress = false;
|
|
2164
2382
|
toBackVisualStateActive = false;
|
|
2165
2383
|
if (cameraXView == source) {
|
|
2166
2384
|
try {
|
|
@@ -35,7 +35,7 @@ extension UIWindow {
|
|
|
35
35
|
*/
|
|
36
36
|
@objc(CameraPreview)
|
|
37
37
|
public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelegate {
|
|
38
|
-
private let pluginVersion: String = "8.4.
|
|
38
|
+
private let pluginVersion: String = "8.4.5"
|
|
39
39
|
public let identifier = "CameraPreviewPlugin"
|
|
40
40
|
public let jsName = "CameraPreview"
|
|
41
41
|
public let pluginMethods: [CAPPluginMethod] = [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capgo/camera-preview",
|
|
3
|
-
"version": "8.4.
|
|
3
|
+
"version": "8.4.5",
|
|
4
4
|
"description": "Camera preview",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -55,7 +55,10 @@
|
|
|
55
55
|
"maestro:smoke": "bunx maestro test .maestro/camera-preview-smoke.yaml",
|
|
56
56
|
"watch": "tsc --watch",
|
|
57
57
|
"prepublishOnly": "bun run build",
|
|
58
|
-
"check:wiring": "node scripts/check-capacitor-plugin-wiring.mjs"
|
|
58
|
+
"check:wiring": "node scripts/check-capacitor-plugin-wiring.mjs",
|
|
59
|
+
"example:install": "cd example-app && bun install --frozen-lockfile",
|
|
60
|
+
"example:build": "bun run build && cd example-app && bun install --frozen-lockfile && bun run build",
|
|
61
|
+
"example:capgo:deploy": "bun run example:build && bun scripts/deploy-example-capgo.mjs"
|
|
59
62
|
},
|
|
60
63
|
"devDependencies": {
|
|
61
64
|
"@capacitor/android": "^8.0.0",
|