From 750c19465a28f4e6e47ec98ef136ec76d8e503cd Mon Sep 17 00:00:00 2001 From: Camille Simon Date: Wed, 16 Jul 2025 14:31:46 -0700 Subject: [PATCH 1/4] fixes --- .../lib/src/android_camera_camerax.dart | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart index e52435b3b10..af966a82a60 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -195,17 +195,6 @@ class AndroidCameraCameraX extends CameraPlatform { @visibleForTesting bool captureOrientationLocked = false; - /// Whether or not the default rotation for [UseCase]s needs to be set - /// manually because the capture orientation was previously locked. - /// - /// Currently, CameraX provides no way to unset target rotations for - /// [UseCase]s, so once they are set and unset, this plugin must start setting - /// the default orientation manually. - /// - /// See https://developer.android.com/reference/androidx/camera/core/ImageCapture#setTargetRotation(int) - /// for an example on how setting target rotations for [UseCase]s works. - bool shouldSetDefaultRotation = false; - /// Error code indicating that an exposure offset value failed to be set. static const String setExposureOffsetFailedErrorCode = 'setExposureOffsetFailed'; @@ -390,17 +379,18 @@ class AndroidCameraCameraX extends CameraPlatform { ); // Configure ImageCapture instance. + final int defaultDisplayRotation = + await deviceOrientationManager.getDefaultDisplayRotation(); imageCapture = proxy.newImageCapture( resolutionSelector: presetResolutionSelector, - /* use CameraX default target rotation */ targetRotation: - await deviceOrientationManager.getDefaultDisplayRotation(), + targetRotation: defaultDisplayRotation, ); // Configure ImageAnalysis instance. // Defaults to YUV_420_888 image format. imageAnalysis = proxy.newImageAnalysis( resolutionSelector: presetResolutionSelector, - /* use CameraX default target rotation */ targetRotation: null, + targetRotation: defaultDisplayRotation, ); // Configure VideoCapture and Recorder instances. @@ -552,10 +542,7 @@ class AndroidCameraCameraX extends CameraPlatform { int cameraId, DeviceOrientation orientation, ) async { - // Flag that (1) default rotation for UseCases will need to be set manually - // if orientation is ever unlocked and (2) the capture orientation is locked - // and should not be changed until unlocked. - shouldSetDefaultRotation = true; + // Flag that the capture orientation is locked and should not be changed until unlocked. captureOrientationLocked = true; // Get target rotation based on locked orientation. @@ -1104,7 +1091,7 @@ class AndroidCameraCameraX extends CameraPlatform { // Set target rotation to default CameraX rotation only if capture // orientation not locked. - if (!captureOrientationLocked && shouldSetDefaultRotation) { + if (!captureOrientationLocked) { await videoCapture!.setTargetRotation( await deviceOrientationManager.getDefaultDisplayRotation(), ); @@ -1253,7 +1240,7 @@ class AndroidCameraCameraX extends CameraPlatform { // Set target rotation to default CameraX rotation only if capture // orientation not locked. - if (!captureOrientationLocked && shouldSetDefaultRotation) { + if (!captureOrientationLocked) { await imageAnalysis!.setTargetRotation( await deviceOrientationManager.getDefaultDisplayRotation(), ); From 1788b4596db7fcf793b2e9dca70f3a106f9817b0 Mon Sep 17 00:00:00 2001 From: Camille Simon Date: Wed, 16 Jul 2025 14:44:32 -0700 Subject: [PATCH 2/4] small refactor --- .../lib/src/android_camera_camerax.dart | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart index af966a82a60..c59c59d2ee2 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -379,18 +379,18 @@ class AndroidCameraCameraX extends CameraPlatform { ); // Configure ImageCapture instance. - final int defaultDisplayRotation = + _initialDefaultDisplayRotation = await deviceOrientationManager.getDefaultDisplayRotation(); imageCapture = proxy.newImageCapture( resolutionSelector: presetResolutionSelector, - targetRotation: defaultDisplayRotation, + targetRotation: _initialDefaultDisplayRotation, ); // Configure ImageAnalysis instance. // Defaults to YUV_420_888 image format. imageAnalysis = proxy.newImageAnalysis( resolutionSelector: presetResolutionSelector, - targetRotation: defaultDisplayRotation, + targetRotation: _initialDefaultDisplayRotation, ); // Configure VideoCapture and Recorder instances. @@ -427,8 +427,6 @@ class AndroidCameraCameraX extends CameraPlatform { _initialDeviceOrientation = _deserializeDeviceOrientation( await deviceOrientationManager.getUiOrientation(), ); - _initialDefaultDisplayRotation = - await deviceOrientationManager.getDefaultDisplayRotation(); return flutterSurfaceTextureId; } From b7d54df1a5124e4c808746a775dbdcb6528f87ab Mon Sep 17 00:00:00 2001 From: Camille Simon Date: Tue, 22 Jul 2025 14:23:25 -0700 Subject: [PATCH 3/4] some debugging --- .../plugins/camerax/DeviceOrientationManager.java | 3 +++ .../lib/src/android_camera_camerax.dart | 11 ++++++++--- .../lib/src/image_reader_rotated_preview.dart | 4 ++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/DeviceOrientationManager.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/DeviceOrientationManager.java index f4b509bfe9a..ad62c84f436 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/DeviceOrientationManager.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/DeviceOrientationManager.java @@ -19,6 +19,7 @@ import io.flutter.embedding.engine.systemchannels.PlatformChannel; import io.flutter.embedding.engine.systemchannels.PlatformChannel.DeviceOrientation; import java.util.Objects; +import androidx.camera.core.UseCase; /** * Support class to help to determine the media orientation based on the orientation of the device. @@ -71,6 +72,8 @@ protected OrientationEventListener createOrientationEventListener() { return new OrientationEventListener(getContext()) { @Override public void onOrientationChanged(int orientation) { + System.out.println("CAMILLE: ORIENTAITON ---------------" + Integer.toString(orientation)); + System.out.println("CAMILLE: SNAP TO --------------" + Integer.toString(UseCase.snapToSurfaceRotation(orientation))); handleUiOrientationChange(); } }; diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart index c59c59d2ee2..8894d226b07 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -381,6 +381,9 @@ class AndroidCameraCameraX extends CameraPlatform { // Configure ImageCapture instance. _initialDefaultDisplayRotation = await deviceOrientationManager.getDefaultDisplayRotation(); + print( + 'CAMILLE222222222222222222222222222222222222222222222222: $_initialDefaultDisplayRotation', + ); imageCapture = proxy.newImageCapture( resolutionSelector: presetResolutionSelector, targetRotation: _initialDefaultDisplayRotation, @@ -1090,9 +1093,11 @@ class AndroidCameraCameraX extends CameraPlatform { // Set target rotation to default CameraX rotation only if capture // orientation not locked. if (!captureOrientationLocked) { - await videoCapture!.setTargetRotation( - await deviceOrientationManager.getDefaultDisplayRotation(), - ); + int targetRotation = + await deviceOrientationManager.getDefaultDisplayRotation(); + print('CAMILLE:::::::::::::::::::::::::::::::::::::::::::::::::::'); + print('target rotation: $targetRotation'); + await videoCapture!.setTargetRotation(targetRotation); } videoOutputPath = await systemServicesManager.getTempFilePath( diff --git a/packages/camera/camera_android_camerax/lib/src/image_reader_rotated_preview.dart b/packages/camera/camera_android_camerax/lib/src/image_reader_rotated_preview.dart index 2cb8f44bf1a..886fcccac4f 100644 --- a/packages/camera/camera_android_camerax/lib/src/image_reader_rotated_preview.dart +++ b/packages/camera/camera_android_camerax/lib/src/image_reader_rotated_preview.dart @@ -150,6 +150,10 @@ final class _ImageReaderRotatedPreviewState builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done) { final int currentDefaultDisplayRotation = snapshot.data!; + print( + 'currentDefaultDisplayRotation: $currentDefaultDisplayRotation', + ); + print('deviceOrientation: $deviceOrientation'); final double rotationDegrees = _computeRotationDegrees( deviceOrientation, currentDefaultDisplayRotation, From 36f1ca4581b310c1ea4ae6e4cd7e87518e7dea22 Mon Sep 17 00:00:00 2001 From: Camille Simon Date: Wed, 30 Jul 2025 10:43:48 -0700 Subject: [PATCH 4/4] why is it working? --- .../camerax/DeviceOrientationManager.java | 4 +- .../lib/src/android_camera_camerax.dart | 105 +++++++++--------- .../lib/src/image_reader_rotated_preview.dart | 11 +- 3 files changed, 61 insertions(+), 59 deletions(-) diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/DeviceOrientationManager.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/DeviceOrientationManager.java index ad62c84f436..5ff4faa716d 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/DeviceOrientationManager.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/DeviceOrientationManager.java @@ -72,8 +72,8 @@ protected OrientationEventListener createOrientationEventListener() { return new OrientationEventListener(getContext()) { @Override public void onOrientationChanged(int orientation) { - System.out.println("CAMILLE: ORIENTAITON ---------------" + Integer.toString(orientation)); - System.out.println("CAMILLE: SNAP TO --------------" + Integer.toString(UseCase.snapToSurfaceRotation(orientation))); + System.out.println("CAMILLE:::::::::::::::::::::::::::::::: NEW ORIENTAITON ---------------" + Integer.toString(orientation)); + // System.out.println("CAMILLE:::::::::::::::::::::::::::::::: CAMERAX SNAP TO SUGGESTION --------------" + Integer.toString(UseCase.snapToSurfaceRotation(orientation))); handleUiOrientationChange(); } }; diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart index 8894d226b07..9eb4f514227 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -275,12 +275,14 @@ class AndroidCameraCameraX extends CameraPlatform { // Determine the lens direction by filtering the CameraInfo // TODO(gmackall): replace this with call to CameraInfo.getLensFacing when changes containing that method are available if ((await proxy - .newCameraSelector(requireLensFacing: LensFacing.back) - .filter([cameraInfo])).isNotEmpty) { + .newCameraSelector(requireLensFacing: LensFacing.back) + .filter([cameraInfo])) + .isNotEmpty) { cameraLensDirection = CameraLensDirection.back; } else if ((await proxy - .newCameraSelector(requireLensFacing: LensFacing.front) - .filter([cameraInfo])).isNotEmpty) { + .newCameraSelector(requireLensFacing: LensFacing.front) + .filter([cameraInfo])) + .isNotEmpty) { cameraLensDirection = CameraLensDirection.front; } else { //Skip this CameraInfo as its lens direction is unknown @@ -379,10 +381,10 @@ class AndroidCameraCameraX extends CameraPlatform { ); // Configure ImageCapture instance. - _initialDefaultDisplayRotation = - await deviceOrientationManager.getDefaultDisplayRotation(); + _initialDefaultDisplayRotation = await deviceOrientationManager + .getDefaultDisplayRotation(); print( - 'CAMILLE222222222222222222222222222222222222222222222222: $_initialDefaultDisplayRotation', + 'CAMILLE::::::::::::::::::::::::::::::::::::::::initial default display rotation: $_initialDefaultDisplayRotation', ); imageCapture = proxy.newImageCapture( resolutionSelector: presetResolutionSelector, @@ -425,8 +427,8 @@ class AndroidCameraCameraX extends CameraPlatform { .toDouble(); sensorOrientationDegrees = cameraDescription.sensorOrientation.toDouble(); - _handlesCropAndRotation = - await preview!.surfaceProducerHandlesCropAndRotation(); + _handlesCropAndRotation = await preview! + .surfaceProducerHandlesCropAndRotation(); _initialDeviceOrientation = _deserializeDeviceOrientation( await deviceOrientationManager.getUiOrientation(), ); @@ -460,8 +462,8 @@ class AndroidCameraCameraX extends CameraPlatform { ); } - final ResolutionInfo previewResolutionInfo = - (await preview!.getResolutionInfo())!; + final ResolutionInfo previewResolutionInfo = (await preview! + .getResolutionInfo())!; // Mark auto-focus, auto-exposure and setting points for focus & exposure // as available operations as CameraX does its best across devices to @@ -628,10 +630,9 @@ class AndroidCameraCameraX extends CameraPlatform { case FocusMode.auto: // Determine auto-focus point to restore, if any. We do not restore // default auto-focus point if set previously to lock focus. - final MeteringPoint? unLockedFocusPoint = - _defaultFocusPointLocked - ? null - : currentFocusMeteringAction!.meteringPointsAf.first; + final MeteringPoint? unLockedFocusPoint = _defaultFocusPointLocked + ? null + : currentFocusMeteringAction!.meteringPointsAf.first; _defaultFocusPointLocked = false; autoFocusPoint = unLockedFocusPoint; disableAutoCancel = false; @@ -642,10 +643,9 @@ class AndroidCameraCameraX extends CameraPlatform { if (currentFocusMeteringAction != null) { final List possibleCurrentAfPoints = currentFocusMeteringAction!.meteringPointsAf; - lockedFocusPoint = - possibleCurrentAfPoints.isEmpty - ? null - : possibleCurrentAfPoints.first; + lockedFocusPoint = possibleCurrentAfPoints.isEmpty + ? null + : possibleCurrentAfPoints.first; } // If there isn't, lock center of entire sensor area by default. @@ -1093,11 +1093,12 @@ class AndroidCameraCameraX extends CameraPlatform { // Set target rotation to default CameraX rotation only if capture // orientation not locked. if (!captureOrientationLocked) { - int targetRotation = - await deviceOrientationManager.getDefaultDisplayRotation(); - print('CAMILLE:::::::::::::::::::::::::::::::::::::::::::::::::::'); - print('target rotation: $targetRotation'); - await videoCapture!.setTargetRotation(targetRotation); + int targetRotation = await deviceOrientationManager + .getDefaultDisplayRotation(); + print( + 'CAMILLE::::::::::::::::::::::::::::::::::::::::::::::::::: target rotation: $targetRotation', + ); + // await videoCapture!.setTargetRotation(targetRotation); } videoOutputPath = await systemServicesManager.getTempFilePath( @@ -1477,13 +1478,12 @@ class AndroidCameraCameraX extends CameraPlatform { ); final ResolutionFilter resolutionFilter = proxy .createWithOnePreferredSizeResolutionFilter(preferredSize: boundSize); - final AspectRatioStrategy? aspectRatioStrategy = - aspectRatio == null - ? null - : proxy.newAspectRatioStrategy( - preferredAspectRatio: aspectRatio, - fallbackRule: AspectRatioStrategyFallbackRule.auto, - ); + final AspectRatioStrategy? aspectRatioStrategy = aspectRatio == null + ? null + : proxy.newAspectRatioStrategy( + preferredAspectRatio: aspectRatio, + fallbackRule: AspectRatioStrategyFallbackRule.auto, + ); return proxy.newResolutionSelector( resolutionStrategy: resolutionStrategy, resolutionFilter: resolutionFilter, @@ -1600,17 +1600,17 @@ class AndroidCameraCameraX extends CameraPlatform { // Remove metering point with specified meteringMode from current focus // and metering action, as only one focus or exposure point may be set // at once in this plugin. - final List<(MeteringPoint, MeteringMode)> newMeteringPointInfos = - originalMeteringPoints - .where( - ((MeteringPoint, MeteringMode) meteringPointInfo) => - // meteringPointInfo may technically include points without a - // mode specified, but this logic is safe because this plugin - // only uses points that explicitly have mode - // FocusMeteringAction.flagAe or FocusMeteringAction.flagAf. - meteringPointInfo.$2 != meteringMode, - ) - .toList(); + final List<(MeteringPoint, MeteringMode)> + newMeteringPointInfos = originalMeteringPoints + .where( + ((MeteringPoint, MeteringMode) meteringPointInfo) => + // meteringPointInfo may technically include points without a + // mode specified, but this logic is safe because this plugin + // only uses points that explicitly have mode + // FocusMeteringAction.flagAe or FocusMeteringAction.flagAf. + meteringPointInfo.$2 != meteringMode, + ) + .toList(); if (newMeteringPointInfos.isEmpty) { // If no other metering points were specified, cancel any previously @@ -1647,17 +1647,16 @@ class AndroidCameraCameraX extends CameraPlatform { final Iterable<(MeteringPoint, MeteringMode)> originalMeteringPoints = _combineMeteringPoints(currentFocusMeteringAction!); - newMeteringPointInfos = - originalMeteringPoints - .where( - ((MeteringPoint, MeteringMode) meteringPointInfo) => - // meteringPointInfo may technically include points without a - // mode specified, but this logic is safe because this plugin - // only uses points that explicitly have mode - // FocusMeteringAction.flagAe or FocusMeteringAction.flagAf. - meteringPointInfo.$2 != meteringMode, - ) - .toList(); + newMeteringPointInfos = originalMeteringPoints + .where( + ((MeteringPoint, MeteringMode) meteringPointInfo) => + // meteringPointInfo may technically include points without a + // mode specified, but this logic is safe because this plugin + // only uses points that explicitly have mode + // FocusMeteringAction.flagAe or FocusMeteringAction.flagAf. + meteringPointInfo.$2 != meteringMode, + ) + .toList(); } newMeteringPointInfos.add((meteringPoint, meteringMode)); diff --git a/packages/camera/camera_android_camerax/lib/src/image_reader_rotated_preview.dart b/packages/camera/camera_android_camerax/lib/src/image_reader_rotated_preview.dart index 886fcccac4f..2b095436d75 100644 --- a/packages/camera/camera_android_camerax/lib/src/image_reader_rotated_preview.dart +++ b/packages/camera/camera_android_camerax/lib/src/image_reader_rotated_preview.dart @@ -78,8 +78,9 @@ final class _ImageReaderRotatedPreviewState late StreamSubscription deviceOrientationSubscription; Future _getCurrentDefaultDisplayRotationDegrees() async { - final int currentDefaultDisplayRotationQuarterTurns = - await widget.deviceOrientationManager.getDefaultDisplayRotation(); + final int currentDefaultDisplayRotationQuarterTurns = await widget + .deviceOrientationManager + .getDefaultDisplayRotation(); return getQuarterTurnsFromSurfaceRotationConstant( currentDefaultDisplayRotationQuarterTurns, ) * @@ -151,9 +152,11 @@ final class _ImageReaderRotatedPreviewState if (snapshot.connectionState == ConnectionState.done) { final int currentDefaultDisplayRotation = snapshot.data!; print( - 'currentDefaultDisplayRotation: $currentDefaultDisplayRotation', + 'CAMILLE:::::::::::::::::::::::: currentDefaultDisplayRotation detected for preview: $currentDefaultDisplayRotation', + ); + print( + 'CAMILLE:::::::::::::::::::::::: deviceOrientation detected for preview: $deviceOrientation', ); - print('deviceOrientation: $deviceOrientation'); final double rotationDegrees = _computeRotationDegrees( deviceOrientation, currentDefaultDisplayRotation,