diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 0e49912b4ed4..33178dee8999 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,9 +1,13 @@ +## 0.8.1+4 + +* Fixes an issue where `preferredCameraDevice` option is not working for `getVideo` method. +* Refactor unit tests that were device-only before. + ## 0.8.1+3 * Fix image picker causing a crash when the cache directory is deleted. ## 0.8.1+2 - * Update the example app to support the multi-image feature. ## 0.8.1+1 diff --git a/packages/image_picker/image_picker/example/ios/Podfile b/packages/image_picker/image_picker/example/ios/Podfile index 75efae48b439..8979c25fea5e 100644 --- a/packages/image_picker/image_picker/example/ios/Podfile +++ b/packages/image_picker/image_picker/example/ios/Podfile @@ -31,7 +31,10 @@ target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) target 'RunnerTests' do + platform :ios, '9.0' inherit! :search_paths + # Pods for testing + pod 'OCMock', '~> 3.8.1' end target 'RunnerUITests' do inherit! :search_paths diff --git a/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj b/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj index 547c2be4f914..fc1609f5eeda 100644 --- a/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj @@ -877,7 +877,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = NHAKRD9N7D; + DEVELOPMENT_TEAM = ""; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = RunnerUITestiOS14/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.1; diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m b/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m index f667526671f7..cc901f084071 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m +++ b/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m @@ -6,6 +6,7 @@ @import image_picker; @import XCTest; +#import @interface MockViewController : UIViewController @property(nonatomic, retain) UIViewController *mockPresented; @@ -27,15 +28,33 @@ - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker; @end @interface ImagePickerPluginTests : XCTestCase +@property(readonly, nonatomic) id mockUIImagePicker; +@property(readonly, nonatomic) id mockAVCaptureDevice; @end @implementation ImagePickerPluginTests -#pragma mark - Test camera devices, no op on simulators +- (void)setUp { + _mockUIImagePicker = OCMClassMock([UIImagePickerController class]); + _mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]); +} + - (void)testPluginPickImageDeviceBack { - if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { - return; - } + // UIImagePickerControllerSourceTypeCamera is supported + OCMStub(ClassMethod( + [_mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])) + .andReturn(YES); + + // UIImagePickerControllerCameraDeviceRear is supported + OCMStub(ClassMethod( + [_mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear])) + .andReturn(YES); + + // AVAuthorizationStatusAuthorized is supported + OCMStub([_mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + .andReturn(AVAuthorizationStatusAuthorized); + + // Run test FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickImage" @@ -43,14 +62,27 @@ - (void)testPluginPickImageDeviceBack { [plugin handleMethodCall:call result:^(id _Nullable r){ }]; + XCTAssertEqual([plugin getImagePickerController].cameraDevice, UIImagePickerControllerCameraDeviceRear); } - (void)testPluginPickImageDeviceFront { - if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { - return; - } + // UIImagePickerControllerSourceTypeCamera is supported + OCMStub(ClassMethod( + [_mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])) + .andReturn(YES); + + // UIImagePickerControllerCameraDeviceFront is supported + OCMStub(ClassMethod([_mockUIImagePicker + isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront])) + .andReturn(YES); + + // AVAuthorizationStatusAuthorized is supported + OCMStub([_mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + .andReturn(AVAuthorizationStatusAuthorized); + + // Run test FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickImage" @@ -58,14 +90,27 @@ - (void)testPluginPickImageDeviceFront { [plugin handleMethodCall:call result:^(id _Nullable r){ }]; + XCTAssertEqual([plugin getImagePickerController].cameraDevice, UIImagePickerControllerCameraDeviceFront); } - (void)testPluginPickVideoDeviceBack { - if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { - return; - } + // UIImagePickerControllerSourceTypeCamera is supported + OCMStub(ClassMethod( + [_mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])) + .andReturn(YES); + + // UIImagePickerControllerCameraDeviceRear is supported + OCMStub(ClassMethod( + [_mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear])) + .andReturn(YES); + + // AVAuthorizationStatusAuthorized is supported + OCMStub([_mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + .andReturn(AVAuthorizationStatusAuthorized); + + // Run test FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickVideo" @@ -73,44 +118,62 @@ - (void)testPluginPickVideoDeviceBack { [plugin handleMethodCall:call result:^(id _Nullable r){ }]; + XCTAssertEqual([plugin getImagePickerController].cameraDevice, UIImagePickerControllerCameraDeviceRear); } -- (void)testPluginPickImageDeviceCancelClickMultipleTimes { - if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { - return; - } +- (void)testPluginPickVideoDeviceFront { + // UIImagePickerControllerSourceTypeCamera is supported + OCMStub(ClassMethod( + [_mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])) + .andReturn(YES); + + // UIImagePickerControllerCameraDeviceFront is supported + OCMStub(ClassMethod([_mockUIImagePicker + isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront])) + .andReturn(YES); + + // AVAuthorizationStatusAuthorized is supported + OCMStub([_mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + .andReturn(AVAuthorizationStatusAuthorized); + + // Run test FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"pickImage" + [FlutterMethodCall methodCallWithMethodName:@"pickVideo" arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}]; [plugin handleMethodCall:call result:^(id _Nullable r){ }]; - plugin.result = ^(id result) { - }; - [plugin imagePickerControllerDidCancel:[plugin getImagePickerController]]; - [plugin imagePickerControllerDidCancel:[plugin getImagePickerController]]; + XCTAssertEqual([plugin getImagePickerController].cameraDevice, + UIImagePickerControllerCameraDeviceFront); } -- (void)testPluginPickVideoDeviceFront { - if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { +#pragma mark - Test camera devices, no op on simulators + +- (void)testPluginPickImageDeviceCancelClickMultipleTimes { + if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { return; } FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"pickVideo" + [FlutterMethodCall methodCallWithMethodName:@"pickImage" arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}]; [plugin handleMethodCall:call result:^(id _Nullable r){ }]; - XCTAssertEqual([plugin getImagePickerController].cameraDevice, - UIImagePickerControllerCameraDeviceFront); + plugin.result = ^(id result) { + + }; + // To ensure the flow does not crash by multiple cancel call + [plugin imagePickerControllerDidCancel:[plugin getImagePickerController]]; + [plugin imagePickerControllerDidCancel:[plugin getImagePickerController]]; } #pragma mark - Test video duration + - (void)testPickingVideoWithDuration { FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; FlutterMethodCall *call = [FlutterMethodCall diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m index 7c91606ba535..4084ae65b5e0 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m @@ -37,7 +37,6 @@ @interface FLTImagePickerPlugin () *)registrar { @@ -70,6 +69,21 @@ - (UIViewController *)viewControllerWithWindow:(UIWindow *)window { return topController; } +/** + * Returns the UIImagePickerControllerCameraDevice to use given [arguments]. + * + * If the cameraDevice value that is fetched from arguments is 1 then returns + * UIImagePickerControllerCameraDeviceFront. If the cameraDevice value that is fetched + * from arguments is 0 then returns UIImagePickerControllerCameraDeviceRear. + * + * @param arguments that should be used to get cameraDevice value. + */ +- (UIImagePickerControllerCameraDevice)getCameraDeviceFromArguments:(NSDictionary *)arguments { + NSInteger cameraDevice = [[arguments objectForKey:@"cameraDevice"] intValue]; + return (cameraDevice == 1) ? UIImagePickerControllerCameraDeviceFront + : UIImagePickerControllerCameraDeviceRear; +} + - (void)pickImageWithPHPicker:(int)maxImagesAllowed API_AVAILABLE(ios(14)) { PHPickerConfiguration *config = [[PHPickerConfiguration alloc] initWithPhotoLibrary:PHPhotoLibrary.sharedPhotoLibrary]; @@ -95,13 +109,9 @@ - (void)pickImageWithUIImagePicker { self.maxImagesAllowed = 1; switch (imageSource) { - case SOURCE_CAMERA: { - NSInteger cameraDevice = [[_arguments objectForKey:@"cameraDevice"] intValue]; - _device = (cameraDevice == 1) ? UIImagePickerControllerCameraDeviceFront - : UIImagePickerControllerCameraDeviceRear; + case SOURCE_CAMERA: [self checkCameraAuthorization]; break; - } case SOURCE_GALLERY: [self checkPhotoAuthorization]; break; @@ -188,11 +198,12 @@ - (void)showCamera { return; } } + UIImagePickerControllerCameraDevice device = [self getCameraDeviceFromArguments:_arguments]; // Camera is not available on simulators if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] && - [UIImagePickerController isCameraDeviceAvailable:_device]) { + [UIImagePickerController isCameraDeviceAvailable:device]) { _imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera; - _imagePickerController.cameraDevice = _device; + _imagePickerController.cameraDevice = device; [[self viewControllerWithWindow:nil] presentViewController:_imagePickerController animated:YES completion:nil]; @@ -406,8 +417,8 @@ - (void)picker:(PHPickerViewController *)picker * The difference with initWithCapacity is that initWithCapacity still gives an empty array making * it impossible to add objects on an index larger than the size. * - * @param @size The length of the required array - * @return @NSMutableArray An array of a specified size + * @param size The length of the required array + * @return NSMutableArray An array of a specified size */ - (NSMutableArray *)createNSMutableArrayWithSize:(NSUInteger)size { NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithCapacity:size]; @@ -528,14 +539,14 @@ - (void)saveImageWithPickerInfo:(NSDictionary *)info * Applies NSMutableArray on the FLutterResult. * * NSString must be returned by FlutterResult if the single image - * mode is active. It is checked by @c maxImagesAllowed and - * returns the first object of the @c pathlist. + * mode is active. It is checked by maxImagesAllowed and + * returns the first object of the pathlist. * * NSMutableArray must be returned by FlutterResult if the multi-image - * mode is active. After the @c pathlist count is checked then it returns - * the @c pathlist. + * mode is active. After the pathlist count is checked then it returns + * the pathlist. * - * @param @pathList that should be applied to FlutterResult. + * @param pathList that should be applied to FlutterResult. */ - (void)handleSavedPathList:(NSArray *)pathList { if (!self.result) { diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index bcda757b4bbf..c9866dbcda02 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.1+3 +version: 0.8.1+4 environment: sdk: ">=2.12.0 <3.0.0"