@capgo/camera-preview 7.4.0-beta.7 → 7.4.0-beta.8

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.
Files changed (41) hide show
  1. package/README.md +62 -28
  2. package/android/.gradle/8.14.2/checksums/checksums.lock +0 -0
  3. package/android/.gradle/8.14.2/checksums/md5-checksums.bin +0 -0
  4. package/android/.gradle/8.14.2/checksums/sha1-checksums.bin +0 -0
  5. package/android/.gradle/8.14.2/executionHistory/executionHistory.bin +0 -0
  6. package/android/.gradle/8.14.2/executionHistory/executionHistory.lock +0 -0
  7. package/android/.gradle/8.14.2/fileHashes/fileHashes.bin +0 -0
  8. package/android/.gradle/8.14.2/fileHashes/fileHashes.lock +0 -0
  9. package/android/.gradle/8.14.2/fileHashes/resourceHashesCache.bin +0 -0
  10. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  11. package/android/.gradle/buildOutputCleanup/cache.properties +1 -1
  12. package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
  13. package/android/.gradle/file-system.probe +0 -0
  14. package/android/build.gradle +1 -0
  15. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java +127 -14
  16. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraXView.java +479 -29
  17. package/android/src/main/java/com/ahm/capacitor/camera/preview/GridOverlayView.java +2 -0
  18. package/dist/docs.json +46 -7
  19. package/dist/esm/definitions.d.ts +42 -5
  20. package/dist/esm/definitions.js.map +1 -1
  21. package/dist/esm/web.d.ts +25 -1
  22. package/dist/esm/web.js +81 -9
  23. package/dist/esm/web.js.map +1 -1
  24. package/dist/plugin.cjs.js +81 -9
  25. package/dist/plugin.cjs.js.map +1 -1
  26. package/dist/plugin.js +81 -9
  27. package/dist/plugin.js.map +1 -1
  28. package/ios/Sources/CapgoCameraPreview/CameraController.swift +95 -18
  29. package/ios/Sources/CapgoCameraPreview/Plugin.swift +271 -91
  30. package/package.json +1 -1
  31. package/android/.gradle/config.properties +0 -2
  32. package/android/.idea/AndroidProjectSystem.xml +0 -6
  33. package/android/.idea/caches/deviceStreaming.xml +0 -811
  34. package/android/.idea/compiler.xml +0 -6
  35. package/android/.idea/gradle.xml +0 -18
  36. package/android/.idea/migrations.xml +0 -10
  37. package/android/.idea/misc.xml +0 -10
  38. package/android/.idea/runConfigurations.xml +0 -17
  39. package/android/.idea/vcs.xml +0 -6
  40. package/android/.idea/workspace.xml +0 -55
  41. package/android/local.properties +0 -8
@@ -5,6 +5,7 @@ import android.hardware.camera2.CameraAccessException;
5
5
  import android.hardware.camera2.CameraManager;
6
6
  import android.os.Build;
7
7
  import android.util.Base64;
8
+ import android.util.DisplayMetrics;
8
9
  import android.util.Log;
9
10
  import android.util.Size;
10
11
  import android.view.ViewGroup;
@@ -68,6 +69,7 @@ import android.widget.FrameLayout;
68
69
  import androidx.lifecycle.LifecycleObserver;
69
70
  import androidx.lifecycle.OnLifecycleEvent;
70
71
  import android.util.Rational;
72
+ import android.view.ViewGroup;
71
73
 
72
74
  public class CameraXView implements LifecycleOwner, LifecycleObserver {
73
75
  private static final String TAG = "CameraPreview CameraXView";
@@ -202,19 +204,86 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
202
204
 
203
205
  // Create and setup the grid overlay
204
206
  gridOverlayView = new GridOverlayView(context);
205
- gridOverlayView.setGridMode(sessionConfig.getGridMode());
206
207
  previewContainer.addView(gridOverlayView, new FrameLayout.LayoutParams(
207
208
  FrameLayout.LayoutParams.MATCH_PARENT,
208
209
  FrameLayout.LayoutParams.MATCH_PARENT
209
210
  ));
211
+ // Set grid mode after adding to container to ensure proper layout
212
+ gridOverlayView.post(() -> {
213
+ String currentGridMode = sessionConfig.getGridMode();
214
+ Log.d(TAG, "setupPreviewView: Setting grid mode to: " + currentGridMode);
215
+ gridOverlayView.setGridMode(currentGridMode);
216
+ });
210
217
 
211
218
  ViewGroup parent = (ViewGroup) webView.getParent();
212
219
  if (parent != null) {
213
- parent.addView(previewContainer, new ViewGroup.LayoutParams(sessionConfig.getWidth(), sessionConfig.getHeight()));
220
+ FrameLayout.LayoutParams layoutParams = calculatePreviewLayoutParams();
221
+ parent.addView(previewContainer, layoutParams);
214
222
  if(sessionConfig.isToBack()) webView.bringToFront();
215
223
  }
216
224
  }
