diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index 3417432a054..aa067429dd2 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.16+2 + +* Fixes regression taking a picture in torch mode. + ## 0.9.16+1 * Fixes sample times not being numeric after pause/resume. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.m index 1962a6b7457..da8fe2647f7 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.m @@ -142,7 +142,7 @@ - (void)testSettings_shouldPassConfigurationToCameraDeviceAndWriter { [[TestMediaSettingsAVWrapper alloc] initWithTestCase:self]; FLTCam *camera = FLTCreateCamWithCaptureSessionQueueAndMediaSettings( - dispatch_queue_create("test", NULL), settings, injectedWrapper); + dispatch_queue_create("test", NULL), settings, injectedWrapper, nil); // Expect FPS configuration is passed to camera device. [self waitForExpectations:@[ diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h index eded154995e..295cbce3649 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h @@ -12,11 +12,13 @@ NS_ASSUME_NONNULL_BEGIN /// @param mediaSettings media settings configuration parameters /// @param mediaSettingsAVWrapper provider to perform media settings operations (for unit test /// dependency injection). +/// @param captureDeviceFactory a callback to create capture device instances /// @return an FLTCam object. extern FLTCam *_Nullable FLTCreateCamWithCaptureSessionQueueAndMediaSettings( dispatch_queue_t _Nullable captureSessionQueue, FCPPlatformMediaSettings *_Nullable mediaSettings, - FLTCamMediaSettingsAVWrapper *_Nullable mediaSettingsAVWrapper); + FLTCamMediaSettingsAVWrapper *_Nullable mediaSettingsAVWrapper, + CaptureDeviceFactory _Nullable captureDeviceFactory); extern FLTCam *FLTCreateCamWithCaptureSessionQueue(dispatch_queue_t captureSessionQueue); diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m index 0dac5c4a59b..e1a3aaec702 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m @@ -18,12 +18,13 @@ } FLTCam *FLTCreateCamWithCaptureSessionQueue(dispatch_queue_t captureSessionQueue) { - return FLTCreateCamWithCaptureSessionQueueAndMediaSettings(captureSessionQueue, nil, nil); + return FLTCreateCamWithCaptureSessionQueueAndMediaSettings(captureSessionQueue, nil, nil, nil); } FLTCam *FLTCreateCamWithCaptureSessionQueueAndMediaSettings( dispatch_queue_t captureSessionQueue, FCPPlatformMediaSettings *mediaSettings, - FLTCamMediaSettingsAVWrapper *mediaSettingsAVWrapper) { + FLTCamMediaSettingsAVWrapper *mediaSettingsAVWrapper, + CaptureDeviceFactory captureDeviceFactory) { if (!mediaSettings) { mediaSettings = FCPGetDefaultMediaSettings(FCPPlatformResolutionPresetMedium); } @@ -51,14 +52,19 @@ OCMStub([audioSessionMock addInputWithNoConnections:[OCMArg any]]); OCMStub([audioSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); - id fltCam = [[FLTCam alloc] initWithCameraName:@"camera" - mediaSettings:mediaSettings - mediaSettingsAVWrapper:mediaSettingsAVWrapper - orientation:UIDeviceOrientationPortrait + id fltCam = [[FLTCam alloc] initWithMediaSettings:mediaSettings + mediaSettingsAVWrapper:mediaSettingsAVWrapper + orientation:UIDeviceOrientationPortrait videoCaptureSession:videoSessionMock audioCaptureSession:audioSessionMock captureSessionQueue:captureSessionQueue - error:nil]; + captureDeviceFactory:captureDeviceFactory ?: ^AVCaptureDevice *(void) { + return [AVCaptureDevice deviceWithUniqueID:@"camera"]; + } + videoDimensionsForFormat:^CMVideoDimensions(AVCaptureDeviceFormat *format) { + return CMVideoFormatDescriptionGetDimensions(format.formatDescription); + } + error:nil]; id captureVideoDataOutputMock = [OCMockObject niceMockForClass:[AVCaptureVideoDataOutput class]]; diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m index f81625f849f..20d0836ac7d 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m @@ -169,4 +169,56 @@ - (void)testCaptureToFile_mustReportFileExtensionWithJpgWhenHEVCNotAvailableAndF }); [self waitForExpectationsWithTimeout:1 handler:nil]; } + +- (void)testCaptureToFile_handlesTorchMode { + XCTestExpectation *pathExpectation = + [self expectationWithDescription: + @"Must send file path to result if save photo delegate completes with file path."]; + + id captureDeviceMock = OCMClassMock([AVCaptureDevice class]); + OCMStub([captureDeviceMock hasTorch]).andReturn(YES); + OCMStub([captureDeviceMock isTorchAvailable]).andReturn(YES); + OCMStub([captureDeviceMock torchMode]).andReturn(AVCaptureTorchModeAuto); + OCMExpect([captureDeviceMock setTorchMode:AVCaptureTorchModeOn]); + + dispatch_queue_t captureSessionQueue = dispatch_queue_create("capture_session_queue", NULL); + dispatch_queue_set_specific(captureSessionQueue, FLTCaptureSessionQueueSpecific, + (void *)FLTCaptureSessionQueueSpecific, NULL); + + FLTCam *cam = FLTCreateCamWithCaptureSessionQueueAndMediaSettings(captureSessionQueue, nil, nil, + ^AVCaptureDevice *(void) { + return captureDeviceMock; + }); + + AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings]; + id mockSettings = OCMClassMock([AVCapturePhotoSettings class]); + OCMStub([mockSettings photoSettings]).andReturn(settings); + + NSString *filePath = @"test"; + + id mockOutput = OCMClassMock([AVCapturePhotoOutput class]); + OCMStub([mockOutput capturePhotoWithSettings:OCMOCK_ANY delegate:OCMOCK_ANY]) + .andDo(^(NSInvocation *invocation) { + FLTSavePhotoDelegate *delegate = cam.inProgressSavePhotoDelegates[@(settings.uniqueID)]; + // Completion runs on IO queue. + dispatch_queue_t ioQueue = dispatch_queue_create("io_queue", NULL); + dispatch_async(ioQueue, ^{ + delegate.completionHandler(filePath, nil); + }); + }); + cam.capturePhotoOutput = mockOutput; + + // `FLTCam::captureToFile` runs on capture session queue. + dispatch_async(captureSessionQueue, ^{ + [cam setFlashMode:FCPPlatformFlashModeTorch + withCompletion:^(FlutterError *_){ + }]; + [cam captureToFileWithCompletion:^(NSString *result, FlutterError *error) { + XCTAssertEqual(result, filePath); + [pathExpectation fulfill]; + }]; + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; + OCMVerifyAll(captureDeviceMock); +} @end diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index 5b16c7c729a..42383a98d31 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -374,9 +374,9 @@ - (void)captureToFileWithCompletion:(void (^)(NSString *_Nullable, extension = @"jpg"; } - AVCaptureFlashMode avFlashMode = FCPGetAVCaptureFlashModeForPigeonFlashMode(_flashMode); - if (avFlashMode != -1) { - [settings setFlashMode:avFlashMode]; + // If the flash is in torch mode, no capture-level flash setting is needed. + if (_flashMode != FCPPlatformFlashModeTorch) { + [settings setFlashMode:FCPGetAVCaptureFlashModeForPigeonFlashMode(_flashMode)]; } NSError *error; NSString *path = [self getTemporaryFilePathWithExtension:extension diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 004ddcd9b51..4823e0ac52b 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.16+1 +version: 0.9.16+2 environment: sdk: ^3.2.3