Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
[image_picker] Default to gallery instead of camera when picking mult…
Browse files Browse the repository at this point in the history
…iple images on pre-iOS 14 devices. (#4718)
  • Loading branch information
mvanbeusekom authored Feb 10, 2022
1 parent 7169829 commit aa36a8a
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 105 deletions.
5 changes: 5 additions & 0 deletions packages/image_picker/image_picker/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.8.4+8

* Configures the `UIImagePicker` to default to gallery instead of camera when
picking multiple images on pre-iOS 14 devices.

## 0.8.4+7

* Refactors unit test to expose private interface via a separate test header instead of the inline declaration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,127 +23,161 @@ - (UIViewController *)presentedViewController {
@end

@interface ImagePickerPluginTests : XCTestCase
@property(readonly, nonatomic) id mockUIImagePicker;
@property(readonly, nonatomic) id mockAVCaptureDevice;

@end

@implementation ImagePickerPluginTests

- (void)setUp {
_mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
_mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]);
}

- (void)testPluginPickImageDeviceBack {
id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]);
// UIImagePickerControllerSourceTypeCamera is supported
OCMStub(ClassMethod(
[_mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
[mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
.andReturn(YES);

// UIImagePickerControllerCameraDeviceRear is supported
OCMStub(ClassMethod(
[_mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear]))
[mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear]))
.andReturn(YES);

// AVAuthorizationStatusAuthorized is supported
OCMStub([_mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
OCMStub([mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
.andReturn(AVAuthorizationStatusAuthorized);

// Run test
FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new];
FlutterMethodCall *call =
[FlutterMethodCall methodCallWithMethodName:@"pickImage"
arguments:@{@"source" : @(0), @"cameraDevice" : @(0)}];
UIImagePickerController *controller = [[UIImagePickerController alloc] init];
[plugin setImagePickerControllerOverrides:@[ controller ]];
[plugin handleMethodCall:call
result:^(id _Nullable r){
}];

XCTAssertEqual([plugin getImagePickerController].cameraDevice,
UIImagePickerControllerCameraDeviceRear);
XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceRear);
}

- (void)testPluginPickImageDeviceFront {
id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]);
// UIImagePickerControllerSourceTypeCamera is supported
OCMStub(ClassMethod(
[_mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
[mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
.andReturn(YES);

// UIImagePickerControllerCameraDeviceFront is supported
OCMStub(ClassMethod([_mockUIImagePicker
isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]))
OCMStub(ClassMethod(
[mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]))
.andReturn(YES);

// AVAuthorizationStatusAuthorized is supported
OCMStub([_mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
OCMStub([mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
.andReturn(AVAuthorizationStatusAuthorized);

// Run test
FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new];
FlutterMethodCall *call =
[FlutterMethodCall methodCallWithMethodName:@"pickImage"
arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}];
UIImagePickerController *controller = [[UIImagePickerController alloc] init];
[plugin setImagePickerControllerOverrides:@[ controller ]];
[plugin handleMethodCall:call
result:^(id _Nullable r){
}];

XCTAssertEqual([plugin getImagePickerController].cameraDevice,
UIImagePickerControllerCameraDeviceFront);
XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceFront);
}

- (void)testPluginPickVideoDeviceBack {
id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]);
// UIImagePickerControllerSourceTypeCamera is supported
OCMStub(ClassMethod(
[_mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
[mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
.andReturn(YES);

// UIImagePickerControllerCameraDeviceRear is supported
OCMStub(ClassMethod(
[_mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear]))
[mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear]))
.andReturn(YES);

// AVAuthorizationStatusAuthorized is supported
OCMStub([_mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
OCMStub([mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
.andReturn(AVAuthorizationStatusAuthorized);

// Run test
FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new];
FlutterMethodCall *call =
[FlutterMethodCall methodCallWithMethodName:@"pickVideo"
arguments:@{@"source" : @(0), @"cameraDevice" : @(0)}];
UIImagePickerController *controller = [[UIImagePickerController alloc] init];
[plugin setImagePickerControllerOverrides:@[ controller ]];
[plugin handleMethodCall:call
result:^(id _Nullable r){
}];

XCTAssertEqual([plugin getImagePickerController].cameraDevice,
UIImagePickerControllerCameraDeviceRear);
XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceRear);
}

- (void)testPluginPickVideoDeviceFront {
id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
id mockAVCaptureDevice = OCMClassMock([AVCaptureDevice class]);

// UIImagePickerControllerSourceTypeCamera is supported
OCMStub(ClassMethod(
[_mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
[mockUIImagePicker isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]))
.andReturn(YES);

// UIImagePickerControllerCameraDeviceFront is supported
OCMStub(ClassMethod([_mockUIImagePicker
isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]))
OCMStub(ClassMethod(
[mockUIImagePicker isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]))
.andReturn(YES);

// AVAuthorizationStatusAuthorized is supported
OCMStub([_mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
OCMStub([mockAVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
.andReturn(AVAuthorizationStatusAuthorized);

// Run test
FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new];
FlutterMethodCall *call =
[FlutterMethodCall methodCallWithMethodName:@"pickVideo"
arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}];
UIImagePickerController *controller = [[UIImagePickerController alloc] init];
[plugin setImagePickerControllerOverrides:@[ controller ]];
[plugin handleMethodCall:call
result:^(id _Nullable r){
}];

XCTAssertEqual([plugin getImagePickerController].cameraDevice,
UIImagePickerControllerCameraDeviceFront);
XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceFront);
}

- (void)testPickMultiImageShouldUseUIImagePickerControllerOnPreiOS14 {
if (@available(iOS 14, *)) {
return;
}

id mockUIImagePicker = OCMClassMock([UIImagePickerController class]);
id photoLibrary = OCMClassMock([PHPhotoLibrary class]);
OCMStub(ClassMethod([photoLibrary authorizationStatus]))
.andReturn(PHAuthorizationStatusAuthorized);

FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new];
[plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]];
FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickMultiImage"
arguments:@{
@"maxWidth" : @(100),
@"maxHeight" : @(200),
@"imageQuality" : @(50),
}];

[plugin handleMethodCall:call
result:^(id _Nullable r){
}];

OCMVerify(times(1),
[mockUIImagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]);
}

#pragma mark - Test camera devices, no op on simulators
Expand All @@ -156,15 +190,18 @@ - (void)testPluginPickImageDeviceCancelClickMultipleTimes {
FlutterMethodCall *call =
[FlutterMethodCall methodCallWithMethodName:@"pickImage"
arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}];
UIImagePickerController *controller = [[UIImagePickerController alloc] init];
plugin.imagePickerControllerOverrides = @[ controller ];
[plugin handleMethodCall:call
result:^(id _Nullable r){
}];
plugin.result = ^(id result) {

};

// To ensure the flow does not crash by multiple cancel call
[plugin imagePickerControllerDidCancel:[plugin getImagePickerController]];
[plugin imagePickerControllerDidCancel:[plugin getImagePickerController]];
[plugin imagePickerControllerDidCancel:controller];
[plugin imagePickerControllerDidCancel:controller];
}

#pragma mark - Test video duration
Expand All @@ -174,10 +211,12 @@ - (void)testPickingVideoWithDuration {
FlutterMethodCall *call = [FlutterMethodCall
methodCallWithMethodName:@"pickVideo"
arguments:@{@"source" : @(0), @"cameraDevice" : @(0), @"maxDuration" : @95}];
UIImagePickerController *controller = [[UIImagePickerController alloc] init];
[plugin setImagePickerControllerOverrides:@[ controller ]];
[plugin handleMethodCall:call
result:^(id _Nullable r){
}];
XCTAssertEqual([plugin getImagePickerController].videoMaximumDuration, 95);
XCTAssertEqual(controller.videoMaximumDuration, 95);
}

- (void)testViewController {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
@interface FLTImagePickerPlugin : NSObject <FlutterPlugin>

// For testing only.
- (UIImagePickerController *)getImagePickerController;
- (UIViewController *)viewControllerWithWindow:(UIWindow *)window;

@end
Loading

0 comments on commit aa36a8a

Please sign in to comment.