@capgo/camera-preview 7.4.0-beta.1 → 7.4.0-beta.11

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 (35) hide show
  1. package/README.md +195 -31
  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/file-system.probe +0 -0
  12. package/android/build.gradle +3 -1
  13. package/android/src/main/AndroidManifest.xml +5 -3
  14. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java +473 -88
  15. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraXView.java +2065 -704
  16. package/android/src/main/java/com/ahm/capacitor/camera/preview/GridOverlayView.java +95 -0
  17. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraDevice.java +55 -46
  18. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraLens.java +61 -52
  19. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraSessionConfiguration.java +152 -59
  20. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/LensInfo.java +29 -23
  21. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/ZoomFactors.java +24 -23
  22. package/dist/docs.json +235 -6
  23. package/dist/esm/definitions.d.ts +119 -3
  24. package/dist/esm/definitions.js.map +1 -1
  25. package/dist/esm/web.d.ts +47 -3
  26. package/dist/esm/web.js +297 -96
  27. package/dist/esm/web.js.map +1 -1
  28. package/dist/plugin.cjs.js +293 -96
  29. package/dist/plugin.cjs.js.map +1 -1
  30. package/dist/plugin.js +293 -96
  31. package/dist/plugin.js.map +1 -1
  32. package/ios/Sources/CapgoCameraPreview/CameraController.swift +364 -218
  33. package/ios/Sources/CapgoCameraPreview/GridOverlayView.swift +65 -0
  34. package/ios/Sources/CapgoCameraPreview/Plugin.swift +886 -242
  35. package/package.json +1 -1
@@ -3,12 +3,17 @@ package com.ahm.capacitor.camera.preview;
3
3
  import static android.Manifest.permission.CAMERA;
4
4
  import static android.Manifest.permission.RECORD_AUDIO;
5
5
 
6
+ import android.Manifest;
6
7
  import android.content.pm.ActivityInfo;
7
- import android.graphics.Point;
8
+ import android.location.Location;
8
9
  import android.util.DisplayMetrics;
9
- import android.util.TypedValue;
10
- import android.view.Display;
11
- import androidx.annotation.NonNull;
10
+ import android.util.Log;
11
+ import android.util.Size;
12
+ import android.view.ViewGroup;
13
+ import com.ahm.capacitor.camera.preview.model.CameraDevice;
14
+ import com.ahm.capacitor.camera.preview.model.CameraSessionConfiguration;
15
+ import com.ahm.capacitor.camera.preview.model.LensInfo;
16
+ import com.ahm.capacitor.camera.preview.model.ZoomFactors;
12
17
  import com.getcapacitor.JSArray;
13
18
  import com.getcapacitor.JSObject;
14
19
  import com.getcapacitor.Logger;
@@ -19,14 +24,11 @@ import com.getcapacitor.PluginMethod;
19
24
  import com.getcapacitor.annotation.CapacitorPlugin;
20
25
  import com.getcapacitor.annotation.Permission;
21
26
  import com.getcapacitor.annotation.PermissionCallback;
22
- import com.ahm.capacitor.camera.preview.model.CameraDevice;
23
- import com.ahm.capacitor.camera.preview.model.CameraSessionConfiguration;
24
- import com.ahm.capacitor.camera.preview.model.ZoomFactors;
27
+ import com.google.android.gms.location.FusedLocationProviderClient;
28
+ import com.google.android.gms.location.LocationServices;
25
29
  import java.util.List;
26
30
  import java.util.Objects;
27
- import android.util.Size;
28
- import android.util.Log;
29
- import com.ahm.capacitor.camera.preview.model.LensInfo;
31
+ import org.json.JSONObject;
30
32
 
31
33
  @CapacitorPlugin(
32
34
  name = "CameraPreview",
@@ -39,6 +41,13 @@ import com.ahm.capacitor.camera.preview.model.LensInfo;
39
41
  strings = { CAMERA },
40
42
  alias = CameraPreview.CAMERA_ONLY_PERMISSION_ALIAS
41
43
  ),
44
+ @Permission(
45
+ strings = {
46
+ Manifest.permission.ACCESS_COARSE_LOCATION,
47
+ Manifest.permission.ACCESS_FINE_LOCATION,
48
+ },
49
+ alias = CameraPreview.CAMERA_WITH_LOCATION_PERMISSION_ALIAS
50
+ ),
42
51
  }
43
52
  )
44
53
  public class CameraPreview
@@ -47,16 +56,23 @@ public class CameraPreview
47
56
 
48
57
  static final String CAMERA_WITH_AUDIO_PERMISSION_ALIAS = "cameraWithAudio";
49
58
  static final String CAMERA_ONLY_PERMISSION_ALIAS = "cameraOnly";
59
+ static final String CAMERA_WITH_LOCATION_PERMISSION_ALIAS =
60
+ "cameraWithLocation";
50
61
 
51
62
  private String captureCallbackId = "";
52
63
  private String snapshotCallbackId = "";
53
64
  private String cameraStartCallbackId = "";
54
- private int previousOrientationRequest = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
65
+ private int previousOrientationRequest =
66
+ ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
55
67
  private CameraXView cameraXView;
68
+ private FusedLocationProviderClient fusedLocationClient;
69
+ private Location lastLocation;
56
70
 
57
71
  @PluginMethod
58
72
  public void start(PluginCall call) {
59
- boolean disableAudio = Boolean.TRUE.equals(call.getBoolean("disableAudio", true));
73
+ boolean disableAudio = Boolean.TRUE.equals(
74
+ call.getBoolean("disableAudio", true)
75
+ );
60
76
  String permissionAlias = disableAudio
61
77
  ? CAMERA_ONLY_PERMISSION_ALIAS
62
78
  : CAMERA_WITH_AUDIO_PERMISSION_ALIAS;
@@ -83,15 +99,79 @@ public class CameraPreview
83
99
  }
84
100
 
85
101
  @PluginMethod
86
- public void capture(PluginCall call) {
102
+ public void capture(final PluginCall call) {
87
103
  if (cameraXView == null || !cameraXView.isRunning()) {
88
104
  call.reject("Camera is not running");
89
105
  return;
90
106
  }
107
+
108
+ final boolean withExifLocation = call.getBoolean("withExifLocation", false);
109
+
110
+ if (withExifLocation) {
111
+ if (
112
+ getPermissionState(CAMERA_WITH_LOCATION_PERMISSION_ALIAS) !=
113
+ PermissionState.GRANTED
114
+ ) {
115
+ requestPermissionForAlias(
116
+ CAMERA_WITH_LOCATION_PERMISSION_ALIAS,
117
+ call,
118
+ "captureWithLocationPermission"
119
+ );
120
+ } else {
121
+ getLocationAndCapture(call);
122
+ }
123
+ } else {
124
+ captureWithoutLocation(call);
125
+ }
126
+ }
127
+
128
+ @PermissionCallback
129
+ private void captureWithLocationPermission(PluginCall call) {
130
+ if (
131
+ getPermissionState(CAMERA_WITH_LOCATION_PERMISSION_ALIAS) ==
132
+ PermissionState.GRANTED
133
+ ) {
134
+ getLocationAndCapture(call);
135
+ } else {
136
+ Logger.warn(
137
+ "Location permission denied. Capturing photo without location data."
138
+ );
139
+ captureWithoutLocation(call);
140
+ }
141
+ }
142
+
143
+ private void getLocationAndCapture(PluginCall call) {
144
+ if (fusedLocationClient == null) {
145
+ fusedLocationClient = LocationServices.getFusedLocationProviderClient(
146
+ getContext()
147
+ );
148
+ }
149
+ fusedLocationClient
150
+ .getLastLocation()
151
+ .addOnSuccessListener(getActivity(), location -> {
152
+ lastLocation = location;
153
+ proceedWithCapture(call, lastLocation);
154
+ })
155
+ .addOnFailureListener(e -> {
156
+ Logger.error("Failed to get location: " + e.getMessage());
157
+ proceedWithCapture(call, null);
158
+ });
159
+ }
160
+
161
+ private void captureWithoutLocation(PluginCall call) {
162
+ proceedWithCapture(call, null);
163
+ }
164
+
165
+ private void proceedWithCapture(PluginCall call, Location location) {
91
166
  bridge.saveCall(call);
92
167
  captureCallbackId = call.getCallbackId();
168
+
93
169
  Integer quality = Objects.requireNonNull(call.getInt("quality", 85));
94
- cameraXView.capturePhoto(quality);
170
+ final boolean saveToGallery = call.getBoolean("saveToGallery", false);
171
+ Integer width = call.getInt("width");
172
+ Integer height = call.getInt("height");
173
+
174
+ cameraXView.capturePhoto(quality, saveToGallery, width, height, location);
95
175
  }
96
176
 
97
177
  @PluginMethod
@@ -110,19 +190,17 @@ public class CameraPreview
110
190
  public void stop(final PluginCall call) {
111
191
  bridge
112
192
  .getActivity()
113
- .runOnUiThread(
114
- () -> {
115
- getBridge()
116
- .getActivity()
117
- .setRequestedOrientation(previousOrientationRequest);
118
-
119
- if (cameraXView != null && cameraXView.isRunning()) {
120
- cameraXView.stopSession();
121
- cameraXView = null;
122
- }
123
- call.resolve();
193
+ .runOnUiThread(() -> {
194
+ getBridge()
195
+ .getActivity()
196
+ .setRequestedOrientation(previousOrientationRequest);
197
+
198
+ if (cameraXView != null && cameraXView.isRunning()) {
199
+ cameraXView.stopSession();
200
+ cameraXView = null;
124
201
  }
125
- );
202
+ call.resolve();
203
+ });
126
204
  }
127
205
 
128
206
  @PluginMethod
@@ -150,7 +228,9 @@ public class CameraPreview
150
228
 
151
229
  @PluginMethod
152
230
  public void getAvailableDevices(PluginCall call) {
153
- List<CameraDevice> devices = CameraXView.getAvailableDevicesStatic(getContext());
231
+ List<CameraDevice> devices = CameraXView.getAvailableDevicesStatic(
232
+ getContext()
233
+ );
154
234
  JSArray devicesArray = new JSArray();
155
235
  for (CameraDevice device : devices) {
156
236
  JSObject deviceJson = new JSObject();
@@ -227,7 +307,7 @@ public class CameraPreview
227
307
  JSObject rear = new JSObject();
228
308
  rear.put("facing", "rear");
229
309
  JSArray rearSizesJs = new JSArray();
230
- for(Size size : rearSizes) {
310
+ for (Size size : rearSizes) {
231
311
  JSObject sizeJs = new JSObject();
232
312
  sizeJs.put("width", size.getWidth());
233
313
  sizeJs.put("height", size.getHeight());
@@ -235,12 +315,12 @@ public class CameraPreview
235
315
  }
236
316
  rear.put("supportedPictureSizes", rearSizesJs);
237
317
  supportedPictureSizesResult.put(rear);
238
-
318
+
239
319
  List<Size> frontSizes = CameraXView.getSupportedPictureSizes("front");
240
320
  JSObject front = new JSObject();
241
321
  front.put("facing", "front");
242
322
  JSArray frontSizesJs = new JSArray();
243
- for(Size size : frontSizes) {
323
+ for (Size size : frontSizes) {
244
324
  JSObject sizeJs = new JSObject();
245
325
  sizeJs.put("width", size.getWidth());
246
326
  sizeJs.put("height", size.getHeight());
@@ -248,7 +328,7 @@ public class CameraPreview
248
328
  }
249
329
  front.put("supportedPictureSizes", frontSizesJs);
250
330
  supportedPictureSizesResult.put(front);
251
-
331
+
252
332
  JSObject ret = new JSObject();
253
333
  ret.put("supportedPictureSizes", supportedPictureSizesResult);
254
334
  call.resolve(ret);
@@ -307,8 +387,14 @@ public class CameraPreview
307
387
 
308
388
  @PermissionCallback
309
389
  private void handleCameraPermissionResult(PluginCall call) {
310
- if (PermissionState.GRANTED.equals(getPermissionState(CAMERA_ONLY_PERMISSION_ALIAS)) ||
311
- PermissionState.GRANTED.equals(getPermissionState(CAMERA_WITH_AUDIO_PERMISSION_ALIAS))) {
390
+ if (
391
+ PermissionState.GRANTED.equals(
392
+ getPermissionState(CAMERA_ONLY_PERMISSION_ALIAS)
393
+ ) ||
394
+ PermissionState.GRANTED.equals(
395
+ getPermissionState(CAMERA_WITH_AUDIO_PERMISSION_ALIAS)
396
+ )
397
+ ) {
312
398
  startCamera(call);
313
399
  } else {
314
400
  call.reject("Permission failed");
@@ -320,106 +406,405 @@ public class CameraPreview
320
406
  String originalDeviceId = call.getString("deviceId");
321
407
  String deviceId = originalDeviceId; // Use a mutable variable
322
408
 
323
- final String position = (positionParam == null || positionParam.isEmpty() || "rear".equals(positionParam) || "back".equals(positionParam)) ? "back" : "front";
409
+ final String position = (positionParam == null ||
410
+ positionParam.isEmpty() ||
411
+ "rear".equals(positionParam) ||
412
+ "back".equals(positionParam))
413
+ ? "back"
414
+ : "front";
324
415
  final int x = call.getInt("x", 0);
325
416
  final int y = call.getInt("y", 0);
326
417
  final int width = call.getInt("width", 0);
327
418
  final int height = call.getInt("height", 0);
328
419
  final int paddingBottom = call.getInt("paddingBottom", 0);
329
- final boolean toBack = call.getBoolean("toBack", true);
330
- final boolean storeToFile = call.getBoolean("storeToFile", false);
331
- final boolean enableOpacity = call.getBoolean("enableOpacity", false);
332
- final boolean enableZoom = call.getBoolean("enableZoom", false);
333
- final boolean disableExifHeaderStripping = call.getBoolean("disableExifHeaderStripping", false);
334
- final boolean lockOrientation = call.getBoolean("lockAndroidOrientation", false);
335
- final boolean disableAudio = call.getBoolean("disableAudio", true);
336
-
420
+ final boolean toBack = Boolean.TRUE.equals(call.getBoolean("toBack", true));
421
+ final boolean storeToFile = Boolean.TRUE.equals(
422
+ call.getBoolean("storeToFile", false)
423
+ );
424
+ final boolean enableOpacity = Boolean.TRUE.equals(
425
+ call.getBoolean("enableOpacity", false)
426
+ );
427
+ final boolean enableZoom = Boolean.TRUE.equals(
428
+ call.getBoolean("enableZoom", false)
429
+ );
430
+ final boolean disableExifHeaderStripping = Boolean.TRUE.equals(
431
+ call.getBoolean("disableExifHeaderStripping", false)
432
+ );
433
+ final boolean lockOrientation = Boolean.TRUE.equals(
434
+ call.getBoolean("lockAndroidOrientation", false)
435
+ );
436
+ final boolean disableAudio = Boolean.TRUE.equals(
437
+ call.getBoolean("disableAudio", true)
438
+ );
439
+ final String aspectRatio = call.getString("aspectRatio", "4:3");
440
+ final String gridMode = call.getString("gridMode", "none");
441
+
442
+ // Check for conflict between aspectRatio and size
443
+ if (
444
+ call.getData().has("aspectRatio") &&
445
+ (call.getData().has("width") || call.getData().has("height"))
446
+ ) {
447
+ call.reject(
448
+ "Cannot set both aspectRatio and size (width/height). Use setPreviewSize after start."
449
+ );
450
+ return;
451
+ }
452
+
337
453
  float targetZoom = 1.0f;
338
454
  // Check if the selected device is a physical ultra-wide
339
455
  if (originalDeviceId != null) {
340
- List<CameraDevice> devices = CameraXView.getAvailableDevicesStatic(getContext());
341
- for (CameraDevice device : devices) {
342
- if (originalDeviceId.equals(device.getDeviceId()) && !device.isLogical()) {
343
- for (LensInfo lens : device.getLenses()) {
344
- if ("ultraWide".equals(lens.getDeviceType())) {
345
- Log.d("CameraPreview", "Ultra-wide lens selected. Targeting 0.5x zoom on logical camera.");
346
- targetZoom = 0.5f;
347
- // Force the use of the logical camera by clearing the specific deviceId
348
- deviceId = null;
349
- break;
350
- }
351
- }
456
+ List<CameraDevice> devices = CameraXView.getAvailableDevicesStatic(
457
+ getContext()
458
+ );
459
+ for (CameraDevice device : devices) {
460
+ if (
461
+ originalDeviceId.equals(device.getDeviceId()) && !device.isLogical()
462
+ ) {
463
+ for (LensInfo lens : device.getLenses()) {
464
+ if ("ultraWide".equals(lens.getDeviceType())) {
465
+ Log.d(
466
+ "CameraPreview",
467
+ "Ultra-wide lens selected. Targeting 0.5x zoom on logical camera."
468
+ );
469
+ targetZoom = 0.5f;
470
+ // Force the use of the logical camera by clearing the specific deviceId
471
+ deviceId = null;
472
+ break;
352
473
  }
353
- if (deviceId == null) break; // Exit outer loop once we've made our decision
474
+ }
354
475
  }
476
+ if (deviceId == null) break; // Exit outer loop once we've made our decision
477
+ }
355
478
  }
356
479
 
357
- previousOrientationRequest = getBridge().getActivity().getRequestedOrientation();
480
+ previousOrientationRequest = getBridge()
481
+ .getActivity()
482
+ .getRequestedOrientation();
358
483
  cameraXView = new CameraXView(getContext(), getBridge().getWebView());
359
484
  cameraXView.setListener(this);
360
485
 
361
486
  String finalDeviceId = deviceId;
362
487
  float finalTargetZoom = targetZoom;
363
- getBridge().getActivity().runOnUiThread(() -> {
364
- DisplayMetrics metrics = getBridge().getActivity().getResources().getDisplayMetrics();
488
+ getBridge()
489
+ .getActivity()
490
+ .runOnUiThread(() -> {
491
+ DisplayMetrics metrics = getBridge()
492
+ .getActivity()
493
+ .getResources()
494
+ .getDisplayMetrics();
365
495
  if (lockOrientation) {
366
- getBridge().getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
496
+ getBridge()
497
+ .getActivity()
498
+ .setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
367
499
  }
368
- int computedX = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, x, metrics);
369
- int computedY = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, y, metrics);
370
- int computedWidth = width != 0 ? (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, width, metrics) : (int) getBridge().getWebView().getWidth();
371
- int computedHeight = height != 0 ? (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, height, metrics) : (int) getBridge().getWebView().getHeight();
372
- computedHeight -= (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, paddingBottom, metrics);
373
500
 
374
- CameraSessionConfiguration config = new CameraSessionConfiguration(finalDeviceId, position, computedX, computedY, computedWidth, computedHeight, paddingBottom, toBack, storeToFile, enableOpacity, enableZoom, disableExifHeaderStripping, disableAudio, 1.0f);
501
+ // Debug: Let's check all the positioning information
502
+ ViewGroup webViewParent = (ViewGroup) getBridge()
503
+ .getWebView()
504
+ .getParent();
505
+
506
+ // Get webview position in different coordinate systems
507
+ int[] webViewLocationInWindow = new int[2];
508
+ int[] webViewLocationOnScreen = new int[2];
509
+ getBridge().getWebView().getLocationInWindow(webViewLocationInWindow);
510
+ getBridge().getWebView().getLocationOnScreen(webViewLocationOnScreen);
511
+
512
+ int webViewLeft = getBridge().getWebView().getLeft();
513
+ int webViewTop = getBridge().getWebView().getTop();
514
+
515
+ // Check parent position too
516
+ int[] parentLocationInWindow = new int[2];
517
+ int[] parentLocationOnScreen = new int[2];
518
+ webViewParent.getLocationInWindow(parentLocationInWindow);
519
+ webViewParent.getLocationOnScreen(parentLocationOnScreen);
520
+
521
+ // Calculate pixel ratio
522
+ float pixelRatio = metrics.density;
523
+
524
+ // Try using just the pixel ratio without any webview offset for now
525
+ int computedX = (int) (x * pixelRatio);
526
+ int computedY = (int) (y * pixelRatio);
527
+
528
+ Log.d("CameraPreview", "=== COORDINATE DEBUG ===");
529
+ Log.d(
530
+ "CameraPreview",
531
+ "WebView getLeft/getTop: (" + webViewLeft + ", " + webViewTop + ")"
532
+ );
533
+ Log.d(
534
+ "CameraPreview",
535
+ "WebView locationInWindow: (" +
536
+ webViewLocationInWindow[0] +
537
+ ", " +
538
+ webViewLocationInWindow[1] +
539
+ ")"
540
+ );
541
+ Log.d(
542
+ "CameraPreview",
543
+ "WebView locationOnScreen: (" +
544
+ webViewLocationOnScreen[0] +
545
+ ", " +
546
+ webViewLocationOnScreen[1] +
547
+ ")"
548
+ );
549
+ Log.d(
550
+ "CameraPreview",
551
+ "Parent locationInWindow: (" +
552
+ parentLocationInWindow[0] +
553
+ ", " +
554
+ parentLocationInWindow[1] +
555
+ ")"
556
+ );
557
+ Log.d(
558
+ "CameraPreview",
559
+ "Parent locationOnScreen: (" +
560
+ parentLocationOnScreen[0] +
561
+ ", " +
562
+ parentLocationOnScreen[1] +
563
+ ")"
564
+ );
565
+ Log.d(
566
+ "CameraPreview",
567
+ "Parent class: " + webViewParent.getClass().getSimpleName()
568
+ );
569
+ Log.d(
570
+ "CameraPreview",
571
+ "Requested position (logical): (" + x + ", " + y + ")"
572
+ );
573
+ Log.d("CameraPreview", "Pixel ratio: " + pixelRatio);
574
+ Log.d(
575
+ "CameraPreview",
576
+ "Final computed position (no offset): (" +
577
+ computedX +
578
+ ", " +
579
+ computedY +
580
+ ")"
581
+ );
582
+ Log.d("CameraPreview", "========================");
583
+ int computedWidth = width != 0
584
+ ? (int) (width * pixelRatio)
585
+ : getBridge().getWebView().getWidth();
586
+ int computedHeight = height != 0
587
+ ? (int) (height * pixelRatio)
588
+ : getBridge().getWebView().getHeight();
589
+ computedHeight -= (int) (paddingBottom * pixelRatio);
590
+
591
+ CameraSessionConfiguration config = new CameraSessionConfiguration(
592
+ finalDeviceId,
593
+ position,
594
+ computedX,
595
+ computedY,
596
+ computedWidth,
597
+ computedHeight,
598
+ paddingBottom,
599
+ toBack,
600
+ storeToFile,
601
+ enableOpacity,
602
+ enableZoom,
603
+ disableExifHeaderStripping,
604
+ disableAudio,
605
+ 1.0f,
606
+ aspectRatio,
607
+ gridMode
608
+ );
375
609
  config.setTargetZoom(finalTargetZoom);
376
-
610
+
377
611
  bridge.saveCall(call);
378
612
  cameraStartCallbackId = call.getCallbackId();
379
613
  cameraXView.startSession(config);
380
- }
381
- );
614
+ });
382
615
  }
383
616
 
384
617
  @Override
385
- public void onPictureTaken(String result) {
386
- JSObject jsObject = new JSObject();
387
- jsObject.put("value", result);
388
- bridge.getSavedCall(captureCallbackId).resolve(jsObject);
618
+ public void onPictureTaken(String base64, JSONObject exif) {
619
+ PluginCall pluginCall = bridge.getSavedCall(captureCallbackId);
620
+ if (pluginCall == null) {
621
+ Log.e("CameraPreview", "onPictureTaken: captureCallbackId is null");
622
+ return;
623
+ }
624
+ JSObject result = new JSObject();
625
+ result.put("value", base64);
626
+ result.put("exif", exif);
627
+ pluginCall.resolve(result);
628
+ bridge.releaseCall(pluginCall);
389
629
  }
390
630
 
391
631
  @Override
392
632
  public void onPictureTakenError(String message) {
393
- bridge.getSavedCall(captureCallbackId).reject(message);
633
+ PluginCall pluginCall = bridge.getSavedCall(captureCallbackId);
634
+ if (pluginCall == null) {
635
+ Log.e("CameraPreview", "onPictureTakenError: captureCallbackId is null");
636
+ return;
637
+ }
638
+ pluginCall.reject(message);
639
+ bridge.releaseCall(pluginCall);
640
+ }
641
+
642
+ @Override
643
+ public void onCameraStarted(int width, int height, int x, int y) {
644
+ PluginCall call = bridge.getSavedCall(cameraStartCallbackId);
645
+ if (call != null) {
646
+ // Convert pixel values back to logical units
647
+ DisplayMetrics metrics = getBridge()
648
+ .getActivity()
649
+ .getResources()
650
+ .getDisplayMetrics();
651
+ float pixelRatio = metrics.density;
652
+
653
+ JSObject result = new JSObject();
654
+ result.put("width", width / pixelRatio);
655
+ result.put("height", height / pixelRatio);
656
+ result.put("x", x / pixelRatio);
657
+ result.put("y", y / pixelRatio);
658
+ call.resolve(result);
659
+ bridge.releaseCall(call);
660
+ cameraStartCallbackId = null; // Prevent re-use
661
+ }
394
662
  }
395
663
 
396
664
  @Override
397
665
  public void onSampleTaken(String result) {
398
- JSObject jsObject = new JSObject();
399
- jsObject.put("value", result);
400
- bridge.getSavedCall(snapshotCallbackId).resolve(jsObject);
666
+ // Handle sample taken if needed
667
+ Log.i("CameraPreview", "Sample taken: " + result);
401
668
  }
402
669
 
403
670
  @Override
404
671
  public void onSampleTakenError(String message) {
405
- bridge.getSavedCall(snapshotCallbackId).reject(message);
672
+ // Handle sample taken error if needed
673
+ Log.e("CameraPreview", "Sample taken error: " + message);
406
674
  }
407
675
 
408
676
  @Override
409
- public void onCameraStarted() {
410
- PluginCall pluginCall = bridge.getSavedCall(cameraStartCallbackId);
411
- if (pluginCall != null) {
412
- pluginCall.resolve();
413
- bridge.releaseCall(pluginCall);
677
+ public void onCameraStartError(String message) {
678
+ PluginCall call = bridge.getSavedCall(cameraStartCallbackId);
679
+ if (call != null) {
680
+ call.reject(message);
681
+ bridge.releaseCall(call);
682
+ cameraStartCallbackId = null;
414
683
  }
415
684
  }
416
685
 
417
- @Override
418
- public void onCameraStartError(String message) {
419
- PluginCall pluginCall = bridge.getSavedCall(cameraStartCallbackId);
420
- if (pluginCall != null) {
421
- pluginCall.reject(message);
422
- bridge.releaseCall(pluginCall);
686
+ @PluginMethod
687
+ public void setAspectRatio(PluginCall call) {
688
+ if (cameraXView == null || !cameraXView.isRunning()) {
689
+ call.reject("Camera is not running");
690
+ return;
691
+ }
692
+ String aspectRatio = call.getString("aspectRatio", "4:3");
693
+ Float x = call.getFloat("x");
694
+ Float y = call.getFloat("y");
695
+
696
+ getActivity()
697
+ .runOnUiThread(() -> {
698
+ cameraXView.setAspectRatio(aspectRatio, x, y, () -> {
699
+ // Return the actual preview bounds after layout and camera operations are complete
700
+ int[] bounds = cameraXView.getCurrentPreviewBounds();
701
+ JSObject ret = new JSObject();
702
+ ret.put("x", bounds[0]);
703
+ ret.put("y", bounds[1]);
704
+ ret.put("width", bounds[2]);
705
+ ret.put("height", bounds[3]);
706
+ call.resolve(ret);
707
+ });
708
+ });
709
+ }
710
+
711
+ @PluginMethod
712
+ public void getAspectRatio(PluginCall call) {
713
+ if (cameraXView == null || !cameraXView.isRunning()) {
714
+ call.reject("Camera is not running");
715
+ return;
716
+ }
717
+ String aspectRatio = cameraXView.getAspectRatio();
718
+ JSObject ret = new JSObject();
719
+ ret.put("aspectRatio", aspectRatio);
720
+ call.resolve(ret);
721
+ }
722
+
723
+ @PluginMethod
724
+ public void setGridMode(PluginCall call) {
725
+ if (cameraXView == null || !cameraXView.isRunning()) {
726
+ call.reject("Camera is not running");
727
+ return;
728
+ }
729
+ String gridMode = call.getString("gridMode", "none");
730
+ getActivity()
731
+ .runOnUiThread(() -> {
732
+ cameraXView.setGridMode(gridMode);
733
+ call.resolve();
734
+ });
735
+ }
736
+
737
+ @PluginMethod
738
+ public void getGridMode(PluginCall call) {
739
+ if (cameraXView == null || !cameraXView.isRunning()) {
740
+ call.reject("Camera is not running");
741
+ return;
742
+ }
743
+ JSObject ret = new JSObject();
744
+ ret.put("gridMode", cameraXView.getGridMode());
745
+ call.resolve(ret);
746
+ }
747
+
748
+ @PluginMethod
749
+ public void getPreviewSize(PluginCall call) {
750
+ if (cameraXView == null || !cameraXView.isRunning()) {
751
+ call.reject("Camera is not running");
752
+ return;
753
+ }
754
+
755
+ // Convert pixel values back to logical units
756
+ DisplayMetrics metrics = getBridge()
757
+ .getActivity()
758
+ .getResources()
759
+ .getDisplayMetrics();
760
+ float pixelRatio = metrics.density;
761
+
762
+ JSObject ret = new JSObject();
763
+ ret.put("x", cameraXView.getPreviewX() / pixelRatio);
764
+ ret.put("y", cameraXView.getPreviewY() / pixelRatio);
765
+ ret.put("width", cameraXView.getPreviewWidth() / pixelRatio);
766
+ ret.put("height", cameraXView.getPreviewHeight() / pixelRatio);
767
+ call.resolve(ret);
768
+ }
769
+
770
+ @PluginMethod
771
+ public void setPreviewSize(PluginCall call) {
772
+ if (cameraXView == null || !cameraXView.isRunning()) {
773
+ call.reject("Camera is not running");
774
+ return;
423
775
  }
776
+
777
+ // Get values from call - null values will become 0
778
+ Integer xParam = call.getInt("x");
779
+ Integer yParam = call.getInt("y");
780
+ Integer widthParam = call.getInt("width");
781
+ Integer heightParam = call.getInt("height");
782
+
783
+ // Apply pixel ratio conversion to non-null values
784
+ DisplayMetrics metrics = getBridge()
785
+ .getActivity()
786
+ .getResources()
787
+ .getDisplayMetrics();
788
+ float pixelRatio = metrics.density;
789
+
790
+ int x = (xParam != null && xParam > 0) ? (int) (xParam * pixelRatio) : 0;
791
+ int y = (yParam != null && yParam > 0) ? (int) (yParam * pixelRatio) : 0;
792
+ int width = (widthParam != null && widthParam > 0)
793
+ ? (int) (widthParam * pixelRatio)
794
+ : 0;
795
+ int height = (heightParam != null && heightParam > 0)
796
+ ? (int) (heightParam * pixelRatio)
797
+ : 0;
798
+
799
+ cameraXView.setPreviewSize(x, y, width, height, () -> {
800
+ // Return the actual preview bounds after layout operations are complete
801
+ int[] bounds = cameraXView.getCurrentPreviewBounds();
802
+ JSObject ret = new JSObject();
803
+ ret.put("x", bounds[0]);
804
+ ret.put("y", bounds[1]);
805
+ ret.put("width", bounds[2]);
806
+ ret.put("height", bounds[3]);
807
+ call.resolve(ret);
808
+ });
424
809
  }
425
810
  }