@capgo/camera-preview 7.3.9 → 7.4.0-beta.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.
Files changed (55) hide show
  1. package/CapgoCameraPreview.podspec +16 -13
  2. package/README.md +298 -68
  3. package/android/.gradle/8.14.2/checksums/checksums.lock +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/fileChanges/last-build.bin +0 -0
  8. package/android/.gradle/8.14.2/fileHashes/fileHashes.bin +0 -0
  9. package/android/.gradle/8.14.2/fileHashes/fileHashes.lock +0 -0
  10. package/android/.gradle/8.14.2/fileHashes/resourceHashesCache.bin +0 -0
  11. package/android/.gradle/8.14.2/gc.properties +0 -0
  12. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  13. package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
  14. package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
  15. package/android/.gradle/file-system.probe +0 -0
  16. package/android/.gradle/vcs-1/gc.properties +0 -0
  17. package/android/build.gradle +9 -0
  18. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java +239 -545
  19. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraXView.java +848 -0
  20. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraDevice.java +54 -0
  21. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraLens.java +70 -0
  22. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraSessionConfiguration.java +65 -0
  23. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/LensInfo.java +34 -0
  24. package/android/src/main/java/com/ahm/capacitor/camera/preview/model/ZoomFactors.java +34 -0
  25. package/dist/docs.json +702 -151
  26. package/dist/esm/definitions.d.ts +326 -80
  27. package/dist/esm/definitions.js +10 -1
  28. package/dist/esm/definitions.js.map +1 -1
  29. package/dist/esm/web.d.ts +27 -1
  30. package/dist/esm/web.js +244 -4
  31. package/dist/esm/web.js.map +1 -1
  32. package/dist/plugin.cjs.js +254 -4
  33. package/dist/plugin.cjs.js.map +1 -1
  34. package/dist/plugin.js +254 -4
  35. package/dist/plugin.js.map +1 -1
  36. package/ios/{Plugin → Sources/CapgoCameraPreview}/CameraController.swift +359 -34
  37. package/ios/{Plugin → Sources/CapgoCameraPreview}/Plugin.swift +307 -16
  38. package/ios/Tests/CameraPreviewPluginTests/CameraPreviewPluginTests.swift +15 -0
  39. package/package.json +1 -1
  40. package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraActivity.java +0 -1279
  41. package/android/src/main/java/com/ahm/capacitor/camera/preview/CustomSurfaceView.java +0 -29
  42. package/android/src/main/java/com/ahm/capacitor/camera/preview/CustomTextureView.java +0 -39
  43. package/android/src/main/java/com/ahm/capacitor/camera/preview/Preview.java +0 -461
  44. package/android/src/main/java/com/ahm/capacitor/camera/preview/TapGestureDetector.java +0 -24
  45. package/ios/Plugin/Info.plist +0 -24
  46. package/ios/Plugin/Plugin.h +0 -10
  47. package/ios/Plugin/Plugin.m +0 -18
  48. package/ios/Plugin.xcodeproj/project.pbxproj +0 -593
  49. package/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  50. package/ios/Plugin.xcworkspace/contents.xcworkspacedata +0 -10
  51. package/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  52. package/ios/PluginTests/Info.plist +0 -22
  53. package/ios/PluginTests/PluginTests.swift +0 -83
  54. package/ios/Podfile +0 -13
  55. package/ios/Podfile.lock +0 -23
@@ -3,26 +3,11 @@ 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.app.FragmentManager;
7
- import android.app.FragmentTransaction;
8
- import android.content.Context;
9
6
  import android.content.pm.ActivityInfo;
10
- import android.graphics.Color;
11
- import android.graphics.ImageFormat;
12
7
  import android.graphics.Point;
13
- import android.hardware.Camera;
14
- import android.hardware.camera2.CameraAccessException;
15
- import android.hardware.camera2.CameraCharacteristics;
16
- import android.hardware.camera2.CameraManager;
17
- import android.hardware.camera2.params.StreamConfigurationMap;
18
8
  import android.util.DisplayMetrics;
19
- import android.util.Size;
20
9
  import android.util.TypedValue;
21
10
  import android.view.Display;
22
- import android.view.MotionEvent;
23
- import android.view.View;
24
- import android.view.ViewGroup;
25
- import android.widget.FrameLayout;
26
11
  import androidx.annotation.NonNull;
27
12
  import com.getcapacitor.JSArray;
28
13
  import com.getcapacitor.JSObject;
@@ -34,11 +19,14 @@ import com.getcapacitor.PluginMethod;
34
19
  import com.getcapacitor.annotation.CapacitorPlugin;
35
20
  import com.getcapacitor.annotation.Permission;
36
21
  import com.getcapacitor.annotation.PermissionCallback;
37
- import java.io.File;
38
- import java.util.ArrayList;
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;
39
25
  import java.util.List;
40
26
  import java.util.Objects;
