diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index 51effb47112..d40aeca0e55 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.18+1 + +* Refactors implementations to reduce usage of OCMock in internal testing. + ## 0.9.18 * Adds API support query for image streaming. diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj index 041d918da5b..8dd444160cb 100644 --- a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,22 +3,26 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 60; objects = { /* Begin PBXBuildFile section */ 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */; }; 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB766A2665316900CE5A93 /* CameraFocusTests.m */; }; + 1000364CB781922C6D6AAA4A /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DDC4CE84A8B378AE4A8CD9C /* libPods-RunnerTests.a */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 236906D1621AE863A5B2E770 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 89D82918721FABF772705DB0 /* libPods-Runner.a */; }; - 25C3919135C3D981E6F800D0 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */; }; 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 408D7A792C3C9CD000B71F9A /* OCMock in Frameworks */ = {isa = PBXBuildFile; productRef = 408D7A782C3C9CD000B71F9A /* OCMock */; }; 43ED1537282570DE00EB00DE /* AvailableCamerasTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */; }; + 54D650172516862D30686934 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ECAF63F924EFA2D68883BA85 /* libPods-Runner.a */; }; 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 788A065927B0E02900533D74 /* StreamingTest.m */; }; 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 7D5FCCD42AEF9D0200FB7108 /* CameraSettingsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D5FCCD32AEF9D0200FB7108 /* CameraSettingsTests.m */; }; + 7F56D0382D1EDDCE005676A5 /* CameraPermissionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */; }; + 7F87E8022D01FD6F00A3549C /* MockCaptureDeviceController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F87E8012D01FD5600A3549C /* MockCaptureDeviceController.m */; }; + 7F87E80C2D0325D900A3549C /* MockDeviceOrientationProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F87E80B2D0325D700A3549C /* MockDeviceOrientationProvider.m */; }; + 7FA99E592D22C75300582559 /* CameraExposureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FA99E582D22C75300582559 /* CameraExposureTests.m */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -30,7 +34,6 @@ E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */; }; E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */; }; E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */; }; - E0B0D2BB27DFF2AF00E71E4B /* CameraPermissionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */; }; E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */; }; E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */; }; @@ -68,17 +71,19 @@ 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraOrientationTests.m; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 14AE82C910C2A12F2ECB2094 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AvailableCamerasTest.m; sourceTree = ""; }; - 59848A7CA98C1FADF8840207 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 4A191381C3593DF1AC4E7559 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 788A065927B0E02900533D74 /* StreamingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StreamingTest.m; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 7D5FCCD32AEF9D0200FB7108 /* CameraSettingsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraSettingsTests.m; sourceTree = ""; }; - 89D82918721FABF772705DB0 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 7F87E8012D01FD5600A3549C /* MockCaptureDeviceController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockCaptureDeviceController.m; sourceTree = ""; }; + 7F87E8032D02FF8C00A3549C /* MockCaptureDeviceController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockCaptureDeviceController.h; sourceTree = ""; }; + 7F87E80A2D0325B200A3549C /* MockDeviceOrientationProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockDeviceOrientationProvider.h; sourceTree = ""; }; + 7F87E80B2D0325D700A3549C /* MockDeviceOrientationProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockDeviceOrientationProvider.m; sourceTree = ""; }; + 7FA99E582D22C75300582559 /* CameraExposureTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraExposureTests.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -87,8 +92,9 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; - A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 9DDC4CE84A8B378AE4A8CD9C /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + A8F314CD1C64E9257EBC811D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + B61D98BBC8FB276D1C4A7BB2 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; CEF661192B5E36A500D33FD4 /* CameraSessionPresetsTests.m */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = CameraSessionPresetsTests.m; sourceTree = ""; }; E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QueueUtilsTests.m; sourceTree = ""; }; E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraCaptureSessionQueueRaceConditionTests.m; sourceTree = ""; }; @@ -101,6 +107,8 @@ E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraTestUtils.m; sourceTree = ""; }; E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPropertiesTests.m; sourceTree = ""; }; E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPreviewPauseTests.m; sourceTree = ""; }; + E67C6DBF6478BE708993169F /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + ECAF63F924EFA2D68883BA85 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -109,7 +117,7 @@ buildActionMask = 2147483647; files = ( 408D7A792C3C9CD000B71F9A /* OCMock in Frameworks */, - 25C3919135C3D981E6F800D0 /* libPods-RunnerTests.a in Frameworks */, + 1000364CB781922C6D6AAA4A /* libPods-RunnerTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -118,7 +126,7 @@ buildActionMask = 2147483647; files = ( 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, - 236906D1621AE863A5B2E770 /* libPods-Runner.a in Frameworks */, + 54D650172516862D30686934 /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -128,8 +136,13 @@ 03BB76692665316900CE5A93 /* RunnerTests */ = { isa = PBXGroup; children = ( + 7F87E80B2D0325D700A3549C /* MockDeviceOrientationProvider.m */, + 7F87E80A2D0325B200A3549C /* MockDeviceOrientationProvider.h */, + 7F87E8032D02FF8C00A3549C /* MockCaptureDeviceController.h */, + 7F87E8012D01FD5600A3549C /* MockCaptureDeviceController.m */, 7D5FCCD32AEF9D0200FB7108 /* CameraSettingsTests.m */, 03BB766A2665316900CE5A93 /* CameraFocusTests.m */, + 7FA99E582D22C75300582559 /* CameraExposureTests.m */, 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */, 03BB766C2665316900CE5A93 /* Info.plist */, 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */, @@ -154,8 +167,8 @@ 3242FD2B467C15C62200632F /* Frameworks */ = { isa = PBXGroup; children = ( - 89D82918721FABF772705DB0 /* libPods-Runner.a */, - 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */, + ECAF63F924EFA2D68883BA85 /* libPods-Runner.a */, + 9DDC4CE84A8B378AE4A8CD9C /* libPods-RunnerTests.a */, ); name = Frameworks; sourceTree = ""; @@ -219,10 +232,10 @@ FD386F00E98D73419C929072 /* Pods */ = { isa = PBXGroup; children = ( - 59848A7CA98C1FADF8840207 /* Pods-Runner.debug.xcconfig */, - 14AE82C910C2A12F2ECB2094 /* Pods-Runner.release.xcconfig */, - 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */, - A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */, + A8F314CD1C64E9257EBC811D /* Pods-Runner.debug.xcconfig */, + 4A191381C3593DF1AC4E7559 /* Pods-Runner.release.xcconfig */, + B61D98BBC8FB276D1C4A7BB2 /* Pods-RunnerTests.debug.xcconfig */, + E67C6DBF6478BE708993169F /* Pods-RunnerTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -263,6 +276,7 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 2B62C73988DE02487EF557D4 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -305,7 +319,7 @@ ); mainGroup = 97C146E51CF9000F007C117D; packageReferences = ( - 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, 408D7A772C3C9CD000B71F9A /* XCRemoteSwiftPackageReference "ocmock" */, ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; @@ -340,6 +354,28 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 2B62C73988DE02487EF557D4 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/camera_avfoundation/camera_avfoundation_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/path_provider_foundation/path_provider_foundation_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/video_player_avfoundation/video_player_avfoundation_privacy.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/camera_avfoundation_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/path_provider_foundation_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/video_player_avfoundation_privacy.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -424,10 +460,14 @@ files = ( 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */, E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */, + 7F56D0382D1EDDCE005676A5 /* CameraPermissionTests.m in Sources */, E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */, 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */, + 7F87E8022D01FD6F00A3549C /* MockCaptureDeviceController.m in Sources */, 7D5FCCD42AEF9D0200FB7108 /* CameraSettingsTests.m in Sources */, + 7FA99E592D22C75300582559 /* CameraExposureTests.m in Sources */, E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */, + 7F87E80C2D0325D900A3549C /* MockDeviceOrientationProvider.m in Sources */, E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */, E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */, 43ED1537282570DE00EB00DE /* AvailableCamerasTest.m in Sources */, @@ -437,7 +477,6 @@ E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */, 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */, E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, - E0B0D2BB27DFF2AF00E71E4B /* CameraPermissionTests.m in Sources */, E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -484,7 +523,7 @@ /* Begin XCBuildConfiguration section */ 03BB766F2665316900CE5A93 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = B61D98BBC8FB276D1C4A7BB2 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -513,7 +552,7 @@ }; 03BB76702665316900CE5A93 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = E67C6DBF6478BE708993169F /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -726,7 +765,7 @@ /* End XCConfigurationList section */ /* Begin XCLocalSwiftPackageReference section */ - 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { isa = XCLocalSwiftPackageReference; relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; }; diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.m index 7b641a5746c..30de3182c33 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.m @@ -5,51 +5,79 @@ @import camera_avfoundation; @import XCTest; @import AVFoundation; -#import -@interface FLTCam : NSObject - -- (void)setExposurePointWithResult:(FlutterResult)result x:(double)x y:(double)y; -@end +#import "CameraTestUtils.h" +#import "MockCaptureDeviceController.h" +#import "MockDeviceOrientationProvider.h" @interface CameraExposureTests : XCTestCase @property(readonly, nonatomic) FLTCam *camera; -@property(readonly, nonatomic) id mockDevice; -@property(readonly, nonatomic) id mockUIDevice; +@property(readonly, nonatomic) MockCaptureDeviceController *mockDevice; +@property(readonly, nonatomic) MockDeviceOrientationProvider *mockDeviceOrientationProvider; @end @implementation CameraExposureTests - (void)setUp { - _camera = [[FLTCam alloc] init]; - _mockDevice = OCMClassMock([AVCaptureDevice class]); - _mockUIDevice = OCMPartialMock([UIDevice currentDevice]); -} + MockCaptureDeviceController *mockDevice = [[MockCaptureDeviceController alloc] init]; + _mockDeviceOrientationProvider = [[MockDeviceOrientationProvider alloc] init]; + _mockDevice = mockDevice; -- (void)tearDown { - [_mockDevice stopMocking]; - [_mockUIDevice stopMocking]; + _camera = FLTCreateCamWithCaptureSessionQueueAndMediaSettings( + nil, nil, nil, + ^id(void) { + return mockDevice; + }, + _mockDeviceOrientationProvider); } -- (void)testSetExpsourePointWithResult_SetsExposurePointOfInterest { +- (void)testSetExposurePointWithResult_SetsExposurePointOfInterest { // UI is currently in landscape left orientation - OCMStub([(UIDevice *)_mockUIDevice orientation]).andReturn(UIDeviceOrientationLandscapeLeft); + _mockDeviceOrientationProvider.orientation = UIDeviceOrientationLandscapeLeft; // Exposure point of interest is supported - OCMStub([_mockDevice isExposurePointOfInterestSupported]).andReturn(true); - // Set mock device as the current capture device - [_camera setValue:_mockDevice forKey:@"captureDevice"]; + _mockDevice.exposurePointOfInterestSupported = YES; + + // Verify the focus point of interest has been set + __block CGPoint setPoint = CGPointZero; + _mockDevice.setExposurePointOfInterestStub = ^(CGPoint point) { + if (CGPointEqualToPoint(CGPointMake(1, 1), point)) { + setPoint = point; + } + }; // Run test + XCTestExpectation *completionExpectation = [self expectationWithDescription:@"Completion called"]; + [_camera setExposurePoint:[FCPPlatformPoint makeWithX:1 y:1] + withCompletion:^(FlutterError *_Nullable error) { + XCTAssertNil(error); + [completionExpectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; + XCTAssertEqual(setPoint.x, 1.0); + XCTAssertEqual(setPoint.y, 1.0); +} + +- (void)testSetExposurePoint_WhenNotSupported_ReturnsError { + // UI is currently in landscape left orientation + _mockDeviceOrientationProvider.orientation = UIDeviceOrientationLandscapeLeft; + // Exposure point of interest is not supported + _mockDevice.exposurePointOfInterestSupported = NO; + + XCTestExpectation *expectation = [self expectationWithDescription:@"Completion with error"]; + + // Run [_camera - setExposurePointWithResult:^void(id _Nullable result) { - } - x:1 - y:1]; + setExposurePoint:[FCPPlatformPoint makeWithX:1 y:1] + withCompletion:^(FlutterError *_Nullable error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.code, @"setExposurePointFailed"); + XCTAssertEqualObjects(error.message, @"Device does not have exposure point capabilities"); + [expectation fulfill]; + }]; - // Verify the focus point of interest has been set - OCMVerify([_mockDevice setExposurePointOfInterest:CGPointMake(1, 1)]); + // Verify + [self waitForExpectationsWithTimeout:1 handler:nil]; } @end diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m index 0cb8333345a..63be762f529 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m @@ -8,100 +8,128 @@ #endif @import XCTest; @import AVFoundation; -#import + +#import "CameraTestUtils.h" +#import "MockCaptureDeviceController.h" +#import "MockDeviceOrientationProvider.h" @interface CameraFocusTests : XCTestCase @property(readonly, nonatomic) FLTCam *camera; -@property(readonly, nonatomic) id mockDevice; -@property(readonly, nonatomic) id mockUIDevice; +@property(readonly, nonatomic) MockCaptureDeviceController *mockDevice; +@property(readonly, nonatomic) MockDeviceOrientationProvider *mockDeviceOrientationProvider; @end @implementation CameraFocusTests - (void)setUp { - _camera = [[FLTCam alloc] init]; - _mockDevice = OCMClassMock([AVCaptureDevice class]); - _mockUIDevice = OCMPartialMock([UIDevice currentDevice]); -} - -- (void)tearDown { - [_mockDevice stopMocking]; - [_mockUIDevice stopMocking]; + MockCaptureDeviceController *mockDevice = [[MockCaptureDeviceController alloc] init]; + _mockDevice = mockDevice; + _mockDeviceOrientationProvider = [[MockDeviceOrientationProvider alloc] init]; + + _camera = FLTCreateCamWithCaptureSessionQueueAndMediaSettings( + nil, nil, nil, + ^id(void) { + return mockDevice; + }, + _mockDeviceOrientationProvider); } - (void)testAutoFocusWithContinuousModeSupported_ShouldSetContinuousAutoFocus { - // AVCaptureFocusModeContinuousAutoFocus is supported - OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]).andReturn(true); - // AVCaptureFocusModeContinuousAutoFocus is supported - OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(true); - - // Don't expect setFocusMode:AVCaptureFocusModeAutoFocus - [[_mockDevice reject] setFocusMode:AVCaptureFocusModeAutoFocus]; + // AVCaptureFocusModeContinuousAutoFocus and AVCaptureFocusModeContinuousAutoFocus are supported + _mockDevice.isFocusModeSupportedStub = ^BOOL(AVCaptureFocusMode mode) { + return mode == AVCaptureFocusModeContinuousAutoFocus || mode == AVCaptureFocusModeAutoFocus; + }; + + __block BOOL setFocusModeContinuousAutoFocusCalled = NO; + + _mockDevice.setFocusModeStub = ^(AVCaptureFocusMode mode) { + // Don't expect setFocusMode:AVCaptureFocusModeAutoFocus + if (mode == AVCaptureFocusModeAutoFocus) { + XCTFail(@"Unexpected call to setFocusMode"); + } else if (mode == AVCaptureFocusModeContinuousAutoFocus) { + setFocusModeContinuousAutoFocusCalled = YES; + } + }; // Run test [_camera applyFocusMode:FCPPlatformFocusModeAuto onDevice:_mockDevice]; // Expect setFocusMode:AVCaptureFocusModeContinuousAutoFocus - OCMVerify([_mockDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus]); + XCTAssertTrue(setFocusModeContinuousAutoFocusCalled); } - (void)testAutoFocusWithContinuousModeNotSupported_ShouldSetAutoFocus { // AVCaptureFocusModeContinuousAutoFocus is not supported - OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) - .andReturn(false); - // AVCaptureFocusModeContinuousAutoFocus is supported - OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(true); + // AVCaptureFocusModeAutoFocus is supported + _mockDevice.isFocusModeSupportedStub = ^BOOL(AVCaptureFocusMode mode) { + return mode == AVCaptureFocusModeAutoFocus; + }; + + __block BOOL setFocusModeAutoFocusCalled = NO; // Don't expect setFocusMode:AVCaptureFocusModeContinuousAutoFocus - [[_mockDevice reject] setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; + _mockDevice.setFocusModeStub = ^(AVCaptureFocusMode mode) { + if (mode == AVCaptureFocusModeContinuousAutoFocus) { + XCTFail(@"Unexpected call to setFocusMode"); + } else if (mode == AVCaptureFocusModeAutoFocus) { + setFocusModeAutoFocusCalled = YES; + } + }; // Run test [_camera applyFocusMode:FCPPlatformFocusModeAuto onDevice:_mockDevice]; // Expect setFocusMode:AVCaptureFocusModeAutoFocus - OCMVerify([_mockDevice setFocusMode:AVCaptureFocusModeAutoFocus]); + XCTAssertTrue(setFocusModeAutoFocusCalled); } - (void)testAutoFocusWithNoModeSupported_ShouldSetNothing { - // AVCaptureFocusModeContinuousAutoFocus is not supported - OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) - .andReturn(false); - // AVCaptureFocusModeContinuousAutoFocus is not supported - OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(false); + // No modes are supported + _mockDevice.isFocusModeSupportedStub = ^BOOL(AVCaptureFocusMode mode) { + return NO; + }; // Don't expect any setFocus - [[_mockDevice reject] setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; - [[_mockDevice reject] setFocusMode:AVCaptureFocusModeAutoFocus]; + _mockDevice.setFocusModeStub = ^(AVCaptureFocusMode mode) { + XCTFail(@"Unexpected call to setFocusMode"); + }; // Run test [_camera applyFocusMode:FCPPlatformFocusModeAuto onDevice:_mockDevice]; } - (void)testLockedFocusWithModeSupported_ShouldSetModeAutoFocus { - // AVCaptureFocusModeContinuousAutoFocus is supported - OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]).andReturn(true); - // AVCaptureFocusModeContinuousAutoFocus is supported - OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(true); - - // Don't expect any setFocus - [[_mockDevice reject] setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; + // AVCaptureFocusModeContinuousAutoFocus and AVCaptureFocusModeAutoFocus are supported + _mockDevice.isFocusModeSupportedStub = ^BOOL(AVCaptureFocusMode mode) { + return mode == AVCaptureFocusModeContinuousAutoFocus || mode == AVCaptureFocusModeAutoFocus; + }; + + __block BOOL setFocusModeAutoFocusCalled = NO; + + // Expect only setFocusMode:AVCaptureFocusModeAutoFocus + _mockDevice.setFocusModeStub = ^(AVCaptureFocusMode mode) { + if (mode == AVCaptureFocusModeContinuousAutoFocus) { + XCTFail(@"Unexpected call to setFocusMode"); + } else if (mode == AVCaptureFocusModeAutoFocus) { + setFocusModeAutoFocusCalled = YES; + } + }; // Run test [_camera applyFocusMode:FCPPlatformFocusModeLocked onDevice:_mockDevice]; - // Expect setFocusMode:AVCaptureFocusModeAutoFocus - OCMVerify([_mockDevice setFocusMode:AVCaptureFocusModeAutoFocus]); + XCTAssertTrue(setFocusModeAutoFocusCalled); } - (void)testLockedFocusWithModeNotSupported_ShouldSetNothing { - // AVCaptureFocusModeContinuousAutoFocus is supported - OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]).andReturn(true); - // AVCaptureFocusModeContinuousAutoFocus is not supported - OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(false); + _mockDevice.isFocusModeSupportedStub = ^BOOL(AVCaptureFocusMode mode) { + return mode == AVCaptureFocusModeContinuousAutoFocus; + }; // Don't expect any setFocus - [[_mockDevice reject] setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; - [[_mockDevice reject] setFocusMode:AVCaptureFocusModeAutoFocus]; + _mockDevice.setFocusModeStub = ^(AVCaptureFocusMode mode) { + XCTFail(@"Unexpected call to setFocusMode"); + }; // Run test [_camera applyFocusMode:FCPPlatformFocusModeLocked onDevice:_mockDevice]; @@ -109,11 +137,16 @@ - (void)testLockedFocusWithModeNotSupported_ShouldSetNothing { - (void)testSetFocusPointWithResult_SetsFocusPointOfInterest { // UI is currently in landscape left orientation - OCMStub([(UIDevice *)_mockUIDevice orientation]).andReturn(UIDeviceOrientationLandscapeLeft); + _mockDeviceOrientationProvider.orientation = UIDeviceOrientationLandscapeLeft; // Focus point of interest is supported - OCMStub([_mockDevice isFocusPointOfInterestSupported]).andReturn(true); - // Set mock device as the current capture device - [_camera setValue:_mockDevice forKey:@"captureDevice"]; + _mockDevice.focusPointOfInterestSupported = YES; + + __block BOOL setFocusPointOfInterestCalled = NO; + _mockDevice.setFocusPointOfInterestStub = ^(CGPoint point) { + if (point.x == 1 && point.y == 1) { + setFocusPointOfInterestCalled = YES; + } + }; // Run test [_camera setFocusPoint:[FCPPlatformPoint makeWithX:1 y:1] @@ -121,7 +154,28 @@ - (void)testSetFocusPointWithResult_SetsFocusPointOfInterest { }]; // Verify the focus point of interest has been set - OCMVerify([_mockDevice setFocusPointOfInterest:CGPointMake(1, 1)]); + XCTAssertTrue(setFocusPointOfInterestCalled); +} + +- (void)testSetFocusPoint_WhenNotSupported_ReturnsError { + // UI is currently in landscape left orientation + _mockDeviceOrientationProvider.orientation = UIDeviceOrientationLandscapeLeft; + // Exposure point of interest is not supported + _mockDevice.focusPointOfInterestSupported = NO; + + XCTestExpectation *expectation = [self expectationWithDescription:@"Completion with error"]; + + // Run + [_camera setFocusPoint:[FCPPlatformPoint makeWithX:1 y:1] + withCompletion:^(FlutterError *_Nullable error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.code, @"setFocusPointFailed"); + XCTAssertEqualObjects(error.message, @"Device does not have focus point capabilities"); + [expectation fulfill]; + }]; + + // Verify + [self waitForExpectationsWithTimeout:1 handler:nil]; } @end diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.m index e6ce8d48bc5..b1547c53212 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.m @@ -11,6 +11,8 @@ #import +#import "MockCaptureDeviceController.h" + @interface StubGlobalEventApi : FCPCameraGlobalEventApi @property(nonatomic) BOOL called; @property(nonatomic) FCPPlatformDeviceOrientation lastOrientation; @@ -35,10 +37,21 @@ - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString #pragma mark - @interface CameraOrientationTests : XCTestCase +@property(readonly, nonatomic) FLTCam *camera; +@property(readonly, nonatomic) MockCaptureDeviceController *mockDevice; +@property(readonly, nonatomic) StubGlobalEventApi *eventAPI; @end @implementation CameraOrientationTests +- (void)setUp { + [super setUp]; + _mockDevice = [[MockCaptureDeviceController alloc] init]; + _camera = [[FLTCam alloc] init]; + + [_camera setValue:_mockDevice forKey:@"captureDevice"]; +} + // Ensure that the given queue and then the main queue have both cycled, to wait for any pending // async events that may have been bounced between them. - (void)waitForRoundTripWithQueue:(dispatch_queue_t)queue { @@ -97,13 +110,13 @@ - (void)testOrientationNotificationsNotCalledForFaceDown { - (void)testOrientationUpdateMustBeOnCaptureSessionQueue { XCTestExpectation *queueExpectation = [self expectationWithDescription:@"Orientation update must happen on the capture session queue"]; - CameraPlugin *camera = [[CameraPlugin alloc] initWithRegistry:nil messenger:nil]; const char *captureSessionQueueSpecific = "capture_session_queue"; dispatch_queue_set_specific(camera.captureSessionQueue, captureSessionQueueSpecific, (void *)captureSessionQueueSpecific, NULL); FLTCam *mockCam = OCMClassMock([FLTCam class]); camera.camera = mockCam; + OCMStub([mockCam setDeviceOrientation:UIDeviceOrientationLandscapeLeft]) .andDo(^(NSInvocation *invocation) { if (dispatch_get_specific(captureSessionQueueSpecific)) { diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m index 04bdd0795da..60dfca54168 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m @@ -8,7 +8,6 @@ #endif @import XCTest; @import AVFoundation; -#import @interface CameraPreviewPauseTests : XCTestCase @end diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.m index 9bed6bea488..98e12020b59 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSettingsTests.m @@ -144,7 +144,7 @@ - (void)testSettings_shouldPassConfigurationToCameraDeviceAndWriter { [[TestMediaSettingsAVWrapper alloc] initWithTestCase:self]; FLTCam *camera = FLTCreateCamWithCaptureSessionQueueAndMediaSettings( - dispatch_queue_create("test", NULL), settings, injectedWrapper, nil); + dispatch_queue_create("test", NULL), settings, injectedWrapper, nil, nil); // Expect FPS configuration is passed to camera device. [self waitForExpectations:@[ @@ -211,7 +211,7 @@ - (void)testSettings_ShouldSelectFormatWhichSupports60FPS { enableAudio:gTestEnableAudio]; FLTCam *camera = FLTCreateCamWithCaptureSessionQueueAndMediaSettings( - dispatch_queue_create("test", NULL), settings, nil, nil); + dispatch_queue_create("test", NULL), settings, nil, nil, nil); AVFrameRateRange *range = camera.captureDevice.activeFormat.videoSupportedFrameRateRanges[0]; XCTAssertLessThanOrEqual(range.minFrameRate, 60); diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h index 2bbb56c51a7..008ba9df48f 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h @@ -20,7 +20,8 @@ extern FLTCam *_Nullable FLTCreateCamWithCaptureSessionQueueAndMediaSettings( dispatch_queue_t _Nullable captureSessionQueue, FCPPlatformMediaSettings *_Nullable mediaSettings, FLTCamMediaSettingsAVWrapper *_Nullable mediaSettingsAVWrapper, - CaptureDeviceFactory _Nullable captureDeviceFactory); + CaptureDeviceFactory _Nullable captureDeviceFactory, + id _Nullable deviceOrientationProvider); 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 503a5c255c5..f10a3b122d7 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m @@ -8,6 +8,8 @@ @import AVFoundation; @import camera_avfoundation; +#import "MockDeviceOrientationProvider.h" + static FCPPlatformMediaSettings *FCPGetDefaultMediaSettings( FCPPlatformResolutionPreset resolutionPreset) { return [FCPPlatformMediaSettings makeWithResolutionPreset:resolutionPreset @@ -18,13 +20,14 @@ } FLTCam *FLTCreateCamWithCaptureSessionQueue(dispatch_queue_t captureSessionQueue) { - return FLTCreateCamWithCaptureSessionQueueAndMediaSettings(captureSessionQueue, nil, nil, nil); + return FLTCreateCamWithCaptureSessionQueueAndMediaSettings(captureSessionQueue, nil, nil, nil, + nil); } FLTCam *FLTCreateCamWithCaptureSessionQueueAndMediaSettings( dispatch_queue_t captureSessionQueue, FCPPlatformMediaSettings *mediaSettings, - FLTCamMediaSettingsAVWrapper *mediaSettingsAVWrapper, - CaptureDeviceFactory captureDeviceFactory) { + FLTCamMediaSettingsAVWrapper *mediaSettingsAVWrapper, CaptureDeviceFactory captureDeviceFactory, + id deviceOrientationProvider) { if (!mediaSettings) { mediaSettings = FCPGetDefaultMediaSettings(FCPPlatformResolutionPresetMedium); } @@ -33,6 +36,10 @@ mediaSettingsAVWrapper = [[FLTCamMediaSettingsAVWrapper alloc] init]; } + if (!deviceOrientationProvider) { + deviceOrientationProvider = [[MockDeviceOrientationProvider alloc] init]; + } + id inputMock = OCMClassMock([AVCaptureDeviceInput class]); OCMStub([inputMock deviceInputWithDevice:[OCMArg any] error:[OCMArg setTo:nil]]) .andReturn(inputMock); @@ -88,12 +95,13 @@ videoCaptureSession:videoSessionMock audioCaptureSession:audioSessionMock captureSessionQueue:captureSessionQueue - captureDeviceFactory:captureDeviceFactory ?: ^AVCaptureDevice *(void) { - return captureDeviceMock; + captureDeviceFactory:captureDeviceFactory ?: ^id(void) { + return [[FLTDefaultCaptureDeviceController alloc] initWithDevice:captureDeviceMock]; } videoDimensionsForFormat:^CMVideoDimensions(AVCaptureDeviceFormat *format) { return CMVideoFormatDescriptionGetDimensions(format.formatDescription); } + deviceOrientationProvider:deviceOrientationProvider error:nil]; id captureVideoDataOutputMock = [OCMockObject niceMockForClass:[AVCaptureVideoDataOutput class]]; @@ -155,17 +163,19 @@ OCMStub([audioSessionMock addInputWithNoConnections:[OCMArg any]]); OCMStub([audioSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); - return [[FLTCam alloc] initWithMediaSettings:FCPGetDefaultMediaSettings(resolutionPreset) - mediaSettingsAVWrapper:[[FLTCamMediaSettingsAVWrapper alloc] init] - orientation:UIDeviceOrientationPortrait - videoCaptureSession:captureSession - audioCaptureSession:audioSessionMock - captureSessionQueue:dispatch_queue_create("capture_session_queue", NULL) - captureDeviceFactory:^AVCaptureDevice *(void) { - return captureDevice; - } - videoDimensionsForFormat:videoDimensionsForFormat - error:nil]; + return [[FLTCam alloc] + initWithMediaSettings:FCPGetDefaultMediaSettings(resolutionPreset) + mediaSettingsAVWrapper:[[FLTCamMediaSettingsAVWrapper alloc] init] + orientation:UIDeviceOrientationPortrait + videoCaptureSession:captureSession + audioCaptureSession:audioSessionMock + captureSessionQueue:dispatch_queue_create("capture_session_queue", NULL) + captureDeviceFactory:^id(void) { + return [[FLTDefaultCaptureDeviceController alloc] initWithDevice:captureDevice]; + } + videoDimensionsForFormat:videoDimensionsForFormat + deviceOrientationProvider:[[MockDeviceOrientationProvider alloc] init] + error:nil]; } CMSampleBufferRef FLTCreateTestSampleBuffer(void) { diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m index 7f17e39a6b0..c1bfb6ad376 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m @@ -187,10 +187,12 @@ - (void)testCaptureToFile_handlesTorchMode { dispatch_queue_set_specific(captureSessionQueue, FLTCaptureSessionQueueSpecific, (void *)FLTCaptureSessionQueueSpecific, NULL); - FLTCam *cam = FLTCreateCamWithCaptureSessionQueueAndMediaSettings(captureSessionQueue, nil, nil, - ^AVCaptureDevice *(void) { - return captureDeviceMock; - }); + FLTCam *cam = FLTCreateCamWithCaptureSessionQueueAndMediaSettings( + captureSessionQueue, nil, nil, + ^id(void) { + return [[FLTDefaultCaptureDeviceController alloc] initWithDevice:captureDeviceMock]; + }, + nil); AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings]; id mockSettings = OCMClassMock([AVCapturePhotoSettings class]); diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockCaptureDeviceController.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockCaptureDeviceController.h new file mode 100644 index 00000000000..bd7b2ee9a58 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockCaptureDeviceController.h @@ -0,0 +1,105 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import camera_avfoundation; +#if __has_include() +@import camera_avfoundation.Test; +#endif +@import AVFoundation; + +NS_ASSUME_NONNULL_BEGIN + +@interface MockCaptureDeviceController : NSObject +// Position/Orientation +@property(nonatomic, assign) AVCaptureDevicePosition position; + +// Format/Configuration +@property(nonatomic, strong) AVCaptureDeviceFormat *activeFormat; +@property(nonatomic, strong) NSArray *formats; +/// Overrides the default implementation of setting active format. +/// @param format The format being set +@property(nonatomic, copy) void (^setActiveFormatStub)(AVCaptureDeviceFormat *format); + +// Flash/Torch +@property(nonatomic, assign) BOOL hasFlash; +@property(nonatomic, assign) BOOL hasTorch; +@property(nonatomic, assign) BOOL isTorchAvailable; +@property(nonatomic, assign) AVCaptureTorchMode torchMode; +/// Overrides the default implementation of setting torch mode. +/// @param mode The torch mode being set +@property(nonatomic, copy) void (^setTorchModeStub)(AVCaptureTorchMode mode); +@property(nonatomic, assign) BOOL flashModeSupported; + +// Focus +@property(nonatomic, assign) BOOL focusPointOfInterestSupported; +/// Overrides the default implementation of checking if focus mode is supported. +/// @param mode The focus mode to check +/// @return Whether the focus mode is supported +@property(nonatomic, copy) BOOL (^isFocusModeSupportedStub)(AVCaptureFocusMode mode); +@property(nonatomic, assign) AVCaptureFocusMode focusMode; +/// Overrides the default implementation of setting focus mode. +/// @param mode The focus mode being set +@property(nonatomic, copy) void (^setFocusModeStub)(AVCaptureFocusMode mode); +@property(nonatomic, assign) CGPoint focusPointOfInterest; +/// Overrides the default implementation of setting focus point of interest. +/// @param point The focus point being set +@property(nonatomic, copy) void (^setFocusPointOfInterestStub)(CGPoint point); + +// Exposure +@property(nonatomic, assign) BOOL exposurePointOfInterestSupported; +@property(nonatomic, assign) AVCaptureExposureMode exposureMode; +@property(nonatomic, assign) BOOL exposureModeSupported; +/// Overrides the default implementation of setting exposure mode. +/// @param mode The exposure mode being set +@property(nonatomic, copy) void (^setExposureModeStub)(AVCaptureExposureMode mode); +@property(nonatomic, assign) CGPoint exposurePointOfInterest; +/// Override the default implementation of setting exposure point of interest. +/// @param point The exposure point being set +@property(nonatomic, copy) void (^setExposurePointOfInterestStub)(CGPoint point); +@property(nonatomic, assign) float minExposureTargetBias; +@property(nonatomic, assign) float maxExposureTargetBias; +/// Overrides the default implementation of setting exposure target bias. +/// @param bias The exposure bias being set +/// @param handler The completion handler to be called +@property(nonatomic, copy) void (^setExposureTargetBiasStub) + (float bias, void (^_Nullable handler)(CMTime)); + +// Zoom +@property(nonatomic, assign) float maxAvailableVideoZoomFactor; +@property(nonatomic, assign) float minAvailableVideoZoomFactor; +@property(nonatomic, assign) float videoZoomFactor; +/// Overrides the default implementation of setting video zoom factor. +/// @param factor The zoom factor being set +@property(nonatomic, copy) void (^setVideoZoomFactorStub)(float factor); + +// Camera Properties +@property(nonatomic, assign) float lensAperture; +@property(nonatomic, assign) CMTime exposureDuration; +@property(nonatomic, assign) float ISO; + +// Configuration Lock +/// Overrides the default implementation of locking device for configuration. +/// @param error Error pointer to be set if lock fails +@property(nonatomic, copy) BOOL (^lockForConfigurationStub)(NSError **error); +/// Overrides the default implementation of unlocking device configuration. +@property(nonatomic, copy) void (^unlockForConfigurationStub)(void); + +// Frame Duration +@property(nonatomic, assign) CMTime activeVideoMinFrameDuration; +@property(nonatomic, assign) CMTime activeVideoMaxFrameDuration; +/// Overrides the default implementation of setting minimum frame duration. +/// @param duration The minimum frame duration being set +@property(nonatomic, copy) void (^setActiveVideoMinFrameDurationStub)(CMTime duration); +/// Overrides the default implementation of setting maximum frame duration. +/// @param duration The maximum frame duration being set +@property(nonatomic, copy) void (^setActiveVideoMaxFrameDurationStub)(CMTime duration); + +// Input Creation +/// Overrides the default implementation of creating capture input. +/// @param error Error pointer to be set if creation fails +@property(nonatomic, copy) AVCaptureInput * (^createInputStub)(NSError **error); + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockCaptureDeviceController.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockCaptureDeviceController.m new file mode 100644 index 00000000000..38403656fff --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockCaptureDeviceController.m @@ -0,0 +1,111 @@ + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "MockCaptureDeviceController.h" + +@import camera_avfoundation; +#if __has_include() +@import camera_avfoundation.Test; +#endif +@import AVFoundation; + +@implementation MockCaptureDeviceController + +- (void)setActiveFormat:(AVCaptureDeviceFormat *)format { + if (self.setActiveFormatStub) { + self.setActiveFormatStub(format); + } +} + +- (BOOL)isFlashModeSupported:(AVCaptureFlashMode)mode { + return self.flashModeSupported; +} + +- (void)setTorchMode:(AVCaptureTorchMode)mode { + if (self.setTorchModeStub) { + self.setTorchModeStub(mode); + } +} + +- (BOOL)isFocusModeSupported:(AVCaptureFocusMode)mode { + if (self.isFocusModeSupportedStub) { + return self.isFocusModeSupportedStub(mode); + } + return NO; +} + +- (void)setFocusMode:(AVCaptureFocusMode)mode { + if (self.setFocusModeStub) { + self.setFocusModeStub(mode); + } +} + +- (void)setFocusPointOfInterest:(CGPoint)point { + if (self.setFocusPointOfInterestStub) { + self.setFocusPointOfInterestStub(point); + } +} + +- (void)setExposureMode:(AVCaptureExposureMode)mode { + if (self.setExposureModeStub) { + self.setExposureModeStub(mode); + } +} + +- (void)setExposurePointOfInterest:(CGPoint)point { + if (self.setExposurePointOfInterestStub) { + self.setExposurePointOfInterestStub(point); + } +} + +- (void)setExposureTargetBias:(float)bias completionHandler:(void (^)(CMTime))handler { + if (self.setExposureTargetBiasStub) { + self.setExposureTargetBiasStub(bias, handler); + } +} + +- (void)setVideoZoomFactor:(float)factor { + if (self.setVideoZoomFactorStub) { + self.setVideoZoomFactorStub(factor); + } +} + +- (BOOL)lockForConfiguration:(NSError **)error { + if (self.lockForConfigurationStub) { + return self.lockForConfigurationStub(error); + } + return YES; +} + +- (void)unlockForConfiguration { + if (self.unlockForConfigurationStub) { + self.unlockForConfigurationStub(); + } +} + +- (void)setActiveVideoMinFrameDuration:(CMTime)duration { + if (self.setActiveVideoMinFrameDurationStub) { + self.setActiveVideoMinFrameDurationStub(duration); + } +} + +- (void)setActiveVideoMaxFrameDuration:(CMTime)duration { + if (self.setActiveVideoMaxFrameDurationStub) { + self.setActiveVideoMaxFrameDurationStub(duration); + } +} + +- (BOOL)isExposureModeSupported:(AVCaptureExposureMode)mode { + return self.exposureModeSupported; +} + +- (AVCaptureInput *)createInput:(NSError *_Nullable *_Nullable)error { + if (self.createInputStub) { + return self.createInputStub(error); + } + return NULL; +} + +@end diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockDeviceOrientationProvider.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockDeviceOrientationProvider.h new file mode 100644 index 00000000000..0421ec66529 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockDeviceOrientationProvider.h @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import camera_avfoundation; +#if __has_include() +@import camera_avfoundation.Test; +#endif +@import AVFoundation; + +NS_ASSUME_NONNULL_BEGIN + +@interface MockDeviceOrientationProvider : NSObject +@property(nonatomic, assign) UIDeviceOrientation orientation; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockDeviceOrientationProvider.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockDeviceOrientationProvider.m new file mode 100644 index 00000000000..730572b315a --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockDeviceOrientationProvider.m @@ -0,0 +1,14 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "MockDeviceOrientationProvider.h" + +@import camera_avfoundation; +#if __has_include() +@import camera_avfoundation.Test; +#endif +@import AVFoundation; + +@implementation MockDeviceOrientationProvider +@end diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCam.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCam.m index 699dbf3a806..577460b1a86 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCam.m @@ -9,6 +9,8 @@ @import Flutter; #import +#import "./include/camera_avfoundation/FLTCaptureDeviceControlling.h" +#import "./include/camera_avfoundation/FLTDeviceOrientationProviding.h" #import "./include/camera_avfoundation/FLTSavePhotoDelegate.h" #import "./include/camera_avfoundation/FLTThreadSafeEventChannel.h" #import "./include/camera_avfoundation/QueueUtils.h" @@ -103,7 +105,7 @@ @interface FLTCam () deviceOrientationProvider; /// Reports the given error message to the Dart side of the plugin. /// /// Can be called from any thread. @@ -144,12 +146,14 @@ - (instancetype)initWithCameraName:(NSString *)cameraName videoCaptureSession:videoCaptureSession audioCaptureSession:videoCaptureSession captureSessionQueue:captureSessionQueue - captureDeviceFactory:^AVCaptureDevice *(void) { - return [AVCaptureDevice deviceWithUniqueID:cameraName]; + captureDeviceFactory:^id(void) { + AVCaptureDevice *device = [AVCaptureDevice deviceWithUniqueID:cameraName]; + return [[FLTDefaultCaptureDeviceController alloc] initWithDevice:device]; } videoDimensionsForFormat:^CMVideoDimensions(AVCaptureDeviceFormat *format) { return CMVideoFormatDescriptionGetDimensions(format.formatDescription); } + deviceOrientationProvider:[[FLTDefaultDeviceOrientationProvider alloc] init] error:error]; } @@ -214,6 +218,7 @@ - (instancetype)initWithMediaSettings:(FCPPlatformMediaSettings *)mediaSettings captureSessionQueue:(dispatch_queue_t)captureSessionQueue captureDeviceFactory:(CaptureDeviceFactory)captureDeviceFactory videoDimensionsForFormat:(VideoDimensionsForFormat)videoDimensionsForFormat + deviceOrientationProvider:(id)deviceOrientationProvider error:(NSError **)error { self = [super init]; NSAssert(self, @"super init cannot be nil"); @@ -265,6 +270,8 @@ - (instancetype)initWithMediaSettings:(FCPPlatformMediaSettings *)mediaSettings _motionManager = [[CMMotionManager alloc] init]; [_motionManager startAccelerometerUpdates]; + _deviceOrientationProvider = deviceOrientationProvider; + if (_mediaSettings.framesPerSecond) { // The frame rate can be changed only on a locked for configuration device. if ([mediaSettingsAVWrapper lockDevice:_captureDevice error:error]) { @@ -311,7 +318,7 @@ - (instancetype)initWithMediaSettings:(FCPPlatformMediaSettings *)mediaSettings - (AVCaptureConnection *)createConnection:(NSError **)error { // Setup video capture input. - _captureVideoInput = [AVCaptureDeviceInput deviceInputWithDevice:_captureDevice error:error]; + _captureVideoInput = [_captureDevice createInput:error]; // Test the return value of the `deviceInputWithDevice` method to see whether an error occurred. // Don’t just test to see whether the error pointer was set to point to an error. @@ -1041,7 +1048,8 @@ - (void)applyFocusMode { [self applyFocusMode:_focusMode onDevice:_captureDevice]; } -- (void)applyFocusMode:(FCPPlatformFocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice { +- (void)applyFocusMode:(FCPPlatformFocusMode)focusMode + onDevice:(id)captureDevice { [captureDevice lockForConfiguration:nil]; switch (focusMode) { case FCPPlatformFocusModeLocked: @@ -1150,7 +1158,7 @@ - (CGPoint)CGPointForPoint:(nonnull FCPPlatformPoint *)point - (void)setExposurePoint:(FCPPlatformPoint *)point withCompletion:(void (^)(FlutterError *_Nullable))completion { - if (!_captureDevice.isExposurePointOfInterestSupported) { + if (!_captureDevice.exposurePointOfInterestSupported) { completion([FlutterError errorWithCode:@"setExposurePointFailed" message:@"Device does not have exposure point capabilities" details:nil]); @@ -1172,13 +1180,13 @@ - (void)setExposurePoint:(FCPPlatformPoint *)point - (void)setFocusPoint:(FCPPlatformPoint *)point withCompletion:(void (^)(FlutterError *_Nullable))completion { - if (!_captureDevice.isFocusPointOfInterestSupported) { + if (!_captureDevice.focusPointOfInterestSupported) { completion([FlutterError errorWithCode:@"setFocusPointFailed" message:@"Device does not have focus point capabilities" details:nil]); return; } - UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; + UIDeviceOrientation orientation = [_deviceOrientationProvider orientation]; [_captureDevice lockForConfiguration:nil]; // A nil point resets to the center. [_captureDevice diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCamMediaSettingsAVWrapper.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCamMediaSettingsAVWrapper.m index b975daa4b5c..21a0d9a68ee 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCamMediaSettingsAVWrapper.m +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCamMediaSettingsAVWrapper.m @@ -3,14 +3,16 @@ // found in the LICENSE file. #import "./include/camera_avfoundation/FLTCamMediaSettingsAVWrapper.h" +#import "./include/camera_avfoundation/FLTCaptureDeviceControlling.h" @implementation FLTCamMediaSettingsAVWrapper -- (BOOL)lockDevice:(AVCaptureDevice *)captureDevice error:(NSError *_Nullable *_Nullable)outError { +- (BOOL)lockDevice:(id)captureDevice + error:(NSError *_Nullable *_Nullable)outError { return [captureDevice lockForConfiguration:outError]; } -- (void)unlockDevice:(AVCaptureDevice *)captureDevice { +- (void)unlockDevice:(id)captureDevice { return [captureDevice unlockForConfiguration]; } @@ -22,11 +24,13 @@ - (void)commitConfigurationForSession:(AVCaptureSession *)videoCaptureSession { [videoCaptureSession commitConfiguration]; } -- (void)setMinFrameDuration:(CMTime)duration onDevice:(AVCaptureDevice *)captureDevice { +- (void)setMinFrameDuration:(CMTime)duration + onDevice:(id)captureDevice { captureDevice.activeVideoMinFrameDuration = duration; } -- (void)setMaxFrameDuration:(CMTime)duration onDevice:(AVCaptureDevice *)captureDevice { +- (void)setMaxFrameDuration:(CMTime)duration + onDevice:(id)captureDevice { captureDevice.activeVideoMaxFrameDuration = duration; } diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCaptureDeviceControlling.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCaptureDeviceControlling.m new file mode 100644 index 00000000000..95d3f80519f --- /dev/null +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTCaptureDeviceControlling.m @@ -0,0 +1,172 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "./include/camera_avfoundation/FLTCaptureDeviceControlling.h" + +@interface FLTDefaultCaptureDeviceController () +@property(nonatomic, strong) AVCaptureDevice *device; +@end + +@implementation FLTDefaultCaptureDeviceController + +- (instancetype)initWithDevice:(AVCaptureDevice *)device { + self = [super init]; + if (self) { + _device = device; + } + return self; +} + +// Position/Orientation +- (AVCaptureDevicePosition)position { + return self.device.position; +} + +// Format/Configuration +- (AVCaptureDeviceFormat *)activeFormat { + return self.device.activeFormat; +} + +- (NSArray *)formats { + return self.device.formats; +} + +- (void)setActiveFormat:(AVCaptureDeviceFormat *)format { + self.device.activeFormat = format; +} + +// Flash/Torch +- (BOOL)hasFlash { + return self.device.hasFlash; +} + +- (BOOL)hasTorch { + return self.device.hasTorch; +} + +- (BOOL)isTorchAvailable { + return self.device.isTorchAvailable; +} + +- (AVCaptureTorchMode)torchMode { + return self.device.torchMode; +} + +- (void)setTorchMode:(AVCaptureTorchMode)torchMode { + self.device.torchMode = torchMode; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +- (BOOL)isFlashModeSupported:(AVCaptureFlashMode)mode { + return [self.device isFlashModeSupported:mode]; +} +#pragma clang diagnostic pop + +// Focus +- (BOOL)focusPointOfInterestSupported { + return self.device.focusPointOfInterestSupported; +} + +- (BOOL)isFocusModeSupported:(AVCaptureFocusMode)mode { + return [self.device isFocusModeSupported:mode]; +} + +- (void)setFocusMode:(AVCaptureFocusMode)focusMode { + self.device.focusMode = focusMode; +} + +- (void)setFocusPointOfInterest:(CGPoint)point { + self.device.focusPointOfInterest = point; +} + +// Exposure +- (BOOL)exposurePointOfInterestSupported { + return self.device.exposurePointOfInterestSupported; +} + +- (void)setExposureMode:(AVCaptureExposureMode)exposureMode { + self.device.exposureMode = exposureMode; +} + +- (void)setExposurePointOfInterest:(CGPoint)point { + self.device.exposurePointOfInterest = point; +} + +- (float)minExposureTargetBias { + return self.device.minExposureTargetBias; +} + +- (float)maxExposureTargetBias { + return self.device.maxExposureTargetBias; +} + +- (void)setExposureTargetBias:(float)bias completionHandler:(void (^)(CMTime))handler { + [self.device setExposureTargetBias:bias completionHandler:handler]; +} + +- (BOOL)isExposureModeSupported:(AVCaptureExposureMode)mode { + return [self.device isExposureModeSupported:mode]; +} + +// Zoom +- (float)maxAvailableVideoZoomFactor { + return self.device.maxAvailableVideoZoomFactor; +} + +- (float)minAvailableVideoZoomFactor { + return self.device.minAvailableVideoZoomFactor; +} + +- (float)videoZoomFactor { + return self.device.videoZoomFactor; +} + +- (void)setVideoZoomFactor:(float)factor { + self.device.videoZoomFactor = factor; +} + +// Camera Properties +- (float)lensAperture { + return self.device.lensAperture; +} + +- (CMTime)exposureDuration { + return self.device.exposureDuration; +} + +- (float)ISO { + return self.device.ISO; +} + +// Configuration Lock +- (BOOL)lockForConfiguration:(NSError **)error { + return [self.device lockForConfiguration:error]; +} + +- (void)unlockForConfiguration { + [self.device unlockForConfiguration]; +} + +- (CMTime)activeVideoMinFrameDuration { + return self.device.activeVideoMinFrameDuration; +} + +- (void)setActiveVideoMinFrameDuration:(CMTime)duration { + self.device.activeVideoMinFrameDuration = duration; +} + +- (CMTime)activeVideoMaxFrameDuration { + return self.device.activeVideoMaxFrameDuration; +} + +- (void)setActiveVideoMaxFrameDuration:(CMTime)duration { + self.device.activeVideoMaxFrameDuration = duration; +} + +- (AVCaptureInput *)createInput:(NSError *_Nullable *_Nullable)error { + return [AVCaptureDeviceInput deviceInputWithDevice:_device error:error]; +} + +@end diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTDeviceOrientationProviding.m b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTDeviceOrientationProviding.m new file mode 100644 index 00000000000..5419eb0a358 --- /dev/null +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/FLTDeviceOrientationProviding.m @@ -0,0 +1,13 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "./include/camera_avfoundation/FLTDeviceOrientationProviding.h" + +@implementation FLTDefaultDeviceOrientationProvider + +- (UIDeviceOrientation)orientation { + return [[UIDevice currentDevice] orientation]; +} + +@end diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/CameraPlugin.modulemap b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/CameraPlugin.modulemap index 57d858f894f..899a43a3cab 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/CameraPlugin.modulemap +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/CameraPlugin.modulemap @@ -5,13 +5,15 @@ framework module camera_avfoundation { module * { export * } explicit module Test { + header "FLTCaptureDeviceControlling.h" + header "FLTDeviceOrientationProviding.h" + header "FLTPermissionServicing.h" header "CameraPlugin_Test.h" header "CameraProperties.h" header "FLTCam.h" header "FLTCam_Test.h" header "FLTSavePhotoDelegate_Test.h" header "FLTThreadSafeEventChannel.h" - header "FLTPermissionServicing.h" header "FLTCameraPermissionManager.h" header "QueueUtils.h" } diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCam.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCam.h index c89ee9f98e5..68ab3a86158 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCam.h +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCam.h @@ -8,6 +8,7 @@ #import "CameraProperties.h" #import "FLTCamMediaSettingsAVWrapper.h" +#import "FLTCaptureDeviceControlling.h" #import "messages.g.h" NS_ASSUME_NONNULL_BEGIN @@ -15,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN /// A class that manages camera's state and performs camera operations. @interface FLTCam : NSObject -@property(readonly, nonatomic) AVCaptureDevice *captureDevice; +@property(readonly, nonatomic) id captureDevice; @property(readonly, nonatomic) CGSize previewSize; @property(assign, nonatomic) BOOL isPreviewPaused; @property(nonatomic, copy) void (^onFrameAvailable)(void); @@ -92,7 +93,8 @@ NS_ASSUME_NONNULL_BEGIN /// /// @param focusMode The focus mode that should be applied to the @captureDevice instance. /// @param captureDevice The AVCaptureDevice to which the @focusMode will be applied. -- (void)applyFocusMode:(FCPPlatformFocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice; +- (void)applyFocusMode:(FCPPlatformFocusMode)focusMode + onDevice:(id)captureDevice; - (void)pausePreview; - (void)resumePreview; - (void)setDescriptionWhileRecording:(NSString *)cameraName diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCamMediaSettingsAVWrapper.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCamMediaSettingsAVWrapper.h index 144a84eac13..8d95cf02afe 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCamMediaSettingsAVWrapper.h +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCamMediaSettingsAVWrapper.h @@ -5,6 +5,8 @@ @import AVFoundation; @import Foundation; +#import "FLTCaptureDeviceControlling.h" + NS_ASSUME_NONNULL_BEGIN /** @@ -25,14 +27,15 @@ NS_ASSUME_NONNULL_BEGIN * @param outError The optional error. * @result A BOOL indicating whether the device was successfully locked for configuration. */ -- (BOOL)lockDevice:(AVCaptureDevice *)captureDevice error:(NSError *_Nullable *_Nullable)outError; +- (BOOL)lockDevice:(id)captureDevice + error:(NSError *_Nullable *_Nullable)outError; /** * @method unlockDevice: * @abstract Release exclusive control over device hardware properties. * @param captureDevice The capture device. */ -- (void)unlockDevice:(AVCaptureDevice *)captureDevice; +- (void)unlockDevice:(id)captureDevice; /** * @method beginConfigurationForSession: @@ -57,7 +60,8 @@ NS_ASSUME_NONNULL_BEGIN * @param duration The frame duration. * @param captureDevice The capture device */ -- (void)setMinFrameDuration:(CMTime)duration onDevice:(AVCaptureDevice *)captureDevice; +- (void)setMinFrameDuration:(CMTime)duration + onDevice:(id)captureDevice; /** * @method setMaxFrameDuration:onDevice: @@ -66,7 +70,8 @@ NS_ASSUME_NONNULL_BEGIN * @param duration The frame duration. * @param captureDevice The capture device */ -- (void)setMaxFrameDuration:(CMTime)duration onDevice:(AVCaptureDevice *)captureDevice; +- (void)setMaxFrameDuration:(CMTime)duration + onDevice:(id)captureDevice; /** * @method assetWriterAudioInputWithOutputSettings: diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCam_Test.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCam_Test.h index d05838f49a7..7e27e31a888 100644 --- a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCam_Test.h +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCam_Test.h @@ -3,6 +3,8 @@ // found in the LICENSE file. #import "FLTCam.h" +#import "FLTCaptureDeviceControlling.h" +#import "FLTDeviceOrientationProviding.h" #import "FLTSavePhotoDelegate.h" /// Determines the video dimensions (width and height) for a given capture device format. @@ -11,7 +13,7 @@ typedef CMVideoDimensions (^VideoDimensionsForFormat)(AVCaptureDeviceFormat *); /// Factory block returning an AVCaptureDevice. /// Used in tests to inject a device into FLTCam. -typedef AVCaptureDevice * (^CaptureDeviceFactory)(void); +typedef id (^CaptureDeviceFactory)(void); @interface FLTImageStreamHandler : NSObject @@ -74,6 +76,7 @@ typedef AVCaptureDevice * (^CaptureDeviceFactory)(void); captureSessionQueue:(dispatch_queue_t)captureSessionQueue captureDeviceFactory:(CaptureDeviceFactory)captureDeviceFactory videoDimensionsForFormat:(VideoDimensionsForFormat)videoDimensionsForFormat + deviceOrientationProvider:(id)deviceOrientationProvider error:(NSError **)error; /// Start streaming images. diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCaptureDeviceControlling.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCaptureDeviceControlling.h new file mode 100644 index 00000000000..38f79408959 --- /dev/null +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTCaptureDeviceControlling.h @@ -0,0 +1,79 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import AVFoundation; +@import Foundation; + +NS_ASSUME_NONNULL_BEGIN + +/// A protocol which is a direct passthrough to AVCaptureDevice. +/// It exists to allow replacing AVCaptureDevice in tests. +@protocol FLTCaptureDeviceControlling + +// Position/Orientation +- (AVCaptureDevicePosition)position; + +// Format/Configuration +- (AVCaptureDeviceFormat *)activeFormat; +- (NSArray *)formats; +- (void)setActiveFormat:(AVCaptureDeviceFormat *)format; + +// Flash/Torch +- (BOOL)hasFlash; +- (BOOL)hasTorch; +- (BOOL)isTorchAvailable; +- (AVCaptureTorchMode)torchMode; +- (void)setTorchMode:(AVCaptureTorchMode)torchMode; +- (BOOL)isFlashModeSupported:(AVCaptureFlashMode)mode; + +// Focus +- (BOOL)focusPointOfInterestSupported; +- (BOOL)isFocusModeSupported:(AVCaptureFocusMode)mode; +- (void)setFocusMode:(AVCaptureFocusMode)focusMode; +- (void)setFocusPointOfInterest:(CGPoint)point; + +// Exposure +- (BOOL)exposurePointOfInterestSupported; +- (void)setExposureMode:(AVCaptureExposureMode)exposureMode; +- (void)setExposurePointOfInterest:(CGPoint)point; +- (float)minExposureTargetBias; +- (float)maxExposureTargetBias; +- (void)setExposureTargetBias:(float)bias completionHandler:(void (^_Nullable)(CMTime))handler; +- (BOOL)isExposureModeSupported:(AVCaptureExposureMode)mode; + +// Zoom +- (float)maxAvailableVideoZoomFactor; +- (float)minAvailableVideoZoomFactor; +- (float)videoZoomFactor; +- (void)setVideoZoomFactor:(float)factor; + +// Camera Properties +- (float)lensAperture; +- (CMTime)exposureDuration; +- (float)ISO; + +// Configuration Lock +- (BOOL)lockForConfiguration:(NSError **)error; +- (void)unlockForConfiguration; + +// Frame Duration +- (CMTime)activeVideoMinFrameDuration; +- (void)setActiveVideoMinFrameDuration:(CMTime)duration; +- (CMTime)activeVideoMaxFrameDuration; +- (void)setActiveVideoMaxFrameDuration:(CMTime)duration; + +- (AVCaptureInput *)createInput:(NSError *_Nullable *_Nullable)error; + +@end + +/// A default implementation of FLTCaptureDeviceControlling protocol which +/// wraps an instance of AVCaptureDevice. +@interface FLTDefaultCaptureDeviceController : NSObject + +/// Initializes the controller with the given device. +- (instancetype)initWithDevice:(AVCaptureDevice *)device; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTDeviceOrientationProviding.h b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTDeviceOrientationProviding.h new file mode 100644 index 00000000000..0a9b1a74618 --- /dev/null +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/camera_avfoundation/FLTDeviceOrientationProviding.h @@ -0,0 +1,24 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Foundation; +#import + +NS_ASSUME_NONNULL_BEGIN + +/// A protocol which provides the current device orientation. +/// It exists to allow replacing UIDevice in tests. +@protocol FLTDeviceOrientationProviding + +/// Returns the physical orientation of the device. +- (UIDeviceOrientation)orientation; + +@end + +/// A default implementation of FLTDeviceOrientationProviding which uses orientation +/// of the current device from UIDevice. +@interface FLTDefaultDeviceOrientationProvider : NSObject +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index dbfd772cb41..d5546a0fafb 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.18 +version: 0.9.18+1 environment: sdk: ^3.4.0