@capgo/camera-preview 7.3.12 → 7.4.0-beta.10
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/CapgoCameraPreview.podspec +16 -13
- package/README.md +467 -73
- package/android/.gradle/8.14.2/checksums/checksums.lock +0 -0
- package/android/.gradle/8.14.2/checksums/md5-checksums.bin +0 -0
- package/android/.gradle/8.14.2/checksums/sha1-checksums.bin +0 -0
- package/android/.gradle/8.14.2/executionHistory/executionHistory.bin +0 -0
- package/android/.gradle/8.14.2/executionHistory/executionHistory.lock +0 -0
- package/android/.gradle/8.14.2/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.14.2/fileHashes/fileHashes.bin +0 -0
- package/android/.gradle/8.14.2/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.14.2/fileHashes/resourceHashesCache.bin +0 -0
- package/android/.gradle/8.14.2/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
- package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
- package/android/.gradle/file-system.probe +0 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/build.gradle +11 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +1 -1
- package/android/src/main/AndroidManifest.xml +5 -3
- package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java +472 -541
- package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraXView.java +1648 -0
- package/android/src/main/java/com/ahm/capacitor/camera/preview/GridOverlayView.java +82 -0
- package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraDevice.java +54 -0
- package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraLens.java +70 -0
- package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraSessionConfiguration.java +79 -0
- package/android/src/main/java/com/ahm/capacitor/camera/preview/model/LensInfo.java +34 -0
- package/android/src/main/java/com/ahm/capacitor/camera/preview/model/ZoomFactors.java +34 -0
- package/dist/docs.json +934 -154
- package/dist/esm/definitions.d.ts +445 -83
- package/dist/esm/definitions.js +10 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +73 -3
- package/dist/esm/web.js +492 -68
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +498 -68
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +498 -68
- package/dist/plugin.js.map +1 -1
- package/ios/{Plugin → Sources/CapgoCameraPreview}/CameraController.swift +601 -59
- package/ios/Sources/CapgoCameraPreview/GridOverlayView.swift +65 -0
- package/ios/Sources/CapgoCameraPreview/Plugin.swift +1369 -0
- package/ios/Tests/CameraPreviewPluginTests/CameraPreviewPluginTests.swift +15 -0
- package/package.json +1 -1
- package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraActivity.java +0 -1279
- package/android/src/main/java/com/ahm/capacitor/camera/preview/CustomSurfaceView.java +0 -29
- package/android/src/main/java/com/ahm/capacitor/camera/preview/CustomTextureView.java +0 -39
- package/android/src/main/java/com/ahm/capacitor/camera/preview/Preview.java +0 -461
- package/android/src/main/java/com/ahm/capacitor/camera/preview/TapGestureDetector.java +0 -24
- package/ios/Plugin/Info.plist +0 -24
- package/ios/Plugin/Plugin.h +0 -10
- package/ios/Plugin/Plugin.m +0 -18
- package/ios/Plugin/Plugin.swift +0 -511
- package/ios/Plugin.xcodeproj/project.pbxproj +0 -593
- package/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/ios/Plugin.xcworkspace/contents.xcworkspacedata +0 -10
- package/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/ios/PluginTests/Info.plist +0 -22
- package/ios/PluginTests/PluginTests.swift +0 -83
- package/ios/Podfile +0 -13
- package/ios/Podfile.lock +0 -23
|
@@ -3,30 +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.
|
|
7
|
-
import android.app.FragmentTransaction;
|
|
8
|
-
import android.content.Context;
|
|
6
|
+
import android.Manifest;
|
|
9
7
|
import android.content.pm.ActivityInfo;
|
|
10
|
-
import android.graphics.Color;
|
|
11
|
-
import android.graphics.ImageFormat;
|
|
12
|
-
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
|
-
import android.util.TypedValue;
|
|
21
|
-
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
|
-
import androidx.annotation.NonNull;
|
|
27
9
|
import com.getcapacitor.JSArray;
|
|
28
10
|
import com.getcapacitor.JSObject;
|
|
29
|
-
import com.getcapacitor.Logger;
|
|
30
11
|
import com.getcapacitor.PermissionState;
|
|
31
12
|
import com.getcapacitor.Plugin;
|
|
32
13
|
import com.getcapacitor.PluginCall;
|
|
@@ -34,11 +15,22 @@ import com.getcapacitor.PluginMethod;
|
|
|
34
15
|
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
35
16
|
import com.getcapacitor.annotation.Permission;
|
|
36
17
|
import com.getcapacitor.annotation.PermissionCallback;
|
|
37
|
-
import
|
|
38
|
-
import
|
|
18
|
+
import com.ahm.capacitor.camera.preview.model.CameraDevice;
|
|
19
|
+
import com.ahm.capacitor.camera.preview.model.CameraSessionConfiguration;
|
|
20
|
+
import com.ahm.capacitor.camera.preview.model.ZoomFactors;
|
|
39
21
|
import java.util.List;
|
|
40
22
|
import java.util.Objects;
|
|
41
|
-
import
|
|
23
|
+
import android.util.Size;
|
|
24
|
+
import android.util.Log;
|
|
25
|
+
import com.ahm.capacitor.camera.preview.model.LensInfo;
|
|
26
|
+
import com.google.android.gms.location.FusedLocationProviderClient;
|
|
27
|
+
import com.google.android.gms.location.LocationServices;
|
|
28
|
+
import org.json.JSONObject;
|
|
29
|
+
import android.location.Location;
|
|
30
|
+
import android.view.ViewGroup;
|
|
31
|
+
|
|
32
|
+
import com.getcapacitor.Logger;
|
|
33
|
+
|
|
42
34
|
|
|
43
35
|
@CapacitorPlugin(
|
|
44
36
|
name = "CameraPreview",
|
|
@@ -51,33 +43,31 @@ import org.json.JSONArray;
|
|
|
51
43
|
strings = { CAMERA },
|
|
52
44
|
alias = CameraPreview.CAMERA_ONLY_PERMISSION_ALIAS
|
|
53
45
|
),
|
|
46
|
+
@Permission(
|
|
47
|
+
strings = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION },
|
|
48
|
+
alias = CameraPreview.CAMERA_WITH_LOCATION_PERMISSION_ALIAS
|
|
49
|
+
)
|
|
54
50
|
}
|
|
55
51
|
)
|
|
56
52
|
public class CameraPreview
|
|
57
53
|
extends Plugin
|
|
58
|
-
implements
|
|
54
|
+
implements CameraXView.CameraXViewListener {
|
|
59
55
|
|
|
60
56
|
static final String CAMERA_WITH_AUDIO_PERMISSION_ALIAS = "cameraWithAudio";
|
|
61
57
|
static final String CAMERA_ONLY_PERMISSION_ALIAS = "cameraOnly";
|
|
62
|
-
|
|
63
|
-
private static String VIDEO_FILE_PATH = "";
|
|
64
|
-
private static final String VIDEO_FILE_EXTENSION = ".mp4";
|
|
58
|
+
static final String CAMERA_WITH_LOCATION_PERMISSION_ALIAS = "cameraWithLocation";
|
|
65
59
|
|
|
66
60
|
private String captureCallbackId = "";
|
|
67
61
|
private String snapshotCallbackId = "";
|
|
68
|
-
private String recordCallbackId = "";
|
|
69
62
|
private String cameraStartCallbackId = "";
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
private
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
private CameraActivity fragment;
|
|
76
|
-
private final int containerViewId = 20;
|
|
63
|
+
private int previousOrientationRequest = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
|
64
|
+
private CameraXView cameraXView;
|
|
65
|
+
private FusedLocationProviderClient fusedLocationClient;
|
|
66
|
+
private Location lastLocation;
|
|
77
67
|
|
|
78
68
|
@PluginMethod
|
|
79
69
|
public void start(PluginCall call) {
|
|
80
|
-
boolean disableAudio = call.getBoolean("disableAudio",
|
|
70
|
+
boolean disableAudio = Boolean.TRUE.equals(call.getBoolean("disableAudio", true));
|
|
81
71
|
String permissionAlias = disableAudio
|
|
82
72
|
? CAMERA_ONLY_PERMISSION_ALIAS
|
|
83
73
|
: CAMERA_WITH_AUDIO_PERMISSION_ALIAS;
|
|
@@ -95,118 +85,83 @@ public class CameraPreview
|
|
|
95
85
|
|
|
96
86
|
@PluginMethod
|
|
97
87
|
public void flip(PluginCall call) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
} catch (Exception e) {
|
|
102
|
-
Logger.debug(getLogTag(), "Camera flip exception: " + e);
|
|
103
|
-
call.reject("failed to flip camera");
|
|
88
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
89
|
+
call.reject("Camera is not running");
|
|
90
|
+
return;
|
|
104
91
|
}
|
|
92
|
+
cameraXView.flipCamera();
|
|
93
|
+
call.resolve();
|
|
105
94
|
}
|
|
106
95
|
|
|
107
96
|
@PluginMethod
|
|
108
|
-
public void
|
|
109
|
-
if (!
|
|
97
|
+
public void capture(final PluginCall call) {
|
|
98
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
110
99
|
call.reject("Camera is not running");
|
|
111
100
|
return;
|
|
112
101
|
}
|
|
113
102
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
103
|
+
final boolean withExifLocation = call.getBoolean("withExifLocation", false);
|
|
104
|
+
|
|
105
|
+
if (withExifLocation) {
|
|
106
|
+
if (getPermissionState(CAMERA_WITH_LOCATION_PERMISSION_ALIAS) != PermissionState.GRANTED) {
|
|
107
|
+
requestPermissionForAlias(CAMERA_WITH_LOCATION_PERMISSION_ALIAS, call, "captureWithLocationPermission");
|
|
108
|
+
} else {
|
|
109
|
+
getLocationAndCapture(call);
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
captureWithoutLocation(call);
|
|
113
|
+
}
|
|
117
114
|
}
|
|
118
115
|
|
|
119
|
-
@
|
|
120
|
-
|
|
121
|
-
if (
|
|
122
|
-
call
|
|
123
|
-
|
|
116
|
+
@PermissionCallback
|
|
117
|
+
private void captureWithLocationPermission(PluginCall call) {
|
|
118
|
+
if (getPermissionState(CAMERA_WITH_LOCATION_PERMISSION_ALIAS) == PermissionState.GRANTED) {
|
|
119
|
+
getLocationAndCapture(call);
|
|
120
|
+
} else {
|
|
121
|
+
Logger.warn("Location permission denied. Capturing photo without location data.");
|
|
122
|
+
captureWithoutLocation(call);
|
|
124
123
|
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private void getLocationAndCapture(PluginCall call) {
|
|
127
|
+
if (fusedLocationClient == null) {
|
|
128
|
+
fusedLocationClient = LocationServices.getFusedLocationProviderClient(getContext());
|
|
129
|
+
}
|
|
130
|
+
fusedLocationClient.getLastLocation().addOnSuccessListener(getActivity(), location -> {
|
|
131
|
+
lastLocation = location;
|
|
132
|
+
proceedWithCapture(call, lastLocation);
|
|
133
|
+
}).addOnFailureListener(e -> {
|
|
134
|
+
Logger.error("Failed to get location: " + e.getMessage());
|
|
135
|
+
proceedWithCapture(call, null);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private void captureWithoutLocation(PluginCall call) {
|
|
140
|
+
proceedWithCapture(call, null);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private void proceedWithCapture(PluginCall call, Location location) {
|
|
125
144
|
bridge.saveCall(call);
|
|
126
145
|
captureCallbackId = call.getCallbackId();
|
|
127
146
|
|
|
128
147
|
Integer quality = Objects.requireNonNull(call.getInt("quality", 85));
|
|
129
|
-
|
|
130
|
-
Integer width =
|
|
131
|
-
Integer height =
|
|
132
|
-
|
|
148
|
+
final boolean saveToGallery = call.getBoolean("saveToGallery", false);
|
|
149
|
+
Integer width = call.getInt("width");
|
|
150
|
+
Integer height = call.getInt("height");
|
|
151
|
+
|
|
152
|
+
cameraXView.capturePhoto(quality, saveToGallery, width, height, location);
|
|
133
153
|
}
|
|
134
154
|
|
|
135
155
|
@PluginMethod
|
|
136
156
|
public void captureSample(PluginCall call) {
|
|
137
|
-
if (!
|
|
157
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
138
158
|
call.reject("Camera is not running");
|
|
139
159
|
return;
|
|
140
160
|
}
|
|
141
161
|
bridge.saveCall(call);
|
|
142
162
|
snapshotCallbackId = call.getCallbackId();
|
|
143
|
-
|
|
144
163
|
Integer quality = Objects.requireNonNull(call.getInt("quality", 85));
|
|
145
|
-
|
|
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
|
-
}
|
|
164
|
+
cameraXView.captureSample(quality);
|
|
210
165
|
}
|
|
211
166
|
|
|
212
167
|
@PluginMethod
|
|
@@ -214,190 +169,204 @@ public class CameraPreview
|
|
|
214
169
|
bridge
|
|
215
170
|
.getActivity()
|
|
216
171
|
.runOnUiThread(
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
}
|
|
172
|
+
() -> {
|
|
173
|
+
getBridge()
|
|
174
|
+
.getActivity()
|
|
175
|
+
.setRequestedOrientation(previousOrientationRequest);
|
|
176
|
+
|
|
177
|
+
if (cameraXView != null && cameraXView.isRunning()) {
|
|
178
|
+
cameraXView.stopSession();
|
|
179
|
+
cameraXView = null;
|
|
246
180
|
}
|
|
181
|
+
call.resolve();
|
|
247
182
|
}
|
|
248
183
|
);
|
|
249
184
|
}
|
|
250
185
|
|
|
251
186
|
@PluginMethod
|
|
252
187
|
public void getSupportedFlashModes(PluginCall call) {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
188
|
+
List<String> supportedFlashModes = cameraXView.getSupportedFlashModes();
|
|
189
|
+
JSArray jsonFlashModes = new JSArray();
|
|
190
|
+
for (String mode : supportedFlashModes) {
|
|
191
|
+
jsonFlashModes.put(mode);
|
|
256
192
|
}
|
|
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
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
193
|
JSObject jsObject = new JSObject();
|
|
271
194
|
jsObject.put("result", jsonFlashModes);
|
|
272
195
|
call.resolve(jsObject);
|
|
273
196
|
}
|
|
274
197
|
|
|
275
198
|
@PluginMethod
|
|
276
|
-
public void
|
|
277
|
-
|
|
278
|
-
|
|
199
|
+
public void setFlashMode(PluginCall call) {
|
|
200
|
+
String flashMode = call.getString("flashMode");
|
|
201
|
+
if (flashMode == null || flashMode.isEmpty()) {
|
|
202
|
+
call.reject("flashMode required parameter is missing");
|
|
279
203
|
return;
|
|
280
204
|
}
|
|
205
|
+
cameraXView.setFlashMode(flashMode);
|
|
206
|
+
call.resolve();
|
|
207
|
+
}
|
|
281
208
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
209
|
+
@PluginMethod
|
|
210
|
+
public void getAvailableDevices(PluginCall call) {
|
|
211
|
+
List<CameraDevice> devices = CameraXView.getAvailableDevicesStatic(getContext());
|
|
212
|
+
JSArray devicesArray = new JSArray();
|
|
213
|
+
for (CameraDevice device : devices) {
|
|
214
|
+
JSObject deviceJson = new JSObject();
|
|
215
|
+
deviceJson.put("deviceId", device.getDeviceId());
|
|
216
|
+
deviceJson.put("label", device.getLabel());
|
|
217
|
+
deviceJson.put("position", device.getPosition());
|
|
218
|
+
JSArray lensesArray = new JSArray();
|
|
219
|
+
for (com.ahm.capacitor.camera.preview.model.LensInfo lens : device.getLenses()) {
|
|
220
|
+
JSObject lensJson = new JSObject();
|
|
221
|
+
lensJson.put("focalLength", lens.getFocalLength());
|
|
222
|
+
lensJson.put("deviceType", lens.getDeviceType());
|
|
223
|
+
lensJson.put("baseZoomRatio", lens.getBaseZoomRatio());
|
|
224
|
+
lensJson.put("digitalZoom", lens.getDigitalZoom());
|
|
225
|
+
lensesArray.put(lensJson);
|
|
226
|
+
}
|
|
227
|
+
deviceJson.put("lenses", lensesArray);
|
|
228
|
+
deviceJson.put("minZoom", device.getMinZoom());
|
|
229
|
+
deviceJson.put("maxZoom", device.getMaxZoom());
|
|
230
|
+
devicesArray.put(deviceJson);
|
|
231
|
+
}
|
|
232
|
+
JSObject result = new JSObject();
|
|
233
|
+
result.put("devices", devicesArray);
|
|
234
|
+
call.resolve(result);
|
|
235
|
+
}
|
|
286
236
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
237
|
+
@PluginMethod
|
|
238
|
+
public void getZoom(PluginCall call) {
|
|
239
|
+
ZoomFactors zoomFactors = cameraXView.getZoomFactors();
|
|
240
|
+
JSObject result = new JSObject();
|
|
241
|
+
result.put("min", zoomFactors.getMin());
|
|
242
|
+
result.put("max", zoomFactors.getMax());
|
|
243
|
+
result.put("current", zoomFactors.getCurrent());
|
|
244
|
+
call.resolve(result);
|
|
290
245
|
}
|
|
291
246
|
|
|
292
247
|
@PluginMethod
|
|
293
|
-
public void
|
|
294
|
-
if (!
|
|
248
|
+
public void setZoom(PluginCall call) {
|
|
249
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
295
250
|
call.reject("Camera is not running");
|
|
296
251
|
return;
|
|
297
252
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
call.reject("flashMode required parameter is missing");
|
|
253
|
+
Float level = call.getFloat("level");
|
|
254
|
+
if (level == null) {
|
|
255
|
+
call.reject("level parameter is required");
|
|
302
256
|
return;
|
|
303
257
|
}
|
|
258
|
+
try {
|
|
259
|
+
cameraXView.setZoom(level);
|
|
260
|
+
call.resolve();
|
|
261
|
+
} catch (Exception e) {
|
|
262
|
+
call.reject("Failed to set zoom: " + e.getMessage());
|
|
263
|
+
}
|
|
264
|
+
}
|
|
304
265
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if (
|
|
311
|
-
supportedFlashModes != null && supportedFlashModes.contains(flashMode)
|
|
312
|
-
) {
|
|
313
|
-
params.setFlashMode(flashMode);
|
|
314
|
-
} else {
|
|
315
|
-
call.reject("Flash mode not recognised: " + flashMode);
|
|
266
|
+
@PluginMethod
|
|
267
|
+
public void setDeviceId(PluginCall call) {
|
|
268
|
+
String deviceId = call.getString("deviceId");
|
|
269
|
+
if (deviceId == null || deviceId.isEmpty()) {
|
|
270
|
+
call.reject("deviceId parameter is required");
|
|
316
271
|
return;
|
|
317
272
|
}
|
|
273
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
274
|
+
call.reject("Camera is not running");
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
cameraXView.switchToDevice(deviceId);
|
|
278
|
+
call.resolve();
|
|
279
|
+
}
|
|
318
280
|
|
|
319
|
-
|
|
281
|
+
@PluginMethod
|
|
282
|
+
public void getSupportedPictureSizes(final PluginCall call) {
|
|
283
|
+
JSArray supportedPictureSizesResult = new JSArray();
|
|
284
|
+
List<Size> rearSizes = CameraXView.getSupportedPictureSizes("rear");
|
|
285
|
+
JSObject rear = new JSObject();
|
|
286
|
+
rear.put("facing", "rear");
|
|
287
|
+
JSArray rearSizesJs = new JSArray();
|
|
288
|
+
for(Size size : rearSizes) {
|
|
289
|
+
JSObject sizeJs = new JSObject();
|
|
290
|
+
sizeJs.put("width", size.getWidth());
|
|
291
|
+
sizeJs.put("height", size.getHeight());
|
|
292
|
+
rearSizesJs.put(sizeJs);
|
|
293
|
+
}
|
|
294
|
+
rear.put("supportedPictureSizes", rearSizesJs);
|
|
295
|
+
supportedPictureSizesResult.put(rear);
|
|
296
|
+
|
|
297
|
+
List<Size> frontSizes = CameraXView.getSupportedPictureSizes("front");
|
|
298
|
+
JSObject front = new JSObject();
|
|
299
|
+
front.put("facing", "front");
|
|
300
|
+
JSArray frontSizesJs = new JSArray();
|
|
301
|
+
for(Size size : frontSizes) {
|
|
302
|
+
JSObject sizeJs = new JSObject();
|
|
303
|
+
sizeJs.put("width", size.getWidth());
|
|
304
|
+
sizeJs.put("height", size.getHeight());
|
|
305
|
+
frontSizesJs.put(sizeJs);
|
|
306
|
+
}
|
|
307
|
+
front.put("supportedPictureSizes", frontSizesJs);
|
|
308
|
+
supportedPictureSizesResult.put(front);
|
|
320
309
|
|
|
321
|
-
|
|
310
|
+
JSObject ret = new JSObject();
|
|
311
|
+
ret.put("supportedPictureSizes", supportedPictureSizesResult);
|
|
312
|
+
call.resolve(ret);
|
|
322
313
|
}
|
|
323
314
|
|
|
324
315
|
@PluginMethod
|
|
325
|
-
public void
|
|
326
|
-
if (!
|
|
316
|
+
public void setOpacity(PluginCall call) {
|
|
317
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
327
318
|
call.reject("Camera is not running");
|
|
328
319
|
return;
|
|
329
320
|
}
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
321
|
+
Float opacity = call.getFloat("opacity", 1.0f);
|
|
322
|
+
cameraXView.setOpacity(opacity);
|
|
367
323
|
call.resolve();
|
|
368
324
|
}
|
|
369
325
|
|
|
370
326
|
@PluginMethod
|
|
371
|
-
public void
|
|
372
|
-
|
|
327
|
+
public void getHorizontalFov(PluginCall call) {
|
|
328
|
+
// CameraX does not provide a simple way to get FoV.
|
|
329
|
+
// This would require Camera2 interop to access camera characteristics.
|
|
330
|
+
// Returning a default/estimated value.
|
|
331
|
+
JSObject ret = new JSObject();
|
|
332
|
+
ret.put("result", 60.0); // A common default FoV
|
|
333
|
+
call.resolve(ret);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
@PluginMethod
|
|
337
|
+
public void getDeviceId(PluginCall call) {
|
|
338
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
373
339
|
call.reject("Camera is not running");
|
|
374
340
|
return;
|
|
375
341
|
}
|
|
342
|
+
JSObject ret = new JSObject();
|
|
343
|
+
ret.put("deviceId", cameraXView.getCurrentDeviceId());
|
|
344
|
+
call.resolve(ret);
|
|
345
|
+
}
|
|
376
346
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
// });
|
|
347
|
+
@PluginMethod
|
|
348
|
+
public void getFlashMode(PluginCall call) {
|
|
349
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
350
|
+
call.reject("Camera is not running");
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
JSObject ret = new JSObject();
|
|
354
|
+
ret.put("flashMode", cameraXView.getFlashMode());
|
|
355
|
+
call.resolve(ret);
|
|
356
|
+
}
|
|
388
357
|
|
|
389
|
-
|
|
390
|
-
|
|
358
|
+
@PluginMethod
|
|
359
|
+
public void isRunning(PluginCall call) {
|
|
360
|
+
boolean running = cameraXView != null && cameraXView.isRunning();
|
|
361
|
+
JSObject jsObject = new JSObject();
|
|
362
|
+
jsObject.put("isRunning", running);
|
|
363
|
+
call.resolve(jsObject);
|
|
391
364
|
}
|
|
392
365
|
|
|
393
366
|
@PermissionCallback
|
|
394
367
|
private void handleCameraPermissionResult(PluginCall call) {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
? CAMERA_ONLY_PERMISSION_ALIAS
|
|
398
|
-
: CAMERA_WITH_AUDIO_PERMISSION_ALIAS;
|
|
399
|
-
|
|
400
|
-
if (PermissionState.GRANTED.equals(getPermissionState(permissionAlias))) {
|
|
368
|
+
if (PermissionState.GRANTED.equals(getPermissionState(CAMERA_ONLY_PERMISSION_ALIAS)) ||
|
|
369
|
+
PermissionState.GRANTED.equals(getPermissionState(CAMERA_WITH_AUDIO_PERMISSION_ALIAS))) {
|
|
401
370
|
startCamera(call);
|
|
402
371
|
} else {
|
|
403
372
|
call.reject("Permission failed");
|
|
@@ -405,327 +374,289 @@ public class CameraPreview
|
|
|
405
374
|
}
|
|
406
375
|
|
|
407
376
|
private void startCamera(final PluginCall call) {
|
|
408
|
-
String
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
final
|
|
418
|
-
|
|
419
|
-
final
|
|
420
|
-
|
|
421
|
-
final
|
|
422
|
-
|
|
423
|
-
final
|
|
424
|
-
|
|
425
|
-
final
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
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
|
-
}
|
|
377
|
+
String positionParam = call.getString("position");
|
|
378
|
+
String originalDeviceId = call.getString("deviceId");
|
|
379
|
+
String deviceId = originalDeviceId; // Use a mutable variable
|
|
380
|
+
|
|
381
|
+
final String position = (positionParam == null || positionParam.isEmpty() || "rear".equals(positionParam) || "back".equals(positionParam)) ? "back" : "front";
|
|
382
|
+
final int x = call.getInt("x", 0);
|
|
383
|
+
final int y = call.getInt("y", 0);
|
|
384
|
+
final int width = call.getInt("width", 0);
|
|
385
|
+
final int height = call.getInt("height", 0);
|
|
386
|
+
final int paddingBottom = call.getInt("paddingBottom", 0);
|
|
387
|
+
final boolean toBack = Boolean.TRUE.equals(call.getBoolean("toBack", true));
|
|
388
|
+
final boolean storeToFile = Boolean.TRUE.equals(call.getBoolean("storeToFile", false));
|
|
389
|
+
final boolean enableOpacity = Boolean.TRUE.equals(call.getBoolean("enableOpacity", false));
|
|
390
|
+
final boolean enableZoom = Boolean.TRUE.equals(call.getBoolean("enableZoom", false));
|
|
391
|
+
final boolean disableExifHeaderStripping = Boolean.TRUE.equals(call.getBoolean("disableExifHeaderStripping", false));
|
|
392
|
+
final boolean lockOrientation = Boolean.TRUE.equals(call.getBoolean("lockAndroidOrientation", false));
|
|
393
|
+
final boolean disableAudio = Boolean.TRUE.equals(call.getBoolean("disableAudio", true));
|
|
394
|
+
final String aspectRatio = call.getString("aspectRatio", "4:3");
|
|
395
|
+
final String gridMode = call.getString("gridMode", "none");
|
|
396
|
+
|
|
397
|
+
// Check for conflict between aspectRatio and size
|
|
398
|
+
if (call.getData().has("aspectRatio") && (call.getData().has("width") || call.getData().has("height"))) {
|
|
399
|
+
call.reject("Cannot set both aspectRatio and size (width/height). Use setPreviewSize after start.");
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
508
402
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
TypedValue.COMPLEX_UNIT_PX,
|
|
525
|
-
size.x,
|
|
526
|
-
metrics
|
|
527
|
-
);
|
|
403
|
+
float targetZoom = 1.0f;
|
|
404
|
+
// Check if the selected device is a physical ultra-wide
|
|
405
|
+
if (originalDeviceId != null) {
|
|
406
|
+
List<CameraDevice> devices = CameraXView.getAvailableDevicesStatic(getContext());
|
|
407
|
+
for (CameraDevice device : devices) {
|
|
408
|
+
if (originalDeviceId.equals(device.getDeviceId()) && !device.isLogical()) {
|
|
409
|
+
for (LensInfo lens : device.getLenses()) {
|
|
410
|
+
if ("ultraWide".equals(lens.getDeviceType())) {
|
|
411
|
+
Log.d("CameraPreview", "Ultra-wide lens selected. Targeting 0.5x zoom on logical camera.");
|
|
412
|
+
targetZoom = 0.5f;
|
|
413
|
+
// Force the use of the logical camera by clearing the specific deviceId
|
|
414
|
+
deviceId = null;
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
528
418
|
}
|
|
419
|
+
if (deviceId == null) break; // Exit outer loop once we've made our decision
|
|
420
|
+
}
|
|
421
|
+
}
|
|
529
422
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
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
|
-
}
|
|
423
|
+
previousOrientationRequest = getBridge().getActivity().getRequestedOrientation();
|
|
424
|
+
cameraXView = new CameraXView(getContext(), getBridge().getWebView());
|
|
425
|
+
cameraXView.setListener(this);
|
|
554
426
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
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
|
-
}
|
|
427
|
+
String finalDeviceId = deviceId;
|
|
428
|
+
float finalTargetZoom = targetZoom;
|
|
429
|
+
getBridge().getActivity().runOnUiThread(() -> {
|
|
430
|
+
DisplayMetrics metrics = getBridge().getActivity().getResources().getDisplayMetrics();
|
|
431
|
+
if (lockOrientation) {
|
|
432
|
+
getBridge().getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
|
|
603
433
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
434
|
+
|
|
435
|
+
// Debug: Let's check all the positioning information
|
|
436
|
+
ViewGroup webViewParent = (ViewGroup) getBridge().getWebView().getParent();
|
|
437
|
+
|
|
438
|
+
// Get webview position in different coordinate systems
|
|
439
|
+
int[] webViewLocationInWindow = new int[2];
|
|
440
|
+
int[] webViewLocationOnScreen = new int[2];
|
|
441
|
+
getBridge().getWebView().getLocationInWindow(webViewLocationInWindow);
|
|
442
|
+
getBridge().getWebView().getLocationOnScreen(webViewLocationOnScreen);
|
|
443
|
+
|
|
444
|
+
int webViewLeft = getBridge().getWebView().getLeft();
|
|
445
|
+
int webViewTop = getBridge().getWebView().getTop();
|
|
446
|
+
|
|
447
|
+
// Check parent position too
|
|
448
|
+
int[] parentLocationInWindow = new int[2];
|
|
449
|
+
int[] parentLocationOnScreen = new int[2];
|
|
450
|
+
webViewParent.getLocationInWindow(parentLocationInWindow);
|
|
451
|
+
webViewParent.getLocationOnScreen(parentLocationOnScreen);
|
|
452
|
+
|
|
453
|
+
// Calculate pixel ratio
|
|
454
|
+
float pixelRatio = metrics.density;
|
|
455
|
+
|
|
456
|
+
// Try using just the pixel ratio without any webview offset for now
|
|
457
|
+
int computedX = (int) (x * pixelRatio);
|
|
458
|
+
int computedY = (int) (y * pixelRatio);
|
|
459
|
+
|
|
460
|
+
Log.d("CameraPreview", "=== COORDINATE DEBUG ===");
|
|
461
|
+
Log.d("CameraPreview", "WebView getLeft/getTop: (" + webViewLeft + ", " + webViewTop + ")");
|
|
462
|
+
Log.d("CameraPreview", "WebView locationInWindow: (" + webViewLocationInWindow[0] + ", " + webViewLocationInWindow[1] + ")");
|
|
463
|
+
Log.d("CameraPreview", "WebView locationOnScreen: (" + webViewLocationOnScreen[0] + ", " + webViewLocationOnScreen[1] + ")");
|
|
464
|
+
Log.d("CameraPreview", "Parent locationInWindow: (" + parentLocationInWindow[0] + ", " + parentLocationInWindow[1] + ")");
|
|
465
|
+
Log.d("CameraPreview", "Parent locationOnScreen: (" + parentLocationOnScreen[0] + ", " + parentLocationOnScreen[1] + ")");
|
|
466
|
+
Log.d("CameraPreview", "Parent class: " + webViewParent.getClass().getSimpleName());
|
|
467
|
+
Log.d("CameraPreview", "Requested position (logical): (" + x + ", " + y + ")");
|
|
468
|
+
Log.d("CameraPreview", "Pixel ratio: " + pixelRatio);
|
|
469
|
+
Log.d("CameraPreview", "Final computed position (no offset): (" + computedX + ", " + computedY + ")");
|
|
470
|
+
Log.d("CameraPreview", "========================");
|
|
471
|
+
int computedWidth = width != 0 ? (int) (width * pixelRatio) : getBridge().getWebView().getWidth();
|
|
472
|
+
int computedHeight = height != 0 ? (int) (height * pixelRatio) : getBridge().getWebView().getHeight();
|
|
473
|
+
computedHeight -= (int) (paddingBottom * pixelRatio);
|
|
474
|
+
|
|
475
|
+
CameraSessionConfiguration config = new CameraSessionConfiguration(finalDeviceId, position, computedX, computedY, computedWidth, computedHeight, paddingBottom, toBack, storeToFile, enableOpacity, enableZoom, disableExifHeaderStripping, disableAudio, 1.0f, aspectRatio, gridMode);
|
|
476
|
+
config.setTargetZoom(finalTargetZoom);
|
|
477
|
+
|
|
478
|
+
bridge.saveCall(call);
|
|
479
|
+
cameraStartCallbackId = call.getCallbackId();
|
|
480
|
+
cameraXView.startSession(config);
|
|
481
|
+
}
|
|
482
|
+
);
|
|
610
483
|
}
|
|
611
484
|
|
|
612
485
|
@Override
|
|
613
|
-
public void onPictureTaken(String
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
486
|
+
public void onPictureTaken(String base64, JSONObject exif) {
|
|
487
|
+
PluginCall pluginCall = bridge.getSavedCall(captureCallbackId);
|
|
488
|
+
if (pluginCall == null) {
|
|
489
|
+
Log.e("CameraPreview", "onPictureTaken: captureCallbackId is null");
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
JSObject result = new JSObject();
|
|
493
|
+
result.put("value", base64);
|
|
494
|
+
result.put("exif", exif);
|
|
495
|
+
pluginCall.resolve(result);
|
|
496
|
+
bridge.releaseCall(pluginCall);
|
|
617
497
|
}
|
|
618
498
|
|
|
619
499
|
@Override
|
|
620
500
|
public void onPictureTakenError(String message) {
|
|
621
|
-
bridge.getSavedCall(captureCallbackId)
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
bridge.getSavedCall(snapshotCallbackId).resolve(jsObject);
|
|
501
|
+
PluginCall pluginCall = bridge.getSavedCall(captureCallbackId);
|
|
502
|
+
if (pluginCall == null) {
|
|
503
|
+
Log.e("CameraPreview", "onPictureTakenError: captureCallbackId is null");
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
pluginCall.reject(message);
|
|
507
|
+
bridge.releaseCall(pluginCall);
|
|
629
508
|
}
|
|
630
509
|
|
|
631
510
|
@Override
|
|
632
|
-
public void
|
|
633
|
-
bridge.getSavedCall(
|
|
511
|
+
public void onCameraStarted(int width, int height, int x, int y) {
|
|
512
|
+
PluginCall call = bridge.getSavedCall(cameraStartCallbackId);
|
|
513
|
+
if (call != null) {
|
|
514
|
+
// Convert pixel values back to logical units
|
|
515
|
+
DisplayMetrics metrics = getBridge().getActivity().getResources().getDisplayMetrics();
|
|
516
|
+
float pixelRatio = metrics.density;
|
|
517
|
+
|
|
518
|
+
JSObject result = new JSObject();
|
|
519
|
+
result.put("width", width / pixelRatio);
|
|
520
|
+
result.put("height", height / pixelRatio);
|
|
521
|
+
result.put("x", x / pixelRatio);
|
|
522
|
+
result.put("y", y / pixelRatio);
|
|
523
|
+
call.resolve(result);
|
|
524
|
+
bridge.releaseCall(call);
|
|
525
|
+
cameraStartCallbackId = null; // Prevent re-use
|
|
526
|
+
}
|
|
634
527
|
}
|
|
635
528
|
|
|
636
529
|
@Override
|
|
637
|
-
public void
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
public void onFocusSetError(String message) {}
|
|
641
|
-
|
|
642
|
-
@Override
|
|
643
|
-
public void onBackButton() {}
|
|
644
|
-
|
|
645
|
-
@Override
|
|
646
|
-
public void onCameraStarted() {
|
|
647
|
-
PluginCall pluginCall = bridge.getSavedCall(cameraStartCallbackId);
|
|
648
|
-
pluginCall.resolve();
|
|
649
|
-
bridge.releaseCall(pluginCall);
|
|
530
|
+
public void onSampleTaken(String result) {
|
|
531
|
+
// Handle sample taken if needed
|
|
532
|
+
Log.i("CameraPreview", "Sample taken: " + result);
|
|
650
533
|
}
|
|
651
534
|
|
|
652
535
|
@Override
|
|
653
|
-
public void
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
public void onStartRecordVideoError(String message) {
|
|
657
|
-
bridge.getSavedCall(recordCallbackId).reject(message);
|
|
536
|
+
public void onSampleTakenError(String message) {
|
|
537
|
+
// Handle sample taken error if needed
|
|
538
|
+
Log.e("CameraPreview", "Sample taken error: " + message);
|
|
658
539
|
}
|
|
659
540
|
|
|
660
541
|
@Override
|
|
661
|
-
public void
|
|
662
|
-
PluginCall
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
542
|
+
public void onCameraStartError(String message) {
|
|
543
|
+
PluginCall call = bridge.getSavedCall(cameraStartCallbackId);
|
|
544
|
+
if (call != null) {
|
|
545
|
+
call.reject(message);
|
|
546
|
+
bridge.releaseCall(call);
|
|
547
|
+
cameraStartCallbackId = null;
|
|
548
|
+
}
|
|
666
549
|
}
|
|
667
550
|
|
|
668
|
-
@
|
|
669
|
-
public void
|
|
670
|
-
|
|
551
|
+
@PluginMethod
|
|
552
|
+
public void setAspectRatio(PluginCall call) {
|
|
553
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
554
|
+
call.reject("Camera is not running");
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
String aspectRatio = call.getString("aspectRatio", "4:3");
|
|
558
|
+
Float x = call.getFloat("x");
|
|
559
|
+
Float y = call.getFloat("y");
|
|
560
|
+
|
|
561
|
+
getActivity().runOnUiThread(() -> {
|
|
562
|
+
cameraXView.setAspectRatio(aspectRatio, x, y, () -> {
|
|
563
|
+
// Return the actual preview bounds after layout and camera operations are complete
|
|
564
|
+
int[] bounds = cameraXView.getCurrentPreviewBounds();
|
|
565
|
+
JSObject ret = new JSObject();
|
|
566
|
+
ret.put("x", bounds[0]);
|
|
567
|
+
ret.put("y", bounds[1]);
|
|
568
|
+
ret.put("width", bounds[2]);
|
|
569
|
+
ret.put("height", bounds[3]);
|
|
570
|
+
call.resolve(ret);
|
|
571
|
+
});
|
|
572
|
+
});
|
|
671
573
|
}
|
|
672
574
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
575
|
+
@PluginMethod
|
|
576
|
+
public void getAspectRatio(PluginCall call) {
|
|
577
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
578
|
+
call.reject("Camera is not running");
|
|
579
|
+
return;
|
|
676
580
|
}
|
|
677
|
-
|
|
678
|
-
|
|
581
|
+
String aspectRatio = cameraXView.getAspectRatio();
|
|
582
|
+
JSObject ret = new JSObject();
|
|
583
|
+
ret.put("aspectRatio", aspectRatio);
|
|
584
|
+
call.resolve(ret);
|
|
679
585
|
}
|
|
680
586
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
587
|
+
@PluginMethod
|
|
588
|
+
public void setGridMode(PluginCall call) {
|
|
589
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
590
|
+
call.reject("Camera is not running");
|
|
591
|
+
return;
|
|
684
592
|
}
|
|
593
|
+
String gridMode = call.getString("gridMode", "none");
|
|
594
|
+
getActivity().runOnUiThread(() -> {
|
|
595
|
+
cameraXView.setGridMode(gridMode);
|
|
596
|
+
call.resolve();
|
|
597
|
+
});
|
|
598
|
+
}
|
|
685
599
|
|
|
686
|
-
|
|
687
|
-
|
|
600
|
+
@PluginMethod
|
|
601
|
+
public void getGridMode(PluginCall call) {
|
|
602
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
603
|
+
call.reject("Camera is not running");
|
|
604
|
+
return;
|
|
688
605
|
}
|
|
689
|
-
|
|
690
|
-
|
|
606
|
+
JSObject ret = new JSObject();
|
|
607
|
+
ret.put("gridMode", cameraXView.getGridMode());
|
|
608
|
+
call.resolve(ret);
|
|
691
609
|
}
|
|
692
610
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
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++;
|
|
611
|
+
@PluginMethod
|
|
612
|
+
public void getPreviewSize(PluginCall call) {
|
|
613
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
614
|
+
call.reject("Camera is not running");
|
|
615
|
+
return;
|
|
704
616
|
}
|
|
705
|
-
|
|
706
|
-
|
|
617
|
+
|
|
618
|
+
// Convert pixel values back to logical units
|
|
619
|
+
DisplayMetrics metrics = getBridge().getActivity().getResources().getDisplayMetrics();
|
|
620
|
+
float pixelRatio = metrics.density;
|
|
621
|
+
|
|
622
|
+
JSObject ret = new JSObject();
|
|
623
|
+
ret.put("x", cameraXView.getPreviewX() / pixelRatio);
|
|
624
|
+
ret.put("y", cameraXView.getPreviewY() / pixelRatio);
|
|
625
|
+
ret.put("width", cameraXView.getPreviewWidth() / pixelRatio);
|
|
626
|
+
ret.put("height", cameraXView.getPreviewHeight() / pixelRatio);
|
|
627
|
+
call.resolve(ret);
|
|
707
628
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
629
|
+
@PluginMethod
|
|
630
|
+
public void setPreviewSize(PluginCall call) {
|
|
631
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
632
|
+
call.reject("Camera is not running");
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Get values from call - null values will become 0
|
|
637
|
+
Integer xParam = call.getInt("x");
|
|
638
|
+
Integer yParam = call.getInt("y");
|
|
639
|
+
Integer widthParam = call.getInt("width");
|
|
640
|
+
Integer heightParam = call.getInt("height");
|
|
641
|
+
|
|
642
|
+
// Apply pixel ratio conversion to non-null values
|
|
643
|
+
DisplayMetrics metrics = getBridge().getActivity().getResources().getDisplayMetrics();
|
|
644
|
+
float pixelRatio = metrics.density;
|
|
645
|
+
|
|
646
|
+
int x = (xParam != null && xParam > 0) ? (int) (xParam * pixelRatio) : 0;
|
|
647
|
+
int y = (yParam != null && yParam > 0) ? (int) (yParam * pixelRatio) : 0;
|
|
648
|
+
int width = (widthParam != null && widthParam > 0) ? (int) (widthParam * pixelRatio) : 0;
|
|
649
|
+
int height = (heightParam != null && heightParam > 0) ? (int) (heightParam * pixelRatio) : 0;
|
|
650
|
+
|
|
651
|
+
cameraXView.setPreviewSize(x, y, width, height, () -> {
|
|
652
|
+
// Return the actual preview bounds after layout operations are complete
|
|
653
|
+
int[] bounds = cameraXView.getCurrentPreviewBounds();
|
|
654
|
+
JSObject ret = new JSObject();
|
|
655
|
+
ret.put("x", bounds[0]);
|
|
656
|
+
ret.put("y", bounds[1]);
|
|
657
|
+
ret.put("width", bounds[2]);
|
|
658
|
+
ret.put("height", bounds[3]);
|
|
659
|
+
call.resolve(ret);
|
|
660
|
+
});
|
|
730
661
|
}
|
|
731
662
|
}
|