diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index ec42436d936..d008c309612 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.9.22+9 + +* Migrates `FLTSavePhotoDelegate`, `FLTWritableData`, and `FLTImageStreamHandler` classes to Swift. +* Migrates `CameraProperties` and `QueueUtils` helpers to Swift. + ## 0.9.22+8 * Migrates `FLTPermissionServicing` and `FLTCameraPermissionManager` classes to Swift. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift index 87460e50000..6d21fd3792f 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.swift @@ -3,6 +3,7 @@ // found in the LICENSE file. import AVFoundation +import Flutter import XCTest @testable import camera_avfoundation diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift index cf397b5dd8b..e22e3044af7 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.swift @@ -19,19 +19,13 @@ final class CameraPropertiesTests: XCTestCase { func testGetAVCaptureFlashModeForPigeonFlashMode() { XCTAssertEqual( AVCaptureDevice.FlashMode.off, - FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.off)) + getAVCaptureFlashMode(for: .off)) XCTAssertEqual( AVCaptureDevice.FlashMode.auto, - FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.auto)) + getAVCaptureFlashMode(for: .auto)) XCTAssertEqual( AVCaptureDevice.FlashMode.on, - FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode.always)) - - // TODO(FirentisTFW): Migrate implementation to throw Swift error in this case. - let exception = ExceptionCatcher.catchException { - _ = FCPGetAVCaptureFlashModeForPigeonFlashMode(.torch) - } - XCTAssertNotNil(exception) + getAVCaptureFlashMode(for: .always)) } // MARK: - Video Format Tests @@ -39,10 +33,10 @@ final class CameraPropertiesTests: XCTestCase { func testGetPixelFormatForPigeonFormat() { XCTAssertEqual( kCVPixelFormatType_32BGRA, - FCPGetPixelFormatForPigeonFormat(FCPPlatformImageFormatGroup.bgra8888)) + getPixelFormat(for: .bgra8888)) XCTAssertEqual( kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, - FCPGetPixelFormatForPigeonFormat(FCPPlatformImageFormatGroup.yuv420)) + getPixelFormat(for: .yuv420)) } // MARK: - Device Orientation Tests @@ -50,37 +44,35 @@ final class CameraPropertiesTests: XCTestCase { func testGetUIDeviceOrientationForPigeonDeviceOrientation() { XCTAssertEqual( UIDeviceOrientation.portraitUpsideDown, - FCPGetUIDeviceOrientationForPigeonDeviceOrientation(FCPPlatformDeviceOrientation.portraitDown) + getUIDeviceOrientation(for: .portraitDown) ) XCTAssertEqual( UIDeviceOrientation.landscapeLeft, - FCPGetUIDeviceOrientationForPigeonDeviceOrientation( - FCPPlatformDeviceOrientation.landscapeLeft)) + getUIDeviceOrientation(for: .landscapeLeft)) XCTAssertEqual( UIDeviceOrientation.landscapeRight, - FCPGetUIDeviceOrientationForPigeonDeviceOrientation( - FCPPlatformDeviceOrientation.landscapeRight)) + getUIDeviceOrientation(for: .landscapeRight)) XCTAssertEqual( UIDeviceOrientation.portrait, - FCPGetUIDeviceOrientationForPigeonDeviceOrientation(FCPPlatformDeviceOrientation.portraitUp)) + getUIDeviceOrientation(for: .portraitUp)) } func testGetPigeonDeviceOrientationForUIDeviceOrientation() { XCTAssertEqual( FCPPlatformDeviceOrientation.portraitDown, - FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.portraitUpsideDown)) + getPigeonDeviceOrientation(for: .portraitUpsideDown)) XCTAssertEqual( FCPPlatformDeviceOrientation.landscapeLeft, - FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.landscapeLeft)) + getPigeonDeviceOrientation(for: .landscapeLeft)) XCTAssertEqual( FCPPlatformDeviceOrientation.landscapeRight, - FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.landscapeRight)) + getPigeonDeviceOrientation(for: .landscapeRight)) XCTAssertEqual( FCPPlatformDeviceOrientation.portraitUp, - FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.portrait)) + getPigeonDeviceOrientation(for: .portrait)) // Test default case. XCTAssertEqual( FCPPlatformDeviceOrientation.portraitUp, - FCPGetPigeonDeviceOrientationForOrientation(UIDeviceOrientation.unknown)) + getPigeonDeviceOrientation(for: .unknown)) } } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift index 2442831f0a6..894e6f8ad18 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.swift @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import AVFoundation import XCTest @testable import camera_avfoundation diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockAssetWriter.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockAssetWriter.swift index 9abf84bee2a..0353b2aeb74 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockAssetWriter.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockAssetWriter.swift @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import AVFoundation + @testable import camera_avfoundation // Import Objective-C part of the implementation when SwiftPM is used. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockAssetWriterInput.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockAssetWriterInput.swift index e7f45c472f0..e339371ae23 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockAssetWriterInput.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockAssetWriterInput.swift @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import AVFoundation + @testable import camera_avfoundation // Import Objective-C part of the implementation when SwiftPM is used. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockAssetWriterInputPixelBufferAdaptor.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockAssetWriterInputPixelBufferAdaptor.swift index cd1559c8765..d73e2d8a7d8 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockAssetWriterInputPixelBufferAdaptor.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockAssetWriterInputPixelBufferAdaptor.swift @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import AVFoundation + @testable import camera_avfoundation // Import Objective-C part of the implementation when SwiftPM is used. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCamera.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCamera.swift index 76a416d9211..b3803b119ea 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCamera.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCamera.swift @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import AVFoundation +import Flutter + @testable import camera_avfoundation // Import Objective-C part of the implementation when SwiftPM is used. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCameraDeviceDiscoverer.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCameraDeviceDiscoverer.swift index 2a50f6889c9..7c74ec1da40 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCameraDeviceDiscoverer.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCameraDeviceDiscoverer.swift @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import AVFoundation + @testable import camera_avfoundation // Import Objective-C part of the implementation when SwiftPM is used. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureConnection.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureConnection.swift index 3f4cc9184fb..1df85354da2 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureConnection.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureConnection.swift @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import AVFoundation + @testable import camera_avfoundation // Import Objective-C part of the implementation when SwiftPM is used. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureDevice.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureDevice.swift index 51263bb2ec3..6b690492f24 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureDevice.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureDevice.swift @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import AVFoundation + @testable import camera_avfoundation // Import Objective-C part of the implementation when SwiftPM is used. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureDeviceFormat.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureDeviceFormat.swift index 0afa79f5967..bae401095f3 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureDeviceFormat.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureDeviceFormat.swift @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import AVFoundation + @testable import camera_avfoundation // Import Objective-C part of the implementation when SwiftPM is used. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureInput.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureInput.swift index 0c6a38d1bc2..80ea394d825 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureInput.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureInput.swift @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import AVFoundation + @testable import camera_avfoundation // Import Objective-C part of the implementation when SwiftPM is used. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCapturePhotoOutput.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCapturePhotoOutput.swift index 9eb358aaf82..1767ae8d7bc 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCapturePhotoOutput.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCapturePhotoOutput.swift @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import AVFoundation + @testable import camera_avfoundation // Import Objective-C part of the implementation when SwiftPM is used. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureSession.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureSession.swift index 4f5a7f9e729..528d3cffbd7 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureSession.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureSession.swift @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import AVFoundation + @testable import camera_avfoundation // Import Objective-C part of the implementation when SwiftPM is used. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureVideoDataOutput.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureVideoDataOutput.swift index 654d7dd5d05..4743a56a1be 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureVideoDataOutput.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockCaptureVideoDataOutput.swift @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import AVFoundation + @testable import camera_avfoundation // Import Objective-C part of the implementation when SwiftPM is used. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockEventChannel.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockEventChannel.swift index b0e77611163..516a64c344d 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockEventChannel.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockEventChannel.swift @@ -2,19 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import camera_avfoundation +import Flutter -// Import Objective-C part of the implementation when SwiftPM is used. -#if canImport(camera_avfoundation_objc) - import camera_avfoundation_objc -#endif +@testable import camera_avfoundation -/// A mock implementation of `FLTEventChannel` that allows injecting a custom implementation +/// A mock implementation of `EventChannel` that allows injecting a custom implementation /// for setting a stream handler. -final class MockEventChannel: NSObject, FLTEventChannel { - var setStreamHandlerStub: ((FlutterStreamHandler?) -> Void)? +final class MockEventChannel: EventChannel { + var setStreamHandlerStub: (((any FlutterStreamHandler & NSObjectProtocol)?) -> Void)? - func setStreamHandler(_ handler: (FlutterStreamHandler & NSObjectProtocol)?) { + func setStreamHandler(_ handler: (any FlutterStreamHandler & NSObjectProtocol)?) { setStreamHandlerStub?(handler) } } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockFLTCameraPermissionManager.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockFLTCameraPermissionManager.swift index ddb4f4703e1..0b140c1b78c 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockFLTCameraPermissionManager.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockFLTCameraPermissionManager.swift @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import Flutter + @testable import camera_avfoundation // Import Objective-C part of the implementation when SwiftPM is used. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockGlobalEventApi.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockGlobalEventApi.swift index 774f598168d..3680725e263 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockGlobalEventApi.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockGlobalEventApi.swift @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import camera_avfoundation +import Flutter + +@testable import camera_avfoundation // Import Objective-C part of the implementation when SwiftPM is used. #if canImport(camera_avfoundation_objc) diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockWritableData.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockWritableData.swift index bf0fd30d668..613743d132e 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockWritableData.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Mocks/MockWritableData.swift @@ -2,21 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import camera_avfoundation +@testable import camera_avfoundation -// Import Objective-C part of the implementation when SwiftPM is used. -#if canImport(camera_avfoundation_objc) - import camera_avfoundation_objc -#endif - -/// A mock implementation of `FLTWritableData` that allows injecting a custom implementation +/// A mock implementation of `WritableData` that allows injecting a custom implementation /// for writing to a file. -final class MockWritableData: NSObject, FLTWritableData { - var writeToFileStub: ((String, NSData.WritingOptions) throws -> Void)? +final class MockWritableData: WritableData { + var writeToFileStub: ((String, Data.WritingOptions) throws -> Void)? - func write(toFile path: String, options writeOptionsMask: NSData.WritingOptions) throws { + func write(to path: String, options: Data.WritingOptions) throws { if let stub = self.writeToFileStub { - try stub(path, writeOptionsMask) + try stub(path, options) } } } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.swift index 740999c3e84..3a8a4faab22 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.swift @@ -15,7 +15,7 @@ final class QueueUtilsTests: XCTestCase { func testShouldStayOnMainQueueIfCalledFromMainQueue() { let expectation = expectation(description: "Block must be run on the main queue") - FLTEnsureToRunOnMainQueue { + ensureToRunOnMainQueue { if Thread.isMainThread { expectation.fulfill() } @@ -28,7 +28,7 @@ final class QueueUtilsTests: XCTestCase { let expectation = expectation(description: "Block must be run on the main queue") DispatchQueue.global(qos: .default).async { - FLTEnsureToRunOnMainQueue { + ensureToRunOnMainQueue { if Thread.isMainThread { expectation.fulfill() } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/SavePhotoDelegateTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/SavePhotoDelegateTests.swift index e1807bce358..7b4cc2a3802 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/SavePhotoDelegateTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/SavePhotoDelegateTests.swift @@ -18,7 +18,7 @@ final class SavePhotoDelegateTests: XCTestCase { description: "Must complete with error if failed to capture photo.") let captureError = NSError(domain: "test", code: 0, userInfo: nil) let ioQueue = DispatchQueue(label: "test") - let delegate = FLTSavePhotoDelegate(path: "test", ioQueue: ioQueue) { path, error in + let delegate = SavePhotoDelegate(path: "test", ioQueue: ioQueue) { path, error in XCTAssertEqual(captureError, error as NSError?) XCTAssertNil(path) completionExpectation.fulfill() @@ -35,7 +35,7 @@ final class SavePhotoDelegateTests: XCTestCase { let ioQueue = DispatchQueue(label: "test") let ioError = NSError( domain: "IOError", code: 0, userInfo: [NSLocalizedDescriptionKey: "Localized IO Error"]) - let delegate = FLTSavePhotoDelegate(path: "test", ioQueue: ioQueue) { path, error in + let delegate = SavePhotoDelegate(path: "test", ioQueue: ioQueue) { path, error in XCTAssertEqual(ioError, error as NSError?) XCTAssertNil(path) completionExpectation.fulfill() @@ -56,7 +56,7 @@ final class SavePhotoDelegateTests: XCTestCase { description: "Must complete with file path if succeeds to write file.") let ioQueue = DispatchQueue(label: "test") let filePath = "test" - let delegate = FLTSavePhotoDelegate(path: filePath, ioQueue: ioQueue) { path, error in + let delegate = SavePhotoDelegate(path: filePath, ioQueue: ioQueue) { path, error in XCTAssertNil(error) XCTAssertEqual(filePath, path) completionExpectation.fulfill() @@ -88,7 +88,7 @@ final class SavePhotoDelegateTests: XCTestCase { } let filePath = "test" - let delegate = FLTSavePhotoDelegate(path: filePath, ioQueue: ioQueue) { path, error in + let delegate = SavePhotoDelegate(path: filePath, ioQueue: ioQueue) { path, error in completionExpectation.fulfill() } diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTests.swift index 6b6188262ed..27dcfa81969 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTests.swift @@ -3,6 +3,7 @@ // found in the LICENSE file. import AVFoundation +import Flutter import XCTest @testable import camera_avfoundation @@ -12,10 +13,14 @@ import XCTest import camera_avfoundation_objc #endif -private class MockImageStreamHandler: FLTImageStreamHandler { +private class MockImageStreamHandler: NSObject, ImageStreamHandler { + var captureSessionQueue: DispatchQueue { + preconditionFailure("Attempted to access unimplemented property: captureSessionQueue") + } + var eventSinkStub: ((Any?) -> Void)? - override var eventSink: FlutterEventSink? { + var eventSink: FlutterEventSink? { get { if let stub = eventSinkStub { return { event in @@ -29,6 +34,13 @@ private class MockImageStreamHandler: FLTImageStreamHandler { } } + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) + -> FlutterError? + { nil } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + nil + } } final class StreamingTests: XCTestCase { diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.swift b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.swift index af064e9fd9a..c52920e7a89 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.swift +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.swift @@ -14,7 +14,7 @@ import XCTest final class ThreadSafeEventChannelTests: XCTestCase { func testSetStreamHandler_shouldStayOnMainThreadIfCalledFromMainThread() { let mockEventChannel = MockEventChannel() - let threadSafeEventChannel = FLTThreadSafeEventChannel(eventChannel: mockEventChannel) + let threadSafeEventChannel = ThreadSafeEventChannel(eventChannel: mockEventChannel) let mainThreadExpectation = expectation( description: "setStreamHandler must be called on the main thread") @@ -37,7 +37,7 @@ final class ThreadSafeEventChannelTests: XCTestCase { func testSetStreamHandler_shouldDispatchToMainThreadIfCalledFromBackgroundThread() { let mockEventChannel = MockEventChannel() - let threadSafeEventChannel = FLTThreadSafeEventChannel(eventChannel: mockEventChannel) + let threadSafeEventChannel = ThreadSafeEventChannel(eventChannel: mockEventChannel) let mainThreadExpectation = expectation( description: "setStreamHandler must be called on the main thread") @@ -66,7 +66,7 @@ final class ThreadSafeEventChannelTests: XCTestCase { let expectation = self.expectation(description: "Completion should be called.") DispatchQueue(label: "test").async { - let channel = FLTThreadSafeEventChannel(eventChannel: mockEventChannel) + let channel = ThreadSafeEventChannel(eventChannel: mockEventChannel) channel.setStreamHandler(nil) { expectation.fulfill() diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraPlugin.swift b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraPlugin.swift index de0e78d8222..a998e08d904 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraPlugin.swift +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraPlugin.swift @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import AVFoundation import Flutter -import ObjectiveC // Import Objective-C part of the implementation when SwiftPM is used. #if canImport(camera_avfoundation_objc) @@ -114,7 +114,7 @@ public final class CameraPlugin: NSObject, FlutterPlugin { func sendDeviceOrientation(_ orientation: UIDeviceOrientation) { DispatchQueue.main.async { [weak self] in self?.globalEventAPI.deviceOrientationChangedOrientation( - FCPGetPigeonDeviceOrientationForOrientation(orientation) + getPigeonDeviceOrientation(for: orientation) ) { _ in // Ignore errors; this is essentially a broadcast stream, and // it's fine if the other end doesn't receive the message @@ -267,7 +267,7 @@ extension CameraPlugin: FCPCameraApi { camera?.close() camera = newCamera - FLTEnsureToRunOnMainQueue { [weak self] in + ensureToRunOnMainQueue { [weak self] in guard let strongSelf = self else { return } completion(NSNumber(value: strongSelf.registry.register(newCamera)), nil) } @@ -298,12 +298,12 @@ extension CameraPlugin: FCPCameraApi { ) { guard let camera = camera else { return } - camera.videoFormat = FCPGetPixelFormatForPigeonFormat(imageFormat) + camera.videoFormat = getPixelFormat(for: imageFormat) camera.onFrameAvailable = { [weak self] in guard let camera = self?.camera else { return } if !camera.isPreviewPaused { - FLTEnsureToRunOnMainQueue { + ensureToRunOnMainQueue { self?.registry.textureFrameAvailable(Int64(cameraId)) } } diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraProperties.swift b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraProperties.swift new file mode 100644 index 00000000000..fca5c0fef13 --- /dev/null +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/CameraProperties.swift @@ -0,0 +1,83 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import AVFoundation +import UIKit + +// Import Objective-C part of the implementation when SwiftPM is used. +#if canImport(camera_avfoundation_objc) + import camera_avfoundation_objc +#endif + +/// Gets AVCaptureFlashMode from FCPPlatformFlashMode. +/// mode - flash mode. +func getAVCaptureFlashMode(for mode: FCPPlatformFlashMode) -> AVCaptureDevice.FlashMode { + switch mode { + case .off: + return .off + case .auto: + return .auto + case .always: + return .on + case .torch: + assertionFailure("This mode cannot be converted, and requires custom handling.") + return .off + @unknown default: + assertionFailure("Unknown flash mode") + return .off + } +} + +/// Gets UIDeviceOrientation from its Pigeon representation. +/// orientation - the Pigeon device orientation. +func getUIDeviceOrientation( + for orientation: FCPPlatformDeviceOrientation +) -> UIDeviceOrientation { + switch orientation { + case .portraitDown: + return .portraitUpsideDown + case .landscapeLeft: + return .landscapeLeft + case .landscapeRight: + return .landscapeRight + case .portraitUp: + return .portrait + @unknown default: + assertionFailure("Unknown device orientation") + return .portrait + } +} + +/// Gets a Pigeon representation of UIDeviceOrientation. +/// orientation - the UIDeviceOrientation. +func getPigeonDeviceOrientation( + for orientation: UIDeviceOrientation +) -> FCPPlatformDeviceOrientation { + switch orientation { + case .portraitUpsideDown: + return .portraitDown + case .landscapeLeft: + return .landscapeLeft + case .landscapeRight: + return .landscapeRight + case .portrait: + return .portraitUp + default: + return .portraitUp + } +} + +/// Gets pixel format from its Pigeon representation. +/// imageFormat - the Pigeon image format. +func getPixelFormat(for imageFormat: FCPPlatformImageFormatGroup) -> OSType { + switch imageFormat { + case .bgra8888: + return kCVPixelFormatType_32BGRA + case .yuv420: + return kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + @unknown default: + assertionFailure("Unknown image format") + return kCVPixelFormatType_32BGRA + } +} diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/DefaultCamera.swift b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/DefaultCamera.swift index 7cfebc7e259..15a001d63ef 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/DefaultCamera.swift +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/DefaultCamera.swift @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import AVFoundation import CoreMotion +import Flutter // Import Objective-C part of the implementation when SwiftPM is used. #if canImport(camera_avfoundation_objc) @@ -73,14 +75,14 @@ final class DefaultCamera: NSObject, Camera { private var assetWriterPixelBufferAdaptor: AssetWriterInputPixelBufferAdaptor? private var videoAdaptor: AssetWriterInputPixelBufferAdaptor? - /// A dictionary to retain all in-progress FLTSavePhotoDelegates. The key of the dictionary is the + /// A dictionary to retain all in-progress SavePhotoDelegates. The key of the dictionary is the /// AVCapturePhotoSettings's uniqueID for each photo capture operation, and the value is the - /// FLTSavePhotoDelegate that handles the result of each photo capture operation. Note that photo + /// SavePhotoDelegate that handles the result of each photo capture operation. Note that photo /// capture operations may overlap, so FLTCam has to keep track of multiple delegates in progress, /// instead of just a single delegate reference. - private(set) var inProgressSavePhotoDelegates = [Int64: FLTSavePhotoDelegate]() + private(set) var inProgressSavePhotoDelegates = [Int64: SavePhotoDelegate]() - private var imageStreamHandler: FLTImageStreamHandler? + private var imageStreamHandler: ImageStreamHandler? private var previewSize: CGSize? var deviceOrientation: UIDeviceOrientation { @@ -437,7 +439,7 @@ final class DefaultCamera: NSObject, Camera { focusPointSupported: captureDevice.isFocusPointOfInterestSupported ) - FLTEnsureToRunOnMainQueue { [weak self] in + ensureToRunOnMainQueue { [weak self] in self?.dartAPI?.initialized(with: state) { _ in // Ignore any errors, as this is just an event broadcast. } @@ -668,7 +670,7 @@ final class DefaultCamera: NSObject, Camera { } if flashMode != .torch { - settings.flashMode = FCPGetAVCaptureFlashModeForPigeonFlashMode(flashMode) + settings.flashMode = getAVCaptureFlashMode(for: flashMode) } let path: String @@ -682,7 +684,7 @@ final class DefaultCamera: NSObject, Camera { return } - let savePhotoDelegate = FLTSavePhotoDelegate( + let savePhotoDelegate = SavePhotoDelegate( path: path, ioQueue: photoIOQueue, completionHandler: { [weak self] path, error in @@ -774,7 +776,7 @@ final class DefaultCamera: NSObject, Camera { } func lockCaptureOrientation(_ pigeonOrientation: FCPPlatformDeviceOrientation) { - let orientation = FCPGetUIDeviceOrientationForPigeonDeviceOrientation(pigeonOrientation) + let orientation = getUIDeviceOrientation(for: pigeonOrientation) if lockedCaptureOrientation != orientation { lockedCaptureOrientation = orientation updateOrientation() @@ -987,7 +989,7 @@ final class DefaultCamera: NSObject, Camera { details: nil)) return } - let avFlashMode = FCPGetAVCaptureFlashModeForPigeonFlashMode(mode) + let avFlashMode = getAVCaptureFlashMode(for: mode) guard capturePhotoOutput.supportedFlashModes.contains(avFlashMode) else { completion( @@ -1102,14 +1104,14 @@ final class DefaultCamera: NSObject, Camera { ) { startImageStream( with: messenger, - imageStreamHandler: FLTImageStreamHandler(captureSessionQueue: captureSessionQueue), + imageStreamHandler: DefaultImageStreamHandler(captureSessionQueue: captureSessionQueue), completion: completion ) } func startImageStream( with messenger: FlutterBinaryMessenger, - imageStreamHandler: FLTImageStreamHandler, + imageStreamHandler: ImageStreamHandler & NSObjectProtocol, completion: @escaping (FlutterError?) -> Void ) { if isStreamingImages { @@ -1122,7 +1124,7 @@ final class DefaultCamera: NSObject, Camera { name: "plugins.flutter.io/camera_avfoundation/imageStream", binaryMessenger: messenger ) - let threadSafeEventChannel = FLTThreadSafeEventChannel(eventChannel: eventChannel) + let threadSafeEventChannel = ThreadSafeEventChannel(eventChannel: eventChannel) self.imageStreamHandler = imageStreamHandler threadSafeEventChannel.setStreamHandler(imageStreamHandler) { [weak self] in @@ -1409,7 +1411,7 @@ final class DefaultCamera: NSObject, Camera { /// /// Can be called from any thread. private func reportErrorMessage(_ errorMessage: String) { - FLTEnsureToRunOnMainQueue { [weak self] in + ensureToRunOnMainQueue { [weak self] in self?.dartAPI?.reportError(errorMessage) { _ in // Ignore any errors, as this is just an event broadcast. } diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/EventChannel.swift b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/EventChannel.swift new file mode 100644 index 00000000000..2ec3692869a --- /dev/null +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/EventChannel.swift @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Flutter + +/// A protocol which is a direct passthrough to FlutterEventChannel. +/// It exists to allow replacing FlutterEventChannel in tests. +protocol EventChannel { + func setStreamHandler(_ handler: (any FlutterStreamHandler & NSObjectProtocol)?) +} + +extension FlutterEventChannel: EventChannel {} diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/ImageStreamHandler.swift b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/ImageStreamHandler.swift new file mode 100644 index 00000000000..8bfcbcb975e --- /dev/null +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/ImageStreamHandler.swift @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Flutter + +/// Handles streaming of camera image data to Dart via Flutter event channels. +protocol ImageStreamHandler: FlutterStreamHandler { + /// The queue on which `eventSink` property should be accessed. + var captureSessionQueue: DispatchQueue { get } + + /// The event sink to stream camera events to Dart. + /// + /// The property should only be accessed on `captureSessionQueue`. + /// The block itself should be invoked on the main queue. + var eventSink: FlutterEventSink? { get set } +} + +/// Default implementation of ImageStreamHandler. +class DefaultImageStreamHandler: NSObject, ImageStreamHandler { + let captureSessionQueue: DispatchQueue + var eventSink: FlutterEventSink? + + /// Initialize an image stream handler. + /// captureSessionQueue - the queue on which the event sink should be accessed. + init(captureSessionQueue: DispatchQueue) { + self.captureSessionQueue = captureSessionQueue + super.init() + } + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) + -> FlutterError? + { + captureSessionQueue.async { [weak self] in + self?.eventSink = events + } + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + captureSessionQueue.async { [weak self] in + self?.eventSink = nil + } + return nil + } +} diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/QueueUtils.swift b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/QueueUtils.swift index faefd0e931e..efcf762bdb4 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/QueueUtils.swift +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/QueueUtils.swift @@ -7,3 +7,18 @@ import Dispatch /// Queue-specific context data to be associated with the capture session queue. let captureSessionQueueSpecificKey = DispatchSpecificKey() let captureSessionQueueSpecificValue = "capture_session_queue" + +/// Ensures the given block to be run on the main queue. +/// If caller site is already on the main queue, the block will be run +/// synchronously. Otherwise, the block will be dispatched asynchronously to the +/// main queue. +/// block - the block to be run on the main queue. +func ensureToRunOnMainQueue(_ block: @escaping () -> Void) { + if Thread.isMainThread { + block() + } else { + DispatchQueue.main.async { + block() + } + } +} diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/SavePhotoDelegate.swift b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/SavePhotoDelegate.swift new file mode 100644 index 00000000000..1f99a3ff2c1 --- /dev/null +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/SavePhotoDelegate.swift @@ -0,0 +1,85 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import AVFoundation +import Flutter +import Foundation + +/// The completion handler block for save photo operations. +/// Can be called from either main queue or IO queue. +/// If success, `error` will be present and `path` will be nil. Otherewise, `error` will be nil and +/// `path` will be present. +/// path - the path for successfully saved photo file. +/// error - photo capture error or IO error. +typealias SavePhotoDelegateCompletionHandler = (String?, Error?) -> Void + +/// Delegate object that handles photo capture results. +class SavePhotoDelegate: NSObject, AVCapturePhotoCaptureDelegate { + /// The file path for the captured photo. + private let path: String + + /// The queue on which captured photos are written to disk. + private let ioQueue: DispatchQueue + + /// The completion handler block for capture and save photo operations. + let completionHandler: SavePhotoDelegateCompletionHandler + + /// The path for captured photo file. + /// Exposed for unit tests to verify the captured photo file path. + var filePath: String { + path + } + + /// Initialize a photo capture delegate. + /// path - the path for captured photo file. + /// ioQueue - the queue on which captured photos are written to disk. + /// completionHandler - The completion handler block for save photo operations. Can + /// be called from either main queue or IO queue. + init( + path: String, + ioQueue: DispatchQueue, + completionHandler: @escaping SavePhotoDelegateCompletionHandler + ) { + self.path = path + self.ioQueue = ioQueue + self.completionHandler = completionHandler + super.init() + } + + /// Handler to write captured photo data into a file. + /// - Parameters: + /// - error: The capture error + /// - photoDataProvider: A closure that provides photo data + func handlePhotoCaptureResult( + error: Error?, + photoDataProvider: @escaping () -> WritableData? + ) { + if let error = error { + completionHandler(nil, error) + return + } + + ioQueue.async { [weak self] in + guard let strongSelf = self else { return } + + do { + let data = photoDataProvider() + try data?.write(to: strongSelf.path, options: .atomic) + strongSelf.completionHandler(strongSelf.path, nil) + } catch { + strongSelf.completionHandler(nil, error) + } + } + } + + func photoOutput( + _ output: AVCapturePhotoOutput, + didFinishProcessingPhoto photo: AVCapturePhoto, + error: Error? + ) { + handlePhotoCaptureResult(error: error) { + photo.fileDataRepresentation() + } + } +} diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/ThreadSafeEventChannel.swift b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/ThreadSafeEventChannel.swift new file mode 100644 index 00000000000..4402bee76fe --- /dev/null +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/ThreadSafeEventChannel.swift @@ -0,0 +1,35 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Flutter + +/// A thread safe wrapper for FlutterEventChannel that can be called from any thread, by dispatching +/// its underlying engine calls to the main thread. +class ThreadSafeEventChannel { + private let channel: EventChannel + + /// Creates a ThreadSafeEventChannel by wrapping a FlutterEventChannel object. + /// channel - The FlutterEventChannel object to be wrapped. + init(eventChannel channel: EventChannel) { + self.channel = channel + } + + /// Registers a handler on the main thread for stream setup requests from the Flutter side. + /// The completion block runs on the main thread. + /// handler - The stream handler to register (nil to unregister). + /// completion - The completion block that runs on the main thread. + func setStreamHandler( + _ handler: (any FlutterStreamHandler & NSObjectProtocol)?, completion: @escaping () -> Void + ) { + // WARNING: Should not use weak self, because ThreadSafeEventChannel is a local variable + // (retained within call stack, but not in the heap). ensureToRunOnMainQueue may trigger a + // context switch (when calling from background thread), in which case using weak self will always + // result in a nil self. Alternative to using strong self, we can also create a local strong + // variable to be captured by this block. + ensureToRunOnMainQueue { + self.channel.setStreamHandler(handler) + completion() + } + } +} diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/WritableData.swift b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/WritableData.swift new file mode 100644 index 00000000000..f7bfcc119ad --- /dev/null +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/WritableData.swift @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation + +/// A protocol which abstracts the file writing operation implemented in `Data`. +/// It exists to allow replacing `Data` in tests. +protocol WritableData { + func write( + to path: String, + options: Data.WritingOptions + ) throws +} + +extension Data: WritableData { + func write(to path: String, options: Data.WritingOptions) throws { + try write(to: URL(fileURLWithPath: path), options: options) + } +} diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/CameraProperties.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/CameraProperties.m deleted file mode 100644 index e16f238829b..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/CameraProperties.m +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "./include/camera_avfoundation/CameraProperties.h" - -AVCaptureFlashMode FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode mode) { - switch (mode) { - case FCPPlatformFlashModeOff: - return AVCaptureFlashModeOff; - case FCPPlatformFlashModeAuto: - return AVCaptureFlashModeAuto; - case FCPPlatformFlashModeAlways: - return AVCaptureFlashModeOn; - case FCPPlatformFlashModeTorch: - NSCAssert(false, @"This mode cannot be converted, and requires custom handling."); - return -1; - } -} - -UIDeviceOrientation FCPGetUIDeviceOrientationForPigeonDeviceOrientation( - FCPPlatformDeviceOrientation orientation) { - switch (orientation) { - case FCPPlatformDeviceOrientationPortraitDown: - return UIDeviceOrientationPortraitUpsideDown; - case FCPPlatformDeviceOrientationLandscapeLeft: - return UIDeviceOrientationLandscapeLeft; - case FCPPlatformDeviceOrientationLandscapeRight: - return UIDeviceOrientationLandscapeRight; - case FCPPlatformDeviceOrientationPortraitUp: - return UIDeviceOrientationPortrait; - }; -} - -FCPPlatformDeviceOrientation FCPGetPigeonDeviceOrientationForOrientation( - UIDeviceOrientation orientation) { - switch (orientation) { - case UIDeviceOrientationPortraitUpsideDown: - return FCPPlatformDeviceOrientationPortraitDown; - case UIDeviceOrientationLandscapeLeft: - return FCPPlatformDeviceOrientationLandscapeLeft; - case UIDeviceOrientationLandscapeRight: - return FCPPlatformDeviceOrientationLandscapeRight; - case UIDeviceOrientationPortrait: - default: - return FCPPlatformDeviceOrientationPortraitUp; - }; -} - -OSType FCPGetPixelFormatForPigeonFormat(FCPPlatformImageFormatGroup imageFormat) { - switch (imageFormat) { - case FCPPlatformImageFormatGroupBgra8888: - return kCVPixelFormatType_32BGRA; - case FCPPlatformImageFormatGroupYuv420: - return kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; - } -} diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTImageStreamHandler.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTImageStreamHandler.m deleted file mode 100644 index 530bee3696d..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTImageStreamHandler.m +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -@import Flutter; - -#import "./include/camera_avfoundation/FLTImageStreamHandler.h" - -@implementation FLTImageStreamHandler - -- (instancetype)initWithCaptureSessionQueue:(dispatch_queue_t)captureSessionQueue { - self = [super init]; - NSAssert(self, @"super init cannot be nil"); - _captureSessionQueue = captureSessionQueue; - return self; -} - -- (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { - __weak typeof(self) weakSelf = self; - dispatch_async(self.captureSessionQueue, ^{ - weakSelf.eventSink = nil; - }); - return nil; -} - -- (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments - eventSink:(nonnull FlutterEventSink)events { - __weak typeof(self) weakSelf = self; - dispatch_async(self.captureSessionQueue, ^{ - weakSelf.eventSink = events; - }); - return nil; -} -@end diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTSavePhotoDelegate.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTSavePhotoDelegate.m deleted file mode 100644 index 3cbf76201b9..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTSavePhotoDelegate.m +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "./include/camera_avfoundation/FLTSavePhotoDelegate.h" -#import "./include/camera_avfoundation/FLTSavePhotoDelegate_Test.h" - -@interface FLTSavePhotoDelegate () -/// The file path for the captured photo. -@property(readonly, nonatomic) NSString *path; -/// The queue on which captured photos are written to disk. -@property(readonly, nonatomic) dispatch_queue_t ioQueue; -@end - -@implementation FLTSavePhotoDelegate - -- (instancetype)initWithPath:(NSString *)path - ioQueue:(dispatch_queue_t)ioQueue - completionHandler:(FLTSavePhotoDelegateCompletionHandler)completionHandler { - self = [super init]; - NSAssert(self, @"super init cannot be nil"); - _path = path; - _ioQueue = ioQueue; - _completionHandler = completionHandler; - return self; -} - -- (void)handlePhotoCaptureResultWithError:(NSError *)error - photoDataProvider:(NSObject * (^)(void))photoDataProvider { - if (error) { - self.completionHandler(nil, error); - return; - } - __weak typeof(self) weakSelf = self; - dispatch_async(self.ioQueue, ^{ - typeof(self) strongSelf = weakSelf; - if (!strongSelf) return; - - NSObject *data = photoDataProvider(); - NSError *ioError; - if ([data writeToFile:strongSelf.path options:NSDataWritingAtomic error:&ioError]) { - strongSelf.completionHandler(self.path, nil); - } else { - strongSelf.completionHandler(nil, ioError); - } - }); -} - -- (void)captureOutput:(AVCapturePhotoOutput *)output - didFinishProcessingPhoto:(AVCapturePhoto *)photo - error:(NSError *)error { - [self handlePhotoCaptureResultWithError:error - photoDataProvider:^NSData * { - return [photo fileDataRepresentation]; - }]; -} - -- (NSString *)filePath { - return self.path; -} -@end diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTThreadSafeEventChannel.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTThreadSafeEventChannel.m deleted file mode 100644 index 9c7d4722552..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTThreadSafeEventChannel.m +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "./include/camera_avfoundation/FLTThreadSafeEventChannel.h" -#import "./include/camera_avfoundation/FLTEventChannel.h" -#import "./include/camera_avfoundation/QueueUtils.h" - -@interface FLTThreadSafeEventChannel () -@property(nonatomic, strong) id channel; -@end - -@implementation FLTThreadSafeEventChannel - -- (instancetype)initWithEventChannel:(id)channel { - self = [super init]; - if (self) { - _channel = channel; - } - return self; -} - -- (void)setStreamHandler:(NSObject *)handler - completion:(void (^)(void))completion { - // WARNING: Should not use weak self, because FLTThreadSafeEventChannel is a local variable - // (retained within call stack, but not in the heap). FLTEnsureToRunOnMainQueue may trigger a - // context switch (when calling from background thread), in which case using weak self will always - // result in a nil self. Alternative to using strong self, we can also create a local strong - // variable to be captured by this block. - FLTEnsureToRunOnMainQueue(^{ - [self.channel setStreamHandler:handler]; - completion(); - }); -} - -@end diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTWritableData.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTWritableData.m deleted file mode 100644 index 180e3d70a3d..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/FLTWritableData.m +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "./include/camera_avfoundation/FLTWritableData.h" - -@implementation FLTDefaultWritableData - -- (instancetype)initWithData:(NSData *)data { - self = [super init]; - if (self) { - _data = data; - } - return self; -} - -- (BOOL)writeToFile:(NSString *)path - options:(NSDataWritingOptions)writeOptionsMask - error:(NSError **)errorPtr { - return [self.data writeToFile:path options:writeOptionsMask error:errorPtr]; -} - -@end diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/QueueUtils.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/QueueUtils.m deleted file mode 100644 index 2cfca52a299..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/QueueUtils.m +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "./include/camera_avfoundation/QueueUtils.h" - -void FLTEnsureToRunOnMainQueue(dispatch_block_t block) { - if (!NSThread.isMainThread) { - dispatch_async(dispatch_get_main_queue(), block); - } else { - block(); - } -} diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/CameraProperties.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/CameraProperties.h deleted file mode 100644 index 6645cf7bb06..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/CameraProperties.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -@import AVFoundation; -@import Foundation; -@import UIKit; - -#import "messages.g.h" - -NS_ASSUME_NONNULL_BEGIN - -/// Gets AVCaptureFlashMode from FLTFlashMode. -/// @param mode flash mode. -extern AVCaptureFlashMode FCPGetAVCaptureFlashModeForPigeonFlashMode(FCPPlatformFlashMode mode); - -/// Gets UIDeviceOrientation from its Pigeon representation. -extern UIDeviceOrientation FCPGetUIDeviceOrientationForPigeonDeviceOrientation( - FCPPlatformDeviceOrientation orientation); - -/// Gets a Pigeon representation of UIDeviceOrientation. -extern FCPPlatformDeviceOrientation FCPGetPigeonDeviceOrientationForOrientation( - UIDeviceOrientation orientation); - -/// Gets VideoFormat from its Pigeon representation. -extern OSType FCPGetPixelFormatForPigeonFormat(FCPPlatformImageFormatGroup imageFormat); - -NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTEventChannel.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTEventChannel.h deleted file mode 100644 index cdadc34d5ca..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTEventChannel.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -@import Flutter; - -NS_ASSUME_NONNULL_BEGIN - -/// A protocol which is a direct passthrough to FlutterStreamHandler. -/// It exists to allow replacing FlutterStreamHandler in tests. -@protocol FLTEventChannel -- (void)setStreamHandler:(nullable NSObject *)handler; -@end - -@interface FlutterEventChannel (FLTEventChannel) -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTImageStreamHandler.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTImageStreamHandler.h deleted file mode 100644 index be058e98dc0..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTImageStreamHandler.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -@interface FLTImageStreamHandler : NSObject - -- (instancetype)initWithCaptureSessionQueue:(dispatch_queue_t)captureSessionQueue; - -/// The queue on which `eventSink` property should be accessed. -@property(nonatomic, strong) dispatch_queue_t captureSessionQueue; - -/// The event sink to stream camera events to Dart. -/// -/// The property should only be accessed on `captureSessionQueue`. -/// The block itself should be invoked on the main queue. -@property FlutterEventSink eventSink; - -@end diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTSavePhotoDelegate.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTSavePhotoDelegate.h deleted file mode 100644 index a6616256d87..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTSavePhotoDelegate.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -@import AVFoundation; -@import Flutter; -@import Foundation; - -NS_ASSUME_NONNULL_BEGIN - -/// The completion handler block for save photo operations. -/// Can be called from either main queue or IO queue. -/// If success, `error` will be present and `path` will be nil. Otherewise, `error` will be nil and -/// `path` will be present. -/// @param path the path for successfully saved photo file. -/// @param error photo capture error or IO error. -typedef void (^FLTSavePhotoDelegateCompletionHandler)(NSString *_Nullable path, - NSError *_Nullable error); - -/// Delegate object that handles photo capture results. -@interface FLTSavePhotoDelegate : NSObject - -/// Initialize a photo capture delegate. -/// @param path the path for captured photo file. -/// @param ioQueue the queue on which captured photos are written to disk. -/// @param completionHandler The completion handler block for save photo operations. Can -/// be called from either main queue or IO queue. -- (instancetype)initWithPath:(NSString *)path - ioQueue:(dispatch_queue_t)ioQueue - completionHandler:(FLTSavePhotoDelegateCompletionHandler)completionHandler; -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTSavePhotoDelegate_Test.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTSavePhotoDelegate_Test.h deleted file mode 100644 index 0158f7e482a..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTSavePhotoDelegate_Test.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "FLTSavePhotoDelegate.h" - -#import "FLTWritableData.h" - -/// API exposed for unit tests. -@interface FLTSavePhotoDelegate () - -/// The completion handler block for capture and save photo operations. -/// Can be called from either main queue or IO queue. -/// Exposed for unit tests to manually trigger the completion. -@property(readonly, nonatomic) FLTSavePhotoDelegateCompletionHandler completionHandler; - -/// The path for captured photo file. -/// Exposed for unit tests to verify the captured photo file path. -@property(readwrite, nonatomic) NSString *filePath; - -/// Handler to write captured photo data into a file. -/// @param error the capture error. -/// @param photoDataProvider a closure that provides photo data. -- (void)handlePhotoCaptureResultWithError:(NSError *)error - photoDataProvider:(NSObject * (^)(void))photoDataProvider - NS_SWIFT_NAME(handlePhotoCaptureResult(error:photoDataProvider:)); -@end diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTThreadSafeEventChannel.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTThreadSafeEventChannel.h deleted file mode 100644 index 6faa29af5d6..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTThreadSafeEventChannel.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -#import "FLTEventChannel.h" - -NS_ASSUME_NONNULL_BEGIN - -/// A thread safe wrapper for FlutterEventChannel that can be called from any thread, by dispatching -/// its underlying engine calls to the main thread. -@interface FLTThreadSafeEventChannel : NSObject - -/// Creates a FLTThreadSafeEventChannel by wrapping a FlutterEventChannel object. -/// @param channel The FlutterEventChannel object to be wrapped. -- (instancetype)initWithEventChannel:(NSObject *)channel; - -/// Registers a handler on the main thread for stream setup requests from the Flutter side. -/// The completion block runs on the main thread. -- (void)setStreamHandler:(nullable NSObject *)handler - completion:(void (^)(void))completion; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTWritableData.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTWritableData.h deleted file mode 100644 index 019b6ad2896..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/FLTWritableData.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -@import Foundation; - -NS_ASSUME_NONNULL_BEGIN - -/// A protocol which abstracts the file writing operation implemented in `NSData`. -/// It exists to allow replacing `NSData` in tests. -@protocol FLTWritableData -- (BOOL)writeToFile:(NSString *)path - options:(NSDataWritingOptions)writeOptionsMask - error:(NSError **)errorPtr; -@end - -/// A default implementation of the `FLTWritableData` protocol which operates on -/// an `NSData` instance. -@interface FLTDefaultWritableData : NSObject -@property(nonatomic, strong, readonly) NSData *data; -- (instancetype)initWithData:(NSData *)data; -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/QueueUtils.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/QueueUtils.h deleted file mode 100644 index 7dcb3b948a7..00000000000 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/QueueUtils.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2013 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -NS_ASSUME_NONNULL_BEGIN - -/// Ensures the given block to be run on the main queue. -/// If caller site is already on the main queue, the block will be run -/// synchronously. Otherwise, the block will be dispatched asynchronously to the -/// main queue. -/// @param block the block to be run on the main queue. -extern void FLTEnsureToRunOnMainQueue(dispatch_block_t block); - -NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/camera_avfoundation.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/camera_avfoundation.h index e220dc6bf8b..c16177a7f69 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/camera_avfoundation.h +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation_objc/include/camera_avfoundation/camera_avfoundation.h @@ -2,5 +2,4 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "QueueUtils.h" #import "messages.g.h" diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 801af16c095..4f53f2d6d57 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.22+8 +version: 0.9.22+9 environment: sdk: ^3.9.0