@capgo/camera-preview 7.4.0-beta.1 → 7.4.0-beta.3
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 +62 -9
- package/android/build.gradle +2 -1
- package/android/src/main/AndroidManifest.xml +4 -2
- package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java +90 -20
- package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraXView.java +232 -90
- package/dist/docs.json +44 -3
- package/dist/esm/definitions.d.ts +19 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.js +7 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +3 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +3 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoCameraPreview/CameraController.swift +66 -6
- package/ios/Sources/CapgoCameraPreview/Plugin.swift +64 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -92,6 +92,52 @@ Then run
|
|
|
92
92
|
npx cap sync
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
+
## Optional Configuration
|
|
96
|
+
|
|
97
|
+
To use certain features of this plugin, you will need to add the following permissions/keys to your native project configurations.
|
|
98
|
+
|
|
99
|
+
### Android
|
|
100
|
+
|
|
101
|
+
In your `android/app/src/main/AndroidManifest.xml`:
|
|
102
|
+
|
|
103
|
+
- **Audio Recording** (`disableAudio: false`):
|
|
104
|
+
```xml
|
|
105
|
+
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
- **Saving to Gallery** (`saveToGallery: true`):
|
|
109
|
+
```xml
|
|
110
|
+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
- **Location in EXIF Data** (`withExifLocation: true`):
|
|
114
|
+
```xml
|
|
115
|
+
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
|
116
|
+
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### iOS
|
|
120
|
+
|
|
121
|
+
In your `ios/App/App/Info.plist`, you must provide descriptions for the permissions your app requires. The keys are added automatically, but you need to provide the `string` values.
|
|
122
|
+
|
|
123
|
+
- **Audio Recording** (`disableAudio: false`):
|
|
124
|
+
```xml
|
|
125
|
+
<key>NSMicrophoneUsageDescription</key>
|
|
126
|
+
<string>To record audio with videos</string>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
- **Saving to Gallery** (`saveToGallery: true`):
|
|
130
|
+
```xml
|
|
131
|
+
<key>NSPhotoLibraryAddUsageDescription</key>
|
|
132
|
+
<string>To save photos to your gallery</string>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
- **Location in EXIF Data** (`withExifLocation: true`):
|
|
136
|
+
```xml
|
|
137
|
+
<key>NSLocationWhenInUseUsageDescription</key>
|
|
138
|
+
<string>To add location data to your photos</string>
|
|
139
|
+
```
|
|
140
|
+
|
|
95
141
|
## Extra Android installation steps
|
|
96
142
|
|
|
97
143
|
**Important** `camera-preview` 3+ requires Gradle 7.
|
|
@@ -233,7 +279,7 @@ Stops the camera preview.
|
|
|
233
279
|
### capture(...)
|
|
234
280
|
|
|
235
281
|
```typescript
|
|
236
|
-
capture(options: CameraPreviewPictureOptions) => Promise<{ value: string; }>
|
|
282
|
+
capture(options: CameraPreviewPictureOptions) => Promise<{ value: string; exif: ExifData; }>
|
|
237
283
|
```
|
|
238
284
|
|
|
239
285
|
Captures a picture from the camera.
|
|
@@ -242,7 +288,7 @@ Captures a picture from the camera.
|
|
|
242
288
|
| ------------- | ----------------------------------------------------------------------------------- | ---------------------------------------- |
|
|
243
289
|
| **`options`** | <code><a href="#camerapreviewpictureoptions">CameraPreviewPictureOptions</a></code> | - The options for capturing the picture. |
|
|
244
290
|
|
|
245
|
-
**Returns:** <code>Promise<{ value: string; }></code>
|
|
291
|
+
**Returns:** <code>Promise<{ value: string; exif: <a href="#exifdata">ExifData</a>; }></code>
|
|
246
292
|
|
|
247
293
|
**Since:** 0.0.1
|
|
248
294
|
|
|
@@ -538,7 +584,7 @@ Defines the configuration options for starting the camera preview.
|
|
|
538
584
|
| **`storeToFile`** | <code>boolean</code> | If true, saves the captured image to a file and returns the file path. If false, returns a base64 encoded string. | <code>false</code> |
|
|
539
585
|
| **`disableExifHeaderStripping`** | <code>boolean</code> | If true, prevents the plugin from rotating the image based on EXIF data. | <code>false</code> |
|
|
540
586
|
| **`enableHighResolution`** | <code>boolean</code> | If true, enables high-resolution image capture. | <code>false</code> |
|
|
541
|
-
| **`disableAudio`** | <code>boolean</code> | If true, disables the audio stream, preventing audio permission requests. | <code>
|
|
587
|
+
| **`disableAudio`** | <code>boolean</code> | If true, disables the audio stream, preventing audio permission requests. | <code>true</code> |
|
|
542
588
|
| **`lockAndroidOrientation`** | <code>boolean</code> | If true, locks the device orientation while the camera is active. | <code>false</code> |
|
|
543
589
|
| **`enableOpacity`** | <code>boolean</code> | If true, allows the camera preview's opacity to be changed. | <code>false</code> |
|
|
544
590
|
| **`enableZoom`** | <code>boolean</code> | If true, enables pinch-to-zoom functionality on the preview. | <code>false</code> |
|
|
@@ -546,16 +592,23 @@ Defines the configuration options for starting the camera preview.
|
|
|
546
592
|
| **`deviceId`** | <code>string</code> | The `deviceId` of the camera to use. If provided, `position` is ignored. | |
|
|
547
593
|
|
|
548
594
|
|
|
595
|
+
#### ExifData
|
|
596
|
+
|
|
597
|
+
Represents EXIF data extracted from an image.
|
|
598
|
+
|
|
599
|
+
|
|
549
600
|
#### CameraPreviewPictureOptions
|
|
550
601
|
|
|
551
602
|
Defines the options for capturing a picture.
|
|
552
603
|
|
|
553
|
-
| Prop
|
|
554
|
-
|
|
|
555
|
-
| **`height`**
|
|
556
|
-
| **`width`**
|
|
557
|
-
| **`quality`**
|
|
558
|
-
| **`format`**
|
|
604
|
+
| Prop | Type | Description | Default | Since |
|
|
605
|
+
| ---------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------- | ----- |
|
|
606
|
+
| **`height`** | <code>number</code> | The desired height of the picture in pixels. If not provided, the device default is used. | | |
|
|
607
|
+
| **`width`** | <code>number</code> | The desired width of the picture in pixels. If not provided, the device default is used. | | |
|
|
608
|
+
| **`quality`** | <code>number</code> | The quality of the captured image, from 0 to 100. Does not apply to `png` format. | <code>85</code> | |
|
|
609
|
+
| **`format`** | <code><a href="#pictureformat">PictureFormat</a></code> | The format of the captured image. | <code>"jpeg"</code> | |
|
|
610
|
+
| **`saveToGallery`** | <code>boolean</code> | If true, the captured image will be saved to the user's gallery. | <code>false</code> | 7.5.0 |
|
|
611
|
+
| **`withExifLocation`** | <code>boolean</code> | If true, the plugin will attempt to add GPS location data to the image's EXIF metadata. This may prompt the user for location permissions. | <code>false</code> | 7.6.0 |
|
|
559
612
|
|
|
560
613
|
|
|
561
614
|
#### CameraSampleOptions
|
package/android/build.gradle
CHANGED
|
@@ -49,7 +49,8 @@ dependencies {
|
|
|
49
49
|
implementation project(':capacitor-android')
|
|
50
50
|
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
51
51
|
implementation 'androidx.exifinterface:exifinterface:1.4.1'
|
|
52
|
-
|
|
52
|
+
implementation 'com.google.android.gms:play-services-location:21.3.0'
|
|
53
|
+
|
|
53
54
|
// CameraX dependencies
|
|
54
55
|
def camerax_version = "1.5.0-beta01"
|
|
55
56
|
implementation "androidx.camera:camera-core:${camerax_version}"
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
|
|
2
|
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
-
|
|
2
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
3
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
4
|
+
<uses-feature android:name="android.hardware.camera" />
|
|
5
|
+
<uses-feature android:name="android.hardware.camera.autofocus" />
|
|
4
6
|
</manifest>
|
|
5
7
|
|
|
@@ -3,15 +3,12 @@ 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
8
|
import android.util.DisplayMetrics;
|
|
9
9
|
import android.util.TypedValue;
|
|
10
|
-
import android.view.Display;
|
|
11
|
-
import androidx.annotation.NonNull;
|
|
12
10
|
import com.getcapacitor.JSArray;
|
|
13
11
|
import com.getcapacitor.JSObject;
|
|
14
|
-
import com.getcapacitor.Logger;
|
|
15
12
|
import com.getcapacitor.PermissionState;
|
|
16
13
|
import com.getcapacitor.Plugin;
|
|
17
14
|
import com.getcapacitor.PluginCall;
|
|
@@ -27,6 +24,11 @@ import java.util.Objects;
|
|
|
27
24
|
import android.util.Size;
|
|
28
25
|
import android.util.Log;
|
|
29
26
|
import com.ahm.capacitor.camera.preview.model.LensInfo;
|
|
27
|
+
import com.google.android.gms.location.FusedLocationProviderClient;
|
|
28
|
+
import com.google.android.gms.location.LocationServices;
|
|
29
|
+
import org.json.JSONObject;
|
|
30
|
+
import android.location.Location;
|
|
31
|
+
import com.getcapacitor.Logger;
|
|
30
32
|
|
|
31
33
|
@CapacitorPlugin(
|
|
32
34
|
name = "CameraPreview",
|
|
@@ -39,6 +41,10 @@ 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 = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION },
|
|
46
|
+
alias = CameraPreview.CAMERA_WITH_LOCATION_PERMISSION_ALIAS
|
|
47
|
+
)
|
|
42
48
|
}
|
|
43
49
|
)
|
|
44
50
|
public class CameraPreview
|
|
@@ -47,12 +53,15 @@ public class CameraPreview
|
|
|
47
53
|
|
|
48
54
|
static final String CAMERA_WITH_AUDIO_PERMISSION_ALIAS = "cameraWithAudio";
|
|
49
55
|
static final String CAMERA_ONLY_PERMISSION_ALIAS = "cameraOnly";
|
|
56
|
+
static final String CAMERA_WITH_LOCATION_PERMISSION_ALIAS = "cameraWithLocation";
|
|
50
57
|
|
|
51
58
|
private String captureCallbackId = "";
|
|
52
59
|
private String snapshotCallbackId = "";
|
|
53
60
|
private String cameraStartCallbackId = "";
|
|
54
61
|
private int previousOrientationRequest = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
|
55
62
|
private CameraXView cameraXView;
|
|
63
|
+
private FusedLocationProviderClient fusedLocationClient;
|
|
64
|
+
private Location lastLocation;
|
|
56
65
|
|
|
57
66
|
@PluginMethod
|
|
58
67
|
public void start(PluginCall call) {
|
|
@@ -83,15 +92,62 @@ public class CameraPreview
|
|
|
83
92
|
}
|
|
84
93
|
|
|
85
94
|
@PluginMethod
|
|
86
|
-
public void capture(PluginCall call) {
|
|
95
|
+
public void capture(final PluginCall call) {
|
|
87
96
|
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
88
97
|
call.reject("Camera is not running");
|
|
89
98
|
return;
|
|
90
99
|
}
|
|
100
|
+
|
|
101
|
+
final boolean withExifLocation = call.getBoolean("withExifLocation", false);
|
|
102
|
+
|
|
103
|
+
if (withExifLocation) {
|
|
104
|
+
if (getPermissionState(CAMERA_WITH_LOCATION_PERMISSION_ALIAS) != PermissionState.GRANTED) {
|
|
105
|
+
requestPermissionForAlias(CAMERA_WITH_LOCATION_PERMISSION_ALIAS, call, "captureWithLocationPermission");
|
|
106
|
+
} else {
|
|
107
|
+
getLocationAndCapture(call);
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
captureWithoutLocation(call);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@PermissionCallback
|
|
115
|
+
private void captureWithLocationPermission(PluginCall call) {
|
|
116
|
+
if (getPermissionState(CAMERA_WITH_LOCATION_PERMISSION_ALIAS) == PermissionState.GRANTED) {
|
|
117
|
+
getLocationAndCapture(call);
|
|
118
|
+
} else {
|
|
119
|
+
Logger.warn("Location permission denied. Capturing photo without location data.");
|
|
120
|
+
captureWithoutLocation(call);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private void getLocationAndCapture(PluginCall call) {
|
|
125
|
+
if (fusedLocationClient == null) {
|
|
126
|
+
fusedLocationClient = LocationServices.getFusedLocationProviderClient(getContext());
|
|
127
|
+
}
|
|
128
|
+
fusedLocationClient.getLastLocation().addOnSuccessListener(getActivity(), location -> {
|
|
129
|
+
lastLocation = location;
|
|
130
|
+
proceedWithCapture(call, lastLocation);
|
|
131
|
+
}).addOnFailureListener(e -> {
|
|
132
|
+
Logger.error("Failed to get location: " + e.getMessage());
|
|
133
|
+
proceedWithCapture(call, null);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private void captureWithoutLocation(PluginCall call) {
|
|
138
|
+
proceedWithCapture(call, null);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private void proceedWithCapture(PluginCall call, Location location) {
|
|
91
142
|
bridge.saveCall(call);
|
|
92
143
|
captureCallbackId = call.getCallbackId();
|
|
144
|
+
|
|
93
145
|
Integer quality = Objects.requireNonNull(call.getInt("quality", 85));
|
|
94
|
-
|
|
146
|
+
final boolean saveToGallery = call.getBoolean("saveToGallery", false);
|
|
147
|
+
Integer width = call.getInt("width");
|
|
148
|
+
Integer height = call.getInt("height");
|
|
149
|
+
|
|
150
|
+
cameraXView.capturePhoto(quality, saveToGallery, width, height, location);
|
|
95
151
|
}
|
|
96
152
|
|
|
97
153
|
@PluginMethod
|
|
@@ -326,13 +382,13 @@ public class CameraPreview
|
|
|
326
382
|
final int width = call.getInt("width", 0);
|
|
327
383
|
final int height = call.getInt("height", 0);
|
|
328
384
|
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);
|
|
385
|
+
final boolean toBack = Boolean.TRUE.equals(call.getBoolean("toBack", true));
|
|
386
|
+
final boolean storeToFile = Boolean.TRUE.equals(call.getBoolean("storeToFile", false));
|
|
387
|
+
final boolean enableOpacity = Boolean.TRUE.equals(call.getBoolean("enableOpacity", false));
|
|
388
|
+
final boolean enableZoom = Boolean.TRUE.equals(call.getBoolean("enableZoom", false));
|
|
389
|
+
final boolean disableExifHeaderStripping = Boolean.TRUE.equals(call.getBoolean("disableExifHeaderStripping", false));
|
|
390
|
+
final boolean lockOrientation = Boolean.TRUE.equals(call.getBoolean("lockAndroidOrientation", false));
|
|
391
|
+
final boolean disableAudio = Boolean.TRUE.equals(call.getBoolean("disableAudio", true));
|
|
336
392
|
|
|
337
393
|
float targetZoom = 1.0f;
|
|
338
394
|
// Check if the selected device is a physical ultra-wide
|
|
@@ -367,8 +423,8 @@ public class CameraPreview
|
|
|
367
423
|
}
|
|
368
424
|
int computedX = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, x, metrics);
|
|
369
425
|
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) :
|
|
426
|
+
int computedWidth = width != 0 ? (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, width, metrics) : getBridge().getWebView().getWidth();
|
|
427
|
+
int computedHeight = height != 0 ? (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, height, metrics) : getBridge().getWebView().getHeight();
|
|
372
428
|
computedHeight -= (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, paddingBottom, metrics);
|
|
373
429
|
|
|
374
430
|
CameraSessionConfiguration config = new CameraSessionConfiguration(finalDeviceId, position, computedX, computedY, computedWidth, computedHeight, paddingBottom, toBack, storeToFile, enableOpacity, enableZoom, disableExifHeaderStripping, disableAudio, 1.0f);
|
|
@@ -382,15 +438,28 @@ public class CameraPreview
|
|
|
382
438
|
}
|
|
383
439
|
|
|
384
440
|
@Override
|
|
385
|
-
public void onPictureTaken(String
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
441
|
+
public void onPictureTaken(String base64, JSONObject exif) {
|
|
442
|
+
PluginCall pluginCall = bridge.getSavedCall(captureCallbackId);
|
|
443
|
+
if (pluginCall == null) {
|
|
444
|
+
Log.e("CameraPreview", "onPictureTaken: captureCallbackId is null");
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
JSObject result = new JSObject();
|
|
448
|
+
result.put("value", base64);
|
|
449
|
+
result.put("exif", exif);
|
|
450
|
+
pluginCall.resolve(result);
|
|
451
|
+
bridge.releaseCall(pluginCall);
|
|
389
452
|
}
|
|
390
453
|
|
|
391
454
|
@Override
|
|
392
455
|
public void onPictureTakenError(String message) {
|
|
393
|
-
bridge.getSavedCall(captureCallbackId)
|
|
456
|
+
PluginCall pluginCall = bridge.getSavedCall(captureCallbackId);
|
|
457
|
+
if (pluginCall == null) {
|
|
458
|
+
Log.e("CameraPreview", "onPictureTakenError: captureCallbackId is null");
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
pluginCall.reject(message);
|
|
462
|
+
bridge.releaseCall(pluginCall);
|
|
394
463
|
}
|
|
395
464
|
|
|
396
465
|
@Override
|
|
@@ -422,4 +491,5 @@ public class CameraPreview
|
|
|
422
491
|
bridge.releaseCall(pluginCall);
|
|
423
492
|
}
|
|
424
493
|
}
|
|
494
|
+
|
|
425
495
|
}
|