217
225
 
226
+ private FrameLayout.LayoutParams calculatePreviewLayoutParams() {
227
+ // sessionConfig already contains pixel-converted coordinates with webview offsets applied
228
+ int x = sessionConfig.getX();
229
+ int y = sessionConfig.getY();
230
+ int width = sessionConfig.getWidth();
231
+ int height = sessionConfig.getHeight();
232
+ String aspectRatio = sessionConfig.getAspectRatio();
233
+
234
+ Log.d(TAG, "calculatePreviewLayoutParams: Using sessionConfig values - x:" + x + " y:" + y + " width:" + width + " height:" + height + " aspectRatio:" + aspectRatio);
235
+
236
+ // Apply aspect ratio if specified and no explicit size was given
237
+ if (aspectRatio != null && !aspectRatio.isEmpty()) {
238
+ String[] ratios = aspectRatio.split(":");
239
+ if (ratios.length == 2) {
240
+ try {
241
+ // For camera, use portrait orientation: 4:3 becomes 3:4, 16:9 becomes 9:16
242
+ float ratio = Float.parseFloat(ratios[1]) / Float.parseFloat(ratios[0]);
243
+
244
+ // Calculate optimal size while maintaining aspect ratio
245
+ int optimalWidth = width;
246
+ int optimalHeight = (int) (width / ratio);
247
+
248
+ if (optimalHeight > height) {
249
+ // Height constraint is tighter, fit by height
250
+ optimalHeight = height;
251
+ optimalWidth = (int) (height * ratio);
252
+ }
253
+
254
+ width = optimalWidth;
255
+ height = optimalHeight;
256
+ Log.d(TAG, "calculatePreviewLayoutParams: Applied aspect ratio " + aspectRatio + " - new size: " + width + "x" + height);
257
+ } catch (NumberFormatException e) {
258
+ Log.e(TAG, "Invalid aspect ratio format: " + aspectRatio, e);
259
+ }
260
+ }
261
+ }
262
+
263
+ FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(width, height);
264
+
265
+ // Only add insets for positioning coordinates, not for full-screen sizes
266
+ int webViewTopInset = getWebViewTopInset();
267
+ int webViewLeftInset = getWebViewLeftInset();
268
+
269
+ // Don't add insets if this looks like a calculated full-screen coordinate (x=0, y=0)
270
+ if (x == 0 && y == 0) {
271
+ layoutParams.leftMargin = x;
272
+ layoutParams.topMargin = y;
273
+ Log.d(TAG, "calculatePreviewLayoutParams: Full-screen mode - keeping position (0,0) without insets");
274
+ } else {
275
+ layoutParams.leftMargin = x + webViewLeftInset;
276
+ layoutParams.topMargin = y + webViewTopInset;
277
+ Log.d(TAG, "calculatePreviewLayoutParams: Positioned mode - applying insets");
278
+ }
279
+
280
+ Log.d(TAG, "calculatePreviewLayoutParams: Applied insets - x:" + x + "+" + webViewLeftInset + "=" + layoutParams.leftMargin +
281
+ ", y:" + y + "+" + webViewTopInset + "=" + layoutParams.topMargin);
282
+
283
+ Log.d(TAG, "calculatePreviewLayoutParams: Final layout - x:" + x + " y:" + y + " width:" + width + " height:" + height);
284
+ return layoutParams;
285
+ }
286
+
218
287
  private void removePreviewView() {
219
288
  if (previewContainer != null) {
220
289
  ViewGroup parent = (ViewGroup) previewContainer.getParent();
@@ -305,12 +374,17 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
305
374
 
306
375
  isRunning = true;
307
376
  Log.d(TAG, "bindCameraUseCases: Camera bound successfully");
308
- if (listener != null) listener.onCameraStarted(
309
- sessionConfig.getWidth(),
310
- sessionConfig.getHeight(),
311
- sessionConfig.getX(),
312
- sessionConfig.getY()
313
- );
377
+ if (listener != null) {
378
+ // Post the callback to ensure layout is complete
379
+ previewContainer.post(() -> {
380
+ // Return actual preview container dimensions instead of requested dimensions
381
+ int actualWidth = previewContainer != null ? previewContainer.getWidth() : sessionConfig.getWidth();
382
+ int actualHeight = previewContainer != null ? previewContainer.getHeight() : sessionConfig.getHeight();
383
+ int actualX = previewContainer != null ? previewContainer.getLeft() : sessionConfig.getX();
384
+ int actualY = previewContainer != null ? previewContainer.getTop() : sessionConfig.getY();
385
+ listener.onCameraStarted(actualWidth, actualHeight, actualX, actualY);
386
+ });
387
+ }
314
388
  } catch (Exception e) {
315
389
  if (listener != null) listener.onCameraStartError("Error binding camera: " + e.getMessage());
316
390
  }
@@ -1058,7 +1132,8 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
1058
1132
 
1059
1133
  if (sessionConfig.getAspectRatio() != null) {
1060
1134
  String[] ratios = sessionConfig.getAspectRatio().split(":");
1061
- float ratio = Float.parseFloat(ratios[0]) / Float.parseFloat(ratios[1]);
1135
+ // For camera, use portrait orientation: 4:3 becomes 3:4, 16:9 becomes 9:16
1136
+ float ratio = Float.parseFloat(ratios[1]) / Float.parseFloat(ratios[0]);
1062
1137
  if (sessionConfig.getWidth() > 0) {
1063
1138
  layoutParams.height = (int) (sessionConfig.getWidth() / ratio);
1064
1139
  } else if (sessionConfig.getHeight() > 0) {
@@ -1093,31 +1168,88 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
1093
1168
  }
1094
1169
 
1095
1170
  public void setAspectRatio(String aspectRatio) {
1096
- if (sessionConfig != null) {
1097
- sessionConfig = new CameraSessionConfiguration(
1098
- sessionConfig.getDeviceId(),
1099
- sessionConfig.getPosition(),
1100
- sessionConfig.getX(),
1101
- sessionConfig.getY(),
1102
- sessionConfig.getWidth(),
1103
- sessionConfig.getHeight(),
1104
- sessionConfig.getPaddingBottom(),
1105
- sessionConfig.getToBack(),
1106
- sessionConfig.getStoreToFile(),
1107
- sessionConfig.getEnableOpacity(),
1108
- sessionConfig.getEnableZoom(),
1109
- sessionConfig.getDisableExifHeaderStripping(),
1110
- sessionConfig.getDisableAudio(),
1111
- sessionConfig.getZoomFactor(),
1112
- aspectRatio,
1113
- sessionConfig.getGridMode()
1114
- );
1115
- updateLayoutParams();
1171
+ setAspectRatio(aspectRatio, null, null);
1172
+ }
1173
+
1174
+ public void setAspectRatio(String aspectRatio, Float x, Float y) {
1175
+ setAspectRatio(aspectRatio, x, y, null);
1176
+ }
1177
+
1178
+ public void setAspectRatio(String aspectRatio, Float x, Float y, Runnable callback) {
1179
+ if (sessionConfig == null) {
1180
+ if (callback != null) callback.run();
1181
+ return;
1182
+ }
1183
+
1184
+ String currentAspectRatio = sessionConfig.getAspectRatio();
1185
+
1186
+ // Don't restart camera if aspect ratio hasn't changed and no position specified
1187
+ if (aspectRatio != null && aspectRatio.equals(currentAspectRatio) && x == null && y == null) {
1188
+ Log.d(TAG, "setAspectRatio: Aspect ratio " + aspectRatio + " is already set and no position specified, skipping");
1189
+ if (callback != null) callback.run();
1190
+ return;
1191
+ }
1192
+
1193
+ String currentGridMode = sessionConfig.getGridMode();
1194
+ Log.d(TAG, "setAspectRatio: Changing from " + currentAspectRatio + " to " + aspectRatio +
1195
+ (x != null && y != null ? " at position (" + x + ", " + y + ")" : " with auto-centering") +
1196
+ ", preserving grid mode: " + currentGridMode);
1197
+
1198
+ sessionConfig = new CameraSessionConfiguration(
1199
+ sessionConfig.getDeviceId(),
1200
+ sessionConfig.getPosition(),
1201
+ sessionConfig.getX(),
1202
+ sessionConfig.getY(),
1203
+ sessionConfig.getWidth(),
1204
+ sessionConfig.getHeight(),
1205
+ sessionConfig.getPaddingBottom(),
1206
+ sessionConfig.getToBack(),
1207
+ sessionConfig.getStoreToFile(),
1208
+ sessionConfig.getEnableOpacity(),
1209
+ sessionConfig.getEnableZoom(),
1210
+ sessionConfig.getDisableExifHeaderStripping(),
1211
+ sessionConfig.getDisableAudio(),
1212
+ sessionConfig.getZoomFactor(),
1213
+ aspectRatio,
1214
+ currentGridMode
1215
+ );
1216
+
1217
+ // Update layout and rebind camera with new aspect ratio
1218
+ if (isRunning && previewContainer != null) {
1219
+ mainExecutor.execute(() -> {
1220
+ // First update the UI layout
1221
+ updatePreviewLayoutForAspectRatio(aspectRatio, x, y);
1222
+
1223
+ // Then rebind the camera with new aspect ratio configuration
1224
+ Log.d(TAG, "setAspectRatio: Rebinding camera with new aspect ratio: " + aspectRatio);
1225
+ bindCameraUseCases();
1226
+
1227
+ // Preserve grid mode and wait for completion
1228
+ if (gridOverlayView != null) {
1229
+ gridOverlayView.post(() -> {
1230
+ Log.d(TAG, "setAspectRatio: Re-applying grid mode: " + currentGridMode);
1231
+ gridOverlayView.setGridMode(currentGridMode);
1232
+
1233
+ // Wait one more frame for grid to be applied, then call callback
1234
+ if (callback != null) {
1235
+ gridOverlayView.post(callback);
1236
+ }
1237
+ });
1238
+ } else {
1239
+ // No grid overlay, wait one frame for layout completion then call callback
1240
+ if (callback != null) {
1241
+ previewContainer.post(callback);
1242
+ }
1243
+ }
1244
+ });
1245
+ } else {
1246
+ if (callback != null) callback.run();
1116
1247
  }
1117
1248
  }
1118
1249
 
1119
1250
  public void setGridMode(String gridMode) {
1120
1251
  if (sessionConfig != null) {
1252
+ Log.d(TAG, "setGridMode: Changing grid mode to: " + gridMode);
1121
1253
  sessionConfig = new CameraSessionConfiguration(
1122
1254
  sessionConfig.getDeviceId(),
1123
1255
  sessionConfig.getPosition(),
@@ -1140,9 +1272,327 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
1140
1272
  // Update the grid overlay immediately
1141
1273
  if (gridOverlayView != null) {
1142
1274
  gridOverlayView.post(() -> {
1275
+ Log.d(TAG, "setGridMode: Applying grid mode to overlay: " + gridMode);
1143
1276
  gridOverlayView.setGridMode(gridMode);
1144
1277
  });
1145
1278
  }
1146
1279
  }
1147
1280
  }
1281
+
1282
+ public int getPreviewX() {
1283
+ if (previewContainer == null) return 0;
1284
+ ViewGroup.LayoutParams layoutParams = previewContainer.getLayoutParams();
1285
+ if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
1286
+ // Return position relative to WebView content (subtract insets)
1287
+ int margin = ((ViewGroup.MarginLayoutParams) layoutParams).leftMargin;
1288
+ int leftInset = getWebViewLeftInset();
1289
+ int result = margin - leftInset;
1290
+ Log.d(TAG, "getPreviewX: leftMargin=" + margin + ", leftInset=" + leftInset + ", result=" + result);
1291
+ return result;
1292
+ }
1293
+ return previewContainer.getLeft();
1294
+ }
1295
+ public int getPreviewY() {
1296
+ if (previewContainer == null) return 0;
1297
+ ViewGroup.LayoutParams layoutParams = previewContainer.getLayoutParams();
1298
+ if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
1299
+ // Return position relative to WebView content (subtract insets)
1300
+ int margin = ((ViewGroup.MarginLayoutParams) layoutParams).topMargin;
1301
+ int topInset = getWebViewTopInset();
1302
+ int result = margin - topInset;
1303
+ Log.d(TAG, "getPreviewY: topMargin=" + margin + ", topInset=" + topInset + ", result=" + result);
1304
+ return result;
1305
+ }
1306
+ return previewContainer.getTop();
1307
+ }
1308
+ public int getPreviewWidth() {
1309
+ return previewContainer != null ? previewContainer.getWidth() : 0;
1310
+ }
1311
+ public int getPreviewHeight() {
1312
+ return previewContainer != null ? previewContainer.getHeight() : 0;
1313
+ }
1314
+ public void setPreviewSize(int x, int y, int width, int height) {
1315
+ setPreviewSize(x, y, width, height, null);
1316
+ }
1317
+
1318
+ public void setPreviewSize(int x, int y, int width, int height, Runnable callback) {
1319
+ if (previewContainer == null) {
1320
+ if (callback != null) callback.run();
1321
+ return;
1322
+ }
1323
+
1324
+ // Ensure this runs on the main UI thread
1325
+ mainExecutor.execute(() -> {
1326
+ ViewGroup.LayoutParams layoutParams = previewContainer.getLayoutParams();
1327
+ if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
1328
+ ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) layoutParams;
1329
+
1330
+ // Only add insets for positioning coordinates, not for full-screen sizes
1331
+ int webViewTopInset = getWebViewTopInset();
1332
+ int webViewLeftInset = getWebViewLeftInset();
1333
+
1334
+ // Handle positioning - preserve current values if new values are not specified (negative)
1335
+ if (x >= 0) {
1336
+ // Don't add insets if this looks like a calculated full-screen coordinate (x=0, y=0)
1337
+ if (x == 0 && y == 0) {
1338
+ params.leftMargin = x;
1339
+ Log.d(TAG, "setPreviewSize: Full-screen mode - keeping x=0 without insets");
1340
+ } else {
1341
+ params.leftMargin = x + webViewLeftInset;
1342
+ Log.d(TAG, "setPreviewSize: Positioned mode - x=" + x + " + inset=" + webViewLeftInset + " = " + (x + webViewLeftInset));
1343
+ }
1344
+ }
1345
+ if (y >= 0) {
1346
+ // Don't add insets if this looks like a calculated full-screen coordinate (x=0, y=0)
1347
+ if (x == 0 && y == 0) {
1348
+ params.topMargin = y;
1349
+ Log.d(TAG, "setPreviewSize: Full-screen mode - keeping y=0 without insets");
1350
+ } else {
1351
+ params.topMargin = y + webViewTopInset;
1352
+ Log.d(TAG, "setPreviewSize: Positioned mode - y=" + y + " + inset=" + webViewTopInset + " = " + (y + webViewTopInset));
1353
+ }
1354
+ }
1355
+ if (width > 0) params.width = width;
1356
+ if (height > 0) params.height = height;
1357
+
1358
+ previewContainer.setLayoutParams(params);
1359
+ previewContainer.requestLayout();
1360
+
1361
+ Log.d(TAG, "setPreviewSize: Updated to " + params.width + "x" + params.height + " at (" + params.leftMargin + "," + params.topMargin + ")");
1362
+
1363
+ // Update session config to reflect actual layout
1364
+ if (sessionConfig != null) {
1365
+ String currentAspectRatio = sessionConfig.getAspectRatio();
1366
+
1367
+ // Calculate aspect ratio from actual dimensions if both width and height are provided
1368
+ String calculatedAspectRatio = currentAspectRatio;
1369
+ if (params.width > 0 && params.height > 0) {
1370
+ // Always use larger dimension / smaller dimension for consistent comparison
1371
+ float ratio = Math.max(params.width, params.height) / (float) Math.min(params.width, params.height);
1372
+ // Standard ratios: 16:9 ≈ 1.778, 4:3 ≈ 1.333
1373
+ float ratio16_9 = 16f / 9f; // 1.778
1374
+ float ratio4_3 = 4f / 3f; // 1.333
1375
+
1376
+ // Determine closest standard aspect ratio
1377
+ if (Math.abs(ratio - ratio16_9) < Math.abs(ratio - ratio4_3)) {
1378
+ calculatedAspectRatio = "16:9";
1379
+ } else {
1380
+ calculatedAspectRatio = "4:3";
1381
+ }
1382
+ Log.d(TAG, "setPreviewSize: Calculated aspect ratio from " + params.width + "x" + params.height + " = " + calculatedAspectRatio + " (normalized ratio=" + ratio + ")");
1383
+ }
1384
+
1385
+ sessionConfig = new CameraSessionConfiguration(
1386
+ sessionConfig.getDeviceId(),
1387
+ sessionConfig.getPosition(),
1388
+ params.leftMargin,
1389
+ params.topMargin,
1390
+ params.width,
1391
+ params.height,
1392
+ sessionConfig.getPaddingBottom(),
1393
+ sessionConfig.getToBack(),
1394
+ sessionConfig.getStoreToFile(),
1395
+ sessionConfig.getEnableOpacity(),
1396
+ sessionConfig.getEnableZoom(),
1397
+ sessionConfig.getDisableExifHeaderStripping(),
1398
+ sessionConfig.getDisableAudio(),
1399
+ sessionConfig.getZoomFactor(),
1400
+ calculatedAspectRatio,
1401
+ sessionConfig.getGridMode()
1402
+ );
1403
+
1404
+ // If aspect ratio changed due to size update, rebind camera
1405
+ if (isRunning && !Objects.equals(currentAspectRatio, calculatedAspectRatio)) {
1406
+ Log.d(TAG, "setPreviewSize: Aspect ratio changed from " + currentAspectRatio + " to " + calculatedAspectRatio + ", rebinding camera");
1407
+ bindCameraUseCases();
1408
+
1409
+ // Wait for camera rebinding to complete, then call callback
1410
+ if (callback != null) {
1411
+ previewContainer.post(() -> previewContainer.post(callback));
1412
+ }
1413
+ } else {
1414
+ // No camera rebinding needed, wait for layout to complete then call callback
1415
+ if (callback != null) {
1416
+ previewContainer.post(callback);
1417
+ }
1418
+ }
1419
+ } else {
1420
+ // No sessionConfig, just wait for layout then call callback
1421
+ if (callback != null) {
1422
+ previewContainer.post(callback);
1423
+ }
1424
+ }
1425
+ } else {
1426
+ Log.w(TAG, "setPreviewSize: Cannot set margins on layout params of type " + layoutParams.getClass().getSimpleName());
1427
+ // Fallback: just set width and height if specified
1428
+ if (width > 0) layoutParams.width = width;
1429
+ if (height > 0) layoutParams.height = height;
1430
+ previewContainer.setLayoutParams(layoutParams);
1431
+ previewContainer.requestLayout();
1432
+
1433
+ // Wait for layout then call callback
1434
+ if (callback != null) {
1435
+ previewContainer.post(callback);
1436
+ }
1437
+ }
1438
+ });
1439
+ }
1440
+
1441
+ private void updatePreviewLayoutForAspectRatio(String aspectRatio) {
1442
+ updatePreviewLayoutForAspectRatio(aspectRatio, null, null);
1443
+ }
1444
+
1445
+ private void updatePreviewLayoutForAspectRatio(String aspectRatio, Float x, Float y) {
1446
+ if (previewContainer == null || aspectRatio == null) return;
1447
+
1448
+ // Parse aspect ratio
1449
+ String[] ratios = aspectRatio.split(":");
1450
+ if (ratios.length != 2) return;
1451
+
1452
+ try {
1453
+ // For camera, use portrait orientation: 4:3 becomes 3:4, 16:9 becomes 9:16
1454
+ float ratio = Float.parseFloat(ratios[1]) / Float.parseFloat(ratios[0]);
1455
+
1456
+ // Get available space from webview dimensions
1457
+ int availableWidth = webView.getWidth();
1458
+ int availableHeight = webView.getHeight();
1459
+
1460
+ // Calculate position and size
1461
+ int finalX, finalY, finalWidth, finalHeight;
1462
+
1463
+ if (x != null && y != null) {
1464
+ // Account for WebView insets from edge-to-edge support
1465
+ int webViewTopInset = getWebViewTopInset();
1466
+ int webViewLeftInset = getWebViewLeftInset();
1467
+
1468
+ // Use provided coordinates with boundary checking, adjusted for insets
1469
+ finalX = Math.max(0, Math.min(x.intValue() + webViewLeftInset, availableWidth));
1470
+ finalY = Math.max(0, Math.min(y.intValue() + webViewTopInset, availableHeight));
1471
+
1472
+ // Calculate maximum available space from the given position
1473
+ int maxWidth = availableWidth - finalX;
1474
+ int maxHeight = availableHeight - finalY;
1475
+
1476
+ // Calculate optimal size while maintaining aspect ratio within available space
1477
+ finalWidth = maxWidth;
1478
+ finalHeight = (int) (maxWidth / ratio);
1479
+
1480
+ if (finalHeight > maxHeight) {
1481
+ // Height constraint is tighter, fit by height
1482
+ finalHeight = maxHeight;
1483
+ finalWidth = (int) (maxHeight * ratio);
1484
+ }
1485
+
1486
+ // Ensure final position stays within bounds
1487
+ finalX = Math.max(0, Math.min(finalX, availableWidth - finalWidth));
1488
+ finalY = Math.max(0, Math.min(finalY, availableHeight - finalHeight));
1489
+ } else {
1490
+ // Auto-center the view
1491
+ // Calculate size based on aspect ratio, using a reasonable base size
1492
+ // Use 80% of available space to ensure aspect ratio differences are visible
1493
+ int maxAvailableWidth = (int) (availableWidth * 0.8);
1494
+ int maxAvailableHeight = (int) (availableHeight * 0.8);
1495
+
1496
+ // Start with width-based calculation
1497
+ finalWidth = maxAvailableWidth;
1498
+ finalHeight = (int) (finalWidth / ratio);
1499
+
1500
+ // If height exceeds available space, use height-based calculation
1501
+ if (finalHeight > maxAvailableHeight) {
1502
+ finalHeight = maxAvailableHeight;
1503
+ finalWidth = (int) (finalHeight * ratio);
1504
+ }
1505
+
1506
+ // Center the view
1507
+ finalX = (availableWidth - finalWidth) / 2;
1508
+ finalY = (availableHeight - finalHeight) / 2;
1509
+
1510
+ Log.d(TAG, "updatePreviewLayoutForAspectRatio: Auto-center mode - ratio=" + ratio +
1511
+ ", calculated size=" + finalWidth + "x" + finalHeight +
1512
+ ", available=" + availableWidth + "x" + availableHeight);
1513
+ }
1514
+
1515
+ // Update layout params
1516
+ ViewGroup.LayoutParams currentParams = previewContainer.getLayoutParams();
1517
+ if (currentParams instanceof ViewGroup.MarginLayoutParams) {
1518
+ ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) currentParams;
1519
+ params.width = finalWidth;
1520
+ params.height = finalHeight;
1521
+ params.leftMargin = finalX;
1522
+ params.topMargin = finalY;
1523
+ previewContainer.setLayoutParams(params);
1524
+ previewContainer.requestLayout();
1525
+ Log.d(TAG, "updatePreviewLayoutForAspectRatio: Updated to " + finalWidth + "x" + finalHeight + " at (" + finalX + "," + finalY + ")");
1526
+ }
1527
+ } catch (NumberFormatException e) {
1528
+ Log.e(TAG, "Invalid aspect ratio format: " + aspectRatio, e);
1529
+ }
1530
+ }
1531
+
1532
+ private void updatePreviewLayout() {
1533
+ if (previewContainer == null || sessionConfig == null) return;
1534
+
1535
+ String aspectRatio = sessionConfig.getAspectRatio();
1536
+ if (aspectRatio == null) return;
1537
+
1538
+ updatePreviewLayoutForAspectRatio(aspectRatio);
1539
+ }
1540
+
1541
+ private int getWebViewTopInset() {
1542
+ try {
1543
+ if (webView != null) {
1544
+ ViewGroup.LayoutParams layoutParams = webView.getLayoutParams();
1545
+ if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
1546
+ return ((ViewGroup.MarginLayoutParams) layoutParams).topMargin;
1547
+ }
1548
+ }
1549
+ } catch (Exception e) {
1550
+ Log.w(TAG, "Failed to get WebView top inset", e);
1551
+ }
1552
+ return 0;
1553
+ }
1554
+
1555
+ private int getWebViewLeftInset() {
1556
+ try {
1557
+ if (webView != null) {
1558
+ ViewGroup.LayoutParams layoutParams = webView.getLayoutParams();
1559
+ if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
1560
+ return ((ViewGroup.MarginLayoutParams) layoutParams).leftMargin;
1561
+ }
1562
+ }
1563
+ } catch (Exception e) {
1564
+ Log.w(TAG, "Failed to get WebView left inset", e);
1565
+ }
1566
+ return 0;
1567
+ }
1568
+
1569
+ /**
1570
+ * Get the current preview position and size in DP units (without insets)
1571
+ */
1572
+ public int[] getCurrentPreviewBounds() {
1573
+ if (previewContainer == null) {
1574
+ return new int[]{0, 0, 0, 0}; // x, y, width, height
1575
+ }
1576
+
1577
+ ViewGroup.LayoutParams layoutParams = previewContainer.getLayoutParams();
1578
+ int x = 0, y = 0, width = 0, height = 0;
1579
+
1580
+ if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
1581
+ ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) layoutParams;
1582
+
1583
+ // Remove insets to get original coordinates in DP
1584
+ DisplayMetrics metrics = context.getResources().getDisplayMetrics();
1585
+ float pixelRatio = metrics.density;
1586
+
1587
+ int webViewTopInset = getWebViewTopInset();
1588
+ int webViewLeftInset = getWebViewLeftInset();
1589
+
1590
+ x = Math.max(0, (int) ((params.leftMargin - webViewLeftInset) / pixelRatio));
1591
+ y = Math.max(0, (int) ((params.topMargin - webViewTopInset) / pixelRatio));
1592
+ width = (int) (params.width / pixelRatio);
1593
+ height = (int) (params.height / pixelRatio);
1594
+ }
1595
+
1596
+ return new int[]{x, y, width, height};
1597
+ }
1148
1598
  }
