diff --git a/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraAwesomeX.kt b/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraAwesomeX.kt index 1968f4af..91b98595 100644 --- a/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraAwesomeX.kt +++ b/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraAwesomeX.kt @@ -15,6 +15,7 @@ import android.util.Size import androidx.camera.camera2.Camera2Config import androidx.camera.camera2.interop.ExperimentalCamera2Interop import androidx.camera.core.* +import androidx.camera.core.impl.utils.CameraOrientationUtil import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.video.FileOutputOptions import androidx.camera.video.VideoRecordEvent @@ -124,6 +125,7 @@ class CameraAwesomeX : CameraInterface, FlutterPlugin, ActivityAware { zoom: Double, mirrorFrontCamera: Boolean, enablePhysicalButton: Boolean, + enableRotation: Boolean, flashMode: String, captureMode: String, enableImageStream: Boolean, @@ -162,8 +164,10 @@ class CameraAwesomeX : CameraInterface, FlutterPlugin, ActivityAware { this.enableAudioRecording = videoOptions?.enableAudio ?: true } this.exifPreferences = exifPreferences - orientationStreamListener = - OrientationStreamListener(activity!!, listOf(sensorOrientationListener, cameraState)) + if (enableRotation) { + orientationStreamListener = + OrientationStreamListener(activity!!, listOf(sensorOrientationListener, cameraState)) + } imageStreamChannel.setStreamHandler(cameraState) if (mode != CaptureModes.ANALYSIS_ONLY) { cameraState.updateLifecycle(activity!!) @@ -380,7 +384,7 @@ class CameraAwesomeX : CameraInterface, FlutterPlugin, ActivityAware { val outputFileOptions = ImageCapture.OutputFileOptions.Builder(imageFile).setMetadata(metadata).build() // for (imageCapture in cameraState.imageCaptures) { - imageCapture.targetRotation = orientationStreamListener!!.surfaceOrientation + imageCapture.targetRotation = if (orientationStreamListener != null) orientationStreamListener!!.surfaceOrientation else CameraOrientationUtil.degreesToSurfaceRotation(0) imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(activity!!), object : ImageCapture.OnImageSavedCallback { @@ -511,7 +515,7 @@ class CameraAwesomeX : CameraInterface, FlutterPlugin, ActivityAware { } } } - videoCapture.targetRotation = orientationStreamListener!!.surfaceOrientation + videoCapture.targetRotation = if (orientationStreamListener != null) orientationStreamListener!!.surfaceOrientation else CameraOrientationUtil.degreesToSurfaceRotation(0) cameraState.recordings!!.add(videoCapture.output.prepareRecording( activity!!, FileOutputOptions.Builder(File(paths[index]!!)).build() ).apply { if (cameraState.enableAudioRecording && !ignoreAudio) withAudioEnabled() } diff --git a/android/src/main/kotlin/com/apparence/camerawesome/cameraX/Pigeon.kt b/android/src/main/kotlin/com/apparence/camerawesome/cameraX/Pigeon.kt index 340b7e95..f80eaa49 100644 --- a/android/src/main/kotlin/com/apparence/camerawesome/cameraX/Pigeon.kt +++ b/android/src/main/kotlin/com/apparence/camerawesome/cameraX/Pigeon.kt @@ -759,7 +759,7 @@ private object CameraInterfaceCodec : StandardMessageCodec() { /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface CameraInterface { - fun setupCamera(sensors: List, aspectRatio: String, zoom: Double, mirrorFrontCamera: Boolean, enablePhysicalButton: Boolean, flashMode: String, captureMode: String, enableImageStream: Boolean, exifPreferences: ExifPreferences, videoOptions: VideoOptions?, callback: (Result) -> Unit) + fun setupCamera(sensors: List, aspectRatio: String, zoom: Double, mirrorFrontCamera: Boolean, enablePhysicalButton: Boolean, enableRotation: Boolean, flashMode: String, captureMode: String, enableImageStream: Boolean, exifPreferences: ExifPreferences, videoOptions: VideoOptions?, callback: (Result) -> Unit) fun checkPermissions(permissions: List): List /** * Returns given [CamerAwesomePermission] list (as String). Location permission might be @@ -826,12 +826,13 @@ interface CameraInterface { val zoomArg = args[2] as Double val mirrorFrontCameraArg = args[3] as Boolean val enablePhysicalButtonArg = args[4] as Boolean - val flashModeArg = args[5] as String - val captureModeArg = args[6] as String - val enableImageStreamArg = args[7] as Boolean - val exifPreferencesArg = args[8] as ExifPreferences - val videoOptionsArg = args[9] as VideoOptions? - api.setupCamera(sensorsArg, aspectRatioArg, zoomArg, mirrorFrontCameraArg, enablePhysicalButtonArg, flashModeArg, captureModeArg, enableImageStreamArg, exifPreferencesArg, videoOptionsArg) { result: Result -> + val enableRotationArg = args[5] as Boolean + val flashModeArg = args[6] as String + val captureModeArg = args[7] as String + val enableImageStreamArg = args[8] as Boolean + val exifPreferencesArg = args[9] as ExifPreferences + val videoOptionsArg = args[10] as VideoOptions? + api.setupCamera(sensorsArg, aspectRatioArg, zoomArg, mirrorFrontCameraArg, enablePhysicalButtonArg, enableRotationArg, flashModeArg, captureModeArg, enableImageStreamArg, exifPreferencesArg, videoOptionsArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) diff --git a/ios/Classes/CameraPreview/SingleCameraPreview/SingleCameraPreview.h b/ios/Classes/CameraPreview/SingleCameraPreview/SingleCameraPreview.h index b8ec2003..72b9a43d 100644 --- a/ios/Classes/CameraPreview/SingleCameraPreview/SingleCameraPreview.h +++ b/ios/Classes/CameraPreview/SingleCameraPreview/SingleCameraPreview.h @@ -54,6 +54,7 @@ AVCaptureAudioDataOutputSampleBufferDelegate> @property(readonly, nonatomic) AspectRatio aspectRatio; @property(readonly, nonatomic) CupertinoVideoOptions *videoOptions; @property(readonly, nonatomic) VideoRecordingQuality recordingQuality; +@property(readonly, nonatomic) bool enableRotation; @property(readonly, nonatomic) CameraPreviewTexture* previewTexture; @property(readonly, nonatomic) bool saveGPSLocation; @property(readonly, nonatomic) bool mirrorFrontCamera; @@ -72,6 +73,7 @@ AVCaptureAudioDataOutputSampleBufferDelegate> streamImages:(BOOL)streamImages mirrorFrontCamera:(BOOL)mirrorFrontCamera enablePhysicalButton:(BOOL)enablePhysicalButton + enableRotation:(BOOL)enableRotation aspectRatioMode:(AspectRatio)aspectRatioMode captureMode:(CaptureModes)captureMode completion:(nonnull void (^)(NSNumber * _Nullable, FlutterError * _Nullable))completion diff --git a/ios/Classes/CameraPreview/SingleCameraPreview/SingleCameraPreview.m b/ios/Classes/CameraPreview/SingleCameraPreview/SingleCameraPreview.m index 80468c87..bbec1fb6 100644 --- a/ios/Classes/CameraPreview/SingleCameraPreview/SingleCameraPreview.m +++ b/ios/Classes/CameraPreview/SingleCameraPreview/SingleCameraPreview.m @@ -17,6 +17,7 @@ - (instancetype)initWithCameraSensor:(PigeonSensorPosition)sensor streamImages:(BOOL)streamImages mirrorFrontCamera:(BOOL)mirrorFrontCamera enablePhysicalButton:(BOOL)enablePhysicalButton + enableRotation:(BOOL)enableRotation aspectRatioMode:(AspectRatio)aspectRatioMode captureMode:(CaptureModes)captureMode completion:(nonnull void (^)(NSNumber * _Nullable, FlutterError * _Nullable))completion @@ -33,6 +34,7 @@ - (instancetype)initWithCameraSensor:(PigeonSensorPosition)sensor _mirrorFrontCamera = mirrorFrontCamera; _videoOptions = videoOptions; _recordingQuality = recordingQuality; + _enableRotation = enableRotation; // Creating capture session _captureSession = [[AVCaptureSession alloc] init]; @@ -61,11 +63,15 @@ - (instancetype)initWithCameraSensor:(PigeonSensorPosition)sensor // Controllers init _videoController = [[VideoController alloc] init]; _imageStreamController = [[ImageStreamController alloc] initWithStreamImages:streamImages]; - _motionController = [[MotionController alloc] init]; + if (_enableRotation) { + _motionController = [[MotionController alloc] init]; + } _locationController = [[LocationController alloc] init]; _physicalButtonController = [[PhysicalButtonController alloc] init]; - [_motionController startMotionDetection]; + if (_motionController != nil) { + [_motionController startMotionDetection]; + } if (enablePhysicalButton) { [_physicalButtonController startListening]; @@ -434,7 +440,7 @@ - (void)refresh { - (void)takePictureAtPath:(NSString *)path completion:(nonnull void (^)(NSNumber * _Nullable, FlutterError * _Nullable))completion { // Instanciate camera picture obj CameraPictureController *cameraPicture = [[CameraPictureController alloc] initWithPath:path - orientation:_motionController.deviceOrientation + orientation:_enableRotation ? _motionController.deviceOrientation : UIDeviceOrientationPortrait sensorPosition:_cameraSensorPosition saveGPSLocation:_saveGPSLocation mirrorFrontCamera:_mirrorFrontCamera diff --git a/ios/Classes/CamerawesomePlugin.m b/ios/Classes/CamerawesomePlugin.m index 14376256..fa983f28 100644 --- a/ios/Classes/CamerawesomePlugin.m +++ b/ios/Classes/CamerawesomePlugin.m @@ -65,7 +65,7 @@ + (void)registerWithRegistrar:(NSObject*)registrar { #pragma mark - Camera engine methods -- (void)setupCameraSensors:(nonnull NSArray *)sensors aspectRatio:(nonnull NSString *)aspectRatio zoom:(nonnull NSNumber *)zoom mirrorFrontCamera:(nonnull NSNumber *)mirrorFrontCamera enablePhysicalButton:(nonnull NSNumber *)enablePhysicalButton flashMode:(nonnull NSString *)flashMode captureMode:(nonnull NSString *)captureMode enableImageStream:(nonnull NSNumber *)enableImageStream exifPreferences:(nonnull ExifPreferences *)exifPreferences videoOptions:(nullable VideoOptions *)videoOptions completion:(nonnull void (^)(NSNumber * _Nullable, FlutterError * _Nullable))completion { +- (void)setupCameraSensors:(nonnull NSArray *)sensors aspectRatio:(nonnull NSString *)aspectRatio zoom:(nonnull NSNumber *)zoom mirrorFrontCamera:(nonnull NSNumber *)mirrorFrontCamera enablePhysicalButton:(nonnull NSNumber *)enablePhysicalButton enableRotation:(nonnull NSNumber *)enableRotation flashMode:(nonnull NSString *)flashMode captureMode:(nonnull NSString *)captureMode enableImageStream:(nonnull NSNumber *)enableImageStream exifPreferences:(nonnull ExifPreferences *)exifPreferences videoOptions:(nullable VideoOptions *)videoOptions completion:(nonnull void (^)(NSNumber * _Nullable, FlutterError * _Nullable))completion { CaptureModes captureModeType = [CaptureModeUtils captureModeFromCaptureModeType:captureMode]; if (![CameraPermissionsController checkAndRequestPermission]) { @@ -128,6 +128,7 @@ - (void)setupCameraSensors:(nonnull NSArray *)sensors aspectRati streamImages:[enableImageStream boolValue] mirrorFrontCamera:[mirrorFrontCamera boolValue] enablePhysicalButton:[enablePhysicalButton boolValue] + enableRotation:[enableRotation boolValue] aspectRatioMode:aspectRatioMode captureMode:captureModeType completion:completion diff --git a/ios/Classes/Pigeon/Pigeon.h b/ios/Classes/Pigeon/Pigeon.h index 46510d30..2fc5b241 100644 --- a/ios/Classes/Pigeon/Pigeon.h +++ b/ios/Classes/Pigeon/Pigeon.h @@ -268,7 +268,7 @@ extern void AnalysisImageUtilsSetup(id binaryMessenger, NSObject *CameraInterfaceGetCodec(void); @protocol CameraInterface -- (void)setupCameraSensors:(NSArray *)sensors aspectRatio:(NSString *)aspectRatio zoom:(NSNumber *)zoom mirrorFrontCamera:(NSNumber *)mirrorFrontCamera enablePhysicalButton:(NSNumber *)enablePhysicalButton flashMode:(NSString *)flashMode captureMode:(NSString *)captureMode enableImageStream:(NSNumber *)enableImageStream exifPreferences:(ExifPreferences *)exifPreferences videoOptions:(nullable VideoOptions *)videoOptions completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion; +- (void)setupCameraSensors:(NSArray *)sensors aspectRatio:(NSString *)aspectRatio zoom:(NSNumber *)zoom mirrorFrontCamera:(NSNumber *)mirrorFrontCamera enablePhysicalButton:(NSNumber *)enablePhysicalButton enableRotation:(NSNumber *)enableRotation flashMode:(NSString *)flashMode captureMode:(NSString *)captureMode enableImageStream:(NSNumber *)enableImageStream exifPreferences:(ExifPreferences *)exifPreferences videoOptions:(nullable VideoOptions *)videoOptions completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion; /// @return `nil` only when `error != nil`. - (nullable NSArray *)checkPermissionsPermissions:(NSArray *)permissions error:(FlutterError *_Nullable *_Nonnull)error; /// Returns given [CamerAwesomePermission] list (as String). Location permission might be diff --git a/ios/Classes/Pigeon/Pigeon.m b/ios/Classes/Pigeon/Pigeon.m index 72d045c5..42fa7417 100644 --- a/ios/Classes/Pigeon/Pigeon.m +++ b/ios/Classes/Pigeon/Pigeon.m @@ -672,7 +672,7 @@ void CameraInterfaceSetup(id binaryMessenger, NSObject *arg_sensors = GetNullableObjectAtIndex(args, 0); @@ -680,12 +680,13 @@ void CameraInterfaceSetup(id binaryMessenger, NSObject init( SensorConfig sensorConfig, bool enableImageStream, - bool enablePhysicalButton, { + bool enablePhysicalButton, + bool enableRotation, { CaptureMode captureMode = CaptureMode.photo, required ExifPreferences exifPreferences, required VideoOptions? videoOptions, @@ -195,6 +196,7 @@ class CamerawesomePlugin { sensorConfig.zoom, mirrorFrontCamera, enablePhysicalButton, + enableRotation, sensorConfig.flashMode.name.toUpperCase(), captureMode.name.toUpperCase(), enableImageStream, diff --git a/lib/pigeon.dart b/lib/pigeon.dart index bffdfa9f..117c1d0f 100644 --- a/lib/pigeon.dart +++ b/lib/pigeon.dart @@ -737,6 +737,7 @@ class CameraInterface { double arg_zoom, bool arg_mirrorFrontCamera, bool arg_enablePhysicalButton, + bool arg_enableRotation, String arg_flashMode, String arg_captureMode, bool arg_enableImageStream, @@ -751,6 +752,7 @@ class CameraInterface { arg_zoom, arg_mirrorFrontCamera, arg_enablePhysicalButton, + arg_enableRotation, arg_flashMode, arg_captureMode, arg_enableImageStream, diff --git a/lib/src/orchestrator/camera_context.dart b/lib/src/orchestrator/camera_context.dart index 0c6bc2ca..1a0b9c6e 100644 --- a/lib/src/orchestrator/camera_context.dart +++ b/lib/src/orchestrator/camera_context.dart @@ -35,6 +35,8 @@ class CameraContext { final bool enablePhysicalButton; + final bool enableRotation; + /// allows to create dynamic analysis using the current preview /// Image analysis controller. You may use it to start or stop image analysis. final AnalysisController? analysisController; @@ -69,6 +71,7 @@ class CameraContext { required this.exifPreferences, required this.filterController, required this.enablePhysicalButton, + required this.enableRotation, required this.availableFilters, this.onPermissionsResult, }) { @@ -91,12 +94,14 @@ class CameraContext { required ExifPreferences exifPreferences, required AwesomeFilter filter, required bool enablePhysicalButton, + required bool enableRotation, List? availableFilters, }) : this._( initialCaptureMode: initialCaptureMode, sensorConfigController: BehaviorSubject.seeded(sensorConfig), filterController: BehaviorSubject.seeded(filter), enablePhysicalButton: enablePhysicalButton, + enableRotation: enableRotation, onPermissionsResult: onPermissionsResult, saveConfig: saveConfig, analysisController: onImageForAnalysis != null diff --git a/lib/src/orchestrator/states/preparing_camera_state.dart b/lib/src/orchestrator/states/preparing_camera_state.dart index 2ef4fa66..3b0ce0c7 100644 --- a/lib/src/orchestrator/states/preparing_camera_state.dart +++ b/lib/src/orchestrator/states/preparing_camera_state.dart @@ -65,6 +65,7 @@ class PreparingCameraState extends CameraState { SensorConfig sensorConfig, { required bool enableImageStream, required bool enablePhysicalButton, + required bool enableRotation, }) async { // wait user accept permissions to init widget completely on android if (Platform.isAndroid) { @@ -75,6 +76,7 @@ class PreparingCameraState extends CameraState { _init( enableImageStream: enableImageStream, enablePhysicalButton: enablePhysicalButton, + enableRotation: enableRotation, ); } if (onPermissionsResult != null) { @@ -137,6 +139,7 @@ class PreparingCameraState extends CameraState { await _init( enableImageStream: cameraContext.imageAnalysisEnabled, enablePhysicalButton: cameraContext.enablePhysicalButton, + enableRotation: cameraContext.enableRotation, ); cameraContext.changeState(VideoCameraState.from(cameraContext)); @@ -148,6 +151,7 @@ class PreparingCameraState extends CameraState { await _init( enableImageStream: cameraContext.imageAnalysisEnabled, enablePhysicalButton: cameraContext.enablePhysicalButton, + enableRotation: cameraContext.enableRotation, ); cameraContext.changeState(PhotoCameraState.from(cameraContext)); @@ -159,6 +163,7 @@ class PreparingCameraState extends CameraState { await _init( enableImageStream: cameraContext.imageAnalysisEnabled, enablePhysicalButton: cameraContext.enablePhysicalButton, + enableRotation: cameraContext.enableRotation, ); cameraContext.changeState(PreviewCameraState.from(cameraContext)); @@ -170,6 +175,7 @@ class PreparingCameraState extends CameraState { await _init( enableImageStream: cameraContext.imageAnalysisEnabled, enablePhysicalButton: cameraContext.enablePhysicalButton, + enableRotation: cameraContext.enableRotation, ); // On iOS, we need to start the camera to get the first frame because there @@ -185,16 +191,19 @@ class PreparingCameraState extends CameraState { Future _init({ required bool enableImageStream, required bool enablePhysicalButton, + required bool enableRotation, }) async { initPermissions( sensorConfig, enableImageStream: enableImageStream, enablePhysicalButton: enablePhysicalButton, + enableRotation: enableRotation, ); await CamerawesomePlugin.init( sensorConfig, enableImageStream, enablePhysicalButton, + enableRotation, captureMode: nextCaptureMode, exifPreferences: cameraContext.exifPreferences, videoOptions: saveConfig?.videoOptions, diff --git a/lib/src/widgets/camera_awesome_builder.dart b/lib/src/widgets/camera_awesome_builder.dart index f6bb77e0..b62b41bf 100644 --- a/lib/src/widgets/camera_awesome_builder.dart +++ b/lib/src/widgets/camera_awesome_builder.dart @@ -63,6 +63,9 @@ class CameraAwesomeBuilder extends StatefulWidget { /// Enable physical button (volume +/-) to take photo or record video final bool enablePhysicalButton; + /// Enable or disable device rotation + final bool enableRotation; + /// Path builders when taking photos or recording videos final SaveConfig? saveConfig; @@ -121,6 +124,7 @@ class CameraAwesomeBuilder extends StatefulWidget { const CameraAwesomeBuilder._({ required this.sensorConfig, required this.enablePhysicalButton, + required this.enableRotation, required this.progressIndicator, required this.saveConfig, required this.onMediaTap, @@ -165,6 +169,7 @@ class CameraAwesomeBuilder extends StatefulWidget { CameraAwesomeBuilder.awesome( {SensorConfig? sensorConfig, bool enablePhysicalButton = false, + bool enableRotation = true, Widget? progressIndicator, required SaveConfig saveConfig, Function(MediaCapture)? onMediaTap, @@ -190,6 +195,7 @@ class CameraAwesomeBuilder extends StatefulWidget { sensor: Sensor.position(SensorPosition.back), ), enablePhysicalButton: enablePhysicalButton, + enableRotation: enableRotation, progressIndicator: progressIndicator, builder: (cameraModeState, preview) { return AwesomeCameraLayout( @@ -224,6 +230,7 @@ class CameraAwesomeBuilder extends StatefulWidget { SensorConfig? sensorConfig, bool mirrorFrontCamera = false, bool enablePhysicalButton = false, + bool enableRotation = true, Widget? progressIndicator, required CameraLayoutBuilder builder, required SaveConfig saveConfig, @@ -245,6 +252,7 @@ class CameraAwesomeBuilder extends StatefulWidget { sensor: Sensor.position(SensorPosition.back), ), enablePhysicalButton: enablePhysicalButton, + enableRotation: enableRotation, progressIndicator: progressIndicator, builder: builder, saveConfig: saveConfig, @@ -269,6 +277,7 @@ class CameraAwesomeBuilder extends StatefulWidget { CameraAwesomeBuilder.previewOnly({ SensorConfig? sensorConfig, Widget? progressIndicator, + bool enableRotation = true, required CameraLayoutBuilder builder, AwesomeFilter? filter, OnImageForAnalysis? onImageForAnalysis, @@ -283,6 +292,7 @@ class CameraAwesomeBuilder extends StatefulWidget { sensorConfig: sensorConfig ?? SensorConfig.single(sensor: Sensor.position(SensorPosition.back)), enablePhysicalButton: false, + enableRotation: enableRotation, progressIndicator: progressIndicator, builder: builder, saveConfig: null, @@ -310,6 +320,7 @@ class CameraAwesomeBuilder extends StatefulWidget { CameraAwesomeBuilder.analysisOnly({ SensorConfig? sensorConfig, CameraAspectRatios aspectRatio = CameraAspectRatios.ratio_4_3, + bool enableRotation = true, Widget? progressIndicator, required CameraLayoutBuilder builder, required OnImageForAnalysis onImageForAnalysis, @@ -318,6 +329,7 @@ class CameraAwesomeBuilder extends StatefulWidget { sensorConfig: sensorConfig ?? SensorConfig.single(sensor: Sensor.position(SensorPosition.back)), enablePhysicalButton: false, + enableRotation: enableRotation, progressIndicator: progressIndicator, builder: builder, saveConfig: null, @@ -393,6 +405,7 @@ class _CameraWidgetBuilder extends State _cameraContext = CameraContext.create( widget.sensorConfig, enablePhysicalButton: widget.enablePhysicalButton, + enableRotation: widget.enableRotation, filter: widget.defaultFilter ?? AwesomeFilter.None, initialCaptureMode: widget.saveConfig?.initialCaptureMode ?? (widget.showPreview