@capgo/camera-preview 7.4.0-alpha.23 → 7.4.0-alpha.24
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,6 +6,7 @@ import static android.Manifest.permission.RECORD_AUDIO;
|
|
|
6
6
|
import android.Manifest;
|
|
7
7
|
import android.content.pm.ActivityInfo;
|
|
8
8
|
import android.content.res.Configuration;
|
|
9
|
+
import android.graphics.Color;
|
|
9
10
|
import android.location.Location;
|
|
10
11
|
import android.util.DisplayMetrics;
|
|
11
12
|
import android.util.Log;
|
|
@@ -74,6 +75,7 @@ public class CameraPreview
|
|
|
74
75
|
private int previousOrientationRequest =
|
|
75
76
|
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
|
76
77
|
private CameraXView cameraXView;
|
|
78
|
+
private View rotationOverlay;
|
|
77
79
|
private FusedLocationProviderClient fusedLocationClient;
|
|
78
80
|
private Location lastLocation;
|
|
79
81
|
private OrientationEventListener orientationListener;
|
|
@@ -241,6 +243,12 @@ public class CameraPreview
|
|
|
241
243
|
lastOrientation = Configuration.ORIENTATION_UNDEFINED;
|
|
242
244
|
}
|
|
243
245
|
|
|
246
|
+
// Remove any rotation overlay if present
|
|
247
|
+
if (rotationOverlay != null && rotationOverlay.getParent() != null) {
|
|
248
|
+
((ViewGroup) rotationOverlay.getParent()).removeView(rotationOverlay);
|
|
249
|
+
rotationOverlay = null;
|
|
250
|
+
}
|
|
251
|
+
|
|
244
252
|
if (cameraXView != null && cameraXView.isRunning()) {
|
|
245
253
|
cameraXView.stopSession();
|
|
246
254
|
cameraXView = null;
|
|
@@ -1106,6 +1114,28 @@ public class CameraPreview
|
|
|
1106
1114
|
getBridge()
|
|
1107
1115
|
.getActivity()
|
|
1108
1116
|
.runOnUiThread(() -> {
|
|
1117
|
+
// Create and show a black full-screen overlay during rotation
|
|
1118
|
+
ViewGroup rootView = (ViewGroup) getBridge()
|
|
1119
|
+
.getActivity()
|
|
1120
|
+
.getWindow()
|
|
1121
|
+
.getDecorView()
|
|
1122
|
+
.getRootView();
|
|
1123
|
+
|
|
1124
|
+
// Remove any existing overlay
|
|
1125
|
+
if (rotationOverlay != null && rotationOverlay.getParent() != null) {
|
|
1126
|
+
((ViewGroup) rotationOverlay.getParent()).removeView(rotationOverlay);
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
// Create new black overlay
|
|
1130
|
+
rotationOverlay = new View(getContext());
|
|
1131
|
+
rotationOverlay.setBackgroundColor(Color.BLACK);
|
|
1132
|
+
ViewGroup.LayoutParams overlayParams = new ViewGroup.LayoutParams(
|
|
1133
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
1134
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
1135
|
+
);
|
|
1136
|
+
rotationOverlay.setLayoutParams(overlayParams);
|
|
1137
|
+
rootView.addView(rotationOverlay);
|
|
1138
|
+
|
|
1109
1139
|
// Reapply current aspect ratio to recompute layout, then emit screenResize
|
|
1110
1140
|
String ar = cameraXView.getAspectRatio();
|
|
1111
1141
|
Log.d(TAG, "Reapplying aspect ratio: " + ar);
|
|
@@ -1180,6 +1210,38 @@ public class CameraPreview
|
|
|
1180
1210
|
oData.put("orientation", o);
|
|
1181
1211
|
notifyListeners("orientationChange", oData);
|
|
1182
1212
|
|
|
1213
|
+
// Don't remove the overlay here - wait for camera to fully start
|
|
1214
|
+
// The overlay will be removed after a delay to ensure camera is stable
|
|
1215
|
+
if (rotationOverlay != null && rotationOverlay.getParent() != null) {
|
|
1216
|
+
// Shorter delay for faster transition
|
|
1217
|
+
int delay = "4:3".equals(ar) ? 200 : 150;
|
|
1218
|
+
rotationOverlay.postDelayed(
|
|
1219
|
+
() -> {
|
|
1220
|
+
if (
|
|
1221
|
+
rotationOverlay != null && rotationOverlay.getParent() != null
|
|
1222
|
+
) {
|
|
1223
|
+
rotationOverlay
|
|
1224
|
+
.animate()
|
|
1225
|
+
.alpha(0f)
|
|
1226
|
+
.setDuration(100) // Faster fade out
|
|
1227
|
+
.withEndAction(() -> {
|
|
1228
|
+
if (
|
|
1229
|
+
rotationOverlay != null &&
|
|
1230
|
+
rotationOverlay.getParent() != null
|
|
1231
|
+
) {
|
|
1232
|
+
((ViewGroup) rotationOverlay.getParent()).removeView(
|
|
1233
|
+
rotationOverlay
|
|
1234
|
+
);
|
|
1235
|
+
rotationOverlay = null;
|
|
1236
|
+
}
|
|
1237
|
+
})
|
|
1238
|
+
.start();
|
|
1239
|
+
}
|
|
1240
|
+
},
|
|
1241
|
+
delay
|
|
1242
|
+
);
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1183
1245
|
Log.d(
|
|
1184
1246
|
TAG,
|
|
1185
1247
|
"================================================================================"
|
|
@@ -1213,6 +1275,23 @@ public class CameraPreview
|
|
|
1213
1275
|
bridge.releaseCall(pluginCall);
|
|
1214
1276
|
}
|
|
1215
1277
|
|
|
1278
|
+
private JSObject getViewSize(
|
|
1279
|
+
double x,
|
|
1280
|
+
double y,
|
|
1281
|
+
double width,
|
|
1282
|
+
double height
|
|
1283
|
+
) {
|
|
1284
|
+
JSObject ret = new JSObject();
|
|
1285
|
+
// Return values with proper rounding to avoid gaps
|
|
1286
|
+
// For positions (x, y): ceil to avoid gaps at top/left
|
|
1287
|
+
// For dimensions (width, height): floor to avoid gaps at bottom/right
|
|
1288
|
+
ret.put("x", Math.ceil(x));
|
|
1289
|
+
ret.put("y", Math.ceil(y));
|
|
1290
|
+
ret.put("width", Math.floor(width));
|
|
1291
|
+
ret.put("height", Math.floor(height));
|
|
1292
|
+
return ret;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1216
1295
|
@Override
|
|
1217
1296
|
public void onCameraStarted(int width, int height, int x, int y) {
|
|
1218
1297
|
PluginCall call = bridge.getSavedCall(cameraStartCallbackId);
|
|
@@ -1287,6 +1366,13 @@ public class CameraPreview
|
|
|
1287
1366
|
double logicalX = x / pixelRatio;
|
|
1288
1367
|
double logicalY = relativeY / pixelRatio;
|
|
1289
1368
|
|
|
1369
|
+
JSObject result = getViewSize(
|
|
1370
|
+
logicalX,
|
|
1371
|
+
logicalY,
|
|
1372
|
+
logicalWidth,
|
|
1373
|
+
logicalHeight
|
|
1374
|
+
);
|
|
1375
|
+
|
|
1290
1376
|
// Log exact calculations to debug one-pixel difference
|
|
1291
1377
|
Log.d("CameraPreview", "========================");
|
|
1292
1378
|
Log.d("CameraPreview", "FINAL POSITION CALCULATIONS:");
|
|
@@ -1360,32 +1446,23 @@ public class CameraPreview
|
|
|
1360
1446
|
}
|
|
1361
1447
|
Log.d("CameraPreview", "========================");
|
|
1362
1448
|
|
|
1363
|
-
JSObject result = new JSObject();
|
|
1364
|
-
// Return values with proper rounding to avoid gaps
|
|
1365
|
-
// For positions (x, y): floor to avoid gaps at top/left
|
|
1366
|
-
// For dimensions (width, height): ceil to avoid gaps at bottom/right
|
|
1367
|
-
result.put("width", Math.floor(logicalWidth));
|
|
1368
|
-
result.put("height", Math.floor(logicalHeight));
|
|
1369
|
-
result.put("x", Math.ceil(logicalX));
|
|
1370
|
-
result.put("y", Math.ceil(logicalY));
|
|
1371
|
-
|
|
1372
1449
|
// Log what we're returning
|
|
1373
1450
|
Log.d(
|
|
1374
1451
|
"CameraPreview",
|
|
1375
1452
|
"Returning to JS - x: " +
|
|
1376
|
-
|
|
1453
|
+
logicalX +
|
|
1377
1454
|
" (from " +
|
|
1378
1455
|
logicalX +
|
|
1379
1456
|
"), y: " +
|
|
1380
|
-
|
|
1457
|
+
logicalY +
|
|
1381
1458
|
" (from " +
|
|
1382
1459
|
logicalY +
|
|
1383
1460
|
"), width: " +
|
|
1384
|
-
|
|
1461
|
+
logicalWidth +
|
|
1385
1462
|
" (from " +
|
|
1386
1463
|
logicalWidth +
|
|
1387
1464
|
"), height: " +
|
|
1388
|
-
|
|
1465
|
+
logicalHeight +
|
|
1389
1466
|
" (from " +
|
|
1390
1467
|
logicalHeight +
|
|
1391
1468
|
")"
|
|
@@ -1497,15 +1574,26 @@ public class CameraPreview
|
|
|
1497
1574
|
|
|
1498
1575
|
JSObject ret = new JSObject();
|
|
1499
1576
|
// Use same rounding strategy as start method
|
|
1500
|
-
double x = cameraXView.getPreviewX() / pixelRatio;
|
|
1501
|
-
double y = cameraXView.getPreviewY() / pixelRatio;
|
|
1502
|
-
double width = cameraXView.getPreviewWidth() / pixelRatio;
|
|
1503
|
-
double height = cameraXView.getPreviewHeight() / pixelRatio;
|
|
1577
|
+
double x = Math.ceil(cameraXView.getPreviewX() / pixelRatio);
|
|
1578
|
+
double y = Math.ceil(cameraXView.getPreviewY() / pixelRatio);
|
|
1579
|
+
double width = Math.floor(cameraXView.getPreviewWidth() / pixelRatio);
|
|
1580
|
+
double height = Math.floor(cameraXView.getPreviewHeight() / pixelRatio);
|
|
1504
1581
|
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1582
|
+
Log.d(
|
|
1583
|
+
"CameraPreview",
|
|
1584
|
+
"getPreviewSize: x=" +
|
|
1585
|
+
x +
|
|
1586
|
+
", y=" +
|
|
1587
|
+
y +
|
|
1588
|
+
", width=" +
|
|
1589
|
+
width +
|
|
1590
|
+
", height=" +
|
|
1591
|
+
height
|
|
1592
|
+
);
|
|
1593
|
+
ret.put("x", x);
|
|
1594
|
+
ret.put("y", y);
|
|
1595
|
+
ret.put("width", width);
|
|
1596
|
+
ret.put("height", height);
|
|
1509
1597
|
call.resolve(ret);
|
|
1510
1598
|
}
|
|
1511
1599
|
|
|
@@ -2728,18 +2728,83 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
2728
2728
|
boolean usesFillCenter =
|
|
2729
2729
|
sessionConfig != null && sessionConfig.getAspectRatio() != null;
|
|
2730
2730
|
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
float scale;
|
|
2734
|
-
|
|
2731
|
+
// For FILL_CENTER with aspect ratio, we need to calculate the actual visible bounds
|
|
2732
|
+
// The preview might extend beyond the container bounds and get clipped
|
|
2735
2733
|
if (usesFillCenter) {
|
|
2736
|
-
//
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2734
|
+
// Calculate how the camera preview is scaled to fill the container
|
|
2735
|
+
float widthScale = (float) containerWidth / cameraWidth;
|
|
2736
|
+
float heightScale = (float) containerHeight / cameraHeight;
|
|
2737
|
+
float scale = Math.max(widthScale, heightScale); // max for FILL_CENTER
|
|
2738
|
+
|
|
2739
|
+
// Calculate the scaled dimensions
|
|
2740
|
+
int scaledWidth = Math.round(cameraWidth * scale);
|
|
2741
|
+
int scaledHeight = Math.round(cameraHeight * scale);
|
|
2742
|
+
|
|
2743
|
+
// Calculate how much is clipped on each side
|
|
2744
|
+
int excessWidth = Math.max(0, scaledWidth - containerWidth);
|
|
2745
|
+
int excessHeight = Math.max(0, scaledHeight - containerHeight);
|
|
2746
|
+
|
|
2747
|
+
// For the actual visible bounds, we need to account for potential
|
|
2748
|
+
// internal misalignment of PreviewView's SurfaceView
|
|
2749
|
+
int adjustedWidth = containerWidth;
|
|
2750
|
+
int adjustedHeight = containerHeight;
|
|
2751
|
+
|
|
2752
|
+
// Apply small adjustments for 4:3 ratio to prevent blue line
|
|
2753
|
+
// This compensates for PreviewView's internal SurfaceView misalignment
|
|
2754
|
+
String aspectRatio = sessionConfig != null
|
|
2755
|
+
? sessionConfig.getAspectRatio()
|
|
2756
|
+
: null;
|
|
2757
|
+
if ("4:3".equals(aspectRatio)) {
|
|
2758
|
+
// For 4:3, reduce the reported width slightly to account for
|
|
2759
|
+
// the SurfaceView drawing outside its bounds
|
|
2760
|
+
adjustedWidth = containerWidth - 2;
|
|
2761
|
+
adjustedHeight = containerHeight - 2;
|
|
2762
|
+
}
|
|
2763
|
+
|
|
2764
|
+
Log.d(
|
|
2765
|
+
TAG,
|
|
2766
|
+
"getActualCameraBounds FILL_CENTER: container=" +
|
|
2767
|
+
containerWidth +
|
|
2768
|
+
"x" +
|
|
2769
|
+
containerHeight +
|
|
2770
|
+
", camera=" +
|
|
2771
|
+
cameraWidth +
|
|
2772
|
+
"x" +
|
|
2773
|
+
cameraHeight +
|
|
2774
|
+
" (portrait=" +
|
|
2775
|
+
isPortrait +
|
|
2776
|
+
")" +
|
|
2777
|
+
", scale=" +
|
|
2778
|
+
scale +
|
|
2779
|
+
", scaled=" +
|
|
2780
|
+
scaledWidth +
|
|
2781
|
+
"x" +
|
|
2782
|
+
scaledHeight +
|
|
2783
|
+
", excess=" +
|
|
2784
|
+
excessWidth +
|
|
2785
|
+
"x" +
|
|
2786
|
+
excessHeight +
|
|
2787
|
+
", adjusted=" +
|
|
2788
|
+
adjustedWidth +
|
|
2789
|
+
"x" +
|
|
2790
|
+
adjustedHeight +
|
|
2791
|
+
", ratio=" +
|
|
2792
|
+
aspectRatio
|
|
2793
|
+
);
|
|
2794
|
+
|
|
2795
|
+
// Return slightly inset bounds for 4:3 to avoid blue line
|
|
2796
|
+
if ("4:3".equals(aspectRatio)) {
|
|
2797
|
+
return new Rect(1, 1, adjustedWidth + 1, adjustedHeight + 1);
|
|
2798
|
+
} else {
|
|
2799
|
+
return new Rect(0, 0, containerWidth, containerHeight);
|
|
2800
|
+
}
|
|
2741
2801
|
}
|
|
2742
2802
|
|
|
2803
|
+
// For FIT_CENTER (no aspect ratio), calculate letterboxing
|
|
2804
|
+
float widthScale = (float) containerWidth / cameraWidth;
|
|
2805
|
+
float heightScale = (float) containerHeight / cameraHeight;
|
|
2806
|
+
float scale = Math.min(widthScale, heightScale);
|
|
2807
|
+
|
|
2743
2808
|
// Calculate the actual size of the camera content after scaling
|
|
2744
2809
|
int scaledWidth = Math.round(cameraWidth * scale);
|
|
2745
2810
|
int scaledHeight = Math.round(cameraHeight * scale);
|
|
@@ -2750,7 +2815,7 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
2750
2815
|
|
|
2751
2816
|
Log.d(
|
|
2752
2817
|
TAG,
|
|
2753
|
-
"getActualCameraBounds: container=" +
|
|
2818
|
+
"getActualCameraBounds FIT_CENTER: container=" +
|
|
2754
2819
|
containerWidth +
|
|
2755
2820
|
"x" +
|
|
2756
2821
|
containerHeight +
|
|
@@ -2763,9 +2828,6 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
2763
2828
|
")" +
|
|
2764
2829
|
", scale=" +
|
|
2765
2830
|
scale +
|
|
2766
|
-
" (fillCenter=" +
|
|
2767
|
-
usesFillCenter +
|
|
2768
|
-
")" +
|
|
2769
2831
|
", scaled=" +
|
|
2770
2832
|
scaledWidth +
|
|
2771
2833
|
"x" +
|
|
@@ -3378,6 +3440,38 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
3378
3440
|
int width = (int) Math.floor(actualWidth / pixelRatio);
|
|
3379
3441
|
int height = (int) Math.floor(actualHeight / pixelRatio);
|
|
3380
3442
|
|
|
3443
|
+
// Debug logging to understand the blue line issue
|
|
3444
|
+
Log.d(
|
|
3445
|
+
TAG,
|
|
3446
|
+
"getCurrentPreviewBounds DEBUG: " +
|
|
3447
|
+
"actualBounds=(" +
|
|
3448
|
+
actualX +
|
|
3449
|
+
"," +
|
|
3450
|
+
actualY +
|
|
3451
|
+
"," +
|
|
3452
|
+
actualWidth +
|
|
3453
|
+
"x" +
|
|
3454
|
+
actualHeight +
|
|
3455
|
+
"), " +
|
|
3456
|
+
"logicalBounds=(" +
|
|
3457
|
+
x +
|
|
3458
|
+
"," +
|
|
3459
|
+
y +
|
|
3460
|
+
"," +
|
|
3461
|
+
width +
|
|
3462
|
+
"x" +
|
|
3463
|
+
height +
|
|
3464
|
+
"), " +
|
|
3465
|
+
"pixelRatio=" +
|
|
3466
|
+
pixelRatio +
|
|
3467
|
+
", " +
|
|
3468
|
+
"insets=(" +
|
|
3469
|
+
webViewLeftInset +
|
|
3470
|
+
"," +
|
|
3471
|
+
webViewTopInset +
|
|
3472
|
+
")"
|
|
3473
|
+
);
|
|
3474
|
+
|
|
3381
3475
|
return new int[] { x, y, width, height };
|
|
3382
3476
|
}
|
|
3383
3477
|
|