@capgo/camera-preview 7.3.12 → 7.4.0-alpha.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/CapgoCameraPreview.podspec +16 -13
- package/README.md +492 -73
- 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 +968 -505
- package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraXView.java +3017 -0
- package/android/src/main/java/com/ahm/capacitor/camera/preview/GridOverlayView.java +119 -0
- package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraDevice.java +63 -0
- package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraLens.java +79 -0
- package/android/src/main/java/com/ahm/capacitor/camera/preview/model/CameraSessionConfiguration.java +167 -0
- package/android/src/main/java/com/ahm/capacitor/camera/preview/model/LensInfo.java +40 -0
- package/android/src/main/java/com/ahm/capacitor/camera/preview/model/ZoomFactors.java +35 -0
- package/dist/docs.json +1041 -161
- package/dist/esm/definitions.d.ts +484 -84
- package/dist/esm/definitions.js +10 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +78 -3
- package/dist/esm/web.js +813 -68
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +819 -68
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +819 -68
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoCameraPreviewPlugin/CameraController.swift +1663 -0
- package/ios/Sources/CapgoCameraPreviewPlugin/GridOverlayView.swift +65 -0
- package/ios/Sources/CapgoCameraPreviewPlugin/Plugin.swift +1550 -0
- package/ios/Tests/CameraPreviewPluginTests/CameraPreviewPluginTests.swift +15 -0
- package/package.json +2 -2
- 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/CameraController.swift +0 -809
- 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,27 +3,19 @@ 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.
|
|
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;
|
|
8
|
+
import android.location.Location;
|
|
18
9
|
import android.util.DisplayMetrics;
|
|
10
|
+
import android.util.Log;
|
|
19
11
|
import android.util.Size;
|
|
20
|
-
import android.util.TypedValue;
|
|
21
|
-
import android.view.Display;
|
|
22
|
-
import android.view.MotionEvent;
|
|
23
12
|
import android.view.View;
|
|
24
13
|
import android.view.ViewGroup;
|
|
25
|
-
import android.
|
|
26
|
-
import
|
|
14
|
+
import android.webkit.WebView;
|
|
15
|
+
import com.ahm.capacitor.camera.preview.model.CameraDevice;
|
|
16
|
+
import com.ahm.capacitor.camera.preview.model.CameraSessionConfiguration;
|
|
17
|
+
import com.ahm.capacitor.camera.preview.model.LensInfo;
|
|
18
|
+
import com.ahm.capacitor.camera.preview.model.ZoomFactors;
|
|
27
19
|
import com.getcapacitor.JSArray;
|
|
28
20
|
import com.getcapacitor.JSObject;
|
|
29
21
|
import com.getcapacitor.Logger;
|
|
@@ -34,11 +26,11 @@ import com.getcapacitor.PluginMethod;
|
|
|
34
26
|
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
35
27
|
import com.getcapacitor.annotation.Permission;
|
|
36
28
|
import com.getcapacitor.annotation.PermissionCallback;
|
|
37
|
-
import
|
|
38
|
-
import
|
|
29
|
+
import com.google.android.gms.location.FusedLocationProviderClient;
|
|
30
|
+
import com.google.android.gms.location.LocationServices;
|
|
39
31
|
import java.util.List;
|
|
40
32
|
import java.util.Objects;
|
|
41
|
-
import org.json.
|
|
33
|
+
import org.json.JSONObject;
|
|
42
34
|
|
|
43
35
|
@CapacitorPlugin(
|
|
44
36
|
name = "CameraPreview",
|
|
@@ -51,33 +43,38 @@ import org.json.JSONArray;
|
|
|
51
43
|
strings = { CAMERA },
|
|
52
44
|
alias = CameraPreview.CAMERA_ONLY_PERMISSION_ALIAS
|
|
53
45
|
),
|
|
46
|
+
@Permission(
|
|
47
|
+
strings = {
|
|
48
|
+
Manifest.permission.ACCESS_COARSE_LOCATION,
|
|
49
|
+
Manifest.permission.ACCESS_FINE_LOCATION,
|
|
50
|
+
},
|
|
51
|
+
alias = CameraPreview.CAMERA_WITH_LOCATION_PERMISSION_ALIAS
|
|
52
|
+
),
|
|
54
53
|
}
|
|
55
54
|
)
|
|
56
55
|
public class CameraPreview
|
|
57
56
|
extends Plugin
|
|
58
|
-
implements
|
|
57
|
+
implements CameraXView.CameraXViewListener {
|
|
59
58
|
|
|
60
59
|
static final String CAMERA_WITH_AUDIO_PERMISSION_ALIAS = "cameraWithAudio";
|
|
61
60
|
static final String CAMERA_ONLY_PERMISSION_ALIAS = "cameraOnly";
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
private static final String VIDEO_FILE_EXTENSION = ".mp4";
|
|
61
|
+
static final String CAMERA_WITH_LOCATION_PERMISSION_ALIAS =
|
|
62
|
+
"cameraWithLocation";
|
|
65
63
|
|
|
66
64
|
private String captureCallbackId = "";
|
|
67
65
|
private String snapshotCallbackId = "";
|
|
68
|
-
private String recordCallbackId = "";
|
|
69
66
|
private String cameraStartCallbackId = "";
|
|
70
|
-
|
|
71
|
-
// keep track of previously specified orientation to support locking orientation:
|
|
72
67
|
private int previousOrientationRequest =
|
|
73
68
|
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
|
74
|
-
|
|
75
|
-
private
|
|
76
|
-
private
|
|
69
|
+
private CameraXView cameraXView;
|
|
70
|
+
private FusedLocationProviderClient fusedLocationClient;
|
|
71
|
+
private Location lastLocation;
|
|
77
72
|
|
|
78
73
|
@PluginMethod
|
|
79
74
|
public void start(PluginCall call) {
|
|
80
|
-
boolean disableAudio =
|
|
75
|
+
boolean disableAudio = Boolean.TRUE.equals(
|
|
76
|
+
call.getBoolean("disableAudio", true)
|
|
77
|
+
);
|
|
81
78
|
String permissionAlias = disableAudio
|
|
82
79
|
? CAMERA_ONLY_PERMISSION_ALIAS
|
|
83
80
|
: CAMERA_WITH_AUDIO_PERMISSION_ALIAS;
|
|
@@ -95,309 +92,349 @@ public class CameraPreview
|
|
|
95
92
|
|
|
96
93
|
@PluginMethod
|
|
97
94
|
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");
|
|
95
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
96
|
+
call.reject("Camera is not running");
|
|
97
|
+
return;
|
|
104
98
|
}
|
|
99
|
+
cameraXView.flipCamera();
|
|
100
|
+
call.resolve();
|
|
105
101
|
}
|
|
106
102
|
|
|
107
103
|
@PluginMethod
|
|
108
|
-
public void
|
|
109
|
-
if (!
|
|
104
|
+
public void capture(final PluginCall call) {
|
|
105
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
110
106
|
call.reject("Camera is not running");
|
|
111
107
|
return;
|
|
112
108
|
}
|
|
113
109
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
110
|
+
final boolean withExifLocation = call.getBoolean("withExifLocation", false);
|
|
111
|
+
|
|
112
|
+
if (withExifLocation) {
|
|
113
|
+
if (
|
|
114
|
+
getPermissionState(CAMERA_WITH_LOCATION_PERMISSION_ALIAS) !=
|
|
115
|
+
PermissionState.GRANTED
|
|
116
|
+
) {
|
|
117
|
+
requestPermissionForAlias(
|
|
118
|
+
CAMERA_WITH_LOCATION_PERMISSION_ALIAS,
|
|
119
|
+
call,
|
|
120
|
+
"captureWithLocationPermission"
|
|
121
|
+
);
|
|
122
|
+
} else {
|
|
123
|
+
getLocationAndCapture(call);
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
captureWithoutLocation(call);
|
|
127
|
+
}
|
|
117
128
|
}
|
|
118
129
|
|
|
119
|
-
@
|
|
120
|
-
|
|
121
|
-
if (
|
|
122
|
-
|
|
123
|
-
|
|
130
|
+
@PermissionCallback
|
|
131
|
+
private void captureWithLocationPermission(PluginCall call) {
|
|
132
|
+
if (
|
|
133
|
+
getPermissionState(CAMERA_WITH_LOCATION_PERMISSION_ALIAS) ==
|
|
134
|
+
PermissionState.GRANTED
|
|
135
|
+
) {
|
|
136
|
+
getLocationAndCapture(call);
|
|
137
|
+
} else {
|
|
138
|
+
Logger.warn(
|
|
139
|
+
"Location permission denied. Capturing photo without location data."
|
|
140
|
+
);
|
|
141
|
+
captureWithoutLocation(call);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private void getLocationAndCapture(PluginCall call) {
|
|
146
|
+
if (fusedLocationClient == null) {
|
|
147
|
+
fusedLocationClient = LocationServices.getFusedLocationProviderClient(
|
|
148
|
+
getContext()
|
|
149
|
+
);
|
|
124
150
|
}
|
|
151
|
+
fusedLocationClient
|
|
152
|
+
.getLastLocation()
|
|
153
|
+
.addOnSuccessListener(getActivity(), location -> {
|
|
154
|
+
lastLocation = location;
|
|
155
|
+
proceedWithCapture(call, lastLocation);
|
|
156
|
+
})
|
|
157
|
+
.addOnFailureListener(e -> {
|
|
158
|
+
Logger.error("Failed to get location: " + e.getMessage());
|
|
159
|
+
proceedWithCapture(call, null);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private void captureWithoutLocation(PluginCall call) {
|
|
164
|
+
proceedWithCapture(call, null);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private void proceedWithCapture(PluginCall call, Location location) {
|
|
125
168
|
bridge.saveCall(call);
|
|
126
169
|
captureCallbackId = call.getCallbackId();
|
|
127
170
|
|
|
128
171
|
Integer quality = Objects.requireNonNull(call.getInt("quality", 85));
|
|
129
|
-
|
|
130
|
-
Integer width =
|
|
131
|
-
Integer height =
|
|
132
|
-
|
|
172
|
+
final boolean saveToGallery = call.getBoolean("saveToGallery", false);
|
|
173
|
+
Integer width = call.getInt("width");
|
|
174
|
+
Integer height = call.getInt("height");
|
|
175
|
+
String aspectRatio = call.getString("aspectRatio");
|
|
176
|
+
|
|
177
|
+
cameraXView.capturePhoto(
|
|
178
|
+
quality,
|
|
179
|
+
saveToGallery,
|
|
180
|
+
width,
|
|
181
|
+
height,
|
|
182
|
+
aspectRatio,
|
|
183
|
+
location
|
|
184
|
+
);
|
|
133
185
|
}
|
|
134
186
|
|
|
135
187
|
@PluginMethod
|
|
136
188
|
public void captureSample(PluginCall call) {
|
|
137
|
-
if (!
|
|
189
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
138
190
|
call.reject("Camera is not running");
|
|
139
191
|
return;
|
|
140
192
|
}
|
|
141
193
|
bridge.saveCall(call);
|
|
142
194
|
snapshotCallbackId = call.getCallbackId();
|
|
143
|
-
|
|
144
195
|
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
|
-
}
|
|
196
|
+
cameraXView.captureSample(quality);
|
|
210
197
|
}
|
|
211
198
|
|
|
212
199
|
@PluginMethod
|
|
213
200
|
public void stop(final PluginCall call) {
|
|
214
201
|
bridge
|
|
215
202
|
.getActivity()
|
|
216
|
-
.runOnUiThread(
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
// allow orientation changes after closing camera:
|
|
225
|
-
getBridge()
|
|
226
|
-
.getActivity()
|
|
227
|
-
.setRequestedOrientation(previousOrientationRequest);
|
|
228
|
-
|
|
229
|
-
if (containerView != null) {
|
|
230
|
-
((ViewGroup) getBridge().getWebView().getParent()).removeView(
|
|
231
|
-
containerView
|
|
232
|
-
);
|
|
233
|
-
getBridge().getWebView().setBackgroundColor(Color.WHITE);
|
|
234
|
-
FragmentManager fragmentManager = getActivity()
|
|
235
|
-
.getFragmentManager();
|
|
236
|
-
FragmentTransaction fragmentTransaction =
|
|
237
|
-
fragmentManager.beginTransaction();
|
|
238
|
-
fragmentTransaction.remove(fragment);
|
|
239
|
-
fragmentTransaction.commit();
|
|
240
|
-
fragment = null;
|
|
241
|
-
|
|
242
|
-
call.resolve();
|
|
243
|
-
} else {
|
|
244
|
-
call.reject("camera already stopped");
|
|
245
|
-
}
|
|
246
|
-
}
|
|
203
|
+
.runOnUiThread(() -> {
|
|
204
|
+
getBridge()
|
|
205
|
+
.getActivity()
|
|
206
|
+
.setRequestedOrientation(previousOrientationRequest);
|
|
207
|
+
|
|
208
|
+
if (cameraXView != null && cameraXView.isRunning()) {
|
|
209
|
+
cameraXView.stopSession();
|
|
210
|
+
cameraXView = null;
|
|
247
211
|
}
|
|
248
|
-
|
|
212
|
+
call.resolve();
|
|
213
|
+
});
|
|
249
214
|
}
|
|
250
215
|
|
|
251
216
|
@PluginMethod
|
|
252
217
|
public void getSupportedFlashModes(PluginCall call) {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
218
|
+
List<String> supportedFlashModes = cameraXView.getSupportedFlashModes();
|
|
219
|
+
JSArray jsonFlashModes = new JSArray();
|
|
220
|
+
for (String mode : supportedFlashModes) {
|
|
221
|
+
jsonFlashModes.put(mode);
|
|
256
222
|
}
|
|
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
223
|
JSObject jsObject = new JSObject();
|
|
271
224
|
jsObject.put("result", jsonFlashModes);
|
|
272
225
|
call.resolve(jsObject);
|
|
273
226
|
}
|
|
274
227
|
|
|
275
228
|
@PluginMethod
|
|
276
|
-
public void
|
|
277
|
-
|
|
278
|
-
|
|
229
|
+
public void setFlashMode(PluginCall call) {
|
|
230
|
+
String flashMode = call.getString("flashMode");
|
|
231
|
+
if (flashMode == null || flashMode.isEmpty()) {
|
|
232
|
+
call.reject("flashMode required parameter is missing");
|
|
279
233
|
return;
|
|
280
234
|
}
|
|
235
|
+
cameraXView.setFlashMode(flashMode);
|
|
236
|
+
call.resolve();
|
|
237
|
+
}
|
|
281
238
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
239
|
+
@PluginMethod
|
|
240
|
+
public void getAvailableDevices(PluginCall call) {
|
|
241
|
+
List<CameraDevice> devices = CameraXView.getAvailableDevicesStatic(
|
|
242
|
+
getContext()
|
|
243
|
+
);
|
|
244
|
+
JSArray devicesArray = new JSArray();
|
|
245
|
+
for (CameraDevice device : devices) {
|
|
246
|
+
JSObject deviceJson = new JSObject();
|
|
247
|
+
deviceJson.put("deviceId", device.getDeviceId());
|
|
248
|
+
deviceJson.put("label", device.getLabel());
|
|
249
|
+
deviceJson.put("position", device.getPosition());
|
|
250
|
+
JSArray lensesArray = new JSArray();
|
|
251
|
+
for (com.ahm.capacitor.camera.preview.model.LensInfo lens : device.getLenses()) {
|
|
252
|
+
JSObject lensJson = new JSObject();
|
|
253
|
+
lensJson.put("focalLength", lens.getFocalLength());
|
|
254
|
+
lensJson.put("deviceType", lens.getDeviceType());
|
|
255
|
+
lensJson.put("baseZoomRatio", lens.getBaseZoomRatio());
|
|
256
|
+
lensJson.put("digitalZoom", lens.getDigitalZoom());
|
|
257
|
+
lensesArray.put(lensJson);
|
|
258
|
+
}
|
|
259
|
+
deviceJson.put("lenses", lensesArray);
|
|
260
|
+
deviceJson.put("minZoom", device.getMinZoom());
|
|
261
|
+
deviceJson.put("maxZoom", device.getMaxZoom());
|
|
262
|
+
devicesArray.put(deviceJson);
|
|
263
|
+
}
|
|
264
|
+
JSObject result = new JSObject();
|
|
265
|
+
result.put("devices", devicesArray);
|
|
266
|
+
call.resolve(result);
|
|
267
|
+
}
|
|
286
268
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
269
|
+
@PluginMethod
|
|
270
|
+
public void getZoom(PluginCall call) {
|
|
271
|
+
ZoomFactors zoomFactors = cameraXView.getZoomFactors();
|
|
272
|
+
JSObject result = new JSObject();
|
|
273
|
+
result.put("min", zoomFactors.getMin());
|
|
274
|
+
result.put("max", zoomFactors.getMax());
|
|
275
|
+
result.put("current", zoomFactors.getCurrent());
|
|
276
|
+
call.resolve(result);
|
|
290
277
|
}
|
|
291
278
|
|
|
292
279
|
@PluginMethod
|
|
293
|
-
public void
|
|
294
|
-
if (!
|
|
280
|
+
public void setZoom(PluginCall call) {
|
|
281
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
295
282
|
call.reject("Camera is not running");
|
|
296
283
|
return;
|
|
297
284
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
call.reject("flashMode required parameter is missing");
|
|
285
|
+
Float level = call.getFloat("level");
|
|
286
|
+
if (level == null) {
|
|
287
|
+
call.reject("level parameter is required");
|
|
302
288
|
return;
|
|
303
289
|
}
|
|
290
|
+
Boolean autoFocus = call.getBoolean("autoFocus", true);
|
|
291
|
+
try {
|
|
292
|
+
cameraXView.setZoom(level, autoFocus);
|
|
293
|
+
call.resolve();
|
|
294
|
+
} catch (Exception e) {
|
|
295
|
+
call.reject("Failed to set zoom: " + e.getMessage());
|
|
296
|
+
}
|
|
297
|
+
}
|
|
304
298
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
)
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
299
|
+
@PluginMethod
|
|
300
|
+
public void setFocus(PluginCall call) {
|
|
301
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
302
|
+
call.reject("Camera is not running");
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
Float x = call.getFloat("x");
|
|
306
|
+
Float y = call.getFloat("y");
|
|
307
|
+
if (x == null || y == null) {
|
|
308
|
+
call.reject("x and y parameters are required");
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
// Reject if values are outside 0-1 range
|
|
312
|
+
if (x < 0f || x > 1f || y < 0f || y > 1f) {
|
|
313
|
+
call.reject("Focus coordinates must be between 0 and 1");
|
|
316
314
|
return;
|
|
317
315
|
}
|
|
318
316
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
317
|
+
getActivity()
|
|
318
|
+
.runOnUiThread(() -> {
|
|
319
|
+
try {
|
|
320
|
+
cameraXView.setFocus(x, y);
|
|
321
|
+
call.resolve();
|
|
322
|
+
} catch (Exception e) {
|
|
323
|
+
call.reject("Failed to set focus: " + e.getMessage());
|
|
324
|
+
}
|
|
325
|
+
});
|
|
322
326
|
}
|
|
323
327
|
|
|
324
328
|
@PluginMethod
|
|
325
|
-
public void
|
|
326
|
-
|
|
329
|
+
public void setDeviceId(PluginCall call) {
|
|
330
|
+
String deviceId = call.getString("deviceId");
|
|
331
|
+
if (deviceId == null || deviceId.isEmpty()) {
|
|
332
|
+
call.reject("deviceId parameter is required");
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
327
336
|
call.reject("Camera is not running");
|
|
328
337
|
return;
|
|
329
338
|
}
|
|
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();
|
|
339
|
+
cameraXView.switchToDevice(deviceId);
|
|
340
|
+
call.resolve();
|
|
341
|
+
}
|
|
347
342
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
343
|
+
@PluginMethod
|
|
344
|
+
public void getSupportedPictureSizes(final PluginCall call) {
|
|
345
|
+
JSArray supportedPictureSizesResult = new JSArray();
|
|
346
|
+
List<Size> rearSizes = CameraXView.getSupportedPictureSizes("rear");
|
|
347
|
+
JSObject rear = new JSObject();
|
|
348
|
+
rear.put("facing", "rear");
|
|
349
|
+
JSArray rearSizesJs = new JSArray();
|
|
350
|
+
for (Size size : rearSizes) {
|
|
351
|
+
JSObject sizeJs = new JSObject();
|
|
352
|
+
sizeJs.put("width", size.getWidth());
|
|
353
|
+
sizeJs.put("height", size.getHeight());
|
|
354
|
+
rearSizesJs.put(sizeJs);
|
|
355
|
+
}
|
|
356
|
+
rear.put("supportedPictureSizes", rearSizesJs);
|
|
357
|
+
supportedPictureSizesResult.put(rear);
|
|
358
|
+
|
|
359
|
+
List<Size> frontSizes = CameraXView.getSupportedPictureSizes("front");
|
|
360
|
+
JSObject front = new JSObject();
|
|
361
|
+
front.put("facing", "front");
|
|
362
|
+
JSArray frontSizesJs = new JSArray();
|
|
363
|
+
for (Size size : frontSizes) {
|
|
364
|
+
JSObject sizeJs = new JSObject();
|
|
365
|
+
sizeJs.put("width", size.getWidth());
|
|
366
|
+
sizeJs.put("height", size.getHeight());
|
|
367
|
+
frontSizesJs.put(sizeJs);
|
|
368
|
+
}
|
|
369
|
+
front.put("supportedPictureSizes", frontSizesJs);
|
|
370
|
+
supportedPictureSizesResult.put(front);
|
|
366
371
|
|
|
367
|
-
|
|
372
|
+
JSObject ret = new JSObject();
|
|
373
|
+
ret.put("supportedPictureSizes", supportedPictureSizesResult);
|
|
374
|
+
call.resolve(ret);
|
|
368
375
|
}
|
|
369
376
|
|
|
370
377
|
@PluginMethod
|
|
371
|
-
public void
|
|
372
|
-
if (!
|
|
378
|
+
public void setOpacity(PluginCall call) {
|
|
379
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
373
380
|
call.reject("Camera is not running");
|
|
374
381
|
return;
|
|
375
382
|
}
|
|
383
|
+
Float opacity = call.getFloat("opacity", 1.0f);
|
|
384
|
+
cameraXView.setOpacity(opacity);
|
|
385
|
+
call.resolve();
|
|
386
|
+
}
|
|
376
387
|
|
|
377
|
-
|
|
388
|
+
@PluginMethod
|
|
389
|
+
public void getHorizontalFov(PluginCall call) {
|
|
390
|
+
// CameraX does not provide a simple way to get FoV.
|
|
391
|
+
// This would require Camera2 interop to access camera characteristics.
|
|
392
|
+
// Returning a default/estimated value.
|
|
393
|
+
JSObject ret = new JSObject();
|
|
394
|
+
ret.put("result", 60.0); // A common default FoV
|
|
395
|
+
call.resolve(ret);
|
|
396
|
+
}
|
|
378
397
|
|
|
379
|
-
|
|
380
|
-
|
|
398
|
+
@PluginMethod
|
|
399
|
+
public void getDeviceId(PluginCall call) {
|
|
400
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
401
|
+
call.reject("Camera is not running");
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
JSObject ret = new JSObject();
|
|
405
|
+
ret.put("deviceId", cameraXView.getCurrentDeviceId());
|
|
406
|
+
call.resolve(ret);
|
|
407
|
+
}
|
|
381
408
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
409
|
+
@PluginMethod
|
|
410
|
+
public void getFlashMode(PluginCall call) {
|
|
411
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
412
|
+
call.reject("Camera is not running");
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
JSObject ret = new JSObject();
|
|
416
|
+
ret.put("flashMode", cameraXView.getFlashMode());
|
|
417
|
+
call.resolve(ret);
|
|
418
|
+
}
|
|
388
419
|
|
|
389
|
-
|
|
390
|
-
|
|
420
|
+
@PluginMethod
|
|
421
|
+
public void isRunning(PluginCall call) {
|
|
422
|
+
boolean running = cameraXView != null && cameraXView.isRunning();
|
|
423
|
+
JSObject jsObject = new JSObject();
|
|
424
|
+
jsObject.put("isRunning", running);
|
|
425
|
+
call.resolve(jsObject);
|
|
391
426
|
}
|
|
392
427
|
|
|
393
428
|
@PermissionCallback
|
|
394
429
|
private void handleCameraPermissionResult(PluginCall call) {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
430
|
+
if (
|
|
431
|
+
PermissionState.GRANTED.equals(
|
|
432
|
+
getPermissionState(CAMERA_ONLY_PERMISSION_ALIAS)
|
|
433
|
+
) ||
|
|
434
|
+
PermissionState.GRANTED.equals(
|
|
435
|
+
getPermissionState(CAMERA_WITH_AUDIO_PERMISSION_ALIAS)
|
|
436
|
+
)
|
|
437
|
+
) {
|
|
401
438
|
startCamera(call);
|
|
402
439
|
} else {
|
|
403
440
|
call.reject("Permission failed");
|
|
@@ -405,327 +442,753 @@ public class CameraPreview
|
|
|
405
442
|
}
|
|
406
443
|
|
|
407
444
|
private void startCamera(final PluginCall call) {
|
|
408
|
-
String
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
final Integer
|
|
420
|
-
|
|
421
|
-
final
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
445
|
+
String positionParam = call.getString("position");
|
|
446
|
+
String originalDeviceId = call.getString("deviceId");
|
|
447
|
+
String deviceId = originalDeviceId; // Use a mutable variable
|
|
448
|
+
|
|
449
|
+
final String position = (positionParam == null ||
|
|
450
|
+
positionParam.isEmpty() ||
|
|
451
|
+
"rear".equals(positionParam) ||
|
|
452
|
+
"back".equals(positionParam))
|
|
453
|
+
? "back"
|
|
454
|
+
: "front";
|
|
455
|
+
// Use -1 as default to indicate centering is needed when x/y not provided
|
|
456
|
+
final Integer xParam = call.getInt("x");
|
|
457
|
+
final Integer yParam = call.getInt("y");
|
|
458
|
+
final int x = xParam != null ? xParam : -1;
|
|
459
|
+
final int y = yParam != null ? yParam : -1;
|
|
460
|
+
|
|
461
|
+
Log.d("CameraPreview", "========================");
|
|
462
|
+
Log.d("CameraPreview", "CAMERA POSITION TRACKING START:");
|
|
463
|
+
Log.d(
|
|
464
|
+
"CameraPreview",
|
|
465
|
+
"1. RAW PARAMS - xParam: " + xParam + ", yParam: " + yParam
|
|
427
466
|
);
|
|
428
|
-
|
|
429
|
-
|
|
467
|
+
Log.d(
|
|
468
|
+
"CameraPreview",
|
|
469
|
+
"2. AFTER DEFAULT - x: " +
|
|
470
|
+
x +
|
|
471
|
+
" (center=" +
|
|
472
|
+
(x == -1) +
|
|
473
|
+
"), y: " +
|
|
474
|
+
y +
|
|
475
|
+
" (center=" +
|
|
476
|
+
(y == -1) +
|
|
477
|
+
")"
|
|
430
478
|
);
|
|
431
|
-
final
|
|
479
|
+
final int width = call.getInt("width", 0);
|
|
480
|
+
final int height = call.getInt("height", 0);
|
|
481
|
+
final int paddingBottom = call.getInt("paddingBottom", 0);
|
|
482
|
+
final boolean toBack = Boolean.TRUE.equals(call.getBoolean("toBack", true));
|
|
483
|
+
final boolean storeToFile = Boolean.TRUE.equals(
|
|
432
484
|
call.getBoolean("storeToFile", false)
|
|
433
485
|
);
|
|
434
|
-
final
|
|
486
|
+
final boolean enableOpacity = Boolean.TRUE.equals(
|
|
435
487
|
call.getBoolean("enableOpacity", false)
|
|
436
488
|
);
|
|
437
|
-
final
|
|
489
|
+
final boolean enableZoom = Boolean.TRUE.equals(
|
|
438
490
|
call.getBoolean("enableZoom", false)
|
|
439
491
|
);
|
|
440
|
-
final
|
|
492
|
+
final boolean disableExifHeaderStripping = Boolean.TRUE.equals(
|
|
441
493
|
call.getBoolean("disableExifHeaderStripping", false)
|
|
442
494
|
);
|
|
443
|
-
final
|
|
495
|
+
final boolean lockOrientation = Boolean.TRUE.equals(
|
|
444
496
|
call.getBoolean("lockAndroidOrientation", false)
|
|
445
497
|
);
|
|
498
|
+
final boolean disableAudio = Boolean.TRUE.equals(
|
|
499
|
+
call.getBoolean("disableAudio", true)
|
|
500
|
+
);
|
|
501
|
+
final String aspectRatio = call.getString("aspectRatio", "4:3");
|
|
502
|
+
final String gridMode = call.getString("gridMode", "none");
|
|
503
|
+
final String positioning = call.getString("positioning", "top");
|
|
504
|
+
final float initialZoomLevel = call.getFloat("initialZoomLevel", 1.0f);
|
|
505
|
+
|
|
506
|
+
// Check for conflict between aspectRatio and size
|
|
507
|
+
if (
|
|
508
|
+
call.getData().has("aspectRatio") &&
|
|
509
|
+
(call.getData().has("width") || call.getData().has("height"))
|
|
510
|
+
) {
|
|
511
|
+
call.reject(
|
|
512
|
+
"Cannot set both aspectRatio and size (width/height). Use setPreviewSize after start."
|
|
513
|
+
);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
float targetZoom = initialZoomLevel;
|
|
518
|
+
// Check if the selected device is a physical ultra-wide
|
|
519
|
+
if (originalDeviceId != null) {
|
|
520
|
+
List<CameraDevice> devices = CameraXView.getAvailableDevicesStatic(
|
|
521
|
+
getContext()
|
|
522
|
+
);
|
|
523
|
+
for (CameraDevice device : devices) {
|
|
524
|
+
if (
|
|
525
|
+
originalDeviceId.equals(device.getDeviceId()) && !device.isLogical()
|
|
526
|
+
) {
|
|
527
|
+
for (LensInfo lens : device.getLenses()) {
|
|
528
|
+
if ("ultraWide".equals(lens.getDeviceType())) {
|
|
529
|
+
Log.d(
|
|
530
|
+
"CameraPreview",
|
|
531
|
+
"Ultra-wide lens selected. Targeting 0.5x zoom on logical camera."
|
|
532
|
+
);
|
|
533
|
+
targetZoom = 0.5f;
|
|
534
|
+
// Force the use of the logical camera by clearing the specific deviceId
|
|
535
|
+
deviceId = null;
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if (deviceId == null) break; // Exit outer loop once we've made our decision
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
446
544
|
previousOrientationRequest = getBridge()
|
|
447
545
|
.getActivity()
|
|
448
546
|
.getRequestedOrientation();
|
|
547
|
+
cameraXView = new CameraXView(getContext(), getBridge().getWebView());
|
|
548
|
+
cameraXView.setListener(this);
|
|
449
549
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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
|
|
550
|
+
String finalDeviceId = deviceId;
|
|
551
|
+
float finalTargetZoom = targetZoom;
|
|
552
|
+
getBridge()
|
|
464
553
|
.getActivity()
|
|
465
|
-
.runOnUiThread(
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
getBridge()
|
|
476
|
-
.getActivity()
|
|
477
|
-
.setRequestedOrientation(
|
|
478
|
-
ActivityInfo.SCREEN_ORIENTATION_LOCKED
|
|
479
|
-
);
|
|
480
|
-
}
|
|
554
|
+
.runOnUiThread(() -> {
|
|
555
|
+
DisplayMetrics metrics = getBridge()
|
|
556
|
+
.getActivity()
|
|
557
|
+
.getResources()
|
|
558
|
+
.getDisplayMetrics();
|
|
559
|
+
if (lockOrientation) {
|
|
560
|
+
getBridge()
|
|
561
|
+
.getActivity()
|
|
562
|
+
.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
|
|
563
|
+
}
|
|
481
564
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
565
|
+
// Debug: Let's check all the positioning information
|
|
566
|
+
ViewGroup webViewParent = (ViewGroup) getBridge()
|
|
567
|
+
.getWebView()
|
|
568
|
+
.getParent();
|
|
569
|
+
|
|
570
|
+
// Get webview position in different coordinate systems
|
|
571
|
+
int[] webViewLocationInWindow = new int[2];
|
|
572
|
+
int[] webViewLocationOnScreen = new int[2];
|
|
573
|
+
getBridge().getWebView().getLocationInWindow(webViewLocationInWindow);
|
|
574
|
+
getBridge().getWebView().getLocationOnScreen(webViewLocationOnScreen);
|
|
575
|
+
|
|
576
|
+
int webViewLeft = getBridge().getWebView().getLeft();
|
|
577
|
+
int webViewTop = getBridge().getWebView().getTop();
|
|
578
|
+
|
|
579
|
+
// Check parent position too
|
|
580
|
+
int[] parentLocationInWindow = new int[2];
|
|
581
|
+
int[] parentLocationOnScreen = new int[2];
|
|
582
|
+
webViewParent.getLocationInWindow(parentLocationInWindow);
|
|
583
|
+
webViewParent.getLocationOnScreen(parentLocationOnScreen);
|
|
584
|
+
|
|
585
|
+
// Calculate pixel ratio
|
|
586
|
+
float pixelRatio = metrics.density;
|
|
587
|
+
|
|
588
|
+
// The key insight: JavaScript coordinates are relative to the WebView's viewport
|
|
589
|
+
// If the WebView is positioned below the status bar (webViewLocationOnScreen[1] > 0),
|
|
590
|
+
// we need to add that offset when placing native views
|
|
591
|
+
int webViewTopInset = webViewLocationOnScreen[1];
|
|
592
|
+
boolean isEdgeToEdgeActive = webViewLocationOnScreen[1] > 0;
|
|
593
|
+
|
|
594
|
+
// Log all the positioning information for debugging
|
|
595
|
+
Log.d("CameraPreview", "WebView Position Debug:");
|
|
596
|
+
Log.d("CameraPreview", " - webView.getTop(): " + webViewTop);
|
|
597
|
+
Log.d("CameraPreview", " - webView.getLeft(): " + webViewLeft);
|
|
598
|
+
Log.d(
|
|
599
|
+
"CameraPreview",
|
|
600
|
+
" - webView locationInWindow: (" +
|
|
601
|
+
webViewLocationInWindow[0] +
|
|
602
|
+
", " +
|
|
603
|
+
webViewLocationInWindow[1] +
|
|
604
|
+
")"
|
|
605
|
+
);
|
|
606
|
+
Log.d(
|
|
607
|
+
"CameraPreview",
|
|
608
|
+
" - webView locationOnScreen: (" +
|
|
609
|
+
webViewLocationOnScreen[0] +
|
|
610
|
+
", " +
|
|
611
|
+
webViewLocationOnScreen[1] +
|
|
612
|
+
")"
|
|
613
|
+
);
|
|
614
|
+
Log.d(
|
|
615
|
+
"CameraPreview",
|
|
616
|
+
" - parent locationInWindow: (" +
|
|
617
|
+
parentLocationInWindow[0] +
|
|
618
|
+
", " +
|
|
619
|
+
parentLocationInWindow[1] +
|
|
620
|
+
")"
|
|
621
|
+
);
|
|
622
|
+
Log.d(
|
|
623
|
+
"CameraPreview",
|
|
624
|
+
" - parent locationOnScreen: (" +
|
|
625
|
+
parentLocationOnScreen[0] +
|
|
626
|
+
", " +
|
|
627
|
+
parentLocationOnScreen[1] +
|
|
628
|
+
")"
|
|
629
|
+
);
|
|
493
630
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
631
|
+
// Check if WebView has margins
|
|
632
|
+
View webView = getBridge().getWebView();
|
|
633
|
+
ViewGroup.LayoutParams webViewLayoutParams = webView.getLayoutParams();
|
|
634
|
+
if (webViewLayoutParams instanceof ViewGroup.MarginLayoutParams) {
|
|
635
|
+
ViewGroup.MarginLayoutParams marginParams =
|
|
636
|
+
(ViewGroup.MarginLayoutParams) webViewLayoutParams;
|
|
637
|
+
Log.d(
|
|
638
|
+
"CameraPreview",
|
|
639
|
+
" - webView margins: left=" +
|
|
640
|
+
marginParams.leftMargin +
|
|
641
|
+
", top=" +
|
|
642
|
+
marginParams.topMargin +
|
|
643
|
+
", right=" +
|
|
644
|
+
marginParams.rightMargin +
|
|
645
|
+
", bottom=" +
|
|
646
|
+
marginParams.bottomMargin
|
|
647
|
+
);
|
|
648
|
+
}
|
|
498
649
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
650
|
+
// Check WebView padding
|
|
651
|
+
Log.d(
|
|
652
|
+
"CameraPreview",
|
|
653
|
+
" - webView padding: left=" +
|
|
654
|
+
webView.getPaddingLeft() +
|
|
655
|
+
", top=" +
|
|
656
|
+
webView.getPaddingTop() +
|
|
657
|
+
", right=" +
|
|
658
|
+
webView.getPaddingRight() +
|
|
659
|
+
", bottom=" +
|
|
660
|
+
webView.getPaddingBottom()
|
|
661
|
+
);
|
|
508
662
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
663
|
+
Log.d("CameraPreview", " - Using webViewTopInset: " + webViewTopInset);
|
|
664
|
+
Log.d("CameraPreview", " - isEdgeToEdgeActive: " + isEdgeToEdgeActive);
|
|
665
|
+
|
|
666
|
+
// Calculate position - center if x or y is -1
|
|
667
|
+
int computedX;
|
|
668
|
+
int computedY;
|
|
669
|
+
|
|
670
|
+
// Calculate dimensions first
|
|
671
|
+
int computedWidth = width != 0
|
|
672
|
+
? (int) (width * pixelRatio)
|
|
673
|
+
: getBridge().getWebView().getWidth();
|
|
674
|
+
int computedHeight = height != 0
|
|
675
|
+
? (int) (height * pixelRatio)
|
|
676
|
+
: getBridge().getWebView().getHeight();
|
|
677
|
+
computedHeight -= (int) (paddingBottom * pixelRatio);
|
|
678
|
+
|
|
679
|
+
Log.d("CameraPreview", "========================");
|
|
680
|
+
Log.d("CameraPreview", "POSITIONING CALCULATIONS:");
|
|
681
|
+
Log.d(
|
|
682
|
+
"CameraPreview",
|
|
683
|
+
"1. INPUT - x: " +
|
|
684
|
+
x +
|
|
685
|
+
", y: " +
|
|
686
|
+
y +
|
|
687
|
+
", width: " +
|
|
688
|
+
width +
|
|
689
|
+
", height: " +
|
|
690
|
+
height
|
|
691
|
+
);
|
|
692
|
+
Log.d("CameraPreview", "2. PIXEL RATIO: " + pixelRatio);
|
|
693
|
+
Log.d(
|
|
694
|
+
"CameraPreview",
|
|
695
|
+
"3. SCREEN - width: " +
|
|
696
|
+
metrics.widthPixels +
|
|
697
|
+
", height: " +
|
|
698
|
+
metrics.heightPixels
|
|
699
|
+
);
|
|
700
|
+
Log.d(
|
|
701
|
+
"CameraPreview",
|
|
702
|
+
"4. WEBVIEW - width: " +
|
|
703
|
+
getBridge().getWebView().getWidth() +
|
|
704
|
+
", height: " +
|
|
705
|
+
getBridge().getWebView().getHeight()
|
|
706
|
+
);
|
|
707
|
+
Log.d(
|
|
708
|
+
"CameraPreview",
|
|
709
|
+
"5. COMPUTED DIMENSIONS - width: " +
|
|
710
|
+
computedWidth +
|
|
711
|
+
", height: " +
|
|
712
|
+
computedHeight
|
|
713
|
+
);
|
|
529
714
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
715
|
+
if (x == -1) {
|
|
716
|
+
// Center horizontally
|
|
717
|
+
int screenWidth = metrics.widthPixels;
|
|
718
|
+
computedX = (screenWidth - computedWidth) / 2;
|
|
719
|
+
Log.d(
|
|
720
|
+
"CameraPreview",
|
|
721
|
+
"Centering horizontally: screenWidth=" +
|
|
722
|
+
screenWidth +
|
|
723
|
+
", computedWidth=" +
|
|
724
|
+
computedWidth +
|
|
725
|
+
", computedX=" +
|
|
726
|
+
computedX
|
|
727
|
+
);
|
|
728
|
+
} else {
|
|
729
|
+
computedX = (int) (x * pixelRatio);
|
|
730
|
+
Log.d(
|
|
731
|
+
"CameraPreview",
|
|
732
|
+
"Using provided X position: " +
|
|
733
|
+
x +
|
|
734
|
+
" * " +
|
|
735
|
+
pixelRatio +
|
|
736
|
+
" = " +
|
|
737
|
+
computedX
|
|
738
|
+
);
|
|
739
|
+
}
|
|
554
740
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
computedWidth,
|
|
559
|
-
computedHeight
|
|
560
|
-
);
|
|
741
|
+
if (y == -1) {
|
|
742
|
+
// Position vertically based on positioning parameter
|
|
743
|
+
int screenHeight = metrics.heightPixels;
|
|
561
744
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
745
|
+
switch (positioning) {
|
|
746
|
+
case "top":
|
|
747
|
+
computedY = 0;
|
|
748
|
+
Log.d("CameraPreview", "Positioning at top: computedY=0");
|
|
749
|
+
break;
|
|
750
|
+
case "bottom":
|
|
751
|
+
computedY = screenHeight - computedHeight;
|
|
752
|
+
Log.d(
|
|
753
|
+
"CameraPreview",
|
|
754
|
+
"Positioning at bottom: screenHeight=" +
|
|
755
|
+
screenHeight +
|
|
756
|
+
", computedHeight=" +
|
|
757
|
+
computedHeight +
|
|
758
|
+
", computedY=" +
|
|
759
|
+
computedY
|
|
568
760
|
);
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
761
|
+
break;
|
|
762
|
+
case "center":
|
|
763
|
+
default:
|
|
764
|
+
// Center vertically
|
|
765
|
+
if (isEdgeToEdgeActive) {
|
|
766
|
+
// When WebView is offset from top, center within the available space
|
|
767
|
+
// The camera should be centered in the full screen, not just the WebView area
|
|
768
|
+
computedY = (screenHeight - computedHeight) / 2;
|
|
769
|
+
Log.d(
|
|
770
|
+
"CameraPreview",
|
|
771
|
+
"Centering vertically with WebView offset: screenHeight=" +
|
|
772
|
+
screenHeight +
|
|
773
|
+
", webViewTop=" +
|
|
774
|
+
webViewTopInset +
|
|
775
|
+
", computedHeight=" +
|
|
776
|
+
computedHeight +
|
|
777
|
+
", computedY=" +
|
|
778
|
+
computedY
|
|
779
|
+
);
|
|
780
|
+
} else {
|
|
781
|
+
// Normal mode - use full screen height
|
|
782
|
+
computedY = (screenHeight - computedHeight) / 2;
|
|
783
|
+
Log.d(
|
|
784
|
+
"CameraPreview",
|
|
785
|
+
"Centering vertically (normal): screenHeight=" +
|
|
786
|
+
screenHeight +
|
|
787
|
+
", computedHeight=" +
|
|
788
|
+
computedHeight +
|
|
789
|
+
", computedY=" +
|
|
790
|
+
computedY
|
|
574
791
|
);
|
|
575
|
-
if (toBack) {
|
|
576
|
-
getBridge()
|
|
577
|
-
.getWebView()
|
|
578
|
-
.getParent()
|
|
579
|
-
.bringChildToFront(getBridge().getWebView());
|
|
580
|
-
setupBroadcast();
|
|
581
792
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
} else {
|
|
600
|
-
call.reject("camera already started");
|
|
601
|
-
}
|
|
793
|
+
break;
|
|
794
|
+
}
|
|
795
|
+
} else {
|
|
796
|
+
computedY = (int) (y * pixelRatio);
|
|
797
|
+
// If edge-to-edge is active, JavaScript Y is relative to WebView content area
|
|
798
|
+
// We need to add the inset to get absolute screen position
|
|
799
|
+
if (isEdgeToEdgeActive) {
|
|
800
|
+
computedY += webViewTopInset;
|
|
801
|
+
Log.d(
|
|
802
|
+
"CameraPreview",
|
|
803
|
+
"Edge-to-edge adjustment: Y position " +
|
|
804
|
+
(int) (y * pixelRatio) +
|
|
805
|
+
" + inset " +
|
|
806
|
+
webViewTopInset +
|
|
807
|
+
" = " +
|
|
808
|
+
computedY
|
|
809
|
+
);
|
|
602
810
|
}
|
|
811
|
+
Log.d(
|
|
812
|
+
"CameraPreview",
|
|
813
|
+
"Using provided Y position: " +
|
|
814
|
+
y +
|
|
815
|
+
" * " +
|
|
816
|
+
pixelRatio +
|
|
817
|
+
" = " +
|
|
818
|
+
computedY +
|
|
819
|
+
(isEdgeToEdgeActive ? " (adjusted for edge-to-edge)" : "")
|
|
820
|
+
);
|
|
603
821
|
}
|
|
604
|
-
);
|
|
605
|
-
}
|
|
606
822
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
823
|
+
Log.d(
|
|
824
|
+
"CameraPreview",
|
|
825
|
+
"2b. EDGE-TO-EDGE - " +
|
|
826
|
+
(isEdgeToEdgeActive
|
|
827
|
+
? "ACTIVE (inset=" + webViewTopInset + ")"
|
|
828
|
+
: "INACTIVE")
|
|
829
|
+
);
|
|
830
|
+
Log.d(
|
|
831
|
+
"CameraPreview",
|
|
832
|
+
"3. COMPUTED POSITION - x=" + computedX + ", y=" + computedY
|
|
833
|
+
);
|
|
834
|
+
Log.d(
|
|
835
|
+
"CameraPreview",
|
|
836
|
+
"4. COMPUTED SIZE - width=" +
|
|
837
|
+
computedWidth +
|
|
838
|
+
", height=" +
|
|
839
|
+
computedHeight
|
|
840
|
+
);
|
|
841
|
+
Log.d("CameraPreview", "=== COORDINATE DEBUG ===");
|
|
842
|
+
Log.d(
|
|
843
|
+
"CameraPreview",
|
|
844
|
+
"WebView getLeft/getTop: (" + webViewLeft + ", " + webViewTop + ")"
|
|
845
|
+
);
|
|
846
|
+
Log.d(
|
|
847
|
+
"CameraPreview",
|
|
848
|
+
"WebView locationInWindow: (" +
|
|
849
|
+
webViewLocationInWindow[0] +
|
|
850
|
+
", " +
|
|
851
|
+
webViewLocationInWindow[1] +
|
|
852
|
+
")"
|
|
853
|
+
);
|
|
854
|
+
Log.d(
|
|
855
|
+
"CameraPreview",
|
|
856
|
+
"WebView locationOnScreen: (" +
|
|
857
|
+
webViewLocationOnScreen[0] +
|
|
858
|
+
", " +
|
|
859
|
+
webViewLocationOnScreen[1] +
|
|
860
|
+
")"
|
|
861
|
+
);
|
|
862
|
+
Log.d(
|
|
863
|
+
"CameraPreview",
|
|
864
|
+
"Parent locationInWindow: (" +
|
|
865
|
+
parentLocationInWindow[0] +
|
|
866
|
+
", " +
|
|
867
|
+
parentLocationInWindow[1] +
|
|
868
|
+
")"
|
|
869
|
+
);
|
|
870
|
+
Log.d(
|
|
871
|
+
"CameraPreview",
|
|
872
|
+
"Parent locationOnScreen: (" +
|
|
873
|
+
parentLocationOnScreen[0] +
|
|
874
|
+
", " +
|
|
875
|
+
parentLocationOnScreen[1] +
|
|
876
|
+
")"
|
|
877
|
+
);
|
|
878
|
+
Log.d(
|
|
879
|
+
"CameraPreview",
|
|
880
|
+
"Parent class: " + webViewParent.getClass().getSimpleName()
|
|
881
|
+
);
|
|
882
|
+
Log.d(
|
|
883
|
+
"CameraPreview",
|
|
884
|
+
"Requested position (logical): (" + x + ", " + y + ")"
|
|
885
|
+
);
|
|
886
|
+
Log.d("CameraPreview", "Pixel ratio: " + pixelRatio);
|
|
887
|
+
Log.d(
|
|
888
|
+
"CameraPreview",
|
|
889
|
+
"Final computed position (no offset): (" +
|
|
890
|
+
computedX +
|
|
891
|
+
", " +
|
|
892
|
+
computedY +
|
|
893
|
+
")"
|
|
894
|
+
);
|
|
895
|
+
Log.d("CameraPreview", "5. IS_CENTERED - " + (x == -1 || y == -1));
|
|
896
|
+
Log.d("CameraPreview", "========================");
|
|
897
|
+
|
|
898
|
+
// Pass along whether we're centering so CameraXView knows not to add insets
|
|
899
|
+
boolean isCentered = (x == -1 || y == -1);
|
|
900
|
+
|
|
901
|
+
CameraSessionConfiguration config = new CameraSessionConfiguration(
|
|
902
|
+
finalDeviceId,
|
|
903
|
+
position,
|
|
904
|
+
computedX,
|
|
905
|
+
computedY,
|
|
906
|
+
computedWidth,
|
|
907
|
+
computedHeight,
|
|
908
|
+
paddingBottom,
|
|
909
|
+
toBack,
|
|
910
|
+
storeToFile,
|
|
911
|
+
enableOpacity,
|
|
912
|
+
enableZoom,
|
|
913
|
+
disableExifHeaderStripping,
|
|
914
|
+
disableAudio,
|
|
915
|
+
1.0f,
|
|
916
|
+
aspectRatio,
|
|
917
|
+
gridMode
|
|
918
|
+
);
|
|
919
|
+
config.setTargetZoom(finalTargetZoom);
|
|
920
|
+
config.setCentered(isCentered);
|
|
921
|
+
|
|
922
|
+
bridge.saveCall(call);
|
|
923
|
+
cameraStartCallbackId = call.getCallbackId();
|
|
924
|
+
cameraXView.startSession(config);
|
|
925
|
+
});
|
|
610
926
|
}
|
|
611
927
|
|
|
612
928
|
@Override
|
|
613
|
-
public void onPictureTaken(String
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
929
|
+
public void onPictureTaken(String base64, JSONObject exif) {
|
|
930
|
+
PluginCall pluginCall = bridge.getSavedCall(captureCallbackId);
|
|
931
|
+
if (pluginCall == null) {
|
|
932
|
+
Log.e("CameraPreview", "onPictureTaken: captureCallbackId is null");
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
JSObject result = new JSObject();
|
|
936
|
+
result.put("value", base64);
|
|
937
|
+
result.put("exif", exif);
|
|
938
|
+
pluginCall.resolve(result);
|
|
939
|
+
bridge.releaseCall(pluginCall);
|
|
617
940
|
}
|
|
618
941
|
|
|
619
942
|
@Override
|
|
620
943
|
public void onPictureTakenError(String message) {
|
|
621
|
-
bridge.getSavedCall(captureCallbackId)
|
|
944
|
+
PluginCall pluginCall = bridge.getSavedCall(captureCallbackId);
|
|
945
|
+
if (pluginCall == null) {
|
|
946
|
+
Log.e("CameraPreview", "onPictureTakenError: captureCallbackId is null");
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
pluginCall.reject(message);
|
|
950
|
+
bridge.releaseCall(pluginCall);
|
|
622
951
|
}
|
|
623
952
|
|
|
624
953
|
@Override
|
|
625
|
-
public void
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
954
|
+
public void onCameraStarted(int width, int height, int x, int y) {
|
|
955
|
+
PluginCall call = bridge.getSavedCall(cameraStartCallbackId);
|
|
956
|
+
if (call != null) {
|
|
957
|
+
// Convert pixel values back to logical units
|
|
958
|
+
DisplayMetrics metrics = getBridge()
|
|
959
|
+
.getActivity()
|
|
960
|
+
.getResources()
|
|
961
|
+
.getDisplayMetrics();
|
|
962
|
+
float pixelRatio = metrics.density;
|
|
963
|
+
|
|
964
|
+
// When WebView is offset from the top (e.g., below status bar),
|
|
965
|
+
// we need to convert between JavaScript coordinates (relative to WebView)
|
|
966
|
+
// and native coordinates (relative to screen)
|
|
967
|
+
WebView webView = getBridge().getWebView();
|
|
968
|
+
int webViewTopInset = 0;
|
|
969
|
+
boolean isEdgeToEdgeActive = false;
|
|
970
|
+
if (webView != null) {
|
|
971
|
+
int[] location = new int[2];
|
|
972
|
+
webView.getLocationOnScreen(location);
|
|
973
|
+
webViewTopInset = location[1];
|
|
974
|
+
isEdgeToEdgeActive = webViewTopInset > 0;
|
|
975
|
+
}
|
|
630
976
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
977
|
+
// Only convert to relative position if edge-to-edge is active
|
|
978
|
+
int relativeY = isEdgeToEdgeActive ? (y - webViewTopInset) : y;
|
|
979
|
+
|
|
980
|
+
Log.d("CameraPreview", "========================");
|
|
981
|
+
Log.d("CameraPreview", "CAMERA STARTED - POSITION RETURNED:");
|
|
982
|
+
Log.d(
|
|
983
|
+
"CameraPreview",
|
|
984
|
+
"7. RETURNED (pixels) - x=" +
|
|
985
|
+
x +
|
|
986
|
+
", y=" +
|
|
987
|
+
y +
|
|
988
|
+
", width=" +
|
|
989
|
+
width +
|
|
990
|
+
", height=" +
|
|
991
|
+
height
|
|
992
|
+
);
|
|
993
|
+
Log.d(
|
|
994
|
+
"CameraPreview",
|
|
995
|
+
"8. EDGE-TO-EDGE - " + (isEdgeToEdgeActive ? "ACTIVE" : "INACTIVE")
|
|
996
|
+
);
|
|
997
|
+
Log.d("CameraPreview", "9. WEBVIEW INSET - " + webViewTopInset);
|
|
998
|
+
Log.d(
|
|
999
|
+
"CameraPreview",
|
|
1000
|
+
"10. RELATIVE Y - " +
|
|
1001
|
+
relativeY +
|
|
1002
|
+
" (y=" +
|
|
1003
|
+
y +
|
|
1004
|
+
(isEdgeToEdgeActive ? " - inset=" + webViewTopInset : " unchanged") +
|
|
1005
|
+
")"
|
|
1006
|
+
);
|
|
1007
|
+
Log.d(
|
|
1008
|
+
"CameraPreview",
|
|
1009
|
+
"11. RETURNED (logical) - x=" +
|
|
1010
|
+
(x / pixelRatio) +
|
|
1011
|
+
", y=" +
|
|
1012
|
+
(relativeY / pixelRatio) +
|
|
1013
|
+
", width=" +
|
|
1014
|
+
(width / pixelRatio) +
|
|
1015
|
+
", height=" +
|
|
1016
|
+
(height / pixelRatio)
|
|
1017
|
+
);
|
|
1018
|
+
Log.d("CameraPreview", "12. PIXEL RATIO - " + pixelRatio);
|
|
1019
|
+
Log.d("CameraPreview", "========================");
|
|
1020
|
+
|
|
1021
|
+
JSObject result = new JSObject();
|
|
1022
|
+
result.put("width", width / pixelRatio);
|
|
1023
|
+
result.put("height", height / pixelRatio);
|
|
1024
|
+
result.put("x", x / pixelRatio);
|
|
1025
|
+
result.put("y", relativeY / pixelRatio);
|
|
1026
|
+
call.resolve(result);
|
|
1027
|
+
bridge.releaseCall(call);
|
|
1028
|
+
cameraStartCallbackId = null; // Prevent re-use
|
|
1029
|
+
}
|
|
634
1030
|
}
|
|
635
1031
|
|
|
636
1032
|
@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);
|
|
1033
|
+
public void onSampleTaken(String result) {
|
|
1034
|
+
// Handle sample taken if needed
|
|
1035
|
+
Log.i("CameraPreview", "Sample taken: " + result);
|
|
650
1036
|
}
|
|
651
1037
|
|
|
652
1038
|
@Override
|
|
653
|
-
public void
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
public void onStartRecordVideoError(String message) {
|
|
657
|
-
bridge.getSavedCall(recordCallbackId).reject(message);
|
|
1039
|
+
public void onSampleTakenError(String message) {
|
|
1040
|
+
// Handle sample taken error if needed
|
|
1041
|
+
Log.e("CameraPreview", "Sample taken error: " + message);
|
|
658
1042
|
}
|
|
659
1043
|
|
|
660
1044
|
@Override
|
|
661
|
-
public void
|
|
662
|
-
PluginCall
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
1045
|
+
public void onCameraStartError(String message) {
|
|
1046
|
+
PluginCall call = bridge.getSavedCall(cameraStartCallbackId);
|
|
1047
|
+
if (call != null) {
|
|
1048
|
+
call.reject(message);
|
|
1049
|
+
bridge.releaseCall(call);
|
|
1050
|
+
cameraStartCallbackId = null;
|
|
1051
|
+
}
|
|
666
1052
|
}
|
|
667
1053
|
|
|
668
|
-
@
|
|
669
|
-
public void
|
|
670
|
-
|
|
1054
|
+
@PluginMethod
|
|
1055
|
+
public void setAspectRatio(PluginCall call) {
|
|
1056
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
1057
|
+
call.reject("Camera is not running");
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
String aspectRatio = call.getString("aspectRatio", "4:3");
|
|
1061
|
+
Float x = call.getFloat("x");
|
|
1062
|
+
Float y = call.getFloat("y");
|
|
1063
|
+
|
|
1064
|
+
getActivity()
|
|
1065
|
+
.runOnUiThread(() -> {
|
|
1066
|
+
cameraXView.setAspectRatio(aspectRatio, x, y, () -> {
|
|
1067
|
+
// Return the actual preview bounds after layout and camera operations are complete
|
|
1068
|
+
int[] bounds = cameraXView.getCurrentPreviewBounds();
|
|
1069
|
+
JSObject ret = new JSObject();
|
|
1070
|
+
ret.put("x", bounds[0]);
|
|
1071
|
+
ret.put("y", bounds[1]);
|
|
1072
|
+
ret.put("width", bounds[2]);
|
|
1073
|
+
ret.put("height", bounds[3]);
|
|
1074
|
+
call.resolve(ret);
|
|
1075
|
+
});
|
|
1076
|
+
});
|
|
671
1077
|
}
|
|
672
1078
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
1079
|
+
@PluginMethod
|
|
1080
|
+
public void getAspectRatio(PluginCall call) {
|
|
1081
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
1082
|
+
call.reject("Camera is not running");
|
|
1083
|
+
return;
|
|
676
1084
|
}
|
|
1085
|
+
String aspectRatio = cameraXView.getAspectRatio();
|
|
1086
|
+
JSObject ret = new JSObject();
|
|
1087
|
+
ret.put("aspectRatio", aspectRatio);
|
|
1088
|
+
call.resolve(ret);
|
|
1089
|
+
}
|
|
677
1090
|
|
|
678
|
-
|
|
1091
|
+
@PluginMethod
|
|
1092
|
+
public void setGridMode(PluginCall call) {
|
|
1093
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
1094
|
+
call.reject("Camera is not running");
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
String gridMode = call.getString("gridMode", "none");
|
|
1098
|
+
getActivity()
|
|
1099
|
+
.runOnUiThread(() -> {
|
|
1100
|
+
cameraXView.setGridMode(gridMode);
|
|
1101
|
+
call.resolve();
|
|
1102
|
+
});
|
|
679
1103
|
}
|
|
680
1104
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
1105
|
+
@PluginMethod
|
|
1106
|
+
public void getGridMode(PluginCall call) {
|
|
1107
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
1108
|
+
call.reject("Camera is not running");
|
|
1109
|
+
return;
|
|
684
1110
|
}
|
|
1111
|
+
JSObject ret = new JSObject();
|
|
1112
|
+
ret.put("gridMode", cameraXView.getGridMode());
|
|
1113
|
+
call.resolve(ret);
|
|
1114
|
+
}
|
|
685
1115
|
|
|
686
|
-
|
|
687
|
-
|
|
1116
|
+
@PluginMethod
|
|
1117
|
+
public void getPreviewSize(PluginCall call) {
|
|
1118
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
1119
|
+
call.reject("Camera is not running");
|
|
1120
|
+
return;
|
|
688
1121
|
}
|
|
689
1122
|
|
|
690
|
-
|
|
1123
|
+
// Convert pixel values back to logical units
|
|
1124
|
+
DisplayMetrics metrics = getBridge()
|
|
1125
|
+
.getActivity()
|
|
1126
|
+
.getResources()
|
|
1127
|
+
.getDisplayMetrics();
|
|
1128
|
+
float pixelRatio = metrics.density;
|
|
1129
|
+
|
|
1130
|
+
JSObject ret = new JSObject();
|
|
1131
|
+
ret.put("x", cameraXView.getPreviewX() / pixelRatio);
|
|
1132
|
+
ret.put("y", cameraXView.getPreviewY() / pixelRatio);
|
|
1133
|
+
ret.put("width", cameraXView.getPreviewWidth() / pixelRatio);
|
|
1134
|
+
ret.put("height", cameraXView.getPreviewHeight() / pixelRatio);
|
|
1135
|
+
call.resolve(ret);
|
|
691
1136
|
}
|
|
692
1137
|
|
|
693
|
-
|
|
694
|
-
|
|
1138
|
+
@PluginMethod
|
|
1139
|
+
public void setPreviewSize(PluginCall call) {
|
|
1140
|
+
if (cameraXView == null || !cameraXView.isRunning()) {
|
|
1141
|
+
call.reject("Camera is not running");
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
695
1144
|
|
|
696
|
-
|
|
1145
|
+
// Get values from call - null values will become 0
|
|
1146
|
+
Integer xParam = call.getInt("x");
|
|
1147
|
+
Integer yParam = call.getInt("y");
|
|
1148
|
+
Integer widthParam = call.getInt("width");
|
|
1149
|
+
Integer heightParam = call.getInt("height");
|
|
697
1150
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
1151
|
+
// Apply pixel ratio conversion to non-null values
|
|
1152
|
+
DisplayMetrics metrics = getBridge()
|
|
1153
|
+
.getActivity()
|
|
1154
|
+
.getResources()
|
|
1155
|
+
.getDisplayMetrics();
|
|
1156
|
+
float pixelRatio = metrics.density;
|
|
1157
|
+
|
|
1158
|
+
// Check if edge-to-edge mode is active
|
|
1159
|
+
WebView webView = getBridge().getWebView();
|
|
1160
|
+
int webViewTopInset = 0;
|
|
1161
|
+
boolean isEdgeToEdgeActive = false;
|
|
1162
|
+
if (webView != null) {
|
|
1163
|
+
int[] location = new int[2];
|
|
1164
|
+
webView.getLocationOnScreen(location);
|
|
1165
|
+
webViewTopInset = location[1];
|
|
1166
|
+
isEdgeToEdgeActive = webViewTopInset > 0;
|
|
704
1167
|
}
|
|
705
1168
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
private void setupBroadcast() {
|
|
710
|
-
/** When touch event is triggered, relay it to camera view if needed so it can support pinch zoom */
|
|
1169
|
+
int x = (xParam != null && xParam > 0) ? (int) (xParam * pixelRatio) : 0;
|
|
1170
|
+
int y = (yParam != null && yParam > 0) ? (int) (yParam * pixelRatio) : 0;
|
|
711
1171
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
);
|
|
1172
|
+
// Add edge-to-edge inset to Y if active
|
|
1173
|
+
if (isEdgeToEdgeActive && y > 0) {
|
|
1174
|
+
y += webViewTopInset;
|
|
1175
|
+
}
|
|
1176
|
+
int width = (widthParam != null && widthParam > 0)
|
|
1177
|
+
? (int) (widthParam * pixelRatio)
|
|
1178
|
+
: 0;
|
|
1179
|
+
int height = (heightParam != null && heightParam > 0)
|
|
1180
|
+
? (int) (heightParam * pixelRatio)
|
|
1181
|
+
: 0;
|
|
1182
|
+
|
|
1183
|
+
cameraXView.setPreviewSize(x, y, width, height, () -> {
|
|
1184
|
+
// Return the actual preview bounds after layout operations are complete
|
|
1185
|
+
int[] bounds = cameraXView.getCurrentPreviewBounds();
|
|
1186
|
+
JSObject ret = new JSObject();
|
|
1187
|
+
ret.put("x", bounds[0]);
|
|
1188
|
+
ret.put("y", bounds[1]);
|
|
1189
|
+
ret.put("width", bounds[2]);
|
|
1190
|
+
ret.put("height", bounds[3]);
|
|
1191
|
+
call.resolve(ret);
|
|
1192
|
+
});
|
|
730
1193
|
}
|
|
731
1194
|
}
|