@capgo/camera-preview 8.2.2 → 8.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -29
- package/android/build.gradle +4 -0
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraPreview.java +87 -20
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraXView.java +553 -158
- package/android/src/main/java/app/capgo/capacitor/camera/preview/model/CameraSessionConfiguration.java +13 -0
- package/dist/docs.json +17 -1
- package/dist/esm/definitions.d.ts +9 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +1 -1
- package/dist/esm/web.js +120 -92
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +119 -92
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +119 -92
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoCameraPreviewPlugin/CameraController.swift +14 -12
- package/ios/Sources/CapgoCameraPreviewPlugin/Plugin.swift +16 -14
- package/package.json +8 -7
- package/android/.gradle/8.14.4/checksums/checksums.lock +0 -0
- package/android/.gradle/8.14.4/checksums/md5-checksums.bin +0 -0
- package/android/.gradle/8.14.4/checksums/sha1-checksums.bin +0 -0
- package/android/.gradle/8.14.4/executionHistory/executionHistory.bin +0 -0
- package/android/.gradle/8.14.4/executionHistory/executionHistory.lock +0 -0
- package/android/.gradle/8.14.4/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.14.4/fileHashes/fileHashes.bin +0 -0
- package/android/.gradle/8.14.4/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.14.4/fileHashes/resourceHashesCache.bin +0 -0
- package/android/.gradle/8.14.4/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
- 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
|
@@ -96,11 +96,14 @@ import java.util.Collections;
|
|
|
96
96
|
import java.util.Date;
|
|
97
97
|
import java.util.List;
|
|
98
98
|
import java.util.Locale;
|
|
99
|
+
import java.util.Map;
|
|
99
100
|
import java.util.Objects;
|
|
100
101
|
import java.util.Set;
|
|
102
|
+
import java.util.concurrent.ConcurrentHashMap;
|
|
101
103
|
import java.util.concurrent.Executor;
|
|
102
104
|
import java.util.concurrent.ExecutorService;
|
|
103
105
|
import java.util.concurrent.Executors;
|
|
106
|
+
import java.util.concurrent.RejectedExecutionException;
|
|
104
107
|
import org.json.JSONObject;
|
|
105
108
|
|
|
106
109
|
public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
@@ -138,6 +141,8 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
138
141
|
private long focusIndicatorAnimationId = 0; // Incrementing token to invalidate previous animations
|
|
139
142
|
private CameraSelector currentCameraSelector;
|
|
140
143
|
private String currentDeviceId;
|
|
144
|
+
private String currentPhysicalDeviceId;
|
|
145
|
+
private String currentLogicalDeviceId;
|
|
141
146
|
private int currentFlashMode = ImageCapture.FLASH_MODE_OFF;
|
|
142
147
|
private CameraSessionConfiguration sessionConfig;
|
|
143
148
|
private CameraXViewListener listener;
|
|
@@ -150,6 +155,10 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
150
155
|
private final LifecycleRegistry lifecycleRegistry;
|
|
151
156
|
private final Executor mainExecutor;
|
|
152
157
|
private ExecutorService cameraExecutor;
|
|
158
|
+
private static volatile Map<String, app.capgo.capacitor.camera.preview.model.CameraDevice> enumeratedDeviceCache =
|
|
159
|
+
new ConcurrentHashMap<>();
|
|
160
|
+
private static final Object enumeratedDeviceCacheLock = new Object();
|
|
161
|
+
private static volatile boolean enumeratedDeviceCacheRefreshInProgress = false;
|
|
153
162
|
private boolean isRunning = false;
|
|
154
163
|
private Size currentPreviewResolution = null;
|
|
155
164
|
private ListenableFuture<FocusMeteringResult> currentFocusFuture = null; // Track current focus operation
|
|
@@ -213,6 +222,56 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
213
222
|
}
|
|
214
223
|
};
|
|
215
224
|
|
|
225
|
+
private static final class PhysicalCameraBindingTarget {
|
|
226
|
+
|
|
227
|
+
private final CameraInfo logicalCameraInfo;
|
|
228
|
+
private final String logicalCameraId;
|
|
229
|
+
private final int requiredFacing;
|
|
230
|
+
|
|
231
|
+
private PhysicalCameraBindingTarget(CameraInfo logicalCameraInfo, String logicalCameraId, int requiredFacing) {
|
|
232
|
+
this.logicalCameraInfo = logicalCameraInfo;
|
|
233
|
+
this.logicalCameraId = logicalCameraId;
|
|
234
|
+
this.requiredFacing = requiredFacing;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private static final class PhysicalDeviceMetadata {
|
|
239
|
+
|
|
240
|
+
private final String position;
|
|
241
|
+
private final float fallbackZoom;
|
|
242
|
+
|
|
243
|
+
private PhysicalDeviceMetadata(String position, float fallbackZoom) {
|
|
244
|
+
this.position = position;
|
|
245
|
+
this.fallbackZoom = fallbackZoom;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private static final class CameraBindingPlan {
|
|
250
|
+
|
|
251
|
+
private final CameraSelector selector;
|
|
252
|
+
private final String reportedDeviceId;
|
|
253
|
+
private final String logicalCameraId;
|
|
254
|
+
private final String physicalCameraId;
|
|
255
|
+
private final float fallbackZoom;
|
|
256
|
+
private final boolean usesPhysicalSelection;
|
|
257
|
+
|
|
258
|
+
private CameraBindingPlan(
|
|
259
|
+
CameraSelector selector,
|
|
260
|
+
String reportedDeviceId,
|
|
261
|
+
String logicalCameraId,
|
|
262
|
+
String physicalCameraId,
|
|
263
|
+
float fallbackZoom,
|
|
264
|
+
boolean usesPhysicalSelection
|
|
265
|
+
) {
|
|
266
|
+
this.selector = selector;
|
|
267
|
+
this.reportedDeviceId = reportedDeviceId;
|
|
268
|
+
this.logicalCameraId = logicalCameraId;
|
|
269
|
+
this.physicalCameraId = physicalCameraId;
|
|
270
|
+
this.fallbackZoom = fallbackZoom;
|
|
271
|
+
this.usesPhysicalSelection = usesPhysicalSelection;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
216
275
|
private boolean IsOperationRunning(String name) {
|
|
217
276
|
synchronized (operationLock) {
|
|
218
277
|
if (stopPending) {
|
|
@@ -453,6 +512,7 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
453
512
|
|
|
454
513
|
this.sessionConfig = config;
|
|
455
514
|
cameraExecutor = Executors.newSingleThreadExecutor();
|
|
515
|
+
requestEnumeratedDeviceCacheRefresh();
|
|
456
516
|
|
|
457
517
|
// Reset cached orientation so we don't reuse stale values across sessions
|
|
458
518
|
synchronized (accelerometerLock) {
|
|
@@ -523,6 +583,9 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
523
583
|
|
|
524
584
|
private void performImmediateStop() {
|
|
525
585
|
isRunning = false;
|
|
586
|
+
currentDeviceId = null;
|
|
587
|
+
currentPhysicalDeviceId = null;
|
|
588
|
+
currentLogicalDeviceId = null;
|
|
526
589
|
// Stop accelerometer and rotation vector sensor
|
|
527
590
|
if (sensorManager != null && accelerometer != null) {
|
|
528
591
|
sensorManager.unregisterListener(accelerometerListener);
|
|
@@ -744,6 +807,7 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
744
807
|
int width = sessionConfig.getWidth();
|
|
745
808
|
int height = sessionConfig.getHeight();
|
|
746
809
|
String aspectRatio = sessionConfig.getAspectRatio();
|
|
810
|
+
String aspectMode = sessionConfig.getAspectMode();
|
|
747
811
|
|
|
748
812
|
// Get comprehensive display information
|
|
749
813
|
int screenWidthPx, screenHeightPx;
|
|
@@ -813,7 +877,7 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
813
877
|
);
|
|
814
878
|
|
|
815
879
|
// Apply aspect ratio if specified
|
|
816
|
-
if (aspectRatio != null && !aspectRatio.isEmpty() && sessionConfig.isCentered()) {
|
|
880
|
+
if (aspectRatio != null && !aspectRatio.isEmpty() && sessionConfig.isCentered() && !"cover".equals(aspectMode)) {
|
|
817
881
|
String[] ratios = aspectRatio.split(":");
|
|
818
882
|
if (ratios.length == 2) {
|
|
819
883
|
try {
|
|
@@ -928,7 +992,8 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
928
992
|
" and position: " +
|
|
929
993
|
sessionConfig.getPosition()
|
|
930
994
|
);
|
|
931
|
-
|
|
995
|
+
CameraBindingPlan bindingPlan = buildCameraBindingPlan(sessionConfig);
|
|
996
|
+
currentCameraSelector = bindingPlan.selector;
|
|
932
997
|
|
|
933
998
|
ResolutionSelector.Builder resolutionSelectorBuilder = new ResolutionSelector.Builder().setResolutionStrategy(
|
|
934
999
|
ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY
|
|
@@ -1006,17 +1071,24 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
1006
1071
|
// is connected and video frames are captured correctly
|
|
1007
1072
|
preview.setSurfaceProvider(previewView.getSurfaceProvider());
|
|
1008
1073
|
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1074
|
+
try {
|
|
1075
|
+
bindConfiguredUseCases(bindingPlan, preview);
|
|
1076
|
+
} catch (Exception initialBindError) {
|
|
1077
|
+
if (!bindingPlan.usesPhysicalSelection) {
|
|
1078
|
+
throw initialBindError;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
Log.w(
|
|
1082
|
+
TAG,
|
|
1083
|
+
"bindCameraUseCases: Physical camera binding failed for " +
|
|
1084
|
+
bindingPlan.physicalCameraId +
|
|
1085
|
+
", falling back to logical camera behavior",
|
|
1086
|
+
initialBindError
|
|
1087
|
+
);
|
|
1088
|
+
|
|
1089
|
+
bindingPlan = buildLogicalFallbackPlan(sessionConfig, bindingPlan);
|
|
1090
|
+
currentCameraSelector = bindingPlan.selector;
|
|
1091
|
+
bindConfiguredUseCases(bindingPlan, preview);
|
|
1020
1092
|
}
|
|
1021
1093
|
|
|
1022
1094
|
resetExposureCompensationToDefault();
|
|
@@ -1024,7 +1096,10 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
1024
1096
|
// Log details about the active camera
|
|
1025
1097
|
Log.d(TAG, "Use cases bound. Inspecting active camera and use cases.");
|
|
1026
1098
|
CameraInfo cameraInfo = camera.getCameraInfo();
|
|
1027
|
-
Log.d(TAG, "Bound Camera ID: " +
|
|
1099
|
+
Log.d(TAG, "Bound Camera ID: " + currentLogicalDeviceId);
|
|
1100
|
+
if (currentPhysicalDeviceId != null) {
|
|
1101
|
+
Log.d(TAG, "Bound Physical Camera ID: " + currentPhysicalDeviceId);
|
|
1102
|
+
}
|
|
1028
1103
|
|
|
1029
1104
|
// Log zoom state
|
|
1030
1105
|
ZoomState zoomState = cameraInfo.getZoomState().getValue();
|
|
@@ -1100,7 +1175,11 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
1100
1175
|
previewView.setScaleType("cover".equals(aspectMode) ? PreviewView.ScaleType.FILL_CENTER : PreviewView.ScaleType.FIT_CENTER);
|
|
1101
1176
|
|
|
1102
1177
|
// Set initial zoom if specified, prioritizing targetZoom over default zoomFactor
|
|
1103
|
-
float initialZoom =
|
|
1178
|
+
float initialZoom = !bindingPlan.usesPhysicalSelection &&
|
|
1179
|
+
bindingPlan.fallbackZoom != 1.0f &&
|
|
1180
|
+
sessionConfig.getTargetZoom() == 1.0f
|
|
1181
|
+
? bindingPlan.fallbackZoom
|
|
1182
|
+
: (sessionConfig.getTargetZoom() != 1.0f ? sessionConfig.getTargetZoom() : sessionConfig.getZoomFactor());
|
|
1104
1183
|
if (initialZoom != 1.0f) {
|
|
1105
1184
|
Log.d(TAG, "Applying initial zoom of " + initialZoom);
|
|
1106
1185
|
|
|
@@ -1170,24 +1249,320 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
1170
1249
|
|
|
1171
1250
|
@OptIn(markerClass = ExperimentalCamera2Interop.class)
|
|
1172
1251
|
private CameraSelector buildCameraSelector() {
|
|
1173
|
-
|
|
1174
|
-
|
|
1252
|
+
return buildCameraBindingPlan(sessionConfig).selector;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
@OptIn(markerClass = ExperimentalCamera2Interop.class)
|
|
1256
|
+
private CameraBindingPlan buildCameraBindingPlan(CameraSessionConfiguration config) {
|
|
1257
|
+
final String deviceId = config.getDeviceId();
|
|
1258
|
+
final String position = config.getPosition();
|
|
1175
1259
|
|
|
1176
1260
|
if (deviceId != null && !deviceId.isEmpty()) {
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1261
|
+
CameraInfo directCameraInfo = findAvailableCameraInfoById(deviceId);
|
|
1262
|
+
if (directCameraInfo != null) {
|
|
1263
|
+
CameraSelector.Builder directBuilder = new CameraSelector.Builder();
|
|
1264
|
+
directBuilder.addCameraFilter((cameraInfos) -> {
|
|
1265
|
+
for (CameraInfo cameraInfo : cameraInfos) {
|
|
1266
|
+
if (deviceId.equals(Camera2CameraInfo.from(cameraInfo).getCameraId())) {
|
|
1267
|
+
return Collections.singletonList(cameraInfo);
|
|
1268
|
+
}
|
|
1181
1269
|
}
|
|
1270
|
+
return Collections.emptyList();
|
|
1271
|
+
});
|
|
1272
|
+
|
|
1273
|
+
return new CameraBindingPlan(directBuilder.build(), deviceId, deviceId, null, 1.0f, false);
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
CameraBindingPlan logicalFallbackPlan = buildLogicalFallbackPlanForDeviceId(deviceId);
|
|
1277
|
+
if (config.isPhysicalDeviceSelectionEnabled()) {
|
|
1278
|
+
PhysicalCameraBindingTarget physicalTarget = findPhysicalCameraBindingTarget(deviceId);
|
|
1279
|
+
if (physicalTarget != null) {
|
|
1280
|
+
CameraSelector.Builder physicalBuilder = new CameraSelector.Builder()
|
|
1281
|
+
.requireLensFacing(physicalTarget.requiredFacing)
|
|
1282
|
+
.setPhysicalCameraId(deviceId)
|
|
1283
|
+
.addCameraFilter((cameraInfos) -> {
|
|
1284
|
+
for (CameraInfo cameraInfo : cameraInfos) {
|
|
1285
|
+
if (physicalTarget.logicalCameraId.equals(Camera2CameraInfo.from(cameraInfo).getCameraId())) {
|
|
1286
|
+
return Collections.singletonList(cameraInfo);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
return Collections.emptyList();
|
|
1290
|
+
});
|
|
1291
|
+
|
|
1292
|
+
return new CameraBindingPlan(
|
|
1293
|
+
physicalBuilder.build(),
|
|
1294
|
+
deviceId,
|
|
1295
|
+
physicalTarget.logicalCameraId,
|
|
1296
|
+
deviceId,
|
|
1297
|
+
logicalFallbackPlan != null ? logicalFallbackPlan.fallbackZoom : getFallbackZoomForDeviceId(deviceId),
|
|
1298
|
+
true
|
|
1299
|
+
);
|
|
1182
1300
|
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
if (logicalFallbackPlan != null) {
|
|
1304
|
+
return logicalFallbackPlan;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
throw invalidDeviceId(deviceId);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
return buildPositionPlan(position);
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
private CameraBindingPlan buildLogicalFallbackPlan(CameraSessionConfiguration config, CameraBindingPlan failedPhysicalPlan) {
|
|
1314
|
+
String fallbackPosition = config.getPosition();
|
|
1315
|
+
|
|
1316
|
+
if (failedPhysicalPlan.logicalCameraId != null) {
|
|
1317
|
+
CameraInfo logicalCameraInfo = findAvailableCameraInfoById(failedPhysicalPlan.logicalCameraId);
|
|
1318
|
+
if (logicalCameraInfo != null) {
|
|
1319
|
+
fallbackPosition = isBackCamera(logicalCameraInfo) ? "rear" : "front";
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
CameraBindingPlan positionPlan = buildPositionPlan(fallbackPosition);
|
|
1324
|
+
return new CameraBindingPlan(
|
|
1325
|
+
positionPlan.selector,
|
|
1326
|
+
failedPhysicalPlan.reportedDeviceId,
|
|
1327
|
+
positionPlan.logicalCameraId,
|
|
1328
|
+
null,
|
|
1329
|
+
failedPhysicalPlan.fallbackZoom,
|
|
1330
|
+
false
|
|
1331
|
+
);
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
private CameraBindingPlan buildLogicalFallbackPlanForDeviceId(String deviceId) {
|
|
1335
|
+
String fallbackPosition = resolveFallbackPositionForDeviceId(deviceId);
|
|
1336
|
+
if (fallbackPosition == null) {
|
|
1337
|
+
return null;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
CameraBindingPlan positionPlan = buildPositionPlan(fallbackPosition);
|
|
1341
|
+
return new CameraBindingPlan(
|
|
1342
|
+
positionPlan.selector,
|
|
1343
|
+
deviceId,
|
|
1344
|
+
positionPlan.logicalCameraId,
|
|
1345
|
+
null,
|
|
1346
|
+
getFallbackZoomForDeviceId(deviceId),
|
|
1347
|
+
false
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
private CameraBindingPlan buildPositionPlan(String position) {
|
|
1352
|
+
int requiredFacing = "front".equals(position) ? CameraSelector.LENS_FACING_FRONT : CameraSelector.LENS_FACING_BACK;
|
|
1353
|
+
CameraSelector selector = new CameraSelector.Builder().requireLensFacing(requiredFacing).build();
|
|
1354
|
+
return new CameraBindingPlan(selector, null, null, null, 1.0f, false);
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
private IllegalArgumentException invalidDeviceId(String deviceId) {
|
|
1358
|
+
return new IllegalArgumentException("Unknown or unsupported deviceId: " + deviceId);
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
private CameraInfo findAvailableCameraInfoById(String deviceId) {
|
|
1362
|
+
if (cameraProvider == null || deviceId == null || deviceId.isEmpty()) {
|
|
1363
|
+
return null;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
for (CameraInfo cameraInfo : cameraProvider.getAvailableCameraInfos()) {
|
|
1367
|
+
if (deviceId.equals(Camera2CameraInfo.from(cameraInfo).getCameraId())) {
|
|
1368
|
+
return cameraInfo;
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
return null;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
@OptIn(markerClass = ExperimentalCamera2Interop.class)
|
|
1376
|
+
private PhysicalCameraBindingTarget findPhysicalCameraBindingTarget(String physicalDeviceId) {
|
|
1377
|
+
if (
|
|
1378
|
+
cameraProvider == null ||
|
|
1379
|
+
physicalDeviceId == null ||
|
|
1380
|
+
physicalDeviceId.isEmpty() ||
|
|
1381
|
+
Build.VERSION.SDK_INT < Build.VERSION_CODES.P
|
|
1382
|
+
) {
|
|
1383
|
+
return null;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
CameraManager cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
|
|
1387
|
+
if (cameraManager == null) {
|
|
1388
|
+
return null;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
for (CameraInfo cameraInfo : cameraProvider.getAvailableCameraInfos()) {
|
|
1392
|
+
String logicalCameraId = Camera2CameraInfo.from(cameraInfo).getCameraId();
|
|
1393
|
+
try {
|
|
1394
|
+
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(logicalCameraId);
|
|
1395
|
+
if (characteristics.getPhysicalCameraIds().contains(physicalDeviceId)) {
|
|
1396
|
+
int requiredFacing = isBackCamera(cameraInfo) ? CameraSelector.LENS_FACING_BACK : CameraSelector.LENS_FACING_FRONT;
|
|
1397
|
+
return new PhysicalCameraBindingTarget(cameraInfo, logicalCameraId, requiredFacing);
|
|
1398
|
+
}
|
|
1399
|
+
} catch (CameraAccessException e) {
|
|
1400
|
+
Log.w(TAG, "findPhysicalCameraBindingTarget: Failed to inspect logical camera " + logicalCameraId, e);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
return null;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
private PhysicalDeviceMetadata resolvePhysicalDeviceMetadata(String deviceId) {
|
|
1408
|
+
if (deviceId == null || deviceId.isEmpty()) {
|
|
1409
|
+
return null;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
CameraManager cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
|
|
1413
|
+
if (cameraManager == null) {
|
|
1414
|
+
return null;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
try {
|
|
1418
|
+
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(deviceId);
|
|
1419
|
+
Integer lensFacing = characteristics.get(CameraCharacteristics.LENS_FACING);
|
|
1420
|
+
String position = lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_FRONT ? "front" : "rear";
|
|
1421
|
+
return new PhysicalDeviceMetadata(position, getFallbackZoomForCharacteristics(characteristics));
|
|
1422
|
+
} catch (CameraAccessException | IllegalArgumentException e) {
|
|
1423
|
+
Log.w(TAG, "resolvePhysicalDeviceMetadata: Failed to inspect camera " + deviceId, e);
|
|
1424
|
+
return null;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
private String resolveFallbackPositionForDeviceId(String deviceId) {
|
|
1429
|
+
app.capgo.capacitor.camera.preview.model.CameraDevice device = findEnumeratedDeviceById(deviceId);
|
|
1430
|
+
if (device != null) {
|
|
1431
|
+
return device.getPosition();
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
PhysicalDeviceMetadata metadata = resolvePhysicalDeviceMetadata(deviceId);
|
|
1435
|
+
return metadata != null ? metadata.position : null;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
private app.capgo.capacitor.camera.preview.model.CameraDevice findEnumeratedDeviceById(String deviceId) {
|
|
1439
|
+
if (deviceId == null || deviceId.isEmpty()) {
|
|
1440
|
+
return null;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
app.capgo.capacitor.camera.preview.model.CameraDevice cachedDevice = enumeratedDeviceCache.get(deviceId);
|
|
1444
|
+
if (cachedDevice != null) {
|
|
1445
|
+
return cachedDevice;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
requestEnumeratedDeviceCacheRefresh();
|
|
1449
|
+
return null;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
private float getFallbackZoomForDeviceId(String deviceId) {
|
|
1453
|
+
app.capgo.capacitor.camera.preview.model.CameraDevice device = findEnumeratedDeviceById(deviceId);
|
|
1454
|
+
if (device != null) {
|
|
1455
|
+
for (LensInfo lens : device.getLenses()) {
|
|
1456
|
+
if ("ultraWide".equals(lens.getDeviceType())) {
|
|
1457
|
+
return 0.5f;
|
|
1458
|
+
}
|
|
1459
|
+
if ("telephoto".equals(lens.getDeviceType())) {
|
|
1460
|
+
return 2.0f;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
PhysicalDeviceMetadata metadata = resolvePhysicalDeviceMetadata(deviceId);
|
|
1466
|
+
if (metadata != null) {
|
|
1467
|
+
return metadata.fallbackZoom;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
return 1.0f;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
private float getFallbackZoomForCharacteristics(CameraCharacteristics characteristics) {
|
|
1474
|
+
float[] focalLengths = characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
|
|
1475
|
+
android.util.SizeF sensorSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE);
|
|
1476
|
+
|
|
1477
|
+
if (focalLengths != null && focalLengths.length > 0) {
|
|
1478
|
+
float focalLength = focalLengths[0];
|
|
1479
|
+
if (sensorSize != null && sensorSize.getWidth() > 0) {
|
|
1480
|
+
double fov = 2 * Math.toDegrees(Math.atan(sensorSize.getWidth() / (2 * focalLength)));
|
|
1481
|
+
if (fov > 90) {
|
|
1482
|
+
return 0.5f;
|
|
1483
|
+
}
|
|
1484
|
+
if (fov < 40) {
|
|
1485
|
+
return 2.0f;
|
|
1486
|
+
}
|
|
1487
|
+
} else {
|
|
1488
|
+
if (focalLength < 3.0f) {
|
|
1489
|
+
return 0.5f;
|
|
1490
|
+
}
|
|
1491
|
+
if (focalLength > 5.0f) {
|
|
1492
|
+
return 2.0f;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
return 1.0f;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
private void bindConfiguredUseCases(CameraBindingPlan bindingPlan, Preview preview) {
|
|
1501
|
+
if (sessionConfig.isVideoModeEnabled() && videoCapture != null) {
|
|
1502
|
+
camera = cameraProvider.bindToLifecycle(this, bindingPlan.selector, preview, imageCapture, videoCapture);
|
|
1185
1503
|
} else {
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1504
|
+
camera = cameraProvider.bindToLifecycle(this, bindingPlan.selector, preview, imageCapture);
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
CameraInfo cameraInfo = camera.getCameraInfo();
|
|
1508
|
+
currentLogicalDeviceId = Camera2CameraInfo.from(cameraInfo).getCameraId();
|
|
1509
|
+
currentPhysicalDeviceId = bindingPlan.physicalCameraId;
|
|
1510
|
+
currentDeviceId = currentPhysicalDeviceId != null ? currentPhysicalDeviceId : currentLogicalDeviceId;
|
|
1511
|
+
|
|
1512
|
+
Log.d(
|
|
1513
|
+
TAG,
|
|
1514
|
+
"bindConfiguredUseCases: Camera successfully bound. activeDeviceId=" +
|
|
1515
|
+
currentDeviceId +
|
|
1516
|
+
", logicalCameraId=" +
|
|
1517
|
+
currentLogicalDeviceId +
|
|
1518
|
+
", physicalCameraId=" +
|
|
1519
|
+
currentPhysicalDeviceId
|
|
1520
|
+
);
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
private void copyMutableSessionConfigState(CameraSessionConfiguration source, CameraSessionConfiguration target) {
|
|
1524
|
+
target.setCentered(source.isCentered());
|
|
1525
|
+
target.setTargetZoom(source.getTargetZoom());
|
|
1526
|
+
target.setEnablePhysicalDeviceSelection(source.isPhysicalDeviceSelectionEnabled());
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
private void requestEnumeratedDeviceCacheRefresh() {
|
|
1530
|
+
synchronized (enumeratedDeviceCacheLock) {
|
|
1531
|
+
if (enumeratedDeviceCacheRefreshInProgress) {
|
|
1532
|
+
return;
|
|
1533
|
+
}
|
|
1534
|
+
enumeratedDeviceCacheRefreshInProgress = true;
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
Runnable refreshTask = () -> {
|
|
1538
|
+
try {
|
|
1539
|
+
getAvailableDevicesStatic(context);
|
|
1540
|
+
} finally {
|
|
1541
|
+
synchronized (enumeratedDeviceCacheLock) {
|
|
1542
|
+
enumeratedDeviceCacheRefreshInProgress = false;
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
};
|
|
1546
|
+
|
|
1547
|
+
if (cameraExecutor != null && !cameraExecutor.isShutdown()) {
|
|
1548
|
+
try {
|
|
1549
|
+
cameraExecutor.execute(refreshTask);
|
|
1550
|
+
return;
|
|
1551
|
+
} catch (RejectedExecutionException e) {
|
|
1552
|
+
Log.w(TAG, "requestEnumeratedDeviceCacheRefresh: cameraExecutor rejected refresh task", e);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
try {
|
|
1557
|
+
Thread refreshThread = new Thread(refreshTask, "CameraPreview-DeviceCacheRefresh");
|
|
1558
|
+
refreshThread.setDaemon(true);
|
|
1559
|
+
refreshThread.start();
|
|
1560
|
+
} catch (RuntimeException e) {
|
|
1561
|
+
synchronized (enumeratedDeviceCacheLock) {
|
|
1562
|
+
enumeratedDeviceCacheRefreshInProgress = false;
|
|
1563
|
+
}
|
|
1564
|
+
throw e;
|
|
1189
1565
|
}
|
|
1190
|
-
return builder.build();
|
|
1191
1566
|
}
|
|
1192
1567
|
|
|
1193
1568
|
private static boolean isBackCamera(androidx.camera.core.CameraInfo cameraInfo) {
|
|
@@ -2273,6 +2648,7 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
2273
2648
|
}
|
|
2274
2649
|
|
|
2275
2650
|
Log.d(TAG, "=== Enumeration Complete: " + devices.size() + " cameras ===");
|
|
2651
|
+
updateEnumeratedDeviceCache(devices);
|
|
2276
2652
|
return devices;
|
|
2277
2653
|
} catch (Exception e) {
|
|
2278
2654
|
Log.e(TAG, "getAvailableDevicesStatic: Error getting devices", e);
|
|
@@ -2280,6 +2656,14 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
2280
2656
|
}
|
|
2281
2657
|
}
|
|
2282
2658
|
|
|
2659
|
+
private static void updateEnumeratedDeviceCache(List<app.capgo.capacitor.camera.preview.model.CameraDevice> devices) {
|
|
2660
|
+
Map<String, app.capgo.capacitor.camera.preview.model.CameraDevice> newCache = new ConcurrentHashMap<>();
|
|
2661
|
+
for (app.capgo.capacitor.camera.preview.model.CameraDevice device : devices) {
|
|
2662
|
+
newCache.put(device.getDeviceId(), device);
|
|
2663
|
+
}
|
|
2664
|
+
enumeratedDeviceCache = newCache;
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2283
2667
|
public static ZoomFactors getZoomFactorsStatic() {
|
|
2284
2668
|
try {
|
|
2285
2669
|
// For static method, return default zoom factors
|
|
@@ -2357,6 +2741,9 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
2357
2741
|
}
|
|
2358
2742
|
|
|
2359
2743
|
camera.getCameraControl().setZoomRatio(zoomRatio);
|
|
2744
|
+
if (sessionConfig != null) {
|
|
2745
|
+
sessionConfig.setTargetZoom(zoomRatio);
|
|
2746
|
+
}
|
|
2360
2747
|
// Note: autofocus is intentionally not triggered on zoom because it's done by CameraX
|
|
2361
2748
|
} catch (Exception e) {
|
|
2362
2749
|
Log.e(TAG, "Failed to set zoom: " + e.getMessage());
|
|
@@ -2880,53 +3267,52 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
2880
3267
|
|
|
2881
3268
|
mainExecutor.execute(() -> {
|
|
2882
3269
|
try {
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
3270
|
+
CameraSessionConfiguration previousConfig = sessionConfig;
|
|
3271
|
+
CameraInfo targetCameraInfo = findAvailableCameraInfoById(deviceId);
|
|
3272
|
+
String fallbackPosition = resolveFallbackPositionForDeviceId(deviceId);
|
|
3273
|
+
String position = fallbackPosition != null ? fallbackPosition : previousConfig.getPosition();
|
|
3274
|
+
if (targetCameraInfo != null) {
|
|
3275
|
+
position = isBackCamera(targetCameraInfo) ? "rear" : "front";
|
|
3276
|
+
} else if (previousConfig.isPhysicalDeviceSelectionEnabled()) {
|
|
3277
|
+
PhysicalCameraBindingTarget physicalTarget = findPhysicalCameraBindingTarget(deviceId);
|
|
3278
|
+
if (physicalTarget != null) {
|
|
3279
|
+
position = physicalTarget.requiredFacing == CameraSelector.LENS_FACING_FRONT ? "front" : "rear";
|
|
3280
|
+
} else if (fallbackPosition == null) {
|
|
3281
|
+
Log.e(TAG, "switchToDevice: Could not resolve deviceId: " + deviceId);
|
|
3282
|
+
return;
|
|
2892
3283
|
}
|
|
3284
|
+
} else if (fallbackPosition == null) {
|
|
3285
|
+
Log.e(TAG, "switchToDevice: Could not find any CameraInfo matching deviceId: " + deviceId);
|
|
3286
|
+
return;
|
|
2893
3287
|
}
|
|
2894
3288
|
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
sessionConfig.isVideoModeEnabled(),
|
|
2920
|
-
sessionConfig.getVideoQuality()
|
|
2921
|
-
);
|
|
2922
|
-
|
|
2923
|
-
sessionConfig.setCentered(wasCentered);
|
|
3289
|
+
CameraSessionConfiguration updatedConfig = new CameraSessionConfiguration(
|
|
3290
|
+
deviceId,
|
|
3291
|
+
position,
|
|
3292
|
+
previousConfig.getX(),
|
|
3293
|
+
previousConfig.getY(),
|
|
3294
|
+
previousConfig.getWidth(),
|
|
3295
|
+
previousConfig.getHeight(),
|
|
3296
|
+
previousConfig.getPaddingBottom(),
|
|
3297
|
+
previousConfig.getToBack(),
|
|
3298
|
+
previousConfig.getStoreToFile(),
|
|
3299
|
+
previousConfig.getEnableOpacity(),
|
|
3300
|
+
previousConfig.getDisableExifHeaderStripping(),
|
|
3301
|
+
previousConfig.getDisableAudio(),
|
|
3302
|
+
previousConfig.getZoomFactor(),
|
|
3303
|
+
previousConfig.getAspectRatio(),
|
|
3304
|
+
previousConfig.getAspectMode(),
|
|
3305
|
+
previousConfig.getGridMode(),
|
|
3306
|
+
previousConfig.getDisableFocusIndicator(),
|
|
3307
|
+
previousConfig.isVideoModeEnabled(),
|
|
3308
|
+
previousConfig.getVideoQuality()
|
|
3309
|
+
);
|
|
3310
|
+
copyMutableSessionConfigState(previousConfig, updatedConfig);
|
|
3311
|
+
updatedConfig.setTargetZoom(1.0f);
|
|
3312
|
+
sessionConfig = updatedConfig;
|
|
2924
3313
|
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
} else {
|
|
2928
|
-
Log.e(TAG, "switchToDevice: Could not find any CameraInfo matching deviceId: " + deviceId);
|
|
2929
|
-
}
|
|
3314
|
+
Log.d(TAG, "switchToDevice: Updated sessionConfig with deviceId: " + deviceId);
|
|
3315
|
+
bindCameraUseCases();
|
|
2930
3316
|
} catch (Exception e) {
|
|
2931
3317
|
Log.e(TAG, "switchToDevice: Error switching camera", e);
|
|
2932
3318
|
}
|
|
@@ -2944,38 +3330,39 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
2944
3330
|
boolean wasCentered = sessionConfig.isCentered();
|
|
2945
3331
|
Log.d(TAG, "flipCamera: Switching from " + currentPosition + " to " + newPosition);
|
|
2946
3332
|
|
|
3333
|
+
CameraSessionConfiguration previousConfig = sessionConfig;
|
|
2947
3334
|
sessionConfig = new CameraSessionConfiguration(
|
|
2948
3335
|
null, // deviceId - clear device ID to force position-based selection
|
|
2949
3336
|
newPosition, // position
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
3337
|
+
previousConfig.getX(), // x
|
|
3338
|
+
previousConfig.getY(), // y
|
|
3339
|
+
previousConfig.getWidth(), // width
|
|
3340
|
+
previousConfig.getHeight(), // height
|
|
3341
|
+
previousConfig.getPaddingBottom(), // paddingBottom
|
|
3342
|
+
previousConfig.isToBack(), // toBack
|
|
3343
|
+
previousConfig.isStoreToFile(), // storeToFile
|
|
3344
|
+
previousConfig.isEnableOpacity(), // enableOpacity
|
|
3345
|
+
previousConfig.isDisableExifHeaderStripping(), // disableExifHeaderStripping
|
|
3346
|
+
previousConfig.isDisableAudio(), // disableAudio
|
|
3347
|
+
previousConfig.getZoomFactor(), // zoomFactor
|
|
3348
|
+
previousConfig.getAspectRatio(), // aspectRatio
|
|
3349
|
+
previousConfig.getAspectMode(), // aspectMode
|
|
3350
|
+
previousConfig.getGridMode(), // gridMode
|
|
3351
|
+
previousConfig.getDisableFocusIndicator(), // disableFocusIndicator
|
|
3352
|
+
previousConfig.isVideoModeEnabled(), // enableVideoMode
|
|
3353
|
+
previousConfig.getVideoQuality() // videoQuality
|
|
2967
3354
|
);
|
|
2968
|
-
|
|
3355
|
+
copyMutableSessionConfigState(previousConfig, sessionConfig);
|
|
3356
|
+
sessionConfig.setTargetZoom(1.0f);
|
|
2969
3357
|
sessionConfig.setCentered(wasCentered);
|
|
2970
3358
|
|
|
2971
|
-
// Clear current device
|
|
3359
|
+
// Clear current device IDs to force position-based selection
|
|
2972
3360
|
currentDeviceId = null;
|
|
3361
|
+
currentPhysicalDeviceId = null;
|
|
3362
|
+
currentLogicalDeviceId = null;
|
|
2973
3363
|
|
|
2974
|
-
//
|
|
2975
|
-
|
|
2976
|
-
currentCameraSelector = buildCameraSelector();
|
|
2977
|
-
bindCameraUseCases();
|
|
2978
|
-
});
|
|
3364
|
+
// Rebind camera with the new position
|
|
3365
|
+
bindCameraUseCases();
|
|
2979
3366
|
}
|
|
2980
3367
|
|
|
2981
3368
|
public void setOpacity(float opacity) {
|
|
@@ -3059,32 +3446,34 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
3059
3446
|
return;
|
|
3060
3447
|
}
|
|
3061
3448
|
|
|
3062
|
-
|
|
3449
|
+
CameraSessionConfiguration previousConfig = sessionConfig;
|
|
3450
|
+
String currentGridMode = previousConfig.getGridMode();
|
|
3063
3451
|
Log.d(TAG, "Changing aspect ratio from " + currentAspectRatio + " to " + aspectRatio);
|
|
3064
3452
|
Log.d(TAG, "Auto-centering will be applied (matching iOS behavior)");
|
|
3065
3453
|
|
|
3066
3454
|
// Match iOS behavior: when aspect ratio changes, always auto-center
|
|
3067
3455
|
sessionConfig = new CameraSessionConfiguration(
|
|
3068
|
-
|
|
3069
|
-
|
|
3456
|
+
previousConfig.getDeviceId(),
|
|
3457
|
+
previousConfig.getPosition(),
|
|
3070
3458
|
-1, // Force auto-center X (iOS: self.posX = -1)
|
|
3071
3459
|
-1, // Force auto-center Y (iOS: self.posY = -1)
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3460
|
+
previousConfig.getWidth(),
|
|
3461
|
+
previousConfig.getHeight(),
|
|
3462
|
+
previousConfig.getPaddingBottom(),
|
|
3463
|
+
previousConfig.getToBack(),
|
|
3464
|
+
previousConfig.getStoreToFile(),
|
|
3465
|
+
previousConfig.getEnableOpacity(),
|
|
3466
|
+
previousConfig.getDisableExifHeaderStripping(),
|
|
3467
|
+
previousConfig.getDisableAudio(),
|
|
3468
|
+
previousConfig.getZoomFactor(),
|
|
3081
3469
|
aspectRatio,
|
|
3082
|
-
|
|
3470
|
+
previousConfig.getAspectMode(),
|
|
3083
3471
|
currentGridMode,
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3472
|
+
previousConfig.getDisableFocusIndicator(),
|
|
3473
|
+
previousConfig.isVideoModeEnabled(),
|
|
3474
|
+
previousConfig.getVideoQuality()
|
|
3087
3475
|
);
|
|
3476
|
+
copyMutableSessionConfigState(previousConfig, sessionConfig);
|
|
3088
3477
|
sessionConfig.setCentered(true);
|
|
3089
3478
|
|
|
3090
3479
|
// Update layout and rebind camera with new aspect ratio
|
|
@@ -3135,32 +3524,34 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
3135
3524
|
return;
|
|
3136
3525
|
}
|
|
3137
3526
|
|
|
3138
|
-
|
|
3527
|
+
CameraSessionConfiguration previousConfig = sessionConfig;
|
|
3528
|
+
String currentGridMode = previousConfig.getGridMode();
|
|
3139
3529
|
Log.d(TAG, "Forcing aspect ratio recalculation for: " + aspectRatio);
|
|
3140
3530
|
Log.d(TAG, "Auto-centering will be applied (matching iOS behavior)");
|
|
3141
3531
|
|
|
3142
3532
|
// Match iOS behavior: when aspect ratio changes, always auto-center
|
|
3143
3533
|
sessionConfig = new CameraSessionConfiguration(
|
|
3144
|
-
|
|
3145
|
-
|
|
3534
|
+
previousConfig.getDeviceId(),
|
|
3535
|
+
previousConfig.getPosition(),
|
|
3146
3536
|
-1, // Force auto-center X (iOS: self.posX = -1)
|
|
3147
3537
|
-1, // Force auto-center Y (iOS: self.posY = -1)
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3538
|
+
previousConfig.getWidth(),
|
|
3539
|
+
previousConfig.getHeight(),
|
|
3540
|
+
previousConfig.getPaddingBottom(),
|
|
3541
|
+
previousConfig.getToBack(),
|
|
3542
|
+
previousConfig.getStoreToFile(),
|
|
3543
|
+
previousConfig.getEnableOpacity(),
|
|
3544
|
+
previousConfig.getDisableExifHeaderStripping(),
|
|
3545
|
+
previousConfig.getDisableAudio(),
|
|
3546
|
+
previousConfig.getZoomFactor(),
|
|
3157
3547
|
aspectRatio,
|
|
3158
|
-
|
|
3548
|
+
previousConfig.getAspectMode(),
|
|
3159
3549
|
currentGridMode,
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3550
|
+
previousConfig.getDisableFocusIndicator(),
|
|
3551
|
+
previousConfig.isVideoModeEnabled(),
|
|
3552
|
+
previousConfig.getVideoQuality()
|
|
3163
3553
|
);
|
|
3554
|
+
copyMutableSessionConfigState(previousConfig, sessionConfig);
|
|
3164
3555
|
sessionConfig.setCentered(true);
|
|
3165
3556
|
|
|
3166
3557
|
// Update layout and rebind camera with new aspect ratio
|
|
@@ -3203,27 +3594,29 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
3203
3594
|
public void setGridMode(String gridMode) {
|
|
3204
3595
|
if (sessionConfig != null) {
|
|
3205
3596
|
Log.d(TAG, "setGridMode: Changing grid mode to: " + gridMode);
|
|
3597
|
+
CameraSessionConfiguration previousConfig = sessionConfig;
|
|
3206
3598
|
sessionConfig = new CameraSessionConfiguration(
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3599
|
+
previousConfig.getDeviceId(),
|
|
3600
|
+
previousConfig.getPosition(),
|
|
3601
|
+
previousConfig.getX(),
|
|
3602
|
+
previousConfig.getY(),
|
|
3603
|
+
previousConfig.getWidth(),
|
|
3604
|
+
previousConfig.getHeight(),
|
|
3605
|
+
previousConfig.getPaddingBottom(),
|
|
3606
|
+
previousConfig.getToBack(),
|
|
3607
|
+
previousConfig.getStoreToFile(),
|
|
3608
|
+
previousConfig.getEnableOpacity(),
|
|
3609
|
+
previousConfig.getDisableExifHeaderStripping(),
|
|
3610
|
+
previousConfig.getDisableAudio(),
|
|
3611
|
+
previousConfig.getZoomFactor(),
|
|
3612
|
+
previousConfig.getAspectRatio(),
|
|
3613
|
+
previousConfig.getAspectMode(),
|
|
3222
3614
|
gridMode,
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3615
|
+
previousConfig.getDisableFocusIndicator(),
|
|
3616
|
+
previousConfig.isVideoModeEnabled(),
|
|
3617
|
+
previousConfig.getVideoQuality()
|
|
3226
3618
|
);
|
|
3619
|
+
copyMutableSessionConfigState(previousConfig, sessionConfig);
|
|
3227
3620
|
|
|
3228
3621
|
// Update the grid overlay immediately
|
|
3229
3622
|
if (gridOverlayView != null) {
|
|
@@ -3543,27 +3936,29 @@ public class CameraXView implements LifecycleOwner, LifecycleObserver {
|
|
|
3543
3936
|
);
|
|
3544
3937
|
}
|
|
3545
3938
|
|
|
3939
|
+
CameraSessionConfiguration previousConfig = sessionConfig;
|
|
3546
3940
|
sessionConfig = new CameraSessionConfiguration(
|
|
3547
|
-
|
|
3548
|
-
|
|
3941
|
+
previousConfig.getDeviceId(),
|
|
3942
|
+
previousConfig.getPosition(),
|
|
3549
3943
|
params.leftMargin,
|
|
3550
3944
|
params.topMargin,
|
|
3551
3945
|
params.width,
|
|
3552
3946
|
params.height,
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3947
|
+
previousConfig.getPaddingBottom(),
|
|
3948
|
+
previousConfig.getToBack(),
|
|
3949
|
+
previousConfig.getStoreToFile(),
|
|
3950
|
+
previousConfig.getEnableOpacity(),
|
|
3951
|
+
previousConfig.getDisableExifHeaderStripping(),
|
|
3952
|
+
previousConfig.getDisableAudio(),
|
|
3953
|
+
previousConfig.getZoomFactor(),
|
|
3560
3954
|
calculatedAspectRatio,
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3955
|
+
previousConfig.getAspectMode(),
|
|
3956
|
+
previousConfig.getGridMode(),
|
|
3957
|
+
previousConfig.getDisableFocusIndicator(),
|
|
3958
|
+
previousConfig.isVideoModeEnabled(),
|
|
3959
|
+
previousConfig.getVideoQuality()
|
|
3566
3960
|
);
|
|
3961
|
+
copyMutableSessionConfigState(previousConfig, sessionConfig);
|
|
3567
3962
|
|
|
3568
3963
|
// If aspect ratio changed due to size update, rebind camera
|
|
3569
3964
|
if (isRunning && !Objects.equals(currentAspectRatio, calculatedAspectRatio)) {
|