[camera] Prevent crash when setting unsupported FocusMode (#3992)
* Check if chosen focus mode is supported
* adding unit tests
* Added tests for camera focus
* Fail 1 test on purpose
* formatting
* Fix copyright notion
* Fix test run
* Add documentation and changelog
* Update packages/camera/camera/CHANGELOG.md
Co-authored-by: Maurits van Beusekom <maurits@vnbskm.nl>
* Improve documentation
Co-authored-by: Maurits van Beusekom <maurits@vnbskm.nl>
diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md
index e16a14c..193feec 100644
--- a/packages/camera/camera/CHANGELOG.md
+++ b/packages/camera/camera/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.8.1+2
+
+* Fix iOS crash when selecting an unsupported FocusMode.
+
## 0.8.1+1
* Migrate maven repository from jcenter to mavenCentral.
diff --git a/packages/camera/camera/example/ios/Podfile b/packages/camera/camera/example/ios/Podfile
index f7d6a5e..884573b 100644
--- a/packages/camera/camera/example/ios/Podfile
+++ b/packages/camera/camera/example/ios/Podfile
@@ -29,6 +29,13 @@
target 'Runner' do
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+
+ target 'UnitTests' do
+ platform :ios, '9.0'
+ inherit! :search_paths
+ # Pods for testing
+ pod 'OCMock', '~> 3.8.1'
+ end
end
post_install do |installer|
diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj
index c865799..d39ed6a 100644
--- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj
@@ -7,16 +7,29 @@
objects = {
/* Begin PBXBuildFile section */
+ 01010359265BEB94FD7CE839 /* libPods-UnitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6CCBF0769BA2C53F6AED0F17 /* libPods-UnitTests.a */; };
+ 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB766A2665316900CE5A93 /* CameraFocusTests.m */; };
+ 03BB767326653ABE00CE5A93 /* CameraPluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB767226653ABE00CE5A93 /* CameraPluginTests.m */; };
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
- 75201D617916C49BDEDF852A /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 620DDA07C00B5FF2F937CB5B /* libPods-Runner.a */; };
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 */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+ D065CD815D405ECB22FB1BBA /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A4F2DE74AE0C572296A00BF /* libPods-Runner.a */; };
/* End PBXBuildFile section */
+/* Begin PBXContainerItemProxy section */
+ 03BB766D2665316900CE5A93 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 97C146ED1CF9000F007C117D;
+ remoteInfo = Runner;
+ };
+/* End PBXContainerItemProxy section */
+
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
@@ -31,14 +44,19 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 03BB76682665316900CE5A93 /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 03BB766A2665316900CE5A93 /* CameraFocusTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraFocusTests.m; sourceTree = "<group>"; };
+ 03BB766C2665316900CE5A93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 03BB767226653ABE00CE5A93 /* CameraPluginTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CameraPluginTests.m; path = ../../../ios/Tests/CameraPluginTests.m; sourceTree = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
+ 2A4F2DE74AE0C572296A00BF /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
- 483D985F075B951ADBAD218E /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
- 620DDA07C00B5FF2F937CB5B /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 6CCBF0769BA2C53F6AED0F17 /* libPods-UnitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-UnitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+ 8F7D83D0CFC9B51065F87CE1 /* 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 = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -47,25 +65,46 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
- 9AC7510327AD6A32B7CBD9A5 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
+ A4725B4F24805CD3CA67828F /* 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 = "<group>"; };
+ A903DC9BC9D1CB89BD4FB3CB /* Pods-UnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.debug.xcconfig"; path = "Target Support Files/Pods-UnitTests/Pods-UnitTests.debug.xcconfig"; sourceTree = "<group>"; };
+ C2D350ADCDFC81FCB0D6F12C /* Pods-UnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.release.xcconfig"; path = "Target Support Files/Pods-UnitTests/Pods-UnitTests.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 03BB76652665316900CE5A93 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 01010359265BEB94FD7CE839 /* libPods-UnitTests.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 75201D617916C49BDEDF852A /* libPods-Runner.a in Frameworks */,
+ D065CD815D405ECB22FB1BBA /* libPods-Runner.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
- 8A1387E89A6BBC071B75FD6F /* Frameworks */ = {
+ 03BB76692665316900CE5A93 /* UnitTests */ = {
isa = PBXGroup;
children = (
- 620DDA07C00B5FF2F937CB5B /* libPods-Runner.a */,
+ 03BB767226653ABE00CE5A93 /* CameraPluginTests.m */,
+ 03BB766A2665316900CE5A93 /* CameraFocusTests.m */,
+ 03BB766C2665316900CE5A93 /* Info.plist */,
+ );
+ path = UnitTests;
+ sourceTree = "<group>";
+ };
+ 78D1009194BD06C03BED950D /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 2A4F2DE74AE0C572296A00BF /* libPods-Runner.a */,
+ 6CCBF0769BA2C53F6AED0F17 /* libPods-UnitTests.a */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -86,9 +125,10 @@
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
+ 03BB76692665316900CE5A93 /* UnitTests */,
97C146EF1CF9000F007C117D /* Products */,
- C52D9D4A70956403860EBEB5 /* Pods */,
- 8A1387E89A6BBC071B75FD6F /* Frameworks */,
+ FD386F00E98D73419C929072 /* Pods */,
+ 78D1009194BD06C03BED950D /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -96,6 +136,7 @@
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
+ 03BB76682665316900CE5A93 /* UnitTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -124,23 +165,44 @@
name = "Supporting Files";
sourceTree = "<group>";
};
- C52D9D4A70956403860EBEB5 /* Pods */ = {
+ FD386F00E98D73419C929072 /* Pods */ = {
isa = PBXGroup;
children = (
- 9AC7510327AD6A32B7CBD9A5 /* Pods-Runner.debug.xcconfig */,
- 483D985F075B951ADBAD218E /* Pods-Runner.release.xcconfig */,
+ 8F7D83D0CFC9B51065F87CE1 /* Pods-Runner.debug.xcconfig */,
+ A4725B4F24805CD3CA67828F /* Pods-Runner.release.xcconfig */,
+ A903DC9BC9D1CB89BD4FB3CB /* Pods-UnitTests.debug.xcconfig */,
+ C2D350ADCDFC81FCB0D6F12C /* Pods-UnitTests.release.xcconfig */,
);
- name = Pods;
+ path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
+ 03BB76672665316900CE5A93 /* UnitTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 03BB76712665316900CE5A93 /* Build configuration list for PBXNativeTarget "UnitTests" */;
+ buildPhases = (
+ 604FC00FF5713F40F2A4441D /* [CP] Check Pods Manifest.lock */,
+ 03BB76642665316900CE5A93 /* Sources */,
+ 03BB76652665316900CE5A93 /* Frameworks */,
+ 03BB76662665316900CE5A93 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 03BB766E2665316900CE5A93 /* PBXTargetDependency */,
+ );
+ name = UnitTests;
+ productName = camera_exampleTests;
+ productReference = 03BB76682665316900CE5A93 /* UnitTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
- 3E30118C54AB12C3EB9EDF27 /* [CP] Check Pods Manifest.lock */,
+ 1D0D227A6719C1144CAE5AB5 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
@@ -166,6 +228,11 @@
LastUpgradeCheck = 1100;
ORGANIZATIONNAME = "The Flutter Authors";
TargetAttributes = {
+ 03BB76672665316900CE5A93 = {
+ CreatedOnToolsVersion = 12.5;
+ ProvisioningStyle = Automatic;
+ TestTargetID = 97C146ED1CF9000F007C117D;
+ };
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
DevelopmentTeam = 7624MWN53C;
@@ -186,11 +253,19 @@
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
+ 03BB76672665316900CE5A93 /* UnitTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
+ 03BB76662665316900CE5A93 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -205,6 +280,28 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
+ 1D0D227A6719C1144CAE5AB5 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -219,18 +316,22 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
- 3E30118C54AB12C3EB9EDF27 /* [CP] Check Pods Manifest.lock */ = {
+ 604FC00FF5713F40F2A4441D /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
+ inputFileListPaths = (
+ );
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ "$(DERIVED_FILE_DIR)/Pods-UnitTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@@ -254,6 +355,15 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ 03BB76642665316900CE5A93 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */,
+ 03BB767326653ABE00CE5A93 /* CameraPluginTests.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -266,6 +376,14 @@
};
/* End PBXSourcesBuildPhase section */
+/* Begin PBXTargetDependency section */
+ 03BB766E2665316900CE5A93 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 97C146ED1CF9000F007C117D /* Runner */;
+ targetProxy = 03BB766D2665316900CE5A93 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
@@ -286,6 +404,55 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
+ 03BB766F2665316900CE5A93 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = A903DC9BC9D1CB89BD4FB3CB /* Pods-UnitTests.debug.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_STYLE = Automatic;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ INFOPLIST_FILE = UnitTests/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = "io.flutter.plugins.cameraExample.camera-exampleTests";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
+ };
+ name = Debug;
+ };
+ 03BB76702665316900CE5A93 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = C2D350ADCDFC81FCB0D6F12C /* Pods-UnitTests.release.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CODE_SIGN_STYLE = Automatic;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ INFOPLIST_FILE = UnitTests/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = "io.flutter.plugins.cameraExample.camera-exampleTests";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
+ };
+ name = Release;
+ };
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -439,6 +606,15 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
+ 03BB76712665316900CE5A93 /* Build configuration list for PBXNativeTarget "UnitTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 03BB766F2665316900CE5A93 /* Debug */,
+ 03BB76702665316900CE5A93 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index 44b8736..d9bece2 100644
--- a/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/packages/camera/camera/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -37,6 +37,16 @@
</BuildableReference>
</MacroExpansion>
<Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "03BB76672665316900CE5A93"
+ BuildableName = "UnitTests.xctest"
+ BlueprintName = "UnitTests"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
</Testables>
</TestAction>
<LaunchAction
diff --git a/packages/camera/camera/example/ios/UnitTests/CameraFocusTests.m b/packages/camera/camera/example/ios/UnitTests/CameraFocusTests.m
new file mode 100644
index 0000000..5d93bdf
--- /dev/null
+++ b/packages/camera/camera/example/ios/UnitTests/CameraFocusTests.m
@@ -0,0 +1,120 @@
+// 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;
+@import XCTest;
+@import AVFoundation;
+#import <OCMock/OCMock.h>
+
+// Mirrors FocusMode in camera.dart
+typedef enum {
+ FocusModeAuto,
+ FocusModeLocked,
+} FocusMode;
+
+@interface FLTCam : NSObject <FlutterTexture,
+ AVCaptureVideoDataOutputSampleBufferDelegate,
+ AVCaptureAudioDataOutputSampleBufferDelegate>
+
+- (void)applyFocusMode;
+- (void)applyFocusMode:(FocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice;
+@end
+
+@interface CameraFocusTests : XCTestCase
+@property(readonly, nonatomic) FLTCam *camera;
+@property(readonly, nonatomic) id mockDevice;
+
+@end
+
+@implementation CameraFocusTests
+
+- (void)setUp {
+ _camera = [[FLTCam alloc] init];
+ _mockDevice = OCMClassMock([AVCaptureDevice class]);
+}
+
+- (void)tearDown {
+ // Put teardown code here. This method is called after the invocation of each test method in the
+ // class.
+}
+
+- (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];
+
+ // Run test
+ [_camera applyFocusMode:FocusModeAuto onDevice:_mockDevice];
+
+ // Expect setFocusMode:AVCaptureFocusModeContinuousAutoFocus
+ OCMVerify([_mockDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus]);
+}
+
+- (void)testAutoFocusWithContinuousModeNotSupported_ShouldSetAutoFocus {
+ // AVCaptureFocusModeContinuousAutoFocus is not supported
+ OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus])
+ .andReturn(false);
+ // AVCaptureFocusModeContinuousAutoFocus is supported
+ OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(true);
+
+ // Don't expect setFocusMode:AVCaptureFocusModeContinuousAutoFocus
+ [[_mockDevice reject] setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
+
+ // Run test
+ [_camera applyFocusMode:FocusModeAuto onDevice:_mockDevice];
+
+ // Expect setFocusMode:AVCaptureFocusModeAutoFocus
+ OCMVerify([_mockDevice setFocusMode:AVCaptureFocusModeAutoFocus]);
+}
+
+- (void)testAutoFocusWithNoModeSupported_ShouldSetNothing {
+ // AVCaptureFocusModeContinuousAutoFocus is not supported
+ OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus])
+ .andReturn(false);
+ // AVCaptureFocusModeContinuousAutoFocus is not supported
+ OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(false);
+
+ // Don't expect any setFocus
+ [[_mockDevice reject] setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
+ [[_mockDevice reject] setFocusMode:AVCaptureFocusModeAutoFocus];
+
+ // Run test
+ [_camera applyFocusMode:FocusModeAuto 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];
+
+ // Run test
+ [_camera applyFocusMode:FocusModeLocked onDevice:_mockDevice];
+
+ // Expect setFocusMode:AVCaptureFocusModeAutoFocus
+ OCMVerify([_mockDevice setFocusMode:AVCaptureFocusModeAutoFocus]);
+}
+
+- (void)testLockedFocusWithModeNotSupported_ShouldSetNothing {
+ // AVCaptureFocusModeContinuousAutoFocus is supported
+ OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]).andReturn(true);
+ // AVCaptureFocusModeContinuousAutoFocus is not supported
+ OCMStub([_mockDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]).andReturn(false);
+
+ // Don't expect any setFocus
+ [[_mockDevice reject] setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
+ [[_mockDevice reject] setFocusMode:AVCaptureFocusModeAutoFocus];
+
+ // Run test
+ [_camera applyFocusMode:FocusModeLocked onDevice:_mockDevice];
+}
+
+@end
diff --git a/packages/camera/camera/example/ios/UnitTests/Info.plist b/packages/camera/camera/example/ios/UnitTests/Info.plist
new file mode 100644
index 0000000..64d65ca
--- /dev/null
+++ b/packages/camera/camera/example/ios/UnitTests/Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+</dict>
+</plist>
diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m
index ac84405..1e818ab 100644
--- a/packages/camera/camera/ios/Classes/CameraPlugin.m
+++ b/packages/camera/camera/ios/Classes/CameraPlugin.m
@@ -994,20 +994,40 @@
}
- (void)applyFocusMode {
- [_captureDevice lockForConfiguration:nil];
- switch (_focusMode) {
+ [self applyFocusMode:_focusMode onDevice:_captureDevice];
+}
+
+/**
+ * Applies FocusMode on the AVCaptureDevice.
+ *
+ * If the @c focusMode is set to FocusModeAuto the AVCaptureDevice is configured to use
+ * AVCaptureFocusModeContinuousModeAutoFocus when supported, otherwise it is set to
+ * AVCaptureFocusModeAutoFocus. If neither AVCaptureFocusModeContinuousModeAutoFocus nor
+ * AVCaptureFocusModeAutoFocus are supported focus mode will not be set.
+ * If @c focusMode is set to FocusModeLocked the AVCaptureDevice is configured to use
+ * AVCaptureFocusModeAutoFocus. If AVCaptureFocusModeAutoFocus is not supported focus mode will not
+ * be set.
+ *
+ * @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:(FocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice {
+ [captureDevice lockForConfiguration:nil];
+ switch (focusMode) {
case FocusModeLocked:
- [_captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
+ if ([captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
+ [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
+ }
break;
case FocusModeAuto:
- if ([_captureDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
- [_captureDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
- } else {
- [_captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
+ if ([captureDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
+ [captureDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
+ } else if ([captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
+ [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
}
break;
}
- [_captureDevice unlockForConfiguration];
+ [captureDevice unlockForConfiguration];
}
- (void)setExposurePointWithResult:(FlutterResult)result x:(double)x y:(double)y {
diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml
index dc7bf8e..de28683 100644
--- a/packages/camera/camera/pubspec.yaml
+++ b/packages/camera/camera/pubspec.yaml
@@ -4,7 +4,7 @@
and streaming image buffers to dart.
repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
-version: 0.8.1+1
+version: 0.8.1+2
environment:
sdk: ">=2.12.0 <3.0.0"