@@ -34,8 +34,10 @@ public class GridOverlayView extends View {
34
34
  }
35
35
 
36
36
  public void setGridMode(String mode) {
37
+ String previousMode = this.gridMode;
37
38
  this.gridMode = mode != null ? mode : "none";
38
39
  setVisibility("none".equals(this.gridMode) ? View.GONE : View.VISIBLE);
40
+ android.util.Log.d("GridOverlayView", "setGridMode: Changed from '" + previousMode + "' to '" + this.gridMode + "', visibility: " + ("none".equals(this.gridMode) ? "GONE" : "VISIBLE"));
39
41
  invalidate();
40
42
  }
41
43
 
package/dist/docs.json CHANGED
@@ -141,23 +141,23 @@
141
141
  },
142
142
  {
143
143
  "name": "setAspectRatio",
144
- "signature": "(options: { aspectRatio: '4:3' | '16:9'; }) => Promise<void>",
144
+ "signature": "(options: { aspectRatio: '4:3' | '16:9'; x?: number; y?: number; }) => Promise<{ width: number; height: number; x: number; y: number; }>",
145
145
  "parameters": [
146
146
  {
147
147
  "name": "options",
148
- "docs": "- The desired aspect ratio.",
149
- "type": "{ aspectRatio: '4:3' | '16:9'; }"
148
+ "docs": "- The desired aspect ratio and optional position.\n- aspectRatio: The desired aspect ratio ('4:3' or '16:9')\n- x: Optional x coordinate for positioning. If not provided, view will be auto-centered horizontally.\n- y: Optional y coordinate for positioning. If not provided, view will be auto-centered vertically.",
149
+ "type": "{ aspectRatio: '4:3' | '16:9'; x?: number | undefined; y?: number | undefined; }"
150
150
  }
151
151
  ],
152
- "returns": "Promise<void>",
152
+ "returns": "Promise<{ width: number; height: number; x: number; y: number; }>",
153
153
  "tags": [
154
154
  {
155
155
  "name": "param",
156
- "text": "options - The desired aspect ratio."
156
+ "text": "options - The desired aspect ratio and optional position.\n- aspectRatio: The desired aspect ratio ('4:3' or '16:9')\n- x: Optional x coordinate for positioning. If not provided, view will be auto-centered horizontally.\n- y: Optional y coordinate for positioning. If not provided, view will be auto-centered vertically."
157
157
  },
158
158
  {
159
159
  "name": "returns",
160
- "text": "A promise that resolves when the aspect ratio is set."
160
+ "text": "A promise that resolves with the actual preview dimensions and position."
161
161
  },
162
162
  {
163
163
  "name": "since",
@@ -583,6 +583,45 @@
583
583
  "docs": "Gets the ID of the currently active camera device.",
584
584
  "complexTypes": [],
585
585
  "slug": "getdeviceid"
586
+ },
587
+ {
588
+ "name": "getPreviewSize",
589
+ "signature": "() => Promise<{ x: number; y: number; width: number; height: number; }>",
590
+ "parameters": [],
591
+ "returns": "Promise<{ x: number; y: number; width: number; height: number; }>",
592
+ "tags": [
593
+ {
594
+ "name": "returns"
595
+ }
596
+ ],
597
+ "docs": "Gets the current preview size and position.",
598
+ "complexTypes": [],
599
+ "slug": "getpreviewsize"
600
+ },
601
+ {
602
+ "name": "setPreviewSize",
603
+ "signature": "(options: { x: number; y: number; width: number; height: number; }) => Promise<{ width: number; height: number; x: number; y: number; }>",
604
+ "parameters": [
605
+ {
606
+ "name": "options",
607
+ "docs": "The new position and dimensions.",
608
+ "type": "{ x: number; y: number; width: number; height: number; }"
609
+ }
610
+ ],
611
+ "returns": "Promise<{ width: number; height: number; x: number; y: number; }>",
612
+ "tags": [
613
+ {
614
+ "name": "param",
615
+ "text": "options The new position and dimensions."
616
+ },
617
+ {
618
+ "name": "returns",
619
+ "text": "A promise that resolves with the actual preview dimensions and position."
620
+ }
621
+ ],
622
+ "docs": "Sets the preview size and position.",
623
+ "complexTypes": [],
624
+ "slug": "setpreviewsize"
586
625
  }
587
626
  ],
588
627
  "properties": []
@@ -675,7 +714,7 @@
675
714
  "name": "since"
676
715
  }
677
716
  ],
678
- "docs": "The aspect ratio of the camera preview, '4:3' or '16:9'.\nIf not set, the camera will use the default aspect ratio.",
717
+ "docs": "The aspect ratio of the camera preview, '4:3' or '16:9' or 'fill'.\nCannot be set if width or height is provided, otherwise the call will be rejected.\nUse setPreviewSize to adjust size after starting.",
679
718
  "complexTypes": [],
680
719
  "type": "'4:3' | '16:9' | 'fill' | undefined"
681
720
  },