@capgo/camera-preview 8.2.1 → 8.3.0
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 +31 -29
- package/android/build.gradle +4 -0
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraPreview.java +87 -20
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java +665 -188
- package/android/src/main/java/app/capgo/capacitor/camera/preview/model/CameraSessionConfiguration.java +13 -0
- package/dist/docs.json +17 -1
- package/dist/esm/definitions.d.ts +9 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +1 -1
- package/dist/esm/web.js +120 -92
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +119 -92
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +119 -92
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoCameraPreviewPlugin/CameraController.swift +14 -12
- package/ios/Sources/CapgoCameraPreviewPlugin/Plugin.swift +16 -14
- package/package.json +9 -8
package/README.md
CHANGED
|
@@ -809,7 +809,8 @@ Switches the active camera to the one with the specified `deviceId`.
|
|
|
809
809
|
getDeviceId() => Promise<{ deviceId: string; }>
|
|
810
810
|
```
|
|
811
811
|
|
|
812
|
-
Gets the ID of the
|
|
812
|
+
Gets the ID of the camera device that is currently bound.
|
|
813
|
+
On Android, if a physical-lens request falls back to a logical camera, this returns the bound logical camera ID.
|
|
813
814
|
|
|
814
815
|
**Returns:** <code>Promise<{ deviceId: string; }></code>
|
|
815
816
|
|
|
@@ -1071,34 +1072,35 @@ Get the native Capacitor plugin version
|
|
|
1071
1072
|
|
|
1072
1073
|
Defines the configuration options for starting the camera preview.
|
|
1073
1074
|
|
|
1074
|
-
| Prop
|
|
1075
|
-
|
|
|
1076
|
-
| **`parent`**
|
|
1077
|
-
| **`className`**
|
|
1078
|
-
| **`width`**
|
|
1079
|
-
| **`height`**
|
|
1080
|
-
| **`x`**
|
|
1081
|
-
| **`y`**
|
|
1082
|
-
| **`aspectRatio`**
|
|
1083
|
-
| **`aspectMode`**
|
|
1084
|
-
| **`gridMode`**
|
|
1085
|
-
| **`includeSafeAreaInsets`**
|
|
1086
|
-
| **`toBack`**
|
|
1087
|
-
| **`paddingBottom`**
|
|
1088
|
-
| **`rotateWhenOrientationChanged`**
|
|
1089
|
-
| **`position`**
|
|
1090
|
-
| **`storeToFile`**
|
|
1091
|
-
| **`disableExifHeaderStripping`**
|
|
1092
|
-
| **`disableAudio`**
|
|
1093
|
-
| **`lockAndroidOrientation`**
|
|
1094
|
-
| **`enableOpacity`**
|
|
1095
|
-
| **`disableFocusIndicator`**
|
|
1096
|
-
| **`deviceId`**
|
|
1097
|
-
| **`
|
|
1098
|
-
| **`
|
|
1099
|
-
| **`
|
|
1100
|
-
| **`
|
|
1101
|
-
| **`
|
|
1075
|
+
| Prop | Type | Description | Default | Since |
|
|
1076
|
+
| ----------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | ------ |
|
|
1077
|
+
| **`parent`** | <code>string</code> | The parent element to attach the video preview to. | | |
|
|
1078
|
+
| **`className`** | <code>string</code> | A CSS class name to add to the preview element. | | |
|
|
1079
|
+
| **`width`** | <code>number</code> | The width of the preview in pixels. Defaults to the screen width. | | |
|
|
1080
|
+
| **`height`** | <code>number</code> | The height of the preview in pixels. Defaults to the screen height. | | |
|
|
1081
|
+
| **`x`** | <code>number</code> | The horizontal origin of the preview, in pixels. | | |
|
|
1082
|
+
| **`y`** | <code>number</code> | The vertical origin of the preview, in pixels. | | |
|
|
1083
|
+
| **`aspectRatio`** | <code>'4:3' \| '16:9'</code> | The aspect ratio of the camera preview, '4:3' or '16:9' or 'fill'. Cannot be set if width or height is provided, otherwise the call will be rejected. Use setPreviewSize to adjust size after starting. | | 2.0.0 |
|
|
1084
|
+
| **`aspectMode`** | <code>'cover' \| 'contain'</code> | Controls how the camera preview fills the available space. - 'contain': Fits the entire preview within the space, may show letterboxing (default). - 'cover': Fills the entire space, may crop edges of the preview. | <code>"contain"</code> | |
|
|
1085
|
+
| **`gridMode`** | <code><a href="#gridmode">GridMode</a></code> | The grid overlay to display on the camera preview. | <code>"none"</code> | 2.1.0 |
|
|
1086
|
+
| **`includeSafeAreaInsets`** | <code>boolean</code> | Adjusts the y-position to account for safe areas (e.g., notches). | <code>false</code> | |
|
|
1087
|
+
| **`toBack`** | <code>boolean</code> | If true, places the preview behind the webview. | <code>true</code> | |
|
|
1088
|
+
| **`paddingBottom`** | <code>number</code> | Bottom padding for the preview, in pixels. | | |
|
|
1089
|
+
| **`rotateWhenOrientationChanged`** | <code>boolean</code> | Whether to rotate the preview when the device orientation changes. | <code>true</code> | |
|
|
1090
|
+
| **`position`** | <code>string</code> | The camera to use. | <code>"rear"</code> | |
|
|
1091
|
+
| **`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> | |
|
|
1092
|
+
| **`disableExifHeaderStripping`** | <code>boolean</code> | If true, prevents the plugin from rotating the image based on EXIF data. | <code>false</code> | |
|
|
1093
|
+
| **`disableAudio`** | <code>boolean</code> | If true, disables the audio stream, preventing audio permission requests. | <code>true</code> | |
|
|
1094
|
+
| **`lockAndroidOrientation`** | <code>boolean</code> | If true, locks the device orientation while the camera is active. | <code>false</code> | |
|
|
1095
|
+
| **`enableOpacity`** | <code>boolean</code> | If true, allows the camera preview's opacity to be changed. | <code>false</code> | |
|
|
1096
|
+
| **`disableFocusIndicator`** | <code>boolean</code> | If true, disables the visual focus indicator when tapping to focus. | <code>false</code> | |
|
|
1097
|
+
| **`deviceId`** | <code>string</code> | The `deviceId` of the camera to use. If provided, `position` is ignored. | | |
|
|
1098
|
+
| **`enablePhysicalDeviceSelection`** | <code>boolean</code> | On Android, attempts to bind a physical camera directly when `deviceId` refers to a physical lens. Disabled by default because OEM support is inconsistent; when false, Android keeps the current logical-camera fallback behavior. | <code>false</code> | |
|
|
1099
|
+
| **`initialZoomLevel`** | <code>number</code> | The initial zoom level when starting the camera preview. If the requested zoom level is not available, the native plugin will reject. | <code>1.0</code> | 2.2.0 |
|
|
1100
|
+
| **`positioning`** | <code><a href="#camerapositioning">CameraPositioning</a></code> | The vertical positioning of the camera preview. | <code>"center"</code> | 2.3.0 |
|
|
1101
|
+
| **`enableVideoMode`** | <code>boolean</code> | If true, enables video capture capabilities when the camera starts. | <code>false</code> | 7.11.0 |
|
|
1102
|
+
| **`force`** | <code>boolean</code> | If true, forces the camera to start/restart even if it's already running or busy. This will kill the current camera session and start a new one, ignoring all state checks. | <code>false</code> | |
|
|
1103
|
+
| **`videoQuality`** | <code>'low' \| 'medium' \| 'high'</code> | Sets the quality of video for recording. Options: 'low', 'medium', 'high' | <code>"high"</code> | |
|
|
1102
1104
|
|
|
1103
1105
|
|
|
1104
1106
|
#### ExifData
|
package/android/build.gradle
CHANGED
|
@@ -20,6 +20,10 @@ apply plugin: 'com.android.library'
|
|
|
20
20
|
android {
|
|
21
21
|
namespace = "app.capgo.capacitor.camera.preview.capacitorcamerapreview"
|
|
22
22
|
compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
|
|
23
|
+
compileOptions {
|
|
24
|
+
sourceCompatibility JavaVersion.VERSION_21
|
|
25
|
+
targetCompatibility JavaVersion.VERSION_21
|
|
26
|
+
}
|
|
23
27
|
defaultConfig {
|
|
24
28
|
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
|
|
25
29
|
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
|
|
@@ -15,6 +15,7 @@ import android.graphics.drawable.ColorDrawable;
|
|
|
15
15
|
import android.graphics.drawable.Drawable;
|
|
16
16
|
import android.location.Location;
|
|
17
17
|
import android.net.Uri;
|
|
18
|
+
import android.os.Build;
|
|
18
19
|
import android.provider.Settings;
|
|
19
20
|
import android.util.DisplayMetrics;
|
|
20
21
|
import android.util.Log;
|
|
@@ -46,6 +47,7 @@ import com.getcapacitor.annotation.PermissionCallback;
|
|
|
46
47
|
import com.google.android.gms.location.FusedLocationProviderClient;
|
|
47
48
|
import com.google.android.gms.location.LocationServices;
|
|
48
49
|
import java.util.List;
|
|
50
|
+
import java.util.Locale;
|
|
49
51
|
import java.util.Objects;
|
|
50
52
|
import org.json.JSONObject;
|
|
51
53
|
|
|
@@ -132,6 +134,8 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
132
134
|
private String lastOrientationStr = "unknown";
|
|
133
135
|
private boolean lastDisableAudio = true;
|
|
134
136
|
private Drawable originalWindowBackground;
|
|
137
|
+
private Float originalWebViewAlpha;
|
|
138
|
+
private Drawable originalWebViewParentBackground;
|
|
135
139
|
private boolean isCameraPermissionDialogShowing = false;
|
|
136
140
|
|
|
137
141
|
@PluginMethod
|
|
@@ -428,6 +432,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
428
432
|
} catch (Exception ignored) {}
|
|
429
433
|
originalWindowBackground = null;
|
|
430
434
|
}
|
|
435
|
+
restoreWebViewVisualState();
|
|
431
436
|
call.resolve();
|
|
432
437
|
});
|
|
433
438
|
}
|
|
@@ -905,6 +910,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
905
910
|
//noinspection DataFlowIssue
|
|
906
911
|
final boolean disableFocusIndicator = call.getBoolean("disableFocusIndicator", false);
|
|
907
912
|
final boolean enableVideoMode = Boolean.TRUE.equals(call.getBoolean("enableVideoMode", false));
|
|
913
|
+
final boolean enablePhysicalDeviceSelection = Boolean.TRUE.equals(call.getBoolean("enablePhysicalDeviceSelection", false));
|
|
908
914
|
final String videoQuality = call.getString("videoQuality", "high");
|
|
909
915
|
|
|
910
916
|
// Check for conflict between aspectRatio and size
|
|
@@ -914,8 +920,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
914
920
|
}
|
|
915
921
|
|
|
916
922
|
float targetZoom = initialZoomLevel;
|
|
917
|
-
|
|
918
|
-
if (originalDeviceId != null) {
|
|
923
|
+
if (!enablePhysicalDeviceSelection && originalDeviceId != null) {
|
|
919
924
|
List<CameraDevice> devices = CameraXView.getAvailableDevicesStatic(getContext());
|
|
920
925
|
for (CameraDevice device : devices) {
|
|
921
926
|
if (originalDeviceId.equals(device.getDeviceId()) && !device.isLogical()) {
|
|
@@ -923,13 +928,13 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
923
928
|
if ("ultraWide".equals(lens.getDeviceType())) {
|
|
924
929
|
Log.d("CameraPreview", "Ultra-wide lens selected. Targeting 0.5x zoom on logical camera.");
|
|
925
930
|
targetZoom = 0.5f;
|
|
926
|
-
//
|
|
931
|
+
// Preserve existing default behavior unless the new Android flag is explicitly enabled.
|
|
927
932
|
deviceId = null;
|
|
928
933
|
break;
|
|
929
934
|
}
|
|
930
935
|
}
|
|
931
936
|
}
|
|
932
|
-
if (deviceId == null) break;
|
|
937
|
+
if (deviceId == null) break;
|
|
933
938
|
}
|
|
934
939
|
}
|
|
935
940
|
|
|
@@ -1211,6 +1216,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1211
1216
|
);
|
|
1212
1217
|
config.setTargetZoom(finalTargetZoom);
|
|
1213
1218
|
config.setCentered(isCentered);
|
|
1219
|
+
config.setEnablePhysicalDeviceSelection(enablePhysicalDeviceSelection);
|
|
1214
1220
|
|
|
1215
1221
|
bridge.saveCall(call);
|
|
1216
1222
|
cameraStartCallbackId = call.getCallbackId();
|
|
@@ -1478,6 +1484,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1478
1484
|
if (cameraXView == source) {
|
|
1479
1485
|
cameraXView = null;
|
|
1480
1486
|
}
|
|
1487
|
+
restoreWebViewVisualState();
|
|
1481
1488
|
|
|
1482
1489
|
PluginCall queuedCall = null;
|
|
1483
1490
|
synchronized (pendingStartLock) {
|
|
@@ -1518,6 +1525,80 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1518
1525
|
return false;
|
|
1519
1526
|
}
|
|
1520
1527
|
|
|
1528
|
+
private boolean isMiuiDevice() {
|
|
1529
|
+
String manufacturer = Build.MANUFACTURER != null ? Build.MANUFACTURER.toLowerCase(Locale.US) : "";
|
|
1530
|
+
String brand = Build.BRAND != null ? Build.BRAND.toLowerCase(Locale.US) : "";
|
|
1531
|
+
return manufacturer.contains("xiaomi") || brand.contains("xiaomi") || brand.contains("redmi") || brand.contains("poco");
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
private void applyTransparentBackgroundsForToBack() {
|
|
1535
|
+
if (!isToBackMode()) {
|
|
1536
|
+
return;
|
|
1537
|
+
}
|
|
1538
|
+
Activity activity = getActivity();
|
|
1539
|
+
WebView webView = getBridge().getWebView();
|
|
1540
|
+
if (activity == null || webView == null) {
|
|
1541
|
+
return;
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
if (originalWebViewAlpha == null) {
|
|
1545
|
+
originalWebViewAlpha = webView.getAlpha();
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
final ViewGroup webViewParent = (ViewGroup) webView.getParent();
|
|
1549
|
+
if (webViewParent != null && originalWebViewParentBackground == null) {
|
|
1550
|
+
originalWebViewParentBackground = webViewParent.getBackground();
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
Runnable apply = () -> {
|
|
1554
|
+
try {
|
|
1555
|
+
activity.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
|
1556
|
+
if (webViewParent != null) {
|
|
1557
|
+
webViewParent.setBackgroundColor(Color.TRANSPARENT);
|
|
1558
|
+
}
|
|
1559
|
+
// Keep a tiny alpha on MIUI/Xiaomi devices to avoid compositor bugs that treat
|
|
1560
|
+
// fully transparent layers as black.
|
|
1561
|
+
webView.setBackgroundColor(Color.argb(1, 255, 255, 255));
|
|
1562
|
+
webView.setAlpha(isMiuiDevice() ? 0.99f : (originalWebViewAlpha != null ? originalWebViewAlpha : 1f));
|
|
1563
|
+
} catch (Exception e) {
|
|
1564
|
+
Log.w(TAG, "Failed to set backgrounds to transparent", e);
|
|
1565
|
+
}
|
|
1566
|
+
};
|
|
1567
|
+
|
|
1568
|
+
activity.runOnUiThread(() -> {
|
|
1569
|
+
apply.run();
|
|
1570
|
+
if (isMiuiDevice()) {
|
|
1571
|
+
webView.postDelayed(apply, 50);
|
|
1572
|
+
webView.postDelayed(apply, 250);
|
|
1573
|
+
}
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
private void restoreWebViewVisualState() {
|
|
1578
|
+
Activity activity = getActivity();
|
|
1579
|
+
WebView webView = getBridge().getWebView();
|
|
1580
|
+
final Float alphaToRestore = originalWebViewAlpha;
|
|
1581
|
+
final Drawable parentBackground = originalWebViewParentBackground;
|
|
1582
|
+
originalWebViewAlpha = null;
|
|
1583
|
+
originalWebViewParentBackground = null;
|
|
1584
|
+
|
|
1585
|
+
if (activity == null || webView == null) {
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
final ViewGroup webViewParent = (ViewGroup) webView.getParent();
|
|
1590
|
+
activity.runOnUiThread(() -> {
|
|
1591
|
+
try {
|
|
1592
|
+
if (alphaToRestore != null) {
|
|
1593
|
+
webView.setAlpha(alphaToRestore);
|
|
1594
|
+
}
|
|
1595
|
+
if (webViewParent != null && parentBackground != null) {
|
|
1596
|
+
webViewParent.setBackground(parentBackground);
|
|
1597
|
+
}
|
|
1598
|
+
} catch (Exception ignored) {}
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1521
1602
|
@Override
|
|
1522
1603
|
public void onCameraStarted(int width, int height, int x, int y) {
|
|
1523
1604
|
// Always transition window and WebView backgrounds to transparent when the camera starts,
|
|
@@ -1528,22 +1609,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1528
1609
|
// window and WebView stay black after every background/foreground transition.
|
|
1529
1610
|
// Both backgrounds are set together in the same UI thread operation to avoid race
|
|
1530
1611
|
// conditions and compositor layering issues.
|
|
1531
|
-
|
|
1532
|
-
getBridge()
|
|
1533
|
-
.getActivity()
|
|
1534
|
-
.runOnUiThread(() -> {
|
|
1535
|
-
try {
|
|
1536
|
-
// Set window background to transparent
|
|
1537
|
-
getBridge().getActivity().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
|
1538
|
-
// Set webview background to almost-transparent for MIUI compatibility
|
|
1539
|
-
// Use alpha=1 instead of 0 to work around MIUI/Xiaomi rendering issues
|
|
1540
|
-
// where Color.TRANSPARENT (alpha=0) is not rendered correctly
|
|
1541
|
-
getBridge().getWebView().setBackgroundColor(Color.argb(1, 255, 255, 255));
|
|
1542
|
-
} catch (Exception e) {
|
|
1543
|
-
Log.w(TAG, "Failed to set backgrounds to transparent", e);
|
|
1544
|
-
}
|
|
1545
|
-
});
|
|
1546
|
-
}
|
|
1612
|
+
applyTransparentBackgroundsForToBack();
|
|
1547
1613
|
|
|
1548
1614
|
PluginCall call = bridge.getSavedCall(cameraStartCallbackId);
|
|
1549
1615
|
if (call != null) {
|
|
@@ -1734,6 +1800,7 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
|
|
|
1734
1800
|
});
|
|
1735
1801
|
}
|
|
1736
1802
|
}
|
|
1803
|
+
restoreWebViewVisualState();
|
|
1737
1804
|
}
|
|
1738
1805
|
|
|
1739
1806
|
@PluginMethod
|