@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
- Math.ceil(logicalX) +
1453
+ logicalX +
1377
1454
  " (from " +
1378
1455
  logicalX +
1379
1456
  "), y: " +
1380
- Math.ceil(logicalY) +
1457
+ logicalY +
1381
1458
  " (from " +
1382
1459
  logicalY +
1383
1460
  "), width: " +
1384
- Math.floor(logicalWidth) +
1461
+ logicalWidth +
1385
1462
  " (from " +
1386
1463
  logicalWidth +
1387
1464
  "), height: " +
1388
- Math.floor(logicalHeight) +
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
- ret.put("x", Math.ceil(x));
1506
- ret.put("y", Math.ceil(y));
1507
- ret.put("width", Math.floor(width));
1508
- ret.put("height", Math.floor(height));
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
- float widthScale = (float) containerWidth / cameraWidth;
2732
- float heightScale = (float) containerHeight / cameraHeight;
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
- // FILL_CENTER uses max scale to fill the container
2737
- scale = Math.max(widthScale, heightScale);
2738
- } else {
2739
- // FIT_CENTER uses min scale to fit within the container
2740
- scale = Math.min(widthScale, heightScale);
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/camera-preview",
3
- "version": "7.4.0-alpha.23",
3
+ "version": "7.4.0-alpha.24",
4
4
  "description": "Camera preview",
5
5
  "license": "MIT",
6
6
  "repository": {