@capgo/camera-preview 8.3.7 → 8.3.8-beta.pr356.31.1
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/README.md +187 -35
- package/android/build.gradle +1 -0
- package/android/src/main/AndroidManifest.xml +5 -0
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraPreview.java +337 -9
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java +280 -0
- package/dist/docs.json +345 -11
- package/dist/esm/definitions.d.ts +83 -7
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +13 -3
- package/dist/esm/web.js +169 -9
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +169 -9
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +169 -9
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoCameraPreviewPlugin/CameraController.swift +167 -1
- package/ios/Sources/CapgoCameraPreviewPlugin/Plugin.swift +72 -3
- package/package.json +7 -3
|
@@ -46,9 +46,11 @@ import com.getcapacitor.annotation.Permission;
|
|
|
46
46
|
import com.getcapacitor.annotation.PermissionCallback;
|
|
47
47
|
import com.google.android.gms.location.FusedLocationProviderClient;
|
|
48
48
|
import com.google.android.gms.location.LocationServices;
|
|
49
|
+
import java.util.ArrayList;
|
|
49
50
|
import java.util.List;
|
|
50
51
|
import java.util.Locale;
|
|
51
52
|
import java.util.Objects;
|
|
53
|
+
import org.json.JSONArray;
|
|
52
54
|
import org.json.JSONObject;
|
|
53
55
|
|
|
54
56
|
@CapacitorPlugin(
|
|
@@ -108,6 +110,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
108
110
|
cameraXView = null;
|
|
109
111
|
}
|
|
110
112
|
lastSessionConfig = null;
|
|
113
|
+
restoreSystemUiForToBackMode(getBridge().getActivity());
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
private CameraSessionConfiguration lastSessionConfig;
|
|
@@ -133,10 +136,17 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
133
136
|
private int lastOrientation = Configuration.ORIENTATION_UNDEFINED;
|
|
134
137
|
private String lastOrientationStr = "unknown";
|
|
135
138
|
private boolean lastDisableAudio = true;
|
|
139
|
+
private boolean lastIncludeSafeAreaInsets = false;
|
|
136
140
|
private Drawable originalWindowBackground;
|
|
137
141
|
private Float originalWebViewAlpha;
|
|
138
142
|
private Drawable originalWebViewParentBackground;
|
|
143
|
+
private Integer originalStatusBarColor;
|
|
144
|
+
private Integer originalNavigationBarColor;
|
|
145
|
+
private Boolean originalNavigationBarContrastEnforced;
|
|
139
146
|
private boolean isCameraPermissionDialogShowing = false;
|
|
147
|
+
private boolean pendingStartBarcodeScanner = false;
|
|
148
|
+
private List<String> pendingStartBarcodeFormats = new ArrayList<>();
|
|
149
|
+
private int pendingStartBarcodeDetectionInterval = 500;
|
|
140
150
|
|
|
141
151
|
@PluginMethod
|
|
142
152
|
public void getExposureModes(PluginCall call) {
|
|
@@ -393,6 +403,95 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
393
403
|
cameraXView.captureSample(quality);
|
|
394
404
|
}
|
|
395
405
|
|
|
406
|
+
@PluginMethod
|
|
407
|
+
public void startBarcodeScanner(PluginCall call) {
|
|
408
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
409
|
+
call.reject("Camera is not running");
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
List<String> formats = getStringArray(call, "formats");
|
|
414
|
+
Integer detectionInterval = call.getInt("detectionInterval", 500);
|
|
415
|
+
cameraXView.startBarcodeScanner(
|
|
416
|
+
formats,
|
|
417
|
+
detectionInterval != null ? detectionInterval : 500,
|
|
418
|
+
new CameraXView.BarcodeScannerStartCallback() {
|
|
419
|
+
@Override
|
|
420
|
+
public void onStarted() {
|
|
421
|
+
call.resolve();
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
@Override
|
|
425
|
+
public void onError(String message) {
|
|
426
|
+
call.reject(message);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
@PluginMethod
|
|
433
|
+
public void stopBarcodeScanner(PluginCall call) {
|
|
434
|
+
if (cameraXView != null) {
|
|
435
|
+
cameraXView.stopBarcodeScanner();
|
|
436
|
+
}
|
|
437
|
+
call.resolve();
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
private List<String> getStringArray(PluginCall call, String key) {
|
|
441
|
+
List<String> result = new ArrayList<>();
|
|
442
|
+
JSArray array = call.getArray(key);
|
|
443
|
+
if (array == null) {
|
|
444
|
+
return result;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
for (int i = 0; i < array.length(); i++) {
|
|
448
|
+
String value = array.optString(i, null);
|
|
449
|
+
if (value != null && !value.isEmpty()) {
|
|
450
|
+
result.add(value);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return result;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
private List<String> getStringArray(JSONObject object, String key) {
|
|
457
|
+
List<String> result = new ArrayList<>();
|
|
458
|
+
JSONArray array = object.optJSONArray(key);
|
|
459
|
+
if (array == null) {
|
|
460
|
+
return result;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
for (int i = 0; i < array.length(); i++) {
|
|
464
|
+
String value = array.optString(i, null);
|
|
465
|
+
if (value != null && !value.isEmpty()) {
|
|
466
|
+
result.add(value);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return result;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
private JSONObject getStartBarcodeScannerOptions(PluginCall call) {
|
|
473
|
+
Object barcodeScanner = call.getData().opt("barcodeScanner");
|
|
474
|
+
if (Boolean.TRUE.equals(barcodeScanner)) {
|
|
475
|
+
return new JSONObject();
|
|
476
|
+
}
|
|
477
|
+
if (barcodeScanner instanceof JSONObject) {
|
|
478
|
+
return (JSONObject) barcodeScanner;
|
|
479
|
+
}
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
private void setPendingStartBarcodeScanner(JSONObject options) {
|
|
484
|
+
pendingStartBarcodeScanner = options != null;
|
|
485
|
+
pendingStartBarcodeFormats = options != null ? getStringArray(options, "formats") : new ArrayList<>();
|
|
486
|
+
pendingStartBarcodeDetectionInterval = options != null ? options.optInt("detectionInterval", 500) : 500;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
private void resetPendingStartBarcodeScanner() {
|
|
490
|
+
pendingStartBarcodeScanner = false;
|
|
491
|
+
pendingStartBarcodeFormats = new ArrayList<>();
|
|
492
|
+
pendingStartBarcodeDetectionInterval = 500;
|
|
493
|
+
}
|
|
494
|
+
|
|
396
495
|
@PluginMethod
|
|
397
496
|
public void stop(final PluginCall call) {
|
|
398
497
|
boolean force = Boolean.TRUE.equals(call.getBoolean("force", false));
|
|
@@ -433,6 +532,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
433
532
|
originalWindowBackground = null;
|
|
434
533
|
}
|
|
435
534
|
restoreWebViewVisualState();
|
|
535
|
+
restoreSystemUiForToBackMode(getBridge().getActivity());
|
|
436
536
|
call.resolve();
|
|
437
537
|
});
|
|
438
538
|
}
|
|
@@ -901,6 +1001,8 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
901
1001
|
final boolean lockOrientation = Boolean.TRUE.equals(call.getBoolean("lockAndroidOrientation", false));
|
|
902
1002
|
final boolean disableAudio = Boolean.TRUE.equals(call.getBoolean("disableAudio", true));
|
|
903
1003
|
this.lastDisableAudio = disableAudio;
|
|
1004
|
+
final boolean includeSafeAreaInsets = Boolean.TRUE.equals(call.getBoolean("includeSafeAreaInsets", false));
|
|
1005
|
+
this.lastIncludeSafeAreaInsets = includeSafeAreaInsets;
|
|
904
1006
|
final String aspectRatio = call.getString("aspectRatio", "4:3");
|
|
905
1007
|
final String aspectMode = call.getString("aspectMode", "contain");
|
|
906
1008
|
final String gridMode = call.getString("gridMode", "none");
|
|
@@ -912,6 +1014,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
912
1014
|
final boolean enableVideoMode = Boolean.TRUE.equals(call.getBoolean("enableVideoMode", false));
|
|
913
1015
|
final boolean enablePhysicalDeviceSelection = Boolean.TRUE.equals(call.getBoolean("enablePhysicalDeviceSelection", false));
|
|
914
1016
|
final String videoQuality = call.getString("videoQuality", "high");
|
|
1017
|
+
final JSONObject barcodeScannerOptions = getStartBarcodeScannerOptions(call);
|
|
915
1018
|
|
|
916
1019
|
// Check for conflict between aspectRatio and size
|
|
917
1020
|
if (call.getData().has("aspectRatio") && (call.getData().has("width") || call.getData().has("height"))) {
|
|
@@ -947,6 +1050,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
947
1050
|
getBridge()
|
|
948
1051
|
.getActivity()
|
|
949
1052
|
.runOnUiThread(() -> {
|
|
1053
|
+
lockSystemUiForToBackMode(getBridge().getActivity(), toBack);
|
|
950
1054
|
// Ensure transparent background when preview is behind the WebView (Android 10 fix)
|
|
951
1055
|
if (toBack) {
|
|
952
1056
|
try {
|
|
@@ -989,6 +1093,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
989
1093
|
// we need to add that offset when placing native views
|
|
990
1094
|
int webViewTopInset = webViewLocationOnScreen[1];
|
|
991
1095
|
boolean isEdgeToEdgeActive = webViewLocationOnScreen[1] > 0;
|
|
1096
|
+
int safeAreaTopInsetPx = includeSafeAreaInsets ? getSafeAreaTopInsetPx() : 0;
|
|
992
1097
|
|
|
993
1098
|
// Log all the positioning information for debugging
|
|
994
1099
|
Log.d("CameraPreview", "WebView Position Debug:");
|
|
@@ -1044,6 +1149,10 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1044
1149
|
|
|
1045
1150
|
Log.d("CameraPreview", " - Using webViewTopInset: " + webViewTopInset);
|
|
1046
1151
|
Log.d("CameraPreview", " - isEdgeToEdgeActive: " + isEdgeToEdgeActive);
|
|
1152
|
+
Log.d(
|
|
1153
|
+
"CameraPreview",
|
|
1154
|
+
" - includeSafeAreaInsets: " + includeSafeAreaInsets + " (safeAreaTopInsetPx=" + safeAreaTopInsetPx + ")"
|
|
1155
|
+
);
|
|
1047
1156
|
|
|
1048
1157
|
// Calculate position - center if x or y is -1
|
|
1049
1158
|
int computedX;
|
|
@@ -1165,6 +1274,17 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1165
1274
|
);
|
|
1166
1275
|
}
|
|
1167
1276
|
|
|
1277
|
+
// Capacitor 8 edge-to-edge: WebView can be at y=0 while JS layout is below system bars.
|
|
1278
|
+
// If requested, apply the top system inset only when the WebView itself isn't already offset.
|
|
1279
|
+
if (!isEdgeToEdgeActive && includeSafeAreaInsets && safeAreaTopInsetPx > 0) {
|
|
1280
|
+
int before = computedY;
|
|
1281
|
+
computedY += safeAreaTopInsetPx;
|
|
1282
|
+
Log.d(
|
|
1283
|
+
"CameraPreview",
|
|
1284
|
+
"Safe-area adjustment: computedY " + before + " + safeAreaTopInsetPx " + safeAreaTopInsetPx + " = " + computedY
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1168
1288
|
Log.d(
|
|
1169
1289
|
"CameraPreview",
|
|
1170
1290
|
"2b. EDGE-TO-EDGE - " + (isEdgeToEdgeActive ? "ACTIVE (inset=" + webViewTopInset + ")" : "INACTIVE")
|
|
@@ -1217,6 +1337,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1217
1337
|
config.setTargetZoom(finalTargetZoom);
|
|
1218
1338
|
config.setCentered(isCentered);
|
|
1219
1339
|
config.setEnablePhysicalDeviceSelection(enablePhysicalDeviceSelection);
|
|
1340
|
+
setPendingStartBarcodeScanner(barcodeScannerOptions);
|
|
1220
1341
|
|
|
1221
1342
|
bridge.saveCall(call);
|
|
1222
1343
|
cameraStartCallbackId = call.getCallbackId();
|
|
@@ -1288,6 +1409,18 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1288
1409
|
|
|
1289
1410
|
// Get current preview bounds before rotation
|
|
1290
1411
|
int[] oldBounds = cameraXView.getCurrentPreviewBounds();
|
|
1412
|
+
if (lastIncludeSafeAreaInsets) {
|
|
1413
|
+
int[] location = new int[2];
|
|
1414
|
+
webView.getLocationOnScreen(location);
|
|
1415
|
+
boolean isWebViewOffset = location[1] > 0;
|
|
1416
|
+
if (!isWebViewOffset) {
|
|
1417
|
+
int safeAreaTopInsetPx = getSafeAreaTopInsetPx();
|
|
1418
|
+
if (safeAreaTopInsetPx > 0) {
|
|
1419
|
+
int safeAreaTopInsetLogical = (int) Math.ceil(safeAreaTopInsetPx / density);
|
|
1420
|
+
oldBounds[1] = Math.max(0, oldBounds[1] - safeAreaTopInsetLogical);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1291
1424
|
Log.d(
|
|
1292
1425
|
TAG,
|
|
1293
1426
|
"Current preview bounds before rotation: x=" +
|
|
@@ -1338,6 +1471,18 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1338
1471
|
// Force aspect ratio recalculation on orientation change
|
|
1339
1472
|
cameraXView.forceAspectRatioRecalculation(ar, null, null, () -> {
|
|
1340
1473
|
int[] bounds = cameraXView.getCurrentPreviewBounds();
|
|
1474
|
+
if (lastIncludeSafeAreaInsets) {
|
|
1475
|
+
int[] location = new int[2];
|
|
1476
|
+
webView.getLocationOnScreen(location);
|
|
1477
|
+
boolean isWebViewOffset = location[1] > 0;
|
|
1478
|
+
if (!isWebViewOffset) {
|
|
1479
|
+
int safeAreaTopInsetPx = getSafeAreaTopInsetPx();
|
|
1480
|
+
if (safeAreaTopInsetPx > 0) {
|
|
1481
|
+
int safeAreaTopInsetLogical = (int) Math.ceil(safeAreaTopInsetPx / density);
|
|
1482
|
+
bounds[1] = Math.max(0, bounds[1] - safeAreaTopInsetLogical);
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1341
1486
|
Log.d(
|
|
1342
1487
|
TAG,
|
|
1343
1488
|
"New bounds after orientation change: x=" +
|
|
@@ -1531,6 +1676,71 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1531
1676
|
return manufacturer.contains("xiaomi") || brand.contains("xiaomi") || brand.contains("redmi") || brand.contains("poco");
|
|
1532
1677
|
}
|
|
1533
1678
|
|
|
1679
|
+
private int toOpaqueColor(int color) {
|
|
1680
|
+
return Color.argb(255, Color.red(color), Color.green(color), Color.blue(color));
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
private void lockSystemUiForToBackMode(Activity activity, boolean toBack) {
|
|
1684
|
+
if (activity == null) {
|
|
1685
|
+
return;
|
|
1686
|
+
}
|
|
1687
|
+
if (!toBack) {
|
|
1688
|
+
restoreSystemUiForToBackMode(activity);
|
|
1689
|
+
return;
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
try {
|
|
1693
|
+
if (originalStatusBarColor == null) {
|
|
1694
|
+
originalStatusBarColor = activity.getWindow().getStatusBarColor();
|
|
1695
|
+
}
|
|
1696
|
+
if (originalNavigationBarColor == null) {
|
|
1697
|
+
originalNavigationBarColor = activity.getWindow().getNavigationBarColor();
|
|
1698
|
+
}
|
|
1699
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && originalNavigationBarContrastEnforced == null) {
|
|
1700
|
+
originalNavigationBarContrastEnforced = activity.getWindow().isNavigationBarContrastEnforced();
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
int statusBarColor = toOpaqueColor(originalStatusBarColor != null ? originalStatusBarColor : Color.BLACK);
|
|
1704
|
+
int navigationBarColor = toOpaqueColor(originalNavigationBarColor != null ? originalNavigationBarColor : Color.BLACK);
|
|
1705
|
+
|
|
1706
|
+
activity.getWindow().setStatusBarColor(statusBarColor);
|
|
1707
|
+
activity.getWindow().setNavigationBarColor(navigationBarColor);
|
|
1708
|
+
|
|
1709
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
1710
|
+
activity.getWindow().setNavigationBarContrastEnforced(false);
|
|
1711
|
+
}
|
|
1712
|
+
} catch (Exception e) {
|
|
1713
|
+
Log.w(TAG, "Failed to lock system UI colors for toBack mode", e);
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
private void restoreSystemUiForToBackMode(Activity activity) {
|
|
1718
|
+
final Integer statusBarColor = originalStatusBarColor;
|
|
1719
|
+
final Integer navigationBarColor = originalNavigationBarColor;
|
|
1720
|
+
final Boolean navigationBarContrastEnforced = originalNavigationBarContrastEnforced;
|
|
1721
|
+
originalStatusBarColor = null;
|
|
1722
|
+
originalNavigationBarColor = null;
|
|
1723
|
+
originalNavigationBarContrastEnforced = null;
|
|
1724
|
+
|
|
1725
|
+
if (activity == null) {
|
|
1726
|
+
return;
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
activity.runOnUiThread(() -> {
|
|
1730
|
+
try {
|
|
1731
|
+
if (statusBarColor != null) {
|
|
1732
|
+
activity.getWindow().setStatusBarColor(statusBarColor);
|
|
1733
|
+
}
|
|
1734
|
+
if (navigationBarColor != null) {
|
|
1735
|
+
activity.getWindow().setNavigationBarColor(navigationBarColor);
|
|
1736
|
+
}
|
|
1737
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && navigationBarContrastEnforced != null) {
|
|
1738
|
+
activity.getWindow().setNavigationBarContrastEnforced(navigationBarContrastEnforced);
|
|
1739
|
+
}
|
|
1740
|
+
} catch (Exception ignored) {}
|
|
1741
|
+
});
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1534
1744
|
private void applyTransparentBackgroundsForToBack() {
|
|
1535
1745
|
if (!isToBackMode()) {
|
|
1536
1746
|
return;
|
|
@@ -1630,14 +1840,25 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1630
1840
|
isEdgeToEdgeActive = webViewTopInset > 0;
|
|
1631
1841
|
}
|
|
1632
1842
|
|
|
1633
|
-
|
|
1634
|
-
|
|
1843
|
+
int safeAreaTopInsetPx = lastIncludeSafeAreaInsets ? getSafeAreaTopInsetPx() : 0;
|
|
1844
|
+
|
|
1845
|
+
// Only convert to relative position if WebView is offset or safe-area insets were applied.
|
|
1846
|
+
int relativeY = y;
|
|
1847
|
+
if (isEdgeToEdgeActive) {
|
|
1848
|
+
relativeY = y - webViewTopInset;
|
|
1849
|
+
} else if (lastIncludeSafeAreaInsets && safeAreaTopInsetPx > 0) {
|
|
1850
|
+
relativeY = y - safeAreaTopInsetPx;
|
|
1851
|
+
}
|
|
1635
1852
|
|
|
1636
1853
|
Log.d("CameraPreview", "========================");
|
|
1637
1854
|
Log.d("CameraPreview", "CAMERA STARTED - POSITION RETURNED:");
|
|
1638
1855
|
Log.d("CameraPreview", "7. RETURNED (pixels) - x=" + x + ", y=" + y + ", width=" + width + ", height=" + height);
|
|
1639
1856
|
Log.d("CameraPreview", "8. EDGE-TO-EDGE - " + (isEdgeToEdgeActive ? "ACTIVE" : "INACTIVE"));
|
|
1640
1857
|
Log.d("CameraPreview", "9. WEBVIEW INSET - " + webViewTopInset);
|
|
1858
|
+
Log.d(
|
|
1859
|
+
"CameraPreview",
|
|
1860
|
+
"9b. SAFE AREA - " + (lastIncludeSafeAreaInsets ? ("ENABLED (inset=" + safeAreaTopInsetPx + ")") : "DISABLED")
|
|
1861
|
+
);
|
|
1641
1862
|
Log.d(
|
|
1642
1863
|
"CameraPreview",
|
|
1643
1864
|
"10. RELATIVE Y - " + relativeY + " (y=" + y + (isEdgeToEdgeActive ? " - inset=" + webViewTopInset : " unchanged") + ")"
|
|
@@ -1741,12 +1962,45 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1741
1962
|
")"
|
|
1742
1963
|
);
|
|
1743
1964
|
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1965
|
+
if (pendingStartBarcodeScanner && cameraXView != null) {
|
|
1966
|
+
List<String> formats = new ArrayList<>(pendingStartBarcodeFormats);
|
|
1967
|
+
int detectionInterval = pendingStartBarcodeDetectionInterval;
|
|
1968
|
+
cameraXView.startBarcodeScanner(
|
|
1969
|
+
formats,
|
|
1970
|
+
detectionInterval,
|
|
1971
|
+
new CameraXView.BarcodeScannerStartCallback() {
|
|
1972
|
+
@Override
|
|
1973
|
+
public void onStarted() {
|
|
1974
|
+
resolveCameraStartCall(call, result);
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
@Override
|
|
1978
|
+
public void onError(String message) {
|
|
1979
|
+
rejectCameraStartCall(call, message);
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
);
|
|
1983
|
+
return;
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
resolveCameraStartCall(call, result);
|
|
1747
1987
|
}
|
|
1748
1988
|
}
|
|
1749
1989
|
|
|
1990
|
+
private void resolveCameraStartCall(PluginCall call, JSObject result) {
|
|
1991
|
+
call.resolve(result);
|
|
1992
|
+
bridge.releaseCall(call);
|
|
1993
|
+
cameraStartCallbackId = null; // Prevent re-use
|
|
1994
|
+
resetPendingStartBarcodeScanner();
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
private void rejectCameraStartCall(PluginCall call, String message) {
|
|
1998
|
+
call.reject(message);
|
|
1999
|
+
bridge.releaseCall(call);
|
|
2000
|
+
cameraStartCallbackId = null;
|
|
2001
|
+
resetPendingStartBarcodeScanner();
|
|
2002
|
+
}
|
|
2003
|
+
|
|
1750
2004
|
@Override
|
|
1751
2005
|
public void onSampleTaken(String result) {
|
|
1752
2006
|
PluginCall call = bridge.getSavedCall(sampleCallbackId);
|
|
@@ -1773,6 +2027,20 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1773
2027
|
}
|
|
1774
2028
|
}
|
|
1775
2029
|
|
|
2030
|
+
@Override
|
|
2031
|
+
public void onBarcodesScanned(org.json.JSONArray barcodes) {
|
|
2032
|
+
JSObject data = new JSObject();
|
|
2033
|
+
data.put("barcodes", barcodes);
|
|
2034
|
+
notifyListeners("barcodeScanned", data);
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
@Override
|
|
2038
|
+
public void onBarcodeScanError(String message) {
|
|
2039
|
+
JSObject data = new JSObject();
|
|
2040
|
+
data.put("message", message);
|
|
2041
|
+
notifyListeners("barcodeScanError", data);
|
|
2042
|
+
}
|
|
2043
|
+
|
|
1776
2044
|
@Override
|
|
1777
2045
|
public void onCameraStartError(String message) {
|
|
1778
2046
|
PluginCall call = bridge.getSavedCall(cameraStartCallbackId);
|
|
@@ -1780,6 +2048,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1780
2048
|
call.reject(message);
|
|
1781
2049
|
bridge.releaseCall(call);
|
|
1782
2050
|
cameraStartCallbackId = null;
|
|
2051
|
+
resetPendingStartBarcodeScanner();
|
|
1783
2052
|
}
|
|
1784
2053
|
|
|
1785
2054
|
// Restore original window background on error to prevent black screen
|
|
@@ -1801,6 +2070,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1801
2070
|
}
|
|
1802
2071
|
}
|
|
1803
2072
|
restoreWebViewVisualState();
|
|
2073
|
+
restoreSystemUiForToBackMode(getBridge().getActivity());
|
|
1804
2074
|
}
|
|
1805
2075
|
|
|
1806
2076
|
@PluginMethod
|
|
@@ -1817,6 +2087,26 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1817
2087
|
cameraXView.setAspectRatio(aspectRatio, x, y, () -> {
|
|
1818
2088
|
// Return the actual preview bounds after layout and camera operations are complete
|
|
1819
2089
|
int[] bounds = cameraXView.getCurrentPreviewBounds();
|
|
2090
|
+
if (lastIncludeSafeAreaInsets) {
|
|
2091
|
+
DisplayMetrics metrics = getBridge().getActivity().getResources().getDisplayMetrics();
|
|
2092
|
+
float pixelRatio = metrics.density;
|
|
2093
|
+
WebView webView = getBridge().getWebView();
|
|
2094
|
+
int webViewTopInset = 0;
|
|
2095
|
+
boolean isWebViewOffset = false;
|
|
2096
|
+
if (webView != null) {
|
|
2097
|
+
int[] location = new int[2];
|
|
2098
|
+
webView.getLocationOnScreen(location);
|
|
2099
|
+
webViewTopInset = location[1];
|
|
2100
|
+
isWebViewOffset = webViewTopInset > 0;
|
|
2101
|
+
}
|
|
2102
|
+
if (!isWebViewOffset) {
|
|
2103
|
+
int safeAreaTopInsetPx = getSafeAreaTopInsetPx();
|
|
2104
|
+
if (safeAreaTopInsetPx > 0) {
|
|
2105
|
+
int safeAreaTopInsetLogical = (int) Math.ceil(safeAreaTopInsetPx / pixelRatio);
|
|
2106
|
+
bounds[1] = Math.max(0, bounds[1] - safeAreaTopInsetLogical);
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
1820
2110
|
JSObject ret = new JSObject();
|
|
1821
2111
|
ret.put("x", bounds[0]);
|
|
1822
2112
|
ret.put("y", bounds[1]);
|
|
@@ -1874,6 +2164,17 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1874
2164
|
DisplayMetrics metrics = getBridge().getActivity().getResources().getDisplayMetrics();
|
|
1875
2165
|
float pixelRatio = metrics.density;
|
|
1876
2166
|
|
|
2167
|
+
WebView webView = getBridge().getWebView();
|
|
2168
|
+
int webViewTopInset = 0;
|
|
2169
|
+
boolean isWebViewOffset = false;
|
|
2170
|
+
if (webView != null) {
|
|
2171
|
+
int[] location = new int[2];
|
|
2172
|
+
webView.getLocationOnScreen(location);
|
|
2173
|
+
webViewTopInset = location[1];
|
|
2174
|
+
isWebViewOffset = webViewTopInset > 0;
|
|
2175
|
+
}
|
|
2176
|
+
int safeAreaTopInsetPx = lastIncludeSafeAreaInsets ? getSafeAreaTopInsetPx() : 0;
|
|
2177
|
+
|
|
1877
2178
|
JSObject ret = new JSObject();
|
|
1878
2179
|
// Use same rounding strategy as start method
|
|
1879
2180
|
double x = Math.ceil(cameraXView.getPreviewX() / pixelRatio);
|
|
@@ -1881,6 +2182,11 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1881
2182
|
double width = Math.floor(cameraXView.getPreviewWidth() / pixelRatio);
|
|
1882
2183
|
double height = Math.floor(cameraXView.getPreviewHeight() / pixelRatio);
|
|
1883
2184
|
|
|
2185
|
+
if (!isWebViewOffset && lastIncludeSafeAreaInsets && safeAreaTopInsetPx > 0) {
|
|
2186
|
+
int safeAreaTopInsetLogical = (int) Math.ceil(safeAreaTopInsetPx / pixelRatio);
|
|
2187
|
+
y = Math.max(0, y - safeAreaTopInsetLogical);
|
|
2188
|
+
}
|
|
2189
|
+
|
|
1884
2190
|
Log.d("CameraPreview", "getPreviewSize: x=" + x + ", y=" + y + ", width=" + width + ", height=" + height);
|
|
1885
2191
|
ret.put("x", x);
|
|
1886
2192
|
ret.put("y", y);
|
|
@@ -1909,20 +2215,25 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1909
2215
|
// Check if edge-to-edge mode is active
|
|
1910
2216
|
WebView webView = getBridge().getWebView();
|
|
1911
2217
|
int webViewTopInset = 0;
|
|
1912
|
-
boolean isEdgeToEdgeActive = false;
|
|
1913
2218
|
if (webView != null) {
|
|
1914
2219
|
int[] location = new int[2];
|
|
1915
2220
|
webView.getLocationOnScreen(location);
|
|
1916
2221
|
webViewTopInset = location[1];
|
|
1917
|
-
isEdgeToEdgeActive = webViewTopInset > 0;
|
|
1918
2222
|
}
|
|
2223
|
+
final boolean isWebViewOffset = webViewTopInset > 0;
|
|
2224
|
+
final int safeAreaTopInsetPx = lastIncludeSafeAreaInsets ? getSafeAreaTopInsetPx() : 0;
|
|
2225
|
+
final float pixelRatioFinal = pixelRatio;
|
|
1919
2226
|
|
|
1920
2227
|
int x = (xParam != null && xParam > 0) ? (int) (xParam * pixelRatio) : 0;
|
|
1921
2228
|
int y = (yParam != null && yParam > 0) ? (int) (yParam * pixelRatio) : 0;
|
|
1922
2229
|
|
|
1923
|
-
// Add
|
|
1924
|
-
|
|
2230
|
+
// Add inset to Y for coordinate conversion if needed.
|
|
2231
|
+
// - If the WebView is already offset from the screen top, use that.
|
|
2232
|
+
// - Otherwise, if safe-area insets were requested (Capacitor 8 edge-to-edge), use system inset.
|
|
2233
|
+
if (isWebViewOffset && y > 0) {
|
|
1925
2234
|
y += webViewTopInset;
|
|
2235
|
+
} else if (!isWebViewOffset && lastIncludeSafeAreaInsets && safeAreaTopInsetPx > 0 && y > 0) {
|
|
2236
|
+
y += safeAreaTopInsetPx;
|
|
1926
2237
|
}
|
|
1927
2238
|
int width = (widthParam != null && widthParam > 0) ? (int) (widthParam * pixelRatio) : 0;
|
|
1928
2239
|
int height = (heightParam != null && heightParam > 0) ? (int) (heightParam * pixelRatio) : 0;
|
|
@@ -1930,6 +2241,10 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1930
2241
|
cameraXView.setPreviewSize(x, y, width, height, () -> {
|
|
1931
2242
|
// Return the actual preview bounds after layout operations are complete
|
|
1932
2243
|
int[] bounds = cameraXView.getCurrentPreviewBounds();
|
|
2244
|
+
if (!isWebViewOffset && lastIncludeSafeAreaInsets && safeAreaTopInsetPx > 0) {
|
|
2245
|
+
int safeAreaTopInsetLogical = (int) Math.ceil(safeAreaTopInsetPx / pixelRatioFinal);
|
|
2246
|
+
bounds[1] = Math.max(0, bounds[1] - safeAreaTopInsetLogical);
|
|
2247
|
+
}
|
|
1933
2248
|
JSObject ret = new JSObject();
|
|
1934
2249
|
ret.put("x", bounds[0]);
|
|
1935
2250
|
ret.put("y", bounds[1]);
|
|
@@ -2103,6 +2418,19 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
2103
2418
|
return result;
|
|
2104
2419
|
}
|
|
2105
2420
|
|
|
2421
|
+
private int getSafeAreaTopInsetPx() {
|
|
2422
|
+
try {
|
|
2423
|
+
View decorView = getBridge().getActivity().getWindow().getDecorView();
|
|
2424
|
+
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(decorView);
|
|
2425
|
+
if (insets != null) {
|
|
2426
|
+
Insets cutout = insets.getInsets(WindowInsetsCompat.Type.displayCutout());
|
|
2427
|
+
Insets sysBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
|
2428
|
+
return Math.max(cutout.top, sysBars.top);
|
|
2429
|
+
}
|
|
2430
|
+
} catch (Exception ignored) {}
|
|
2431
|
+
return getStatusBarHeightPx();
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2106
2434
|
@PluginMethod
|
|
2107
2435
|
public void getPluginVersion(final PluginCall call) {
|
|
2108
2436
|
try {
|