@capgo/camera-preview 7.4.0-beta.1 → 7.4.0-beta.2
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.
- package/README.md +14 -8
- package/android/src/main/AndroidManifest.xml +5 -0
- package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java +35 -20
- package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraXView.java +210 -90
- package/dist/docs.json +27 -2
- package/dist/esm/definitions.d.ts +11 -0
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.js +4 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +2 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +2 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoCameraPreview/Plugin.swift +41 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -233,7 +233,7 @@ Stops the camera preview.
|
|
|
233
233
|
### capture(...)
|
|
234
234
|
|
|
235
235
|
```typescript
|
|
236
|
-
capture(options: CameraPreviewPictureOptions) => Promise<{ value: string; }>
|
|
236
|
+
capture(options: CameraPreviewPictureOptions) => Promise<{ value: string; exif: ExifData; }>
|
|
237
237
|
```
|
|
238
238
|
|
|
239
239
|
Captures a picture from the camera.
|
|
@@ -242,7 +242,7 @@ Captures a picture from the camera.
|
|
|
242
242
|
| ------------- | ----------------------------------------------------------------------------------- | ---------------------------------------- |
|
|
243
243
|
| **`options`** | <code><a href="#camerapreviewpictureoptions">CameraPreviewPictureOptions</a></code> | - The options for capturing the picture. |
|
|
244
244
|
|
|
245
|
-
**Returns:** <code>Promise<{ value: string; }></code>
|
|
245
|
+
**Returns:** <code>Promise<{ value: string; exif: <a href="#exifdata">ExifData</a>; }></code>
|
|
246
246
|
|
|
247
247
|
**Since:** 0.0.1
|
|
248
248
|
|
|
@@ -546,16 +546,22 @@ Defines the configuration options for starting the camera preview.
|
|
|
546
546
|
| **`deviceId`** | <code>string</code> | The `deviceId` of the camera to use. If provided, `position` is ignored. | |
|
|
547
547
|
|
|
548
548
|
|
|
549
|
+
#### ExifData
|
|
550
|
+
|
|
551
|
+
Represents EXIF data extracted from an image.
|
|
552
|
+
|
|
553
|
+
|
|
549
554
|
#### CameraPreviewPictureOptions
|
|
550
555
|
|
|
551
556
|
Defines the options for capturing a picture.
|
|
552
557
|
|
|
553
|
-
| Prop
|
|
554
|
-
|
|
|
555
|
-
| **`height`**
|
|
556
|
-
| **`width`**
|
|
557
|
-
| **`quality`**
|
|
558
|
-
| **`format`**
|
|
558
|
+
| Prop | Type | Description | Default | Since |
|
|
559
|
+
| ------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------- | ----- |
|
|
560
|
+
| **`height`** | <code>number</code> | The desired height of the picture in pixels. If not provided, the device default is used. | | |
|
|
561
|
+
| **`width`** | <code>number</code> | The desired width of the picture in pixels. If not provided, the device default is used. | | |
|
|
562
|
+
| **`quality`** | <code>number</code> | The quality of the captured image, from 0 to 100. Does not apply to `png` format. | <code>85</code> | |
|
|
563
|
+
| **`format`** | <code><a href="#pictureformat">PictureFormat</a></code> | The format of the captured image. | <code>"jpeg"</code> | |
|
|
564
|
+
| **`saveToGallery`** | <code>boolean</code> | If true, the captured image will be saved to the user's gallery. | <code>false</code> | 7.5.0 |
|
|
559
565
|
|
|
560
566
|
|
|
561
567
|
#### CameraSampleOptions
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
3
|
>
|
|
4
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
5
|
+
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
|
6
|
+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
7
|
+
<uses-feature android:name="android.hardware.camera" />
|
|
8
|
+
<uses-feature android:name="android.hardware.camera.autofocus" />
|
|
4
9
|
</manifest>
|
|
5
10
|
|
|
@@ -4,14 +4,10 @@ import static android.Manifest.permission.CAMERA;
|
|
|
4
4
|
import static android.Manifest.permission.RECORD_AUDIO;
|
|
5
5
|
|
|
6
6
|
import android.content.pm.ActivityInfo;
|
|
7
|
-
import android.graphics.Point;
|
|
8
7
|
import android.util.DisplayMetrics;
|
|
9
8
|
import android.util.TypedValue;
|
|
10
|
-
import android.view.Display;
|
|
11
|
-
import androidx.annotation.NonNull;
|
|
12
9
|
import com.getcapacitor.JSArray;
|
|
13
10
|
import com.getcapacitor.JSObject;
|
|
14
|
-
import com.getcapacitor.Logger;
|
|
15
11
|
import com.getcapacitor.PermissionState;
|
|
16
12
|
import com.getcapacitor.Plugin;
|
|
17
13
|
import com.getcapacitor.PluginCall;
|
|
@@ -28,6 +24,8 @@ import android.util.Size;
|
|
|
28
24
|
import android.util.Log;
|
|
29
25
|
import com.ahm.capacitor.camera.preview.model.LensInfo;
|
|
30
26
|
|
|
27
|
+
import org.json.JSONObject;
|
|
28
|
+
|
|
31
29
|
@CapacitorPlugin(
|
|
32
30
|
name = "CameraPreview",
|
|
33
31
|
permissions = {
|
|
@@ -83,15 +81,18 @@ public class CameraPreview
|
|
|
83
81
|
}
|
|
84
82
|
|
|
85
83
|
@PluginMethod
|
|
86
|
-
public void capture(PluginCall call) {
|
|
84
|
+
public void capture(final PluginCall call) {
|
|
87
85
|
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
88
86
|
call.reject("Camera is not running");
|
|
89
87
|
return;
|
|
90
88
|
}
|
|
89
|
+
|
|
91
90
|
bridge.saveCall(call);
|
|
92
91
|
captureCallbackId = call.getCallbackId();
|
|
92
|
+
|
|
93
93
|
Integer quality = Objects.requireNonNull(call.getInt("quality", 85));
|
|
94
|
-
|
|
94
|
+
final boolean saveToGallery = Boolean.TRUE.equals(call.getBoolean("saveToGallery", false));
|
|
95
|
+
cameraXView.capturePhoto(quality, saveToGallery);
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
@PluginMethod
|
|
@@ -326,13 +327,13 @@ public class CameraPreview
|
|
|
326
327
|
final int width = call.getInt("width", 0);
|
|
327
328
|
final int height = call.getInt("height", 0);
|
|
328
329
|
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);
|
|
330
|
+
final boolean toBack = Boolean.TRUE.equals(call.getBoolean("toBack", true));
|
|
331
|
+
final boolean storeToFile = Boolean.TRUE.equals(call.getBoolean("storeToFile", false));
|
|
332
|
+
final boolean enableOpacity = Boolean.TRUE.equals(call.getBoolean("enableOpacity", false));
|
|
333
|
+
final boolean enableZoom = Boolean.TRUE.equals(call.getBoolean("enableZoom", false));
|
|
334
|
+
final boolean disableExifHeaderStripping = Boolean.TRUE.equals(call.getBoolean("disableExifHeaderStripping", false));
|
|
335
|
+
final boolean lockOrientation = Boolean.TRUE.equals(call.getBoolean("lockAndroidOrientation", false));
|
|
336
|
+
final boolean disableAudio = Boolean.TRUE.equals(call.getBoolean("disableAudio", true));
|
|
336
337
|
|
|
337
338
|
float targetZoom = 1.0f;
|
|
338
339
|
// Check if the selected device is a physical ultra-wide
|
|
@@ -367,8 +368,8 @@ public class CameraPreview
|
|
|
367
368
|
}
|
|
368
369
|
int computedX = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, x, metrics);
|
|
369
370
|
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) :
|
|
371
|
-
int computedHeight = height != 0 ? (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, height, metrics) :
|
|
371
|
+
int computedWidth = width != 0 ? (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, width, metrics) : getBridge().getWebView().getWidth();
|
|
372
|
+
int computedHeight = height != 0 ? (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, height, metrics) : getBridge().getWebView().getHeight();
|
|
372
373
|
computedHeight -= (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, paddingBottom, metrics);
|
|
373
374
|
|
|
374
375
|
CameraSessionConfiguration config = new CameraSessionConfiguration(finalDeviceId, position, computedX, computedY, computedWidth, computedHeight, paddingBottom, toBack, storeToFile, enableOpacity, enableZoom, disableExifHeaderStripping, disableAudio, 1.0f);
|
|
@@ -382,15 +383,28 @@ public class CameraPreview
|
|
|
382
383
|
}
|
|
383
384
|
|
|
384
385
|
@Override
|
|
385
|
-
public void onPictureTaken(String
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
386
|
+
public void onPictureTaken(String base64, JSONObject exif) {
|
|
387
|
+
PluginCall pluginCall = bridge.getSavedCall(captureCallbackId);
|
|
388
|
+
if (pluginCall == null) {
|
|
389
|
+
Log.e("CameraPreview", "onPictureTaken: captureCallbackId is null");
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
JSObject result = new JSObject();
|
|
393
|
+
result.put("value", base64);
|
|
394
|
+
result.put("exif", exif);
|
|
395
|
+
pluginCall.resolve(result);
|
|
396
|
+
bridge.releaseCall(pluginCall);
|
|
389
397
|
}
|
|
390
398
|
|
|
391
399
|
@Override
|
|
392
400
|
public void onPictureTakenError(String message) {
|
|
393
|
-
bridge.getSavedCall(captureCallbackId)
|
|
401
|
+
PluginCall pluginCall = bridge.getSavedCall(captureCallbackId);
|
|
402
|
+
if (pluginCall == null) {
|
|
403
|
+
Log.e("CameraPreview", "onPictureTakenError: captureCallbackId is null");
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
pluginCall.reject(message);
|
|
407
|
+
bridge.releaseCall(pluginCall);
|
|
394
408
|
}
|
|
395
409
|
|
|
396
410
|
@Override
|
|
@@ -422,4 +436,5 @@ public class CameraPreview
|
|
|
422
436
|
bridge.releaseCall(pluginCall);
|
|
423
437
|
}
|
|
424
438
|
}
|
|
439
|
+
|
|
425
440
|
}
|
|
@@ -4,7 +4,6 @@ import android.content.Context;
|
|
|
4
4
|
import android.hardware.camera2.CameraAccessException;
|
|
5
5
|
import android.hardware.camera2.CameraManager;
|
|
6
6
|
import android.os.Build;
|
|
7
|
-
import android.os.HandlerThread;
|
|
8
7
|
import android.util.Base64;
|
|
9
8
|
import android.util.Log;
|
|
10
9
|
import android.util.Size;
|
|
@@ -32,29 +31,37 @@ import com.ahm.capacitor.camera.preview.model.LensInfo;
|
|
|
32
31
|
import com.ahm.capacitor.camera.preview.model.ZoomFactors;
|
|
33
32
|
import com.google.common.util.concurrent.ListenableFuture;
|
|
34
33
|
import java.nio.ByteBuffer;
|
|
35
|
-
import java.nio.file.Files;
|
|
36
34
|
import java.util.Arrays;
|
|
37
35
|
import java.util.Collections;
|
|
38
36
|
import java.util.List;
|
|
39
37
|
import java.util.ArrayList;
|
|
40
38
|
import java.util.Objects;
|
|
41
|
-
import java.util.concurrent.ExecutionException;
|
|
42
39
|
import java.util.concurrent.Executor;
|
|
43
40
|
import java.util.concurrent.ExecutorService;
|
|
44
41
|
import java.util.concurrent.Executors;
|
|
45
42
|
import androidx.camera.camera2.interop.Camera2CameraInfo;
|
|
46
43
|
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
|
|
47
44
|
import android.hardware.camera2.CameraCharacteristics;
|
|
48
|
-
import androidx.camera.extensions.ExtensionMode;
|
|
49
45
|
import java.util.Set;
|
|
50
46
|
import androidx.camera.core.ZoomState;
|
|
51
47
|
import androidx.camera.core.ResolutionInfo;
|
|
48
|
+
import android.content.Intent;
|
|
49
|
+
import android.net.Uri;
|
|
50
|
+
import android.os.Environment;
|
|
51
|
+
import java.io.File;
|
|
52
|
+
import java.io.FileOutputStream;
|
|
53
|
+
import java.io.IOException;
|
|
54
|
+
import java.text.SimpleDateFormat;
|
|
55
|
+
import java.util.Locale;
|
|
56
|
+
import androidx.exifinterface.media.ExifInterface;
|
|
57
|
+
import org.json.JSONObject;
|
|
58
|
+
import java.nio.file.Files;
|
|
52
59
|
|
|
53
60
|
public class CameraXView implements LifecycleOwner {
|
|
54
61
|
private static final String TAG = "CameraPreview CameraXView";
|
|
55
62
|
|
|
56
63
|
public interface CameraXViewListener {
|
|
57
|
-
void onPictureTaken(String
|
|
64
|
+
void onPictureTaken(String base64, JSONObject exif);
|
|
58
65
|
void onPictureTakenError(String message);
|
|
59
66
|
void onSampleTaken(String result);
|
|
60
67
|
void onSampleTakenError(String message);
|
|
@@ -102,6 +109,26 @@ public class CameraXView implements LifecycleOwner {
|
|
|
102
109
|
return isRunning;
|
|
103
110
|
}
|
|
104
111
|
|
|
112
|
+
private void saveImageToGallery(byte[] data) {
|
|
113
|
+
try {
|
|
114
|
+
File photo = new File(
|
|
115
|
+
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
|
|
116
|
+
"IMG_" + new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new java.util.Date()) + ".jpg"
|
|
117
|
+
);
|
|
118
|
+
FileOutputStream fos = new FileOutputStream(photo);
|
|
119
|
+
fos.write(data);
|
|
120
|
+
fos.close();
|
|
121
|
+
|
|
122
|
+
// Notify the gallery of the new image
|
|
123
|
+
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
|
124
|
+
Uri contentUri = Uri.fromFile(photo);
|
|
125
|
+
mediaScanIntent.setData(contentUri);
|
|
126
|
+
context.sendBroadcast(mediaScanIntent);
|
|
127
|
+
} catch (IOException e) {
|
|
128
|
+
Log.e(TAG, "Error saving image to gallery", e);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
105
132
|
public void startSession(CameraSessionConfiguration config) {
|
|
106
133
|
this.sessionConfig = config;
|
|
107
134
|
cameraExecutor = Executors.newSingleThreadExecutor();
|
|
@@ -167,6 +194,7 @@ public class CameraXView implements LifecycleOwner {
|
|
|
167
194
|
webView.setBackgroundColor(android.graphics.Color.WHITE);
|
|
168
195
|
}
|
|
169
196
|
|
|
197
|
+
@OptIn(markerClass = ExperimentalCamera2Interop.class)
|
|
170
198
|
private void bindCameraUseCases() {
|
|
171
199
|
if (cameraProvider == null) return;
|
|
172
200
|
mainExecutor.execute(() -> {
|
|
@@ -188,7 +216,6 @@ public class CameraXView implements LifecycleOwner {
|
|
|
188
216
|
Log.d(TAG, "Use cases bound. Inspecting active camera and use cases.");
|
|
189
217
|
CameraInfo cameraInfo = camera.getCameraInfo();
|
|
190
218
|
Log.d(TAG, "Bound Camera ID: " + Camera2CameraInfo.from(cameraInfo).getCameraId());
|
|
191
|
-
Log.d(TAG, "Implementation Type: " + cameraInfo.getImplementationType());
|
|
192
219
|
|
|
193
220
|
// Log zoom state
|
|
194
221
|
ZoomState zoomState = cameraInfo.getZoomState().getValue();
|
|
@@ -256,22 +283,6 @@ public class CameraXView implements LifecycleOwner {
|
|
|
256
283
|
return builder.build();
|
|
257
284
|
}
|
|
258
285
|
|
|
259
|
-
private static boolean isIsLogical(CameraManager cameraManager, String cameraId) throws CameraAccessException {
|
|
260
|
-
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
|
|
261
|
-
int[] capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
|
|
262
|
-
|
|
263
|
-
boolean isLogical = false;
|
|
264
|
-
if (capabilities != null) {
|
|
265
|
-
for (int capability : capabilities) {
|
|
266
|
-
if (capability == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) {
|
|
267
|
-
isLogical = true;
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
return isLogical;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
286
|
private static String getCameraId(androidx.camera.core.CameraInfo cameraInfo) {
|
|
276
287
|
try {
|
|
277
288
|
// Generate a stable ID based on camera characteristics
|
|
@@ -303,7 +314,7 @@ public class CameraXView implements LifecycleOwner {
|
|
|
303
314
|
}
|
|
304
315
|
}
|
|
305
316
|
|
|
306
|
-
public void capturePhoto(int quality) {
|
|
317
|
+
public void capturePhoto(int quality, final boolean saveToGallery) {
|
|
307
318
|
Log.d(TAG, "capturePhoto: Starting photo capture with quality: " + quality);
|
|
308
319
|
|
|
309
320
|
if (imageCapture == null) {
|
|
@@ -313,9 +324,8 @@ public class CameraXView implements LifecycleOwner {
|
|
|
313
324
|
return;
|
|
314
325
|
}
|
|
315
326
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
).build();
|
|
327
|
+
File tempFile = new File(context.getCacheDir(), "temp_image.jpg");
|
|
328
|
+
ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(tempFile).build();
|
|
319
329
|
|
|
320
330
|
imageCapture.takePicture(
|
|
321
331
|
outputFileOptions,
|
|
@@ -331,31 +341,24 @@ public class CameraXView implements LifecycleOwner {
|
|
|
331
341
|
|
|
332
342
|
@Override
|
|
333
343
|
public void onImageSaved(@NonNull ImageCapture.OutputFileResults output) {
|
|
334
|
-
// Convert to base64
|
|
335
344
|
try {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
// Fallback for older Android versions
|
|
343
|
-
java.io.FileInputStream fis = new java.io.FileInputStream(tempFile);
|
|
344
|
-
bytes = new byte[(int) tempFile.length()];
|
|
345
|
-
fis.read(bytes);
|
|
346
|
-
fis.close();
|
|
345
|
+
byte[] bytes = Files.readAllBytes(tempFile.toPath());
|
|
346
|
+
ExifInterface exifInterface = new ExifInterface(tempFile.getAbsolutePath());
|
|
347
|
+
JSONObject exifData = getExifData(exifInterface);
|
|
348
|
+
|
|
349
|
+
if (saveToGallery) {
|
|
350
|
+
saveImageToGallery(bytes);
|
|
347
351
|
}
|
|
348
352
|
|
|
349
353
|
String base64 = Base64.encodeToString(bytes, Base64.NO_WRAP);
|
|
350
|
-
|
|
351
|
-
// Clean up temp file
|
|
354
|
+
|
|
352
355
|
tempFile.delete();
|
|
353
356
|
|
|
354
357
|
if (listener != null) {
|
|
355
|
-
listener.onPictureTaken(base64);
|
|
358
|
+
listener.onPictureTaken(base64, exifData);
|
|
356
359
|
}
|
|
357
360
|
} catch (Exception e) {
|
|
358
|
-
Log.e(TAG, "capturePhoto: Error
|
|
361
|
+
Log.e(TAG, "capturePhoto: Error processing image", e);
|
|
359
362
|
if (listener != null) {
|
|
360
363
|
listener.onPictureTakenError("Error processing image: " + e.getMessage());
|
|
361
364
|
}
|
|
@@ -365,6 +368,166 @@ public class CameraXView implements LifecycleOwner {
|
|
|
365
368
|
);
|
|
366
369
|
}
|
|
367
370
|
|
|
371
|
+
private JSONObject getExifData(ExifInterface exifInterface) {
|
|
372
|
+
JSONObject exifData = new JSONObject();
|
|
373
|
+
try {
|
|
374
|
+
// Add all available exif tags to a JSON object
|
|
375
|
+
for (String[] tag : EXIF_TAGS) {
|
|
376
|
+
String value = exifInterface.getAttribute(tag[0]);
|
|
377
|
+
if (value != null) {
|
|
378
|
+
exifData.put(tag[1], value);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
} catch (Exception e) {
|
|
382
|
+
Log.e(TAG, "getExifData: Error reading exif data", e);
|
|
383
|
+
}
|
|
384
|
+
return exifData;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
private static final String[][] EXIF_TAGS = new String[][]{
|
|
388
|
+
{ExifInterface.TAG_APERTURE_VALUE, "ApertureValue"},
|
|
389
|
+
{ExifInterface.TAG_ARTIST, "Artist"},
|
|
390
|
+
{ExifInterface.TAG_BITS_PER_SAMPLE, "BitsPerSample"},
|
|
391
|
+
{ExifInterface.TAG_BRIGHTNESS_VALUE, "BrightnessValue"},
|
|
392
|
+
{ExifInterface.TAG_CFA_PATTERN, "CFAPattern"},
|
|
393
|
+
{ExifInterface.TAG_COLOR_SPACE, "ColorSpace"},
|
|
394
|
+
{ExifInterface.TAG_COMPONENTS_CONFIGURATION, "ComponentsConfiguration"},
|
|
395
|
+
{ExifInterface.TAG_COMPRESSED_BITS_PER_PIXEL, "CompressedBitsPerPixel"},
|
|
396
|
+
{ExifInterface.TAG_COMPRESSION, "Compression"},
|
|
397
|
+
{ExifInterface.TAG_CONTRAST, "Contrast"},
|
|
398
|
+
{ExifInterface.TAG_COPYRIGHT, "Copyright"},
|
|
399
|
+
{ExifInterface.TAG_CUSTOM_RENDERED, "CustomRendered"},
|
|
400
|
+
{ExifInterface.TAG_DATETIME, "DateTime"},
|
|
401
|
+
{ExifInterface.TAG_DATETIME_DIGITIZED, "DateTimeDigitized"},
|
|
402
|
+
{ExifInterface.TAG_DATETIME_ORIGINAL, "DateTimeOriginal"},
|
|
403
|
+
{ExifInterface.TAG_DEVICE_SETTING_DESCRIPTION, "DeviceSettingDescription"},
|
|
404
|
+
{ExifInterface.TAG_DIGITAL_ZOOM_RATIO, "DigitalZoomRatio"},
|
|
405
|
+
{ExifInterface.TAG_DNG_VERSION, "DNGVersion"},
|
|
406
|
+
{ExifInterface.TAG_EXIF_VERSION, "ExifVersion"},
|
|
407
|
+
{ExifInterface.TAG_EXPOSURE_BIAS_VALUE, "ExposureBiasValue"},
|
|
408
|
+
{ExifInterface.TAG_EXPOSURE_INDEX, "ExposureIndex"},
|
|
409
|
+
{ExifInterface.TAG_EXPOSURE_MODE, "ExposureMode"},
|
|
410
|
+
{ExifInterface.TAG_EXPOSURE_PROGRAM, "ExposureProgram"},
|
|
411
|
+
{ExifInterface.TAG_EXPOSURE_TIME, "ExposureTime"},
|
|
412
|
+
{ExifInterface.TAG_FILE_SOURCE, "FileSource"},
|
|
413
|
+
{ExifInterface.TAG_FLASH, "Flash"},
|
|
414
|
+
{ExifInterface.TAG_FLASHPIX_VERSION, "FlashpixVersion"},
|
|
415
|
+
{ExifInterface.TAG_FLASH_ENERGY, "FlashEnergy"},
|
|
416
|
+
{ExifInterface.TAG_FOCAL_LENGTH, "FocalLength"},
|
|
417
|
+
{ExifInterface.TAG_FOCAL_LENGTH_IN_35MM_FILM, "FocalLengthIn35mmFilm"},
|
|
418
|
+
{ExifInterface.TAG_FOCAL_PLANE_RESOLUTION_UNIT, "FocalPlaneResolutionUnit"},
|
|
419
|
+
{ExifInterface.TAG_FOCAL_PLANE_X_RESOLUTION, "FocalPlaneXResolution"},
|
|
420
|
+
{ExifInterface.TAG_FOCAL_PLANE_Y_RESOLUTION, "FocalPlaneYResolution"},
|
|
421
|
+
{ExifInterface.TAG_F_NUMBER, "FNumber"},
|
|
422
|
+
{ExifInterface.TAG_GAIN_CONTROL, "GainControl"},
|
|
423
|
+
{ExifInterface.TAG_GPS_ALTITUDE, "GPSAltitude"},
|
|
424
|
+
{ExifInterface.TAG_GPS_ALTITUDE_REF, "GPSAltitudeRef"},
|
|
425
|
+
{ExifInterface.TAG_GPS_AREA_INFORMATION, "GPSAreaInformation"},
|
|
426
|
+
{ExifInterface.TAG_GPS_DATESTAMP, "GPSDateStamp"},
|
|
427
|
+
{ExifInterface.TAG_GPS_DEST_BEARING, "GPSDestBearing"},
|
|
428
|
+
{ExifInterface.TAG_GPS_DEST_BEARING_REF, "GPSDestBearingRef"},
|
|
429
|
+
{ExifInterface.TAG_GPS_DEST_DISTANCE, "GPSDestDistance"},
|
|
430
|
+
{ExifInterface.TAG_GPS_DEST_DISTANCE_REF, "GPSDestDistanceRef"},
|
|
431
|
+
{ExifInterface.TAG_GPS_DEST_LATITUDE, "GPSDestLatitude"},
|
|
432
|
+
{ExifInterface.TAG_GPS_DEST_LATITUDE_REF, "GPSDestLatitudeRef"},
|
|
433
|
+
{ExifInterface.TAG_GPS_DEST_LONGITUDE, "GPSDestLongitude"},
|
|
434
|
+
{ExifInterface.TAG_GPS_DEST_LONGITUDE_REF, "GPSDestLongitudeRef"},
|
|
435
|
+
{ExifInterface.TAG_GPS_DIFFERENTIAL, "GPSDifferential"},
|
|
436
|
+
{ExifInterface.TAG_GPS_DOP, "GPSDOP"},
|
|
437
|
+
{ExifInterface.TAG_GPS_IMG_DIRECTION, "GPSImgDirection"},
|
|
438
|
+
{ExifInterface.TAG_GPS_IMG_DIRECTION_REF, "GPSImgDirectionRef"},
|
|
439
|
+
{ExifInterface.TAG_GPS_LATITUDE, "GPSLatitude"},
|
|
440
|
+
{ExifInterface.TAG_GPS_LATITUDE_REF, "GPSLatitudeRef"},
|
|
441
|
+
{ExifInterface.TAG_GPS_LONGITUDE, "GPSLongitude"},
|
|
442
|
+
{ExifInterface.TAG_GPS_LONGITUDE_REF, "GPSLongitudeRef"},
|
|
443
|
+
{ExifInterface.TAG_GPS_MAP_DATUM, "GPSMapDatum"},
|
|
444
|
+
{ExifInterface.TAG_GPS_MEASURE_MODE, "GPSMeasureMode"},
|
|
445
|
+
{ExifInterface.TAG_GPS_PROCESSING_METHOD, "GPSProcessingMethod"},
|
|
446
|
+
{ExifInterface.TAG_GPS_SATELLITES, "GPSSatellites"},
|
|
447
|
+
{ExifInterface.TAG_GPS_SPEED, "GPSSpeed"},
|
|
448
|
+
{ExifInterface.TAG_GPS_SPEED_REF, "GPSSpeedRef"},
|
|
449
|
+
{ExifInterface.TAG_GPS_STATUS, "GPSStatus"},
|
|
450
|
+
{ExifInterface.TAG_GPS_TIMESTAMP, "GPSTimeStamp"},
|
|
451
|
+
{ExifInterface.TAG_GPS_TRACK, "GPSTrack"},
|
|
452
|
+
{ExifInterface.TAG_GPS_TRACK_REF, "GPSTrackRef"},
|
|
453
|
+
{ExifInterface.TAG_GPS_VERSION_ID, "GPSVersionID"},
|
|
454
|
+
{ExifInterface.TAG_IMAGE_DESCRIPTION, "ImageDescription"},
|
|
455
|
+
{ExifInterface.TAG_IMAGE_LENGTH, "ImageLength"},
|
|
456
|
+
{ExifInterface.TAG_IMAGE_UNIQUE_ID, "ImageUniqueID"},
|
|
457
|
+
{ExifInterface.TAG_IMAGE_WIDTH, "ImageWidth"},
|
|
458
|
+
{ExifInterface.TAG_INTEROPERABILITY_INDEX, "InteroperabilityIndex"},
|
|
459
|
+
{ExifInterface.TAG_ISO_SPEED, "ISOSpeed"},
|
|
460
|
+
{ExifInterface.TAG_ISO_SPEED_LATITUDE_YYY, "ISOSpeedLatitudeyyy"},
|
|
461
|
+
{ExifInterface.TAG_ISO_SPEED_LATITUDE_ZZZ, "ISOSpeedLatitudezzz"},
|
|
462
|
+
{ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT, "JPEGInterchangeFormat"},
|
|
463
|
+
{ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, "JPEGInterchangeFormatLength"},
|
|
464
|
+
{ExifInterface.TAG_LIGHT_SOURCE, "LightSource"},
|
|
465
|
+
{ExifInterface.TAG_MAKE, "Make"},
|
|
466
|
+
{ExifInterface.TAG_MAKER_NOTE, "MakerNote"},
|
|
467
|
+
{ExifInterface.TAG_MAX_APERTURE_VALUE, "MaxApertureValue"},
|
|
468
|
+
{ExifInterface.TAG_METERING_MODE, "MeteringMode"},
|
|
469
|
+
{ExifInterface.TAG_MODEL, "Model"},
|
|
470
|
+
{ExifInterface.TAG_NEW_SUBFILE_TYPE, "NewSubfileType"},
|
|
471
|
+
{ExifInterface.TAG_OECF, "OECF"},
|
|
472
|
+
{ExifInterface.TAG_OFFSET_TIME, "OffsetTime"},
|
|
473
|
+
{ExifInterface.TAG_OFFSET_TIME_DIGITIZED, "OffsetTimeDigitized"},
|
|
474
|
+
{ExifInterface.TAG_OFFSET_TIME_ORIGINAL, "OffsetTimeOriginal"},
|
|
475
|
+
{ExifInterface.TAG_ORF_ASPECT_FRAME, "ORFAspectFrame"},
|
|
476
|
+
{ExifInterface.TAG_ORF_PREVIEW_IMAGE_LENGTH, "ORFPreviewImageLength"},
|
|
477
|
+
{ExifInterface.TAG_ORF_PREVIEW_IMAGE_START, "ORFPreviewImageStart"},
|
|
478
|
+
{ExifInterface.TAG_ORF_THUMBNAIL_IMAGE, "ORFThumbnailImage"},
|
|
479
|
+
{ExifInterface.TAG_ORIENTATION, "Orientation"},
|
|
480
|
+
{ExifInterface.TAG_PHOTOMETRIC_INTERPRETATION, "PhotometricInterpretation"},
|
|
481
|
+
{ExifInterface.TAG_PIXEL_X_DIMENSION, "PixelXDimension"},
|
|
482
|
+
{ExifInterface.TAG_PIXEL_Y_DIMENSION, "PixelYDimension"},
|
|
483
|
+
{ExifInterface.TAG_PLANAR_CONFIGURATION, "PlanarConfiguration"},
|
|
484
|
+
{ExifInterface.TAG_PRIMARY_CHROMATICITIES, "PrimaryChromaticities"},
|
|
485
|
+
{ExifInterface.TAG_RECOMMENDED_EXPOSURE_INDEX, "RecommendedExposureIndex"},
|
|
486
|
+
{ExifInterface.TAG_REFERENCE_BLACK_WHITE, "ReferenceBlackWhite"},
|
|
487
|
+
{ExifInterface.TAG_RELATED_SOUND_FILE, "RelatedSoundFile"},
|
|
488
|
+
{ExifInterface.TAG_RESOLUTION_UNIT, "ResolutionUnit"},
|
|
489
|
+
{ExifInterface.TAG_ROWS_PER_STRIP, "RowsPerStrip"},
|
|
490
|
+
{ExifInterface.TAG_RW2_ISO, "RW2ISO"},
|
|
491
|
+
{ExifInterface.TAG_RW2_JPG_FROM_RAW, "RW2JpgFromRaw"},
|
|
492
|
+
{ExifInterface.TAG_RW2_SENSOR_BOTTOM_BORDER, "RW2SensorBottomBorder"},
|
|
493
|
+
{ExifInterface.TAG_RW2_SENSOR_LEFT_BORDER, "RW2SensorLeftBorder"},
|
|
494
|
+
{ExifInterface.TAG_RW2_SENSOR_RIGHT_BORDER, "RW2SensorRightBorder"},
|
|
495
|
+
{ExifInterface.TAG_RW2_SENSOR_TOP_BORDER, "RW2SensorTopBorder"},
|
|
496
|
+
{ExifInterface.TAG_SAMPLES_PER_PIXEL, "SamplesPerPixel"},
|
|
497
|
+
{ExifInterface.TAG_SATURATION, "Saturation"},
|
|
498
|
+
{ExifInterface.TAG_SCENE_CAPTURE_TYPE, "SceneCaptureType"},
|
|
499
|
+
{ExifInterface.TAG_SCENE_TYPE, "SceneType"},
|
|
500
|
+
{ExifInterface.TAG_SENSING_METHOD, "SensingMethod"},
|
|
501
|
+
{ExifInterface.TAG_SENSITIVITY_TYPE, "SensitivityType"},
|
|
502
|
+
{ExifInterface.TAG_SHARPNESS, "Sharpness"},
|
|
503
|
+
{ExifInterface.TAG_SHUTTER_SPEED_VALUE, "ShutterSpeedValue"},
|
|
504
|
+
{ExifInterface.TAG_SOFTWARE, "Software"},
|
|
505
|
+
{ExifInterface.TAG_SPATIAL_FREQUENCY_RESPONSE, "SpatialFrequencyResponse"},
|
|
506
|
+
{ExifInterface.TAG_SPECTRAL_SENSITIVITY, "SpectralSensitivity"},
|
|
507
|
+
{ExifInterface.TAG_STANDARD_OUTPUT_SENSITIVITY, "StandardOutputSensitivity"},
|
|
508
|
+
{ExifInterface.TAG_STRIP_BYTE_COUNTS, "StripByteCounts"},
|
|
509
|
+
{ExifInterface.TAG_STRIP_OFFSETS, "StripOffsets"},
|
|
510
|
+
{ExifInterface.TAG_SUBFILE_TYPE, "SubfileType"},
|
|
511
|
+
{ExifInterface.TAG_SUBJECT_AREA, "SubjectArea"},
|
|
512
|
+
{ExifInterface.TAG_SUBJECT_DISTANCE, "SubjectDistance"},
|
|
513
|
+
{ExifInterface.TAG_SUBJECT_DISTANCE_RANGE, "SubjectDistanceRange"},
|
|
514
|
+
{ExifInterface.TAG_SUBJECT_LOCATION, "SubjectLocation"},
|
|
515
|
+
{ExifInterface.TAG_SUBSEC_TIME, "SubSecTime"},
|
|
516
|
+
{ExifInterface.TAG_SUBSEC_TIME_DIGITIZED, "SubSecTimeDigitized"},
|
|
517
|
+
{ExifInterface.TAG_SUBSEC_TIME_ORIGINAL, "SubSecTimeOriginal"},
|
|
518
|
+
{ExifInterface.TAG_THUMBNAIL_IMAGE_LENGTH, "ThumbnailImageLength"},
|
|
519
|
+
{ExifInterface.TAG_THUMBNAIL_IMAGE_WIDTH, "ThumbnailImageWidth"},
|
|
520
|
+
{ExifInterface.TAG_TRANSFER_FUNCTION, "TransferFunction"},
|
|
521
|
+
{ExifInterface.TAG_USER_COMMENT, "UserComment"},
|
|
522
|
+
{ExifInterface.TAG_WHITE_BALANCE, "WhiteBalance"},
|
|
523
|
+
{ExifInterface.TAG_WHITE_POINT, "WhitePoint"},
|
|
524
|
+
{ExifInterface.TAG_X_RESOLUTION, "XResolution"},
|
|
525
|
+
{ExifInterface.TAG_Y_CB_CR_COEFFICIENTS, "YCbCrCoefficients"},
|
|
526
|
+
{ExifInterface.TAG_Y_CB_CR_POSITIONING, "YCbCrPositioning"},
|
|
527
|
+
{ExifInterface.TAG_Y_CB_CR_SUB_SAMPLING, "YCbCrSubSampling"},
|
|
528
|
+
{ExifInterface.TAG_Y_RESOLUTION, "YResolution"}
|
|
529
|
+
};
|
|
530
|
+
|
|
368
531
|
public void captureSample(int quality) {
|
|
369
532
|
Log.d(TAG, "captureSample: Starting sample capture with quality: " + quality);
|
|
370
533
|
|
|
@@ -498,11 +661,10 @@ public class CameraXView implements LifecycleOwner {
|
|
|
498
661
|
}
|
|
499
662
|
}
|
|
500
663
|
|
|
501
|
-
public static ZoomFactors getZoomFactorsStatic(
|
|
664
|
+
public static ZoomFactors getZoomFactorsStatic() {
|
|
502
665
|
try {
|
|
503
666
|
// For static method, return default zoom factors
|
|
504
667
|
// We can try to detect if ultra-wide is available by checking device list
|
|
505
|
-
List<com.ahm.capacitor.camera.preview.model.CameraDevice> devices = getAvailableDevicesStatic(context);
|
|
506
668
|
|
|
507
669
|
float minZoom = 1.0f;
|
|
508
670
|
float maxZoom = 10.0f;
|
|
@@ -519,7 +681,7 @@ public class CameraXView implements LifecycleOwner {
|
|
|
519
681
|
|
|
520
682
|
public ZoomFactors getZoomFactors() {
|
|
521
683
|
if (camera == null) {
|
|
522
|
-
return getZoomFactorsStatic(
|
|
684
|
+
return getZoomFactorsStatic();
|
|
523
685
|
}
|
|
524
686
|
|
|
525
687
|
try {
|
|
@@ -546,8 +708,6 @@ public class CameraXView implements LifecycleOwner {
|
|
|
546
708
|
|
|
547
709
|
try {
|
|
548
710
|
float currentZoom = Objects.requireNonNull(camera.getCameraInfo().getZoomState().getValue()).getZoomRatio();
|
|
549
|
-
float minZoom = camera.getCameraInfo().getZoomState().getValue().getMinZoomRatio();
|
|
550
|
-
float maxZoom = camera.getCameraInfo().getZoomState().getValue().getMaxZoomRatio();
|
|
551
711
|
|
|
552
712
|
// Determine device type based on zoom capabilities
|
|
553
713
|
String deviceType = "wideAngle";
|
|
@@ -594,46 +754,6 @@ public class CameraXView implements LifecycleOwner {
|
|
|
594
754
|
}
|
|
595
755
|
}
|
|
596
756
|
|
|
597
|
-
private List<androidx.camera.core.CameraInfo> getAvailableCamerasForCurrentPosition() {
|
|
598
|
-
if (cameraProvider == null) {
|
|
599
|
-
Log.w(TAG, "getAvailableCamerasForCurrentPosition: cameraProvider is null");
|
|
600
|
-
return Collections.emptyList();
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
List<androidx.camera.core.CameraInfo> allCameras = cameraProvider.getAvailableCameraInfos();
|
|
604
|
-
List<androidx.camera.core.CameraInfo> sameFacingCameras = new ArrayList<>();
|
|
605
|
-
|
|
606
|
-
Log.d(TAG, "getAvailableCamerasForCurrentPosition: Total cameras available: " + allCameras.size());
|
|
607
|
-
|
|
608
|
-
// Determine current facing direction from the session config to avoid restricted API call
|
|
609
|
-
boolean isCurrentBack = "back".equals(sessionConfig.getPosition());
|
|
610
|
-
Log.d(TAG, "getAvailableCamerasForCurrentPosition: Looking for " + (isCurrentBack ? "back" : "front") + " cameras");
|
|
611
|
-
|
|
612
|
-
for (int i = 0; i < allCameras.size(); i++) {
|
|
613
|
-
androidx.camera.core.CameraInfo cameraInfo = allCameras.get(i);
|
|
614
|
-
boolean isCameraBack = isBackCamera(cameraInfo);
|
|
615
|
-
String cameraId = getCameraId(cameraInfo);
|
|
616
|
-
|
|
617
|
-
Log.d(TAG, "getAvailableCamerasForCurrentPosition: Camera " + i + " - ID: " + cameraId + ", isBack: " + isCameraBack);
|
|
618
|
-
|
|
619
|
-
try {
|
|
620
|
-
float minZoom = Objects.requireNonNull(cameraInfo.getZoomState().getValue()).getMinZoomRatio();
|
|
621
|
-
float maxZoom = cameraInfo.getZoomState().getValue().getMaxZoomRatio();
|
|
622
|
-
Log.d(TAG, "getAvailableCamerasForCurrentPosition: Camera " + i + " zoom range: " + minZoom + "-" + maxZoom);
|
|
623
|
-
} catch (Exception e) {
|
|
624
|
-
Log.w(TAG, "getAvailableCamerasForCurrentPosition: Cannot get zoom info for camera " + i + ": " + e.getMessage());
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
if (isCameraBack == isCurrentBack) {
|
|
628
|
-
sameFacingCameras.add(cameraInfo);
|
|
629
|
-
Log.d(TAG, "getAvailableCamerasForCurrentPosition: Added camera " + i + " (" + cameraId + ") to same-facing list");
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
Log.d(TAG, "getAvailableCamerasForCurrentPosition: Found " + sameFacingCameras.size() + " cameras for " + (isCurrentBack ? "back" : "front"));
|
|
634
|
-
return sameFacingCameras;
|
|
635
|
-
}
|
|
636
|
-
|
|
637
757
|
public static List<Size> getSupportedPictureSizes(String facing) {
|
|
638
758
|
List<Size> sizes = new ArrayList<>();
|
|
639
759
|
try {
|
|
@@ -786,13 +906,13 @@ public class CameraXView implements LifecycleOwner {
|
|
|
786
906
|
Log.d(TAG, "switchToDevice: Found matching CameraInfo for deviceId: " + deviceId);
|
|
787
907
|
final CameraInfo finalTarget = targetCameraInfo;
|
|
788
908
|
|
|
789
|
-
|
|
909
|
+
// This filter will receive a list of all cameras and must return the one we want.
|
|
910
|
+
|
|
911
|
+
currentCameraSelector = new CameraSelector.Builder()
|
|
790
912
|
.addCameraFilter(cameras -> {
|
|
791
913
|
// This filter will receive a list of all cameras and must return the one we want.
|
|
792
914
|
return Collections.singletonList(finalTarget);
|
|
793
915
|
}).build();
|
|
794
|
-
|
|
795
|
-
currentCameraSelector = newSelector;
|
|
796
916
|
currentDeviceId = deviceId;
|
|
797
917
|
bindCameraUseCases(); // Rebind with the new, highly specific selector
|
|
798
918
|
} else {
|