41
- import org.json.JSONArray;
27
+ import android.util.Size;
28
+ import android.util.Log;
29
+ import com.ahm.capacitor.camera.preview.model.LensInfo;
42
30
 
43
31
  @CapacitorPlugin(
44
32
  name = "CameraPreview",
@@ -55,29 +43,20 @@ import org.json.JSONArray;
55
43
  )
56
44
  public class CameraPreview
57
45
  extends Plugin
58
- implements CameraActivity.CameraPreviewListener {
46
+ implements CameraXView.CameraXViewListener {
59
47
 
60
48
  static final String CAMERA_WITH_AUDIO_PERMISSION_ALIAS = "cameraWithAudio";
61
49
  static final String CAMERA_ONLY_PERMISSION_ALIAS = "cameraOnly";
62
50
 
63
- private static String VIDEO_FILE_PATH = "";
64
- private static final String VIDEO_FILE_EXTENSION = ".mp4";
65
-
66
51
  private String captureCallbackId = "";
67
52
  private String snapshotCallbackId = "";
68
- private String recordCallbackId = "";
69
53
  private String cameraStartCallbackId = "";
70
-
71
- // keep track of previously specified orientation to support locking orientation:
72
- private int previousOrientationRequest =
73
- ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
74
-
75
- private CameraActivity fragment;
76
- private final int containerViewId = 20;
54
+ private int previousOrientationRequest = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
55
+ private CameraXView cameraXView;
77
56
 
78
57
  @PluginMethod
79
58
  public void start(PluginCall call) {
80
- boolean disableAudio = call.getBoolean("disableAudio", false);
59
+ boolean disableAudio = Boolean.TRUE.equals(call.getBoolean("disableAudio", true));
81
60
  String permissionAlias = disableAudio
82
61
  ? CAMERA_ONLY_PERMISSION_ALIAS
83
62
  : CAMERA_WITH_AUDIO_PERMISSION_ALIAS;
@@ -95,118 +74,36 @@ public class CameraPreview
95
74
 
96
75
  @PluginMethod
97
76
  public void flip(PluginCall call) {
98
- try {
99
- fragment.switchCamera();
100
- call.resolve();
101
- } catch (Exception e) {
102
- Logger.debug(getLogTag(), "Camera flip exception: " + e);
103
- call.reject("failed to flip camera");
104
- }
105
- }
106
-
107
- @PluginMethod
108
- public void setOpacity(PluginCall call) {
109
- if (!this.hasCamera(call)) {
77
+ if (cameraXView == null || !cameraXView.isRunning()) {
110
78
  call.reject("Camera is not running");
111
79
  return;
112
80
  }
113
-
114
- bridge.saveCall(call);
115
- Float opacity = Objects.requireNonNull(call.getFloat("opacity", 1F));
116
- fragment.setOpacity(opacity);
81
+ cameraXView.flipCamera();
82
+ call.resolve();
117
83
  }
118
84
 
119
85
  @PluginMethod
120
86
  public void capture(PluginCall call) {
121
- if (!this.hasCamera(call)) {
87
+ if (cameraXView == null || !cameraXView.isRunning()) {
122
88
  call.reject("Camera is not running");
123
89
  return;
124
90
  }
125
91
  bridge.saveCall(call);
126
92
  captureCallbackId = call.getCallbackId();
127
-
128
93
  Integer quality = Objects.requireNonNull(call.getInt("quality", 85));
129
- // Image Dimensions - Optional
130
- Integer width = Objects.requireNonNull(call.getInt("width", 0));
131
- Integer height = Objects.requireNonNull(call.getInt("height", 0));
132
- fragment.takePicture(width, height, quality);
94
+ cameraXView.capturePhoto(quality);
133
95
  }
134
96
 
135
97
  @PluginMethod
136
98
  public void captureSample(PluginCall call) {
137
- if (!this.hasCamera(call)) {
99
+ if (cameraXView == null || !cameraXView.isRunning()) {
138
100
  call.reject("Camera is not running");
139
101
  return;
140
102
  }
141
103
  bridge.saveCall(call);
142
104
  snapshotCallbackId = call.getCallbackId();
143
-
144
105
  Integer quality = Objects.requireNonNull(call.getInt("quality", 85));
145
- fragment.takeSnapshot(quality);
146
- }
147
-
148
- @PluginMethod
149
- public void getSupportedPictureSizes(final PluginCall call) {
150
- CameraManager cameraManager = (CameraManager) this.getContext()
151
- .getSystemService(Context.CAMERA_SERVICE);
152
-
153
- JSArray ret = new JSArray();
154
- try {
155
- String[] cameraIdList = cameraManager.getCameraIdList();
156
- for (String cameraId : cameraIdList) {
157
- CameraCharacteristics characteristics =
158
- cameraManager.getCameraCharacteristics(cameraId);
159
-
160
- // Determine the facing of the camera
161
- Integer lensFacing = characteristics.get(
162
- CameraCharacteristics.LENS_FACING
163
- );
164
- String facing = "Unknown";
165
- if (lensFacing != null) {
166
- switch (lensFacing) {
167
- case CameraCharacteristics.LENS_FACING_FRONT:
168
- facing = "Front";
169
- break;
170
- case CameraCharacteristics.LENS_FACING_BACK:
171
- facing = "Back";
172
- break;
173
- case CameraCharacteristics.LENS_FACING_EXTERNAL:
174
- facing = "External";
175
- break;
176
- }
177
- }
178
-
179
- StreamConfigurationMap map = characteristics.get(
180
- CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP
181
- );
182
- if (map == null) {
183
- continue;
184
- }
185
-
186
- Size[] jpegSizes = map.getOutputSizes(ImageFormat.JPEG);
187
- JSObject camera = new JSObject();
188
- camera.put("facing", facing);
189
- JSArray supportedPictureSizes = new JSArray();
190
- if (jpegSizes != null) {
191
- for (Size size : jpegSizes) {
192
- JSObject sizeJson = new JSObject();
193
- sizeJson.put("width", size.getWidth());
194
- sizeJson.put("height", size.getHeight());
195
- supportedPictureSizes.put(sizeJson);
196
- }
197
- camera.put("supportedPictureSizes", supportedPictureSizes);
198
- ret.put(camera);
199
- }
200
- }
201
- JSObject finalRet = new JSObject();
202
- finalRet.put("supportedPictureSizes", ret);
203
- call.resolve(finalRet);
204
- } catch (CameraAccessException ex) {
205
- Logger.error(getLogTag(), "Cannot call getSupportedPictureSizes", ex);
206
- call.reject(
207
- String.format("Cannot call getSupportedPictureSizes. Error: %s", ex)
208
- );
209
- }
106
+ cameraXView.captureSample(quality);
210
107
  }
211
108
 
212
109
  @PluginMethod
@@ -214,190 +111,204 @@ public class CameraPreview
214
111
  bridge
215
112
  .getActivity()
216
113
  .runOnUiThread(
217
- new Runnable() {
218
- @Override
219
- public void run() {
220
- FrameLayout containerView = getBridge()
221
- .getActivity()
222
- .findViewById(containerViewId);
223
-
224
- // allow orientation changes after closing camera:
225
- getBridge()
226
- .getActivity()
227
- .setRequestedOrientation(previousOrientationRequest);
228
-
229
- if (containerView != null) {
230
- ((ViewGroup) getBridge().getWebView().getParent()).removeView(
231
- containerView
232
- );
233
- getBridge().getWebView().setBackgroundColor(Color.WHITE);
234
- FragmentManager fragmentManager = getActivity()
235
- .getFragmentManager();
236
- FragmentTransaction fragmentTransaction =
237
- fragmentManager.beginTransaction();
238
- fragmentTransaction.remove(fragment);
239
- fragmentTransaction.commit();
240
- fragment = null;
241
-
242
- call.resolve();
243
- } else {
244
- call.reject("camera already stopped");
245
- }
114
+ () -> {
115
+ getBridge()
116
+ .getActivity()
117
+ .setRequestedOrientation(previousOrientationRequest);
118
+
119
+ if (cameraXView != null && cameraXView.isRunning()) {
120
+ cameraXView.stopSession();
121
+ cameraXView = null;
246
122
  }
123
+ call.resolve();
247
124
  }
248
125
  );
249
126
  }
250
127
 
251
128
  @PluginMethod
252
129
  public void getSupportedFlashModes(PluginCall call) {
253
- if (!this.hasCamera(call)) {
254
- call.reject("Camera is not running");
255
- return;
256
- }
257
-
258
- Camera camera = fragment.getCamera();
259
- Camera.Parameters params = camera.getParameters();
260
- List<String> supportedFlashModes;
261
- supportedFlashModes = params.getSupportedFlashModes();
262
- JSONArray jsonFlashModes = new JSONArray();
263
-
264
- if (supportedFlashModes != null) {
265
- for (int i = 0; i < supportedFlashModes.size(); i++) {
266
- jsonFlashModes.put(supportedFlashModes.get(i));
267
- }
130
+ List<String> supportedFlashModes = cameraXView.getSupportedFlashModes();
131
+ JSArray jsonFlashModes = new JSArray();
132
+ for (String mode : supportedFlashModes) {
133
+ jsonFlashModes.put(mode);
268
134
  }
269
-
270
135
  JSObject jsObject = new JSObject();
271
136
  jsObject.put("result", jsonFlashModes);
272
137
  call.resolve(jsObject);
273
138
  }
274
139
 
275
140
  @PluginMethod
276
- public void getHorizontalFov(PluginCall call) {
277
- if (!this.hasCamera(call)) {
278
- call.reject("Camera is not running");
141
+ public void setFlashMode(PluginCall call) {
142
+ String flashMode = call.getString("flashMode");
143
+ if (flashMode == null || flashMode.isEmpty()) {
144
+ call.reject("flashMode required parameter is missing");
279
145
  return;
280
146
  }
147
+ cameraXView.setFlashMode(flashMode);
148
+ call.resolve();
149
+ }
281
150
 
282
- Camera camera = fragment.getCamera();
283
- Camera.Parameters params = camera.getParameters();
284
-
285
- float horizontalViewAngle = params.getHorizontalViewAngle();
151
+ @PluginMethod
152
+ public void getAvailableDevices(PluginCall call) {
153
+ List<CameraDevice> devices = CameraXView.getAvailableDevicesStatic(getContext());
154
+ JSArray devicesArray = new JSArray();
155
+ for (CameraDevice device : devices) {
156
+ JSObject deviceJson = new JSObject();
157
+ deviceJson.put("deviceId", device.getDeviceId());
158
+ deviceJson.put("label", device.getLabel());
159
+ deviceJson.put("position", device.getPosition());
160
+ JSArray lensesArray = new JSArray();
161
+ for (com.ahm.capacitor.camera.preview.model.LensInfo lens : device.getLenses()) {
162
+ JSObject lensJson = new JSObject();
163
+ lensJson.put("focalLength", lens.getFocalLength());
164
+ lensJson.put("deviceType", lens.getDeviceType());
165
+ lensJson.put("baseZoomRatio", lens.getBaseZoomRatio());
166
+ lensJson.put("digitalZoom", lens.getDigitalZoom());
167
+ lensesArray.put(lensJson);
168
+ }
169
+ deviceJson.put("lenses", lensesArray);
170
+ deviceJson.put("minZoom", device.getMinZoom());
171
+ deviceJson.put("maxZoom", device.getMaxZoom());
172
+ devicesArray.put(deviceJson);
173
+ }
174
+ JSObject result = new JSObject();
175
+ result.put("devices", devicesArray);
176
+ call.resolve(result);
177
+ }
286
178
 
287
- JSObject jsObject = new JSObject();
288
- jsObject.put("result", horizontalViewAngle);
289
- call.resolve(jsObject);
179
+ @PluginMethod
180
+ public void getZoom(PluginCall call) {
181
+ ZoomFactors zoomFactors = cameraXView.getZoomFactors();
182
+ JSObject result = new JSObject();
183
+ result.put("min", zoomFactors.getMin());
184
+ result.put("max", zoomFactors.getMax());
185
+ result.put("current", zoomFactors.getCurrent());
186
+ call.resolve(result);
290
187
  }
291
188
 
292
189
  @PluginMethod
293
- public void setFlashMode(PluginCall call) {
294
- if (!this.hasCamera(call)) {
190
+ public void setZoom(PluginCall call) {
191
+ if (cameraXView == null || !cameraXView.isRunning()) {
295
192
  call.reject("Camera is not running");
296
193
  return;
297
194
  }
298
-
299
- String flashMode = call.getString("flashMode");
300
- if (flashMode == null || flashMode.isEmpty()) {
301
- call.reject("flashMode required parameter is missing");
195
+ Float level = call.getFloat("level");
196
+ if (level == null) {
197
+ call.reject("level parameter is required");
302
198
  return;
303
199
  }
200
+ try {
201
+ cameraXView.setZoom(level);
202
+ call.resolve();
203
+ } catch (Exception e) {
204
+ call.reject("Failed to set zoom: " + e.getMessage());
205
+ }
206
+ }
304
207
 
305
- Camera camera = fragment.getCamera();
306
- Camera.Parameters params = camera.getParameters();
307
-
308
- List<String> supportedFlashModes;
309
- supportedFlashModes = camera.getParameters().getSupportedFlashModes();
310
- if (
311
- supportedFlashModes != null && supportedFlashModes.contains(flashMode)
312
- ) {
313
- params.setFlashMode(flashMode);
314
- } else {
315
- call.reject("Flash mode not recognised: " + flashMode);
208
+ @PluginMethod
209
+ public void setDeviceId(PluginCall call) {
210
+ String deviceId = call.getString("deviceId");
211
+ if (deviceId == null || deviceId.isEmpty()) {
212
+ call.reject("deviceId parameter is required");
316
213
  return;
317
214
  }
318
-
319
- fragment.setCameraParameters(params);
320
-
215
+ if (cameraXView == null || !cameraXView.isRunning()) {
216
+ call.reject("Camera is not running");
217
+ return;
218
+ }
219
+ cameraXView.switchToDevice(deviceId);
321
220
  call.resolve();
322
221
  }
323
222
 
324
223
  @PluginMethod
325
- public void startRecordVideo(final PluginCall call) {
326
- if (!this.hasCamera(call)) {
224
+ public void getSupportedPictureSizes(final PluginCall call) {
225
+ JSArray supportedPictureSizesResult = new JSArray();
226
+ List<Size> rearSizes = CameraXView.getSupportedPictureSizes("rear");
227
+ JSObject rear = new JSObject();
228
+ rear.put("facing", "rear");
229
+ JSArray rearSizesJs = new JSArray();
230
+ for(Size size : rearSizes) {
231
+ JSObject sizeJs = new JSObject();
232
+ sizeJs.put("width", size.getWidth());
233
+ sizeJs.put("height", size.getHeight());
234
+ rearSizesJs.put(sizeJs);
235
+ }
236
+ rear.put("supportedPictureSizes", rearSizesJs);
237
+ supportedPictureSizesResult.put(rear);
238
+
239
+ List<Size> frontSizes = CameraXView.getSupportedPictureSizes("front");
240
+ JSObject front = new JSObject();
241
+ front.put("facing", "front");
242
+ JSArray frontSizesJs = new JSArray();
243
+ for(Size size : frontSizes) {
244
+ JSObject sizeJs = new JSObject();
245
+ sizeJs.put("width", size.getWidth());
246
+ sizeJs.put("height", size.getHeight());
247
+ frontSizesJs.put(sizeJs);
248
+ }
249
+ front.put("supportedPictureSizes", frontSizesJs);
250
+ supportedPictureSizesResult.put(front);
251
+
252
+ JSObject ret = new JSObject();
253
+ ret.put("supportedPictureSizes", supportedPictureSizesResult);
254
+ call.resolve(ret);
255
+ }
256
+
257
+ @PluginMethod
258
+ public void setOpacity(PluginCall call) {
259
+ if (cameraXView == null || !cameraXView.isRunning()) {
327
260
  call.reject("Camera is not running");
328
261
  return;
329
262
  }
330
- final String filename = "videoTmp";
331
- VIDEO_FILE_PATH = getActivity().getCacheDir().toString() + "/";
332
-
333
- final String position = Objects.requireNonNull(
334
- call.getString("position", "front")
335
- );
336
- final Integer width = Objects.requireNonNull(call.getInt("width", 0));
337
- final Integer height = Objects.requireNonNull(call.getInt("height", 0));
338
- final Boolean withFlash = Objects.requireNonNull(
339
- call.getBoolean("withFlash", false)
340
- );
341
- final Integer maxDuration = Objects.requireNonNull(
342
- call.getInt("maxDuration", 0)
343
- );
344
- // final Integer quality = Objects.requireNonNull(call.getInt("quality", 0));
345
- bridge.saveCall(call);
346
- recordCallbackId = call.getCallbackId();
347
-
348
- bridge
349
- .getActivity()
350
- .runOnUiThread(
351
- new Runnable() {
352
- @Override
353
- public void run() {
354
- fragment.startRecord(
355
- getFilePath(),
356
- position,
357
- width,
358
- height,
359
- 70,
360
- withFlash,
361
- maxDuration
362
- );
363
- }
364
- }
365
- );
366
-
263
+ Float opacity = call.getFloat("opacity", 1.0f);
264
+ cameraXView.setOpacity(opacity);
367
265
  call.resolve();
368
266
  }
369
267
 
370
268
  @PluginMethod
371
- public void stopRecordVideo(PluginCall call) {
372
- if (!this.hasCamera(call)) {
269
+ public void getHorizontalFov(PluginCall call) {
270
+ // CameraX does not provide a simple way to get FoV.
271
+ // This would require Camera2 interop to access camera characteristics.
272
+ // Returning a default/estimated value.
273
+ JSObject ret = new JSObject();
274
+ ret.put("result", 60.0); // A common default FoV
275
+ call.resolve(ret);
276
+ }
277
+
278
+ @PluginMethod
279
+ public void getDeviceId(PluginCall call) {
280
+ if (cameraXView == null || !cameraXView.isRunning()) {
373
281
  call.reject("Camera is not running");
374
282
  return;
375
283
  }
284
+ JSObject ret = new JSObject();
285
+ ret.put("deviceId", cameraXView.getCurrentDeviceId());
286
+ call.resolve(ret);
287
+ }
376
288
 
377
- System.out.println("stopRecordVideo - Callbackid=" + call.getCallbackId());
378
-
379
- bridge.saveCall(call);
380
- recordCallbackId = call.getCallbackId();
381
-
382
- // bridge.getActivity().runOnUiThread(new Runnable() {
383
- // @Override
384
- // public void run() {
385
- // fragment.stopRecord();
386
- // }
387
- // });
289
+ @PluginMethod
290
+ public void getFlashMode(PluginCall call) {
291
+ if (cameraXView == null || !cameraXView.isRunning()) {
292
+ call.reject("Camera is not running");
293
+ return;
294
+ }
295
+ JSObject ret = new JSObject();
296
+ ret.put("flashMode", cameraXView.getFlashMode());
297
+ call.resolve(ret);
298
+ }
388
299
 
389
- fragment.stopRecord();
390
- // call.resolve();
300
+ @PluginMethod
301
+ public void isRunning(PluginCall call) {
302
+ boolean running = cameraXView != null && cameraXView.isRunning();
303
+ JSObject jsObject = new JSObject();
304
+ jsObject.put("isRunning", running);
305
+ call.resolve(jsObject);
391
306
  }
392
307
 
393
308
  @PermissionCallback
394
309
  private void handleCameraPermissionResult(PluginCall call) {
395
- boolean disableAudio = call.getBoolean("disableAudio", false);
396
- String permissionAlias = disableAudio
397
- ? CAMERA_ONLY_PERMISSION_ALIAS
398
- : CAMERA_WITH_AUDIO_PERMISSION_ALIAS;
399
-
400
- if (PermissionState.GRANTED.equals(getPermissionState(permissionAlias))) {
310
+ if (PermissionState.GRANTED.equals(getPermissionState(CAMERA_ONLY_PERMISSION_ALIAS)) ||
311
+ PermissionState.GRANTED.equals(getPermissionState(CAMERA_WITH_AUDIO_PERMISSION_ALIAS))) {
401
312
  startCamera(call);
402
313
  } else {
403
314
  call.reject("Permission failed");
@@ -405,214 +316,75 @@ public class CameraPreview
405
316
  }
406
317
 
407
318
  private void startCamera(final PluginCall call) {
408
- String position = call.getString("position");
409
-
410
- if (position == null || position.isEmpty() || "rear".equals(position)) {
411
- position = "back";
412
- } else {
413
- position = "front";
414
- }
415
-
416
- @NonNull
417
- final Integer x = Objects.requireNonNull(call.getInt("x", 0));
418
- @NonNull
419
- final Integer y = Objects.requireNonNull(call.getInt("y", 0));
420
- @NonNull
421
- final Integer width = Objects.requireNonNull(call.getInt("width", 0));
422
- @NonNull
423
- final Integer height = Objects.requireNonNull(call.getInt("height", 0));
424
- @NonNull
425
- final Integer paddingBottom = Objects.requireNonNull(
426
- call.getInt("paddingBottom", 0)
427
- );
428
- final Boolean toBack = Objects.requireNonNull(
429
- call.getBoolean("toBack", false)
430
- );
431
- final Boolean storeToFile = Objects.requireNonNull(
432
- call.getBoolean("storeToFile", false)
433
- );
434
- final Boolean enableOpacity = Objects.requireNonNull(
435
- call.getBoolean("enableOpacity", false)
436
- );
437
- final Boolean enableZoom = Objects.requireNonNull(
438
- call.getBoolean("enableZoom", false)
439
- );
440
- final Boolean disableExifHeaderStripping = Objects.requireNonNull(
441
- call.getBoolean("disableExifHeaderStripping", false)
442
- );
443
- final Boolean lockOrientation = Objects.requireNonNull(
444
- call.getBoolean("lockAndroidOrientation", false)
445
- );
446
- previousOrientationRequest = getBridge()
447
- .getActivity()
448
- .getRequestedOrientation();
449
-
450
- fragment = new CameraActivity();
451
- fragment.setEventListener(this);
452
- fragment.defaultCamera = position;
453
- fragment.tapToTakePicture = false;
454
- fragment.dragEnabled = false;
455
- fragment.tapToFocus = true;
456
- fragment.disableExifHeaderStripping = disableExifHeaderStripping;
457
- fragment.storeToFile = storeToFile;
458
- fragment.toBack = toBack;
459
- fragment.enableOpacity = enableOpacity;
460
- fragment.enableZoom = enableZoom;
461
- fragment.disableAudio = call.getBoolean("disableAudio", false);
462
-
463
- bridge
464
- .getActivity()
465
- .runOnUiThread(
466
- new Runnable() {
467
- @Override
468
- public void run() {
469
- DisplayMetrics metrics = getBridge()
470
- .getActivity()
471
- .getResources()
472
- .getDisplayMetrics();
473
- // lock orientation if specified in options:
474
- if (lockOrientation) {
475
- getBridge()
476
- .getActivity()
477
- .setRequestedOrientation(
478
- ActivityInfo.SCREEN_ORIENTATION_LOCKED
479
- );
480
- }
481
-
482
- // offset
483
- int computedX = (int) TypedValue.applyDimension(
484
- TypedValue.COMPLEX_UNIT_DIP,
485
- x,
486
- metrics
487
- );
488
- int computedY = (int) TypedValue.applyDimension(
489
- TypedValue.COMPLEX_UNIT_DIP,
490
- y,
491
- metrics
492
- );
493
-
494
- // size
495
- int computedWidth;
496
- int computedHeight;
497
- int computedPaddingBottom;
498
-
499
- if (paddingBottom != 0) {
500
- computedPaddingBottom = (int) TypedValue.applyDimension(
501
- TypedValue.COMPLEX_UNIT_DIP,
502
- paddingBottom,
503
- metrics
504
- );
505
- } else {
506
- computedPaddingBottom = 0;
507
- }
508
-
509
- if (width != 0) {
510
- computedWidth = (int) TypedValue.applyDimension(
511
- TypedValue.COMPLEX_UNIT_DIP,
512
- width,
513
- metrics
514
- );
515
- } else {
516
- Display defaultDisplay = getBridge()
517
- .getActivity()
518
- .getWindowManager()
519
- .getDefaultDisplay();
520
- final Point size = new Point();
521
- defaultDisplay.getSize(size);
522
-
523
- computedWidth = (int) TypedValue.applyDimension(
524
- TypedValue.COMPLEX_UNIT_PX,
525
- size.x,
526
- metrics
527
- );
319
+ String positionParam = call.getString("position");
320
+ String originalDeviceId = call.getString("deviceId");
321
+ String deviceId = originalDeviceId; // Use a mutable variable
322
+
323
+ final String position = (positionParam == null || positionParam.isEmpty() || "rear".equals(positionParam) || "back".equals(positionParam)) ? "back" : "front";
324
+ final int x = call.getInt("x", 0);
325
+ final int y = call.getInt("y", 0);
326
+ final int width = call.getInt("width", 0);
327
+ final int height = call.getInt("height", 0);
328
+ 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
+
337
+ float targetZoom = 1.0f;
338
+ // Check if the selected device is a physical ultra-wide
339
+ 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
+ }
528
352
  }
353
+ if (deviceId == null) break; // Exit outer loop once we've made our decision
354
+ }
355
+ }
529
356
 
530
- if (height != 0) {
531
- computedHeight =
532
- (int) TypedValue.applyDimension(
533
- TypedValue.COMPLEX_UNIT_DIP,
534
- height,
535
- metrics
536
- ) -
537
- computedPaddingBottom;
538
- } else {
539
- Display defaultDisplay = getBridge()
540
- .getActivity()
541
- .getWindowManager()
542
- .getDefaultDisplay();
543
- final Point size = new Point();
544
- defaultDisplay.getSize(size);
545
-
546
- computedHeight =
547
- (int) TypedValue.applyDimension(
548
- TypedValue.COMPLEX_UNIT_PX,
549
- size.y,
550
- metrics
551
- ) -
552
- computedPaddingBottom;
553
- }
357
+ previousOrientationRequest = getBridge().getActivity().getRequestedOrientation();
358
+ cameraXView = new CameraXView(getContext(), getBridge().getWebView());
359
+ cameraXView.setListener(this);
554
360
 
555
- fragment.setRect(
556
- computedX,
557
- computedY,
558
- computedWidth,
559
- computedHeight
560
- );
561
-
562
- FrameLayout containerView = getBridge()
563
- .getActivity()
564
- .findViewById(containerViewId);
565
- if (containerView == null) {
566
- containerView = new FrameLayout(
567
- getActivity().getApplicationContext()
568
- );
569
- containerView.setId(containerViewId);
570
-
571
- getBridge().getWebView().setBackgroundColor(Color.TRANSPARENT);
572
- ((ViewGroup) getBridge().getWebView().getParent()).addView(
573
- containerView
574
- );
575
- if (toBack) {
576
- getBridge()
577
- .getWebView()
578
- .getParent()
579
- .bringChildToFront(getBridge().getWebView());
580
- setupBroadcast();
581
- }
582
-
583
- FragmentManager fragmentManager = getBridge()
584
- .getActivity()
585
- .getFragmentManager();
586
- FragmentTransaction fragmentTransaction =
587
- fragmentManager.beginTransaction();
588
- fragmentTransaction.add(containerView.getId(), fragment);
589
- fragmentTransaction.commit();
590
-
591
- // NOTE: we don't return invoke call.resolve here because it must be invoked in onCameraStarted
592
- // otherwise the plugin start method might resolve/return before the camera is actually set in CameraActivity
593
- // onResume method (see this line mCamera = Camera.open(defaultCameraId);) and the next subsequent plugin
594
- // method invocations (for example, getSupportedFlashModes) might fails with "Camera is not running" error
595
- // because camera is not available yet and hasCamera method will return false
596
- // Please also see https://developer.android.com/reference/android/hardware/Camera.html#open%28int%29
597
- bridge.saveCall(call);
598
- cameraStartCallbackId = call.getCallbackId();
599
- } else {
600
- call.reject("camera already started");
601
- }
602
- }
361
+ String finalDeviceId = deviceId;
362
+ float finalTargetZoom = targetZoom;
363
+ getBridge().getActivity().runOnUiThread(() -> {
364
+ DisplayMetrics metrics = getBridge().getActivity().getResources().getDisplayMetrics();
365
+ if (lockOrientation) {
366
+ getBridge().getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
603
367
  }
604
- );
605
- }
606
-
607
- @Override
608
- protected void handleOnResume() {
609
- super.handleOnResume();
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
+
374
+ CameraSessionConfiguration config = new CameraSessionConfiguration(finalDeviceId, position, computedX, computedY, computedWidth, computedHeight, paddingBottom, toBack, storeToFile, enableOpacity, enableZoom, disableExifHeaderStripping, disableAudio, 1.0f);
375
+ config.setTargetZoom(finalTargetZoom);
376
+
377
+ bridge.saveCall(call);
378
+ cameraStartCallbackId = call.getCallbackId();
379
+ cameraXView.startSession(config);
380
+ }
381
+ );
610
382
  }
611
383
 
612
384
  @Override
613
- public void onPictureTaken(String originalPicture) {
385
+ public void onPictureTaken(String result) {
614
386
  JSObject jsObject = new JSObject();
615
- jsObject.put("value", originalPicture);
387
+ jsObject.put("value", result);
616
388
  bridge.getSavedCall(captureCallbackId).resolve(jsObject);
617
389
  }
618
390
 
@@ -622,110 +394,32 @@ public class CameraPreview
622
394
  }
623
395
 
624
396
  @Override
625
- public void onSnapshotTaken(String originalPicture) {
397
+ public void onSampleTaken(String result) {
626
398
  JSObject jsObject = new JSObject();
627
- jsObject.put("value", originalPicture);
399
+ jsObject.put("value", result);
628
400
  bridge.getSavedCall(snapshotCallbackId).resolve(jsObject);
629
401
  }
630
402
 
631
403
  @Override
632
- public void onSnapshotTakenError(String message) {
404
+ public void onSampleTakenError(String message) {
633
405
  bridge.getSavedCall(snapshotCallbackId).reject(message);
634
406
  }
635
407
 
636
- @Override
637
- public void onFocusSet(int pointX, int pointY) {}
638
-
639
- @Override
640
- public void onFocusSetError(String message) {}
641
-
642
- @Override
643
- public void onBackButton() {}
644
-
645
408
  @Override
646
409
  public void onCameraStarted() {
647
410
  PluginCall pluginCall = bridge.getSavedCall(cameraStartCallbackId);
648
- pluginCall.resolve();
649
- bridge.releaseCall(pluginCall);
650
- }
651
-
652
- @Override
653
- public void onStartRecordVideo() {}
654
-
655
- @Override
656
- public void onStartRecordVideoError(String message) {
657
- bridge.getSavedCall(recordCallbackId).reject(message);
658
- }
659
-
660
- @Override
661
- public void onStopRecordVideo(String file) {
662
- PluginCall pluginCall = bridge.getSavedCall(recordCallbackId);
663
- JSObject jsObject = new JSObject();
664
- jsObject.put("videoFilePath", file);
665
- pluginCall.resolve(jsObject);
666
- }
667
-
668
- @Override
669
- public void onStopRecordVideoError(String error) {
670
- bridge.getSavedCall(recordCallbackId).reject(error);
671
- }
672
-
673
- private boolean hasView(PluginCall call) {
674
- if (fragment == null) {
675
- return false;
676
- }
677
-
678
- return true;
679
- }
680
-
681
- private boolean hasCamera(PluginCall call) {
682
- if (!this.hasView(call)) {
683
- return false;
411
+ if (pluginCall != null) {
412
+ pluginCall.resolve();
413
+ bridge.releaseCall(pluginCall);
684
414
  }
685
-
686
- if (fragment.getCamera() == null) {
687
- return false;
688
- }
689
-
690
- return true;
691
415
  }
692
416
 
693
- private String getFilePath() {
694
- String fileName = "videoTmp";
695
-
696
- int i = 1;
697
-
698
- while (
699
- new File(VIDEO_FILE_PATH + fileName + VIDEO_FILE_EXTENSION).exists()
700
- ) {
701
- // Add number suffix if file exists
702
- fileName = "videoTmp" + '_' + i;
703
- i++;
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);
704
423
  }
705
-
706
- return VIDEO_FILE_PATH + fileName + VIDEO_FILE_EXTENSION;
707
- }
708
-
709
- private void setupBroadcast() {
710
- /** When touch event is triggered, relay it to camera view if needed so it can support pinch zoom */
711
-
712
- getBridge().getWebView().setClickable(true);
713
- getBridge()
714
- .getWebView()
715
- .setOnTouchListener(
716
- new View.OnTouchListener() {
717
- @Override
718
- public boolean onTouch(View v, MotionEvent event) {
719
- if (
720
- (null != fragment) &&
721
- (fragment.toBack == true) &&
722
- null != fragment.frameContainerLayout
723
- ) {
724
- fragment.frameContainerLayout.dispatchTouchEvent(event);
725
- }
726
- return false;
727
- }
728
- }
729
- );
730
424
  }
731
425
  }