@capgo/camera-preview 7.4.0-beta.6 → 7.4.0-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -30
- 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/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/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/file-system.probe +0 -0
- package/android/build.gradle +1 -0
- package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraPreview.java +170 -30
- package/android/src/main/java/com/ahm/capacitor/camera/preview/CameraXView.java +510 -15
- package/android/src/main/java/com/ahm/capacitor/camera/preview/GridOverlayView.java +2 -0
- package/dist/docs.json +100 -9
- package/dist/esm/definitions.d.ts +64 -8
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +34 -4
- package/dist/esm/web.js +94 -16
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +94 -16
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +94 -16
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoCameraPreview/CameraController.swift +129 -30
- package/ios/Sources/CapgoCameraPreview/Plugin.swift +334 -79
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import android.hardware.camera2.CameraAccessException;
|
|
|
5
5
|
import android.hardware.camera2.CameraManager;
|
|
6
6
|
import android.os.Build;
|
|
7
7
|
import android.util.Base64;
|
|
8
|
+
import android.util.DisplayMetrics;
|
|
8
9
|
import android.util.Log;
|
|
9
10
|
import android.util.Size;
|
|
10
11
|
import android.view.ViewGroup;
|
|
@@ -68,6 +69,7 @@ import android.widget.FrameLayout;
|
|
|
68
69
|
import androidx.lifecycle.LifecycleObserver;
|
|
69
70
|
import androidx.lifecycle.OnLifecycleEvent;
|
|
70
71
|
import android.util.Rational;
|
|
72
|
+
import android.view.ViewGroup;
|
|
71
73
|
|
|
72
74
|
public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
73
75
|
private static final String TAG = "CameraPreview CameraXView";
|
|
@@ -77,7 +79,7 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
77
79
|
void onPictureTakenError(String message);
|
|
78
80
|
void onSampleTaken(String result);
|
|
79
81
|
void onSampleTakenError(String message);
|
|
80
|
-
void onCameraStarted();
|
|
82
|
+
void onCameraStarted(int width, int height, int x, int y);
|
|
81
83
|
void onCameraStartError(String message);
|
|
82
84
|
}
|
|
83
85
|
|
|
@@ -194,7 +196,7 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
194
196
|
|
|
195
197
|
// Create and setup the preview view
|
|
196
198
|
previewView = new PreviewView(context);
|
|
197
|
-
previewView.setScaleType(PreviewView.ScaleType.
|
|
199
|
+
previewView.setScaleType(PreviewView.ScaleType.FIT_CENTER);
|
|
198
200
|
previewContainer.addView(previewView, new FrameLayout.LayoutParams(
|
|
199
201
|
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
200
202
|
FrameLayout.LayoutParams.MATCH_PARENT
|
|
@@ -202,19 +204,86 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
202
204
|
|
|
203
205
|
// Create and setup the grid overlay
|
|
204
206
|
gridOverlayView = new GridOverlayView(context);
|
|
205
|
-
gridOverlayView.setGridMode(sessionConfig.getGridMode());
|
|
206
207
|
previewContainer.addView(gridOverlayView, new FrameLayout.LayoutParams(
|
|
207
208
|
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
208
209
|
FrameLayout.LayoutParams.MATCH_PARENT
|
|
209
210
|
));
|
|
211
|
+
// Set grid mode after adding to container to ensure proper layout
|
|
212
|
+
gridOverlayView.post(() -> {
|
|
213
|
+
String currentGridMode = sessionConfig.getGridMode();
|
|
214
|
+
Log.d(TAG, "setupPreviewView: Setting grid mode to: " + currentGridMode);
|
|
215
|
+
gridOverlayView.setGridMode(currentGridMode);
|
|
216
|
+
});
|
|
210
217
|
|
|
211
218
|
ViewGroup parent = (ViewGroup) webView.getParent();
|
|
212
219
|
if (parent != null) {
|
|
213
|
-
|
|
220
|
+
FrameLayout.LayoutParams layoutParams = calculatePreviewLayoutParams();
|
|
221
|
+
parent.addView(previewContainer, layoutParams);
|
|
214
222
|
if(sessionConfig.isToBack()) webView.bringToFront();
|
|
215
223
|
}
|
|
216
224
|
}
|
|
217
225
|
|
|
226
|
+
private FrameLayout.LayoutParams calculatePreviewLayoutParams() {
|
|
227
|
+
// sessionConfig already contains pixel-converted coordinates with webview offsets applied
|
|
228
|
+
int x = sessionConfig.getX();
|
|
229
|
+
int y = sessionConfig.getY();
|
|
230
|
+
int width = sessionConfig.getWidth();
|
|
231
|
+
int height = sessionConfig.getHeight();
|
|
232
|
+
String aspectRatio = sessionConfig.getAspectRatio();
|
|
233
|
+
|
|
234
|
+
Log.d(TAG, "calculatePreviewLayoutParams: Using sessionConfig values - x:" + x + " y:" + y + " width:" + width + " height:" + height + " aspectRatio:" + aspectRatio);
|
|
235
|
+
|
|
236
|
+
// Apply aspect ratio if specified and no explicit size was given
|
|
237
|
+
if (aspectRatio != null && !aspectRatio.isEmpty()) {
|
|
238
|
+
String[] ratios = aspectRatio.split(":");
|
|
239
|
+
if (ratios.length == 2) {
|
|
240
|
+
try {
|
|
241
|
+
// For camera, use portrait orientation: 4:3 becomes 3:4, 16:9 becomes 9:16
|
|
242
|
+
float ratio = Float.parseFloat(ratios[1]) / Float.parseFloat(ratios[0]);
|
|
243
|
+
|
|
244
|
+
// Calculate optimal size while maintaining aspect ratio
|
|
245
|
+
int optimalWidth = width;
|
|
246
|
+
int optimalHeight = (int) (width / ratio);
|
|
247
|
+
|
|
248
|
+
if (optimalHeight > height) {
|
|
249
|
+
// Height constraint is tighter, fit by height
|
|
250
|
+
optimalHeight = height;
|
|
251
|
+
optimalWidth = (int) (height * ratio);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
width = optimalWidth;
|
|
255
|
+
height = optimalHeight;
|
|
256
|
+
Log.d(TAG, "calculatePreviewLayoutParams: Applied aspect ratio " + aspectRatio + " - new size: " + width + "x" + height);
|
|
257
|
+
} catch (NumberFormatException e) {
|
|
258
|
+
Log.e(TAG, "Invalid aspect ratio format: " + aspectRatio, e);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(width, height);
|
|
264
|
+
|
|
265
|
+
// Only add insets for positioning coordinates, not for full-screen sizes
|
|
266
|
+
int webViewTopInset = getWebViewTopInset();
|
|
267
|
+
int webViewLeftInset = getWebViewLeftInset();
|
|
268
|
+
|
|
269
|
+
// Don't add insets if this looks like a calculated full-screen coordinate (x=0, y=0)
|
|
270
|
+
if (x == 0 && y == 0) {
|
|
271
|
+
layoutParams.leftMargin = x;
|
|
272
|
+
layoutParams.topMargin = y;
|
|
273
|
+
Log.d(TAG, "calculatePreviewLayoutParams: Full-screen mode - keeping position (0,0) without insets");
|
|
274
|
+
} else {
|
|
275
|
+
layoutParams.leftMargin = x + webViewLeftInset;
|
|
276
|
+
layoutParams.topMargin = y + webViewTopInset;
|
|
277
|
+
Log.d(TAG, "calculatePreviewLayoutParams: Positioned mode - applying insets");
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
Log.d(TAG, "calculatePreviewLayoutParams: Applied insets - x:" + x + "+" + webViewLeftInset + "=" + layoutParams.leftMargin +
|
|
281
|
+
", y:" + y + "+" + webViewTopInset + "=" + layoutParams.topMargin);
|
|
282
|
+
|
|
283
|
+
Log.d(TAG, "calculatePreviewLayoutParams: Final layout - x:" + x + " y:" + y + " width:" + width + " height:" + height);
|
|
284
|
+
return layoutParams;
|
|
285
|
+
}
|
|
286
|
+
|
|
218
287
|
private void removePreviewView() {
|
|
219
288
|
if (previewContainer != null) {
|
|
220
289
|
ViewGroup parent = (ViewGroup) previewContainer.getParent();
|
|
@@ -305,7 +374,17 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
305
374
|
|
|
306
375
|
isRunning = true;
|
|
307
376
|
Log.d(TAG, "bindCameraUseCases: Camera bound successfully");
|
|
308
|
-
if (listener != null)
|
|
377
|
+
if (listener != null) {
|
|
378
|
+
// Post the callback to ensure layout is complete
|
|
379
|
+
previewContainer.post(() -> {
|
|
380
|
+
// Return actual preview container dimensions instead of requested dimensions
|
|
381
|
+
int actualWidth = previewContainer != null ? previewContainer.getWidth() : sessionConfig.getWidth();
|
|
382
|
+
int actualHeight = previewContainer != null ? previewContainer.getHeight() : sessionConfig.getHeight();
|
|
383
|
+
int actualX = previewContainer != null ? previewContainer.getLeft() : sessionConfig.getX();
|
|
384
|
+
int actualY = previewContainer != null ? previewContainer.getTop() : sessionConfig.getY();
|
|
385
|
+
listener.onCameraStarted(actualWidth, actualHeight, actualX, actualY);
|
|
386
|
+
});
|
|
387
|
+
}
|
|
309
388
|
} catch (Exception e) {
|
|
310
389
|
if (listener != null) listener.onCameraStartError("Error binding camera: " + e.getMessage());
|
|
311
390
|
}
|
|
@@ -398,7 +477,7 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
398
477
|
java.io.FileInputStream fis = new java.io.FileInputStream(tempFile);
|
|
399
478
|
fis.read(bytes);
|
|
400
479
|
fis.close();
|
|
401
|
-
|
|
480
|
+
|
|
402
481
|
ExifInterface exifInterface = new ExifInterface(tempFile.getAbsolutePath());
|
|
403
482
|
|
|
404
483
|
if (location != null) {
|
|
@@ -1051,22 +1130,26 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
1051
1130
|
layoutParams.leftMargin = sessionConfig.getX();
|
|
1052
1131
|
layoutParams.topMargin = sessionConfig.getY();
|
|
1053
1132
|
|
|
1054
|
-
if (sessionConfig.getAspectRatio() != null
|
|
1133
|
+
if (sessionConfig.getAspectRatio() != null) {
|
|
1055
1134
|
String[] ratios = sessionConfig.getAspectRatio().split(":");
|
|
1056
|
-
|
|
1135
|
+
// For camera, use portrait orientation: 4:3 becomes 3:4, 16:9 becomes 9:16
|
|
1136
|
+
float ratio = Float.parseFloat(ratios[1]) / Float.parseFloat(ratios[0]);
|
|
1057
1137
|
if (sessionConfig.getWidth() > 0) {
|
|
1058
1138
|
layoutParams.height = (int) (sessionConfig.getWidth() / ratio);
|
|
1059
1139
|
} else if (sessionConfig.getHeight() > 0) {
|
|
1060
1140
|
layoutParams.width = (int) (sessionConfig.getHeight() * ratio);
|
|
1061
1141
|
}
|
|
1062
|
-
} else {
|
|
1063
|
-
previewView.setScaleType(PreviewView.ScaleType.FILL_CENTER);
|
|
1064
1142
|
}
|
|
1065
1143
|
|
|
1066
1144
|
previewView.setLayoutParams(layoutParams);
|
|
1067
1145
|
|
|
1068
1146
|
if (listener != null) {
|
|
1069
|
-
listener.onCameraStarted(
|
|
1147
|
+
listener.onCameraStarted(
|
|
1148
|
+
sessionConfig.getWidth(),
|
|
1149
|
+
sessionConfig.getHeight(),
|
|
1150
|
+
sessionConfig.getX(),
|
|
1151
|
+
sessionConfig.getY()
|
|
1152
|
+
);
|
|
1070
1153
|
}
|
|
1071
1154
|
}
|
|
1072
1155
|
|
|
@@ -1074,11 +1157,99 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
1074
1157
|
if (sessionConfig != null) {
|
|
1075
1158
|
return sessionConfig.getAspectRatio();
|
|
1076
1159
|
}
|
|
1077
|
-
return "
|
|
1160
|
+
return "4:3";
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
public String getGridMode() {
|
|
1164
|
+
if (sessionConfig != null) {
|
|
1165
|
+
return sessionConfig.getGridMode();
|
|
1166
|
+
}
|
|
1167
|
+
return "none";
|
|
1078
1168
|
}
|
|
1079
1169
|
|
|
1080
1170
|
public void setAspectRatio(String aspectRatio) {
|
|
1171
|
+
setAspectRatio(aspectRatio, null, null);
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
public void setAspectRatio(String aspectRatio, Float x, Float y) {
|
|
1175
|
+
setAspectRatio(aspectRatio, x, y, null);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
public void setAspectRatio(String aspectRatio, Float x, Float y, Runnable callback) {
|
|
1179
|
+
if (sessionConfig == null) {
|
|
1180
|
+
if (callback != null) callback.run();
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
String currentAspectRatio = sessionConfig.getAspectRatio();
|
|
1185
|
+
|
|
1186
|
+
// Don't restart camera if aspect ratio hasn't changed and no position specified
|
|
1187
|
+
if (aspectRatio != null && aspectRatio.equals(currentAspectRatio) && x == null && y == null) {
|
|
1188
|
+
Log.d(TAG, "setAspectRatio: Aspect ratio " + aspectRatio + " is already set and no position specified, skipping");
|
|
1189
|
+
if (callback != null) callback.run();
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
String currentGridMode = sessionConfig.getGridMode();
|
|
1194
|
+
Log.d(TAG, "setAspectRatio: Changing from " + currentAspectRatio + " to " + aspectRatio +
|
|
1195
|
+
(x != null && y != null ? " at position (" + x + ", " + y + ")" : " with auto-centering") +
|
|
1196
|
+
", preserving grid mode: " + currentGridMode);
|
|
1197
|
+
|
|
1198
|
+
sessionConfig = new CameraSessionConfiguration(
|
|
1199
|
+
sessionConfig.getDeviceId(),
|
|
1200
|
+
sessionConfig.getPosition(),
|
|
1201
|
+
sessionConfig.getX(),
|
|
1202
|
+
sessionConfig.getY(),
|
|
1203
|
+
sessionConfig.getWidth(),
|
|
1204
|
+
sessionConfig.getHeight(),
|
|
1205
|
+
sessionConfig.getPaddingBottom(),
|
|
1206
|
+
sessionConfig.getToBack(),
|
|
1207
|
+
sessionConfig.getStoreToFile(),
|
|
1208
|
+
sessionConfig.getEnableOpacity(),
|
|
1209
|
+
sessionConfig.getEnableZoom(),
|
|
1210
|
+
sessionConfig.getDisableExifHeaderStripping(),
|
|
1211
|
+
sessionConfig.getDisableAudio(),
|
|
1212
|
+
sessionConfig.getZoomFactor(),
|
|
1213
|
+
aspectRatio,
|
|
1214
|
+
currentGridMode
|
|
1215
|
+
);
|
|
1216
|
+
|
|
1217
|
+
// Update layout and rebind camera with new aspect ratio
|
|
1218
|
+
if (isRunning && previewContainer != null) {
|
|
1219
|
+
mainExecutor.execute(() -> {
|
|
1220
|
+
// First update the UI layout
|
|
1221
|
+
updatePreviewLayoutForAspectRatio(aspectRatio, x, y);
|
|
1222
|
+
|
|
1223
|
+
// Then rebind the camera with new aspect ratio configuration
|
|
1224
|
+
Log.d(TAG, "setAspectRatio: Rebinding camera with new aspect ratio: " + aspectRatio);
|
|
1225
|
+
bindCameraUseCases();
|
|
1226
|
+
|
|
1227
|
+
// Preserve grid mode and wait for completion
|
|
1228
|
+
if (gridOverlayView != null) {
|
|
1229
|
+
gridOverlayView.post(() -> {
|
|
1230
|
+
Log.d(TAG, "setAspectRatio: Re-applying grid mode: " + currentGridMode);
|
|
1231
|
+
gridOverlayView.setGridMode(currentGridMode);
|
|
1232
|
+
|
|
1233
|
+
// Wait one more frame for grid to be applied, then call callback
|
|
1234
|
+
if (callback != null) {
|
|
1235
|
+
gridOverlayView.post(callback);
|
|
1236
|
+
}
|
|
1237
|
+
});
|
|
1238
|
+
} else {
|
|
1239
|
+
// No grid overlay, wait one frame for layout completion then call callback
|
|
1240
|
+
if (callback != null) {
|
|
1241
|
+
previewContainer.post(callback);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
});
|
|
1245
|
+
} else {
|
|
1246
|
+
if (callback != null) callback.run();
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
public void setGridMode(String gridMode) {
|
|
1081
1251
|
if (sessionConfig != null) {
|
|
1252
|
+
Log.d(TAG, "setGridMode: Changing grid mode to: " + gridMode);
|
|
1082
1253
|
sessionConfig = new CameraSessionConfiguration(
|
|
1083
1254
|
sessionConfig.getDeviceId(),
|
|
1084
1255
|
sessionConfig.getPosition(),
|
|
@@ -1094,10 +1265,334 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
1094
1265
|
sessionConfig.getDisableExifHeaderStripping(),
|
|
1095
1266
|
sessionConfig.getDisableAudio(),
|
|
1096
1267
|
sessionConfig.getZoomFactor(),
|
|
1097
|
-
|
|
1098
|
-
|
|
1268
|
+
sessionConfig.getAspectRatio(),
|
|
1269
|
+
gridMode
|
|
1099
1270
|
);
|
|
1100
|
-
|
|
1271
|
+
|
|
1272
|
+
// Update the grid overlay immediately
|
|
1273
|
+
if (gridOverlayView != null) {
|
|
1274
|
+
gridOverlayView.post(() -> {
|
|
1275
|
+
Log.d(TAG, "setGridMode: Applying grid mode to overlay: " + gridMode);
|
|
1276
|
+
gridOverlayView.setGridMode(gridMode);
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
public int getPreviewX() {
|
|
1283
|
+
if (previewContainer == null) return 0;
|
|
1284
|
+
ViewGroup.LayoutParams layoutParams = previewContainer.getLayoutParams();
|
|
1285
|
+
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
|
|
1286
|
+
// Return position relative to WebView content (subtract insets)
|
|
1287
|
+
int margin = ((ViewGroup.MarginLayoutParams) layoutParams).leftMargin;
|
|
1288
|
+
int leftInset = getWebViewLeftInset();
|
|
1289
|
+
int result = margin - leftInset;
|
|
1290
|
+
Log.d(TAG, "getPreviewX: leftMargin=" + margin + ", leftInset=" + leftInset + ", result=" + result);
|
|
1291
|
+
return result;
|
|
1292
|
+
}
|
|
1293
|
+
return previewContainer.getLeft();
|
|
1294
|
+
}
|
|
1295
|
+
public int getPreviewY() {
|
|
1296
|
+
if (previewContainer == null) return 0;
|
|
1297
|
+
ViewGroup.LayoutParams layoutParams = previewContainer.getLayoutParams();
|
|
1298
|
+
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
|
|
1299
|
+
// Return position relative to WebView content (subtract insets)
|
|
1300
|
+
int margin = ((ViewGroup.MarginLayoutParams) layoutParams).topMargin;
|
|
1301
|
+
int topInset = getWebViewTopInset();
|
|
1302
|
+
int result = margin - topInset;
|
|
1303
|
+
Log.d(TAG, "getPreviewY: topMargin=" + margin + ", topInset=" + topInset + ", result=" + result);
|
|
1304
|
+
return result;
|
|
1305
|
+
}
|
|
1306
|
+
return previewContainer.getTop();
|
|
1307
|
+
}
|
|
1308
|
+
public int getPreviewWidth() {
|
|
1309
|
+
return previewContainer != null ? previewContainer.getWidth() : 0;
|
|
1310
|
+
}
|
|
1311
|
+
public int getPreviewHeight() {
|
|
1312
|
+
return previewContainer != null ? previewContainer.getHeight() : 0;
|
|
1313
|
+
}
|
|
1314
|
+
public void setPreviewSize(int x, int y, int width, int height) {
|
|
1315
|
+
setPreviewSize(x, y, width, height, null);
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
public void setPreviewSize(int x, int y, int width, int height, Runnable callback) {
|
|
1319
|
+
if (previewContainer == null) {
|
|
1320
|
+
if (callback != null) callback.run();
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
// Ensure this runs on the main UI thread
|
|
1325
|
+
mainExecutor.execute(() -> {
|
|
1326
|
+
ViewGroup.LayoutParams layoutParams = previewContainer.getLayoutParams();
|
|
1327
|
+
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
|
|
1328
|
+
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) layoutParams;
|
|
1329
|
+
|
|
1330
|
+
// Only add insets for positioning coordinates, not for full-screen sizes
|
|
1331
|
+
int webViewTopInset = getWebViewTopInset();
|
|
1332
|
+
int webViewLeftInset = getWebViewLeftInset();
|
|
1333
|
+
|
|
1334
|
+
// Handle positioning - preserve current values if new values are not specified (negative)
|
|
1335
|
+
if (x >= 0) {
|
|
1336
|
+
// Don't add insets if this looks like a calculated full-screen coordinate (x=0, y=0)
|
|
1337
|
+
if (x == 0 && y == 0) {
|
|
1338
|
+
params.leftMargin = x;
|
|
1339
|
+
Log.d(TAG, "setPreviewSize: Full-screen mode - keeping x=0 without insets");
|
|
1340
|
+
} else {
|
|
1341
|
+
params.leftMargin = x + webViewLeftInset;
|
|
1342
|
+
Log.d(TAG, "setPreviewSize: Positioned mode - x=" + x + " + inset=" + webViewLeftInset + " = " + (x + webViewLeftInset));
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
if (y >= 0) {
|
|
1346
|
+
// Don't add insets if this looks like a calculated full-screen coordinate (x=0, y=0)
|
|
1347
|
+
if (x == 0 && y == 0) {
|
|
1348
|
+
params.topMargin = y;
|
|
1349
|
+
Log.d(TAG, "setPreviewSize: Full-screen mode - keeping y=0 without insets");
|
|
1350
|
+
} else {
|
|
1351
|
+
params.topMargin = y + webViewTopInset;
|
|
1352
|
+
Log.d(TAG, "setPreviewSize: Positioned mode - y=" + y + " + inset=" + webViewTopInset + " = " + (y + webViewTopInset));
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
if (width > 0) params.width = width;
|
|
1356
|
+
if (height > 0) params.height = height;
|
|
1357
|
+
|
|
1358
|
+
previewContainer.setLayoutParams(params);
|
|
1359
|
+
previewContainer.requestLayout();
|
|
1360
|
+
|
|
1361
|
+
Log.d(TAG, "setPreviewSize: Updated to " + params.width + "x" + params.height + " at (" + params.leftMargin + "," + params.topMargin + ")");
|
|
1362
|
+
|
|
1363
|
+
// Update session config to reflect actual layout
|
|
1364
|
+
if (sessionConfig != null) {
|
|
1365
|
+
String currentAspectRatio = sessionConfig.getAspectRatio();
|
|
1366
|
+
|
|
1367
|
+
// Calculate aspect ratio from actual dimensions if both width and height are provided
|
|
1368
|
+
String calculatedAspectRatio = currentAspectRatio;
|
|
1369
|
+
if (params.width > 0 && params.height > 0) {
|
|
1370
|
+
// Always use larger dimension / smaller dimension for consistent comparison
|
|
1371
|
+
float ratio = Math.max(params.width, params.height) / (float) Math.min(params.width, params.height);
|
|
1372
|
+
// Standard ratios: 16:9 ≈ 1.778, 4:3 ≈ 1.333
|
|
1373
|
+
float ratio16_9 = 16f / 9f; // 1.778
|
|
1374
|
+
float ratio4_3 = 4f / 3f; // 1.333
|
|
1375
|
+
|
|
1376
|
+
// Determine closest standard aspect ratio
|
|
1377
|
+
if (Math.abs(ratio - ratio16_9) < Math.abs(ratio - ratio4_3)) {
|
|
1378
|
+
calculatedAspectRatio = "16:9";
|
|
1379
|
+
} else {
|
|
1380
|
+
calculatedAspectRatio = "4:3";
|
|
1381
|
+
}
|
|
1382
|
+
Log.d(TAG, "setPreviewSize: Calculated aspect ratio from " + params.width + "x" + params.height + " = " + calculatedAspectRatio + " (normalized ratio=" + ratio + ")");
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
sessionConfig = new CameraSessionConfiguration(
|
|
1386
|
+
sessionConfig.getDeviceId(),
|
|
1387
|
+
sessionConfig.getPosition(),
|
|
1388
|
+
params.leftMargin,
|
|
1389
|
+
params.topMargin,
|
|
1390
|
+
params.width,
|
|
1391
|
+
params.height,
|
|
1392
|
+
sessionConfig.getPaddingBottom(),
|
|
1393
|
+
sessionConfig.getToBack(),
|
|
1394
|
+
sessionConfig.getStoreToFile(),
|
|
1395
|
+
sessionConfig.getEnableOpacity(),
|
|
1396
|
+
sessionConfig.getEnableZoom(),
|
|
1397
|
+
sessionConfig.getDisableExifHeaderStripping(),
|
|
1398
|
+
sessionConfig.getDisableAudio(),
|
|
1399
|
+
sessionConfig.getZoomFactor(),
|
|
1400
|
+
calculatedAspectRatio,
|
|
1401
|
+
sessionConfig.getGridMode()
|
|
1402
|
+
);
|
|
1403
|
+
|
|
1404
|
+
// If aspect ratio changed due to size update, rebind camera
|
|
1405
|
+
if (isRunning && !Objects.equals(currentAspectRatio, calculatedAspectRatio)) {
|
|
1406
|
+
Log.d(TAG, "setPreviewSize: Aspect ratio changed from " + currentAspectRatio + " to " + calculatedAspectRatio + ", rebinding camera");
|
|
1407
|
+
bindCameraUseCases();
|
|
1408
|
+
|
|
1409
|
+
// Wait for camera rebinding to complete, then call callback
|
|
1410
|
+
if (callback != null) {
|
|
1411
|
+
previewContainer.post(() -> previewContainer.post(callback));
|
|
1412
|
+
}
|
|
1413
|
+
} else {
|
|
1414
|
+
// No camera rebinding needed, wait for layout to complete then call callback
|
|
1415
|
+
if (callback != null) {
|
|
1416
|
+
previewContainer.post(callback);
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
} else {
|
|
1420
|
+
// No sessionConfig, just wait for layout then call callback
|
|
1421
|
+
if (callback != null) {
|
|
1422
|
+
previewContainer.post(callback);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
} else {
|
|
1426
|
+
Log.w(TAG, "setPreviewSize: Cannot set margins on layout params of type " + layoutParams.getClass().getSimpleName());
|
|
1427
|
+
// Fallback: just set width and height if specified
|
|
1428
|
+
if (width > 0) layoutParams.width = width;
|
|
1429
|
+
if (height > 0) layoutParams.height = height;
|
|
1430
|
+
previewContainer.setLayoutParams(layoutParams);
|
|
1431
|
+
previewContainer.requestLayout();
|
|
1432
|
+
|
|
1433
|
+
// Wait for layout then call callback
|
|
1434
|
+
if (callback != null) {
|
|
1435
|
+
previewContainer.post(callback);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
private void updatePreviewLayoutForAspectRatio(String aspectRatio) {
|
|
1442
|
+
updatePreviewLayoutForAspectRatio(aspectRatio, null, null);
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
private void updatePreviewLayoutForAspectRatio(String aspectRatio, Float x, Float y) {
|
|
1446
|
+
if (previewContainer == null || aspectRatio == null) return;
|
|
1447
|
+
|
|
1448
|
+
// Parse aspect ratio
|
|
1449
|
+
String[] ratios = aspectRatio.split(":");
|
|
1450
|
+
if (ratios.length != 2) return;
|
|
1451
|
+
|
|
1452
|
+
try {
|
|
1453
|
+
// For camera, use portrait orientation: 4:3 becomes 3:4, 16:9 becomes 9:16
|
|
1454
|
+
float ratio = Float.parseFloat(ratios[1]) / Float.parseFloat(ratios[0]);
|
|
1455
|
+
|
|
1456
|
+
// Get available space from webview dimensions
|
|
1457
|
+
int availableWidth = webView.getWidth();
|
|
1458
|
+
int availableHeight = webView.getHeight();
|
|
1459
|
+
|
|
1460
|
+
// Calculate position and size
|
|
1461
|
+
int finalX, finalY, finalWidth, finalHeight;
|
|
1462
|
+
|
|
1463
|
+
if (x != null && y != null) {
|
|
1464
|
+
// Account for WebView insets from edge-to-edge support
|
|
1465
|
+
int webViewTopInset = getWebViewTopInset();
|
|
1466
|
+
int webViewLeftInset = getWebViewLeftInset();
|
|
1467
|
+
|
|
1468
|
+
// Use provided coordinates with boundary checking, adjusted for insets
|
|
1469
|
+
finalX = Math.max(0, Math.min(x.intValue() + webViewLeftInset, availableWidth));
|
|
1470
|
+
finalY = Math.max(0, Math.min(y.intValue() + webViewTopInset, availableHeight));
|
|
1471
|
+
|
|
1472
|
+
// Calculate maximum available space from the given position
|
|
1473
|
+
int maxWidth = availableWidth - finalX;
|
|
1474
|
+
int maxHeight = availableHeight - finalY;
|
|
1475
|
+
|
|
1476
|
+
// Calculate optimal size while maintaining aspect ratio within available space
|
|
1477
|
+
finalWidth = maxWidth;
|
|
1478
|
+
finalHeight = (int) (maxWidth / ratio);
|
|
1479
|
+
|
|
1480
|
+
if (finalHeight > maxHeight) {
|
|
1481
|
+
// Height constraint is tighter, fit by height
|
|
1482
|
+
finalHeight = maxHeight;
|
|
1483
|
+
finalWidth = (int) (maxHeight * ratio);
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
// Ensure final position stays within bounds
|
|
1487
|
+
finalX = Math.max(0, Math.min(finalX, availableWidth - finalWidth));
|
|
1488
|
+
finalY = Math.max(0, Math.min(finalY, availableHeight - finalHeight));
|
|
1489
|
+
} else {
|
|
1490
|
+
// Auto-center the view
|
|
1491
|
+
// Calculate size based on aspect ratio, using a reasonable base size
|
|
1492
|
+
// Use 80% of available space to ensure aspect ratio differences are visible
|
|
1493
|
+
int maxAvailableWidth = (int) (availableWidth * 0.8);
|
|
1494
|
+
int maxAvailableHeight = (int) (availableHeight * 0.8);
|
|
1495
|
+
|
|
1496
|
+
// Start with width-based calculation
|
|
1497
|
+
finalWidth = maxAvailableWidth;
|
|
1498
|
+
finalHeight = (int) (finalWidth / ratio);
|
|
1499
|
+
|
|
1500
|
+
// If height exceeds available space, use height-based calculation
|
|
1501
|
+
if (finalHeight > maxAvailableHeight) {
|
|
1502
|
+
finalHeight = maxAvailableHeight;
|
|
1503
|
+
finalWidth = (int) (finalHeight * ratio);
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
// Center the view
|
|
1507
|
+
finalX = (availableWidth - finalWidth) / 2;
|
|
1508
|
+
finalY = (availableHeight - finalHeight) / 2;
|
|
1509
|
+
|
|
1510
|
+
Log.d(TAG, "updatePreviewLayoutForAspectRatio: Auto-center mode - ratio=" + ratio +
|
|
1511
|
+
", calculated size=" + finalWidth + "x" + finalHeight +
|
|
1512
|
+
", available=" + availableWidth + "x" + availableHeight);
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
// Update layout params
|
|
1516
|
+
ViewGroup.LayoutParams currentParams = previewContainer.getLayoutParams();
|
|
1517
|
+
if (currentParams instanceof ViewGroup.MarginLayoutParams) {
|
|
1518
|
+
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) currentParams;
|
|
1519
|
+
params.width = finalWidth;
|
|
1520
|
+
params.height = finalHeight;
|
|
1521
|
+
params.leftMargin = finalX;
|
|
1522
|
+
params.topMargin = finalY;
|
|
1523
|
+
previewContainer.setLayoutParams(params);
|
|
1524
|
+
previewContainer.requestLayout();
|
|
1525
|
+
Log.d(TAG, "updatePreviewLayoutForAspectRatio: Updated to " + finalWidth + "x" + finalHeight + " at (" + finalX + "," + finalY + ")");
|
|
1526
|
+
}
|
|
1527
|
+
} catch (NumberFormatException e) {
|
|
1528
|
+
Log.e(TAG, "Invalid aspect ratio format: " + aspectRatio, e);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
private void updatePreviewLayout() {
|
|
1533
|
+
if (previewContainer == null || sessionConfig == null) return;
|
|
1534
|
+
|
|
1535
|
+
String aspectRatio = sessionConfig.getAspectRatio();
|
|
1536
|
+
if (aspectRatio == null) return;
|
|
1537
|
+
|
|
1538
|
+
updatePreviewLayoutForAspectRatio(aspectRatio);
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
private int getWebViewTopInset() {
|
|
1542
|
+
try {
|
|
1543
|
+
if (webView != null) {
|
|
1544
|
+
ViewGroup.LayoutParams layoutParams = webView.getLayoutParams();
|
|
1545
|
+
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
|
|
1546
|
+
return ((ViewGroup.MarginLayoutParams) layoutParams).topMargin;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
} catch (Exception e) {
|
|
1550
|
+
Log.w(TAG, "Failed to get WebView top inset", e);
|
|
1551
|
+
}
|
|
1552
|
+
return 0;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
private int getWebViewLeftInset() {
|
|
1556
|
+
try {
|
|
1557
|
+
if (webView != null) {
|
|
1558
|
+
ViewGroup.LayoutParams layoutParams = webView.getLayoutParams();
|
|
1559
|
+
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
|
|
1560
|
+
return ((ViewGroup.MarginLayoutParams) layoutParams).leftMargin;
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
} catch (Exception e) {
|
|
1564
|
+
Log.w(TAG, "Failed to get WebView left inset", e);
|
|
1565
|
+
}
|
|
1566
|
+
return 0;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
/**
|
|
1570
|
+
* Get the current preview position and size in DP units (without insets)
|
|
1571
|
+
*/
|
|
1572
|
+
public int[] getCurrentPreviewBounds() {
|
|
1573
|
+
if (previewContainer == null) {
|
|
1574
|
+
return new int[]{0, 0, 0, 0}; // x, y, width, height
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
ViewGroup.LayoutParams layoutParams = previewContainer.getLayoutParams();
|
|
1578
|
+
int x = 0, y = 0, width = 0, height = 0;
|
|
1579
|
+
|
|
1580
|
+
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
|
|
1581
|
+
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) layoutParams;
|
|
1582
|
+
|
|
1583
|
+
// Remove insets to get original coordinates in DP
|
|
1584
|
+
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
|
|
1585
|
+
float pixelRatio = metrics.density;
|
|
1586
|
+
|
|
1587
|
+
int webViewTopInset = getWebViewTopInset();
|
|
1588
|
+
int webViewLeftInset = getWebViewLeftInset();
|
|
1589
|
+
|
|
1590
|
+
x = Math.max(0, (int) ((params.leftMargin - webViewLeftInset) / pixelRatio));
|
|
1591
|
+
y = Math.max(0, (int) ((params.topMargin - webViewTopInset) / pixelRatio));
|
|
1592
|
+
width = (int) (params.width / pixelRatio);
|
|
1593
|
+
height = (int) (params.height / pixelRatio);
|
|
1101
1594
|
}
|
|
1595
|
+
|
|
1596
|
+
return new int[]{x, y, width, height};
|
|
1102
1597
|
}
|
|
1103
1598
|
}
|
|
@@ -34,8 +34,10 @@ public class GridOverlayView extends View {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
public void setGridMode(String mode) {
|
|
37
|
+
String previousMode = this.gridMode;
|
|
37
38
|
this.gridMode = mode != null ? mode : "none";
|
|
38
39
|
setVisibility("none".equals(this.gridMode) ? View.GONE : View.VISIBLE);
|
|
40
|
+
android.util.Log.d("GridOverlayView", "setGridMode: Changed from '" + previousMode + "' to '" + this.gridMode + "', visibility: " + ("none".equals(this.gridMode) ? "GONE" : "VISIBLE"));
|
|
39
41
|
invalidate();
|
|
40
42
|
}
|
|
41
43
|
|