From c7057749e4d02c420a4e4f105391d69d92976386 Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Fri, 13 May 2022 11:27:53 +0200 Subject: [PATCH 01/11] Moved iOS specific changes to a separate branch --- .../image_picker_ios/CHANGELOG.md | 4 + .../ios/RunnerTests/ImagePickerPluginTests.m | 25 ++ .../image_picker_ios/example/pubspec.yaml | 2 +- .../ios/Classes/FLTImagePickerPlugin.m | 35 +- .../ios/Classes/FLTImagePickerPlugin_Test.h | 3 + .../image_picker_ios/ios/Classes/messages.g.h | 29 +- .../image_picker_ios/ios/Classes/messages.g.m | 140 ++++---- .../lib/image_picker_ios.dart | 46 ++- .../image_picker_ios/lib/src/messages.g.dart | 62 ++-- .../image_picker_ios/pigeons/messages.dart | 6 +- .../image_picker_ios/pubspec.yaml | 4 +- .../test/image_picker_ios_test.dart | 337 +++++++++++++++++- .../image_picker_ios/test/test_api.dart | 20 +- 13 files changed, 536 insertions(+), 177 deletions(-) diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index 96b1c7f0d0a4..12822164000b 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.6 + +* Adds `requestFullMetadata` option to `pickImage`, so images on iOS can be picked without `Photo Library Usage` permission. + ## 0.8.5+2 * Minor fixes for new analysis options. diff --git a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m index 04d491131d5b..3e99c19b6bde 100644 --- a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m @@ -54,6 +54,7 @@ - (void)testPluginPickImageDeviceBack { camera:FLTSourceCameraRear] maxSize:[[FLTMaxSize alloc] init] quality:nil + fullMetadata:@(YES) completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ }]; @@ -86,6 +87,7 @@ - (void)testPluginPickImageDeviceFront { camera:FLTSourceCameraFront] maxSize:[[FLTMaxSize alloc] init] quality:nil + fullMetadata:@(YES) completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ }]; @@ -177,6 +179,28 @@ - (void)testPickMultiImageShouldUseUIImagePickerControllerOnPreiOS14 { [mockUIImagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]); } +- (void)testPickImageWithoutFullMetadataPreiOS14 { + if (@available(iOS 14, *)) { + return; + } + id mockUIImagePicker = OCMClassMock([UIImagePickerController class]); + FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + [plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]]; + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickImage" + arguments:@{ + @"source" : @(1), + @"requestFullMetadata" : @(NO), + }]; + + [plugin handleMethodCall:call + result:^(id _Nullable r){ + }]; + + OCMVerify(times(1), + [mockUIImagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]); +} + + #pragma mark - Test camera devices, no op on simulators - (void)testPluginPickImageDeviceCancelClickMultipleTimes { @@ -191,6 +215,7 @@ - (void)testPluginPickImageDeviceCancelClickMultipleTimes { camera:FLTSourceCameraRear] maxSize:[[FLTMaxSize alloc] init] quality:nil + fullMetadata:@(YES) completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ }]; diff --git a/packages/image_picker/image_picker_ios/example/pubspec.yaml b/packages/image_picker/image_picker_ios/example/pubspec.yaml index a47893d7687f..d219831cce03 100755 --- a/packages/image_picker/image_picker_ios/example/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/example/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - image_picker_platform_interface: ^2.3.0 + image_picker_platform_interface: ^2.5.0 video_player: ^2.1.4 dev_dependencies: diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m index 76ed9623a57c..d1e8ce417c71 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m @@ -119,7 +119,12 @@ - (void)launchPHPickerWithContext:(nonnull FLTImagePickerMethodCallContext *)con _pickerViewController.presentationController.delegate = self; self.callContext = context; - [self checkPhotoAuthorizationForAccessLevel]; + BOOL requestFullMetadata = context.requestFullMetadata; + if (requestFullMetadata) { + [self checkPhotoAuthorizationForAccessLevel]; + } else { + [self showPhotoLibraryWithPHPicker:_pickerViewController]; + } } - (void)launchUIImagePickerWithSource:(nonnull FLTSourceSpecification *)source @@ -129,6 +134,7 @@ - (void)launchUIImagePickerWithSource:(nonnull FLTSourceSpecification *)source imagePickerController.delegate = self; imagePickerController.mediaTypes = @[ (NSString *)kUTTypeImage ]; self.callContext = context; + BOOL requestFullMetadata = context.requestFullMetadata; switch (source.type) { case FLTSourceTypeCamera: @@ -136,7 +142,16 @@ - (void)launchUIImagePickerWithSource:(nonnull FLTSourceSpecification *)source camera:[self cameraDeviceForSource:source]]; break; case FLTSourceTypeGallery: - [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; + if (@available(iOS 11, *)) { + if (requestFullMetadata) { + [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; + } else { + [self showPhotoLibraryWithImagePicker:imagePickerController]; + } + } else { + // Prior to iOS 11, accessing gallery requires authorization + [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; + } break; default: [self sendCallResultWithError:[FlutterError errorWithCode:@"invalid_source" @@ -151,6 +166,7 @@ - (void)launchUIImagePickerWithSource:(nonnull FLTSourceSpecification *)source - (void)pickImageWithSource:(nonnull FLTSourceSpecification *)source maxSize:(nonnull FLTMaxSize *)maxSize quality:(nullable NSNumber *)imageQuality + fullMetadata:(NSNumber *)fullMetadata completion: (nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { [self cancelInProgressCall]; @@ -166,6 +182,7 @@ - (void)pickImageWithSource:(nonnull FLTSourceSpecification *)source context.maxSize = maxSize; context.imageQuality = imageQuality; context.maxImageCount = 1; + context.requestFullMetadata = [fullMetadata boolValue]; if (source.type == FLTSourceTypeGallery) { // Capture is not possible with PHPicker if (@available(iOS 14, *)) { @@ -227,6 +244,7 @@ - (void)pickVideoWithSource:(nonnull FLTSourceSpecification *)source } self.callContext = context; + BOOL requestFullMetadata = context.requestFullMetadata; switch (source.type) { case FLTSourceTypeCamera: @@ -234,7 +252,11 @@ - (void)pickVideoWithSource:(nonnull FLTSourceSpecification *)source camera:[self cameraDeviceForSource:source]]; break; case FLTSourceTypeGallery: - [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; + if (requestFullMetadata) { + [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; + } else { + [self showPhotoLibraryWithImagePicker:imagePickerController]; + } break; default: [self sendCallResultWithError:[FlutterError errorWithCode:@"invalid_source" @@ -553,8 +575,13 @@ - (void)imagePickerController:(UIImagePickerController *)picker NSNumber *maxHeight = self.callContext.maxSize.height; NSNumber *imageQuality = self.callContext.imageQuality; NSNumber *desiredImageQuality = [self getDesiredImageQuality:imageQuality]; + BOOL requestFullMetadata = _callContext.requestFullMetadata; - PHAsset *originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromImagePickerInfo:info]; + PHAsset *originalAsset; + if (requestFullMetadata) { + // Full metadata are available only in PHAsset, which requires gallery permission + originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromImagePickerInfo:info]; + } if (maxWidth != nil || maxHeight != nil) { image = [FLTImagePickerImageUtil scaledImage:image diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h index 2c4167746c8e..174b1f7d7ff9 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h @@ -46,6 +46,9 @@ typedef void (^FlutterResultAdapter)(NSArray *_Nullable, FlutterErro /** Maximum number of images to select. 0 indicates no maximum. */ @property(nonatomic, assign) int maxImageCount; +/** Whether the image should be picked with full metadata (requires gallery permissions) */ +@property(nonatomic, assign) BOOL requestFullMetadata; + @end #pragma mark - diff --git a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h index 310165f72f4f..aa9f4eee95be 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h +++ b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v3.0.2), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon #import @protocol FlutterBinaryMessenger; @@ -25,15 +25,17 @@ typedef NS_ENUM(NSUInteger, FLTSourceType) { @class FLTSourceSpecification; @interface FLTMaxSize : NSObject -+ (instancetype)makeWithWidth:(nullable NSNumber *)width height:(nullable NSNumber *)height; -@property(nonatomic, strong, nullable) NSNumber *width; -@property(nonatomic, strong, nullable) NSNumber *height; ++ (instancetype)makeWithWidth:(nullable NSNumber *)width + height:(nullable NSNumber *)height; +@property(nonatomic, strong, nullable) NSNumber * width; +@property(nonatomic, strong, nullable) NSNumber * height; @end @interface FLTSourceSpecification : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithType:(FLTSourceType)type camera:(FLTSourceCamera)camera; ++ (instancetype)makeWithType:(FLTSourceType)type + camera:(FLTSourceCamera)camera; @property(nonatomic, assign) FLTSourceType type; @property(nonatomic, assign) FLTSourceCamera camera; @end @@ -42,20 +44,11 @@ typedef NS_ENUM(NSUInteger, FLTSourceType) { NSObject *FLTImagePickerApiGetCodec(void); @protocol FLTImagePickerApi -- (void)pickImageWithSource:(FLTSourceSpecification *)source - maxSize:(FLTMaxSize *)maxSize - quality:(nullable NSNumber *)imageQuality - completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; -- (void)pickMultiImageWithMaxSize:(FLTMaxSize *)maxSize - quality:(nullable NSNumber *)imageQuality - completion:(void (^)(NSArray *_Nullable, - FlutterError *_Nullable))completion; -- (void)pickVideoWithSource:(FLTSourceSpecification *)source - maxDuration:(nullable NSNumber *)maxDurationSeconds - completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)pickImageWithSource:(FLTSourceSpecification *)source maxSize:(FLTMaxSize *)maxSize quality:(nullable NSNumber *)imageQuality fullMetadata:(NSNumber *)requestFullMetadata completion:(void(^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)pickMultiImageWithMaxSize:(FLTMaxSize *)maxSize quality:(nullable NSNumber *)imageQuality completion:(void(^)(NSArray *_Nullable, FlutterError *_Nullable))completion; +- (void)pickVideoWithSource:(FLTSourceSpecification *)source maxDuration:(nullable NSNumber *)maxDurationSeconds completion:(void(^)(NSString *_Nullable, FlutterError *_Nullable))completion; @end -extern void FLTImagePickerApiSetup(id binaryMessenger, - NSObject *_Nullable api); +extern void FLTImagePickerApiSetup(id binaryMessenger, NSObject *_Nullable api); NS_ASSUME_NONNULL_END diff --git a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m index 6c91c0ab264f..c97e65279252 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v3.0.2), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "messages.g.h" #import @@ -14,25 +14,26 @@ NSDictionary *errorDict = (NSDictionary *)[NSNull null]; if (error) { errorDict = @{ - @"code" : (error.code ? error.code : [NSNull null]), - @"message" : (error.message ? error.message : [NSNull null]), - @"details" : (error.details ? error.details : [NSNull null]), - }; + @"code": (error.code ? error.code : [NSNull null]), + @"message": (error.message ? error.message : [NSNull null]), + @"details": (error.details ? error.details : [NSNull null]), + }; } return @{ - @"result" : (result ? result : [NSNull null]), - @"error" : errorDict, - }; + @"result": (result ? result : [NSNull null]), + @"error": errorDict, + }; } -static id GetNullableObject(NSDictionary *dict, id key) { +static id GetNullableObject(NSDictionary* dict, id key) { id result = dict[key]; return (result == [NSNull null]) ? nil : result; } -static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { +static id GetNullableObjectAtIndex(NSArray* array, NSInteger key) { id result = array[key]; return (result == [NSNull null]) ? nil : result; } + @interface FLTMaxSize () + (FLTMaxSize *)fromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @@ -43,8 +44,9 @@ - (NSDictionary *)toMap; @end @implementation FLTMaxSize -+ (instancetype)makeWithWidth:(nullable NSNumber *)width height:(nullable NSNumber *)height { - FLTMaxSize *pigeonResult = [[FLTMaxSize alloc] init]; ++ (instancetype)makeWithWidth:(nullable NSNumber *)width + height:(nullable NSNumber *)height { + FLTMaxSize* pigeonResult = [[FLTMaxSize alloc] init]; pigeonResult.width = width; pigeonResult.height = height; return pigeonResult; @@ -56,15 +58,14 @@ + (FLTMaxSize *)fromMap:(NSDictionary *)dict { return pigeonResult; } - (NSDictionary *)toMap { - return [NSDictionary - dictionaryWithObjectsAndKeys:(self.width ? self.width : [NSNull null]), @"width", - (self.height ? self.height : [NSNull null]), @"height", nil]; + return [NSDictionary dictionaryWithObjectsAndKeys:(self.width ? self.width : [NSNull null]), @"width", (self.height ? self.height : [NSNull null]), @"height", nil]; } @end @implementation FLTSourceSpecification -+ (instancetype)makeWithType:(FLTSourceType)type camera:(FLTSourceCamera)camera { - FLTSourceSpecification *pigeonResult = [[FLTSourceSpecification alloc] init]; ++ (instancetype)makeWithType:(FLTSourceType)type + camera:(FLTSourceCamera)camera { + FLTSourceSpecification* pigeonResult = [[FLTSourceSpecification alloc] init]; pigeonResult.type = type; pigeonResult.camera = camera; return pigeonResult; @@ -76,24 +77,25 @@ + (FLTSourceSpecification *)fromMap:(NSDictionary *)dict { return pigeonResult; } - (NSDictionary *)toMap { - return [NSDictionary - dictionaryWithObjectsAndKeys:@(self.type), @"type", @(self.camera), @"camera", nil]; + return [NSDictionary dictionaryWithObjectsAndKeys:@(self.type), @"type", @(self.camera), @"camera", nil]; } @end @interface FLTImagePickerApiCodecReader : FlutterStandardReader @end @implementation FLTImagePickerApiCodecReader -- (nullable id)readValueOfType:(UInt8)type { +- (nullable id)readValueOfType:(UInt8)type +{ switch (type) { - case 128: + case 128: return [FLTMaxSize fromMap:[self readValue]]; - - case 129: + + case 129: return [FLTSourceSpecification fromMap:[self readValue]]; - - default: + + default: return [super readValueOfType:type]; + } } @end @@ -101,14 +103,17 @@ - (nullable id)readValueOfType:(UInt8)type { @interface FLTImagePickerApiCodecWriter : FlutterStandardWriter @end @implementation FLTImagePickerApiCodecWriter -- (void)writeValue:(id)value { +- (void)writeValue:(id)value +{ if ([value isKindOfClass:[FLTMaxSize class]]) { [self writeByte:128]; [self writeValue:[value toMap]]; - } else if ([value isKindOfClass:[FLTSourceSpecification class]]) { + } else + if ([value isKindOfClass:[FLTSourceSpecification class]]) { [self writeByte:129]; [self writeValue:[value toMap]]; - } else { + } else +{ [super writeValue:value]; } } @@ -129,87 +134,76 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { static dispatch_once_t sPred = 0; static FlutterStandardMessageCodec *sSharedObject = nil; dispatch_once(&sPred, ^{ - FLTImagePickerApiCodecReaderWriter *readerWriter = - [[FLTImagePickerApiCodecReaderWriter alloc] init]; + FLTImagePickerApiCodecReaderWriter *readerWriter = [[FLTImagePickerApiCodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } -void FLTImagePickerApiSetup(id binaryMessenger, - NSObject *api) { + +void FLTImagePickerApiSetup(id binaryMessenger, NSObject *api) { { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickImage" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickImage" binaryMessenger:binaryMessenger - codec:FLTImagePickerApiGetCodec()]; + codec:FLTImagePickerApiGetCodec() ]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickImageWithSource:maxSize:quality:completion:)], - @"FLTImagePickerApi api (%@) doesn't respond to " - @"@selector(pickImageWithSource:maxSize:quality:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(pickImageWithSource:maxSize:quality:fullMetadata:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to @selector(pickImageWithSource:maxSize:quality:fullMetadata:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTSourceSpecification *arg_source = GetNullableObjectAtIndex(args, 0); FLTMaxSize *arg_maxSize = GetNullableObjectAtIndex(args, 1); NSNumber *arg_imageQuality = GetNullableObjectAtIndex(args, 2); - [api pickImageWithSource:arg_source - maxSize:arg_maxSize - quality:arg_imageQuality - completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + NSNumber *arg_requestFullMetadata = GetNullableObjectAtIndex(args, 3); + [api pickImageWithSource:arg_source maxSize:arg_maxSize quality:arg_imageQuality fullMetadata:arg_requestFullMetadata completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; - } else { + } + else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickMultiImage" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickMultiImage" binaryMessenger:binaryMessenger - codec:FLTImagePickerApiGetCodec()]; + codec:FLTImagePickerApiGetCodec() ]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickMultiImageWithMaxSize:quality:completion:)], - @"FLTImagePickerApi api (%@) doesn't respond to " - @"@selector(pickMultiImageWithMaxSize:quality:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(pickMultiImageWithMaxSize:quality:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to @selector(pickMultiImageWithMaxSize:quality:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTMaxSize *arg_maxSize = GetNullableObjectAtIndex(args, 0); NSNumber *arg_imageQuality = GetNullableObjectAtIndex(args, 1); - [api pickMultiImageWithMaxSize:arg_maxSize - quality:arg_imageQuality - completion:^(NSArray *_Nullable output, - FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api pickMultiImageWithMaxSize:arg_maxSize quality:arg_imageQuality completion:^(NSArray *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; - } else { + } + else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickVideo" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickVideo" binaryMessenger:binaryMessenger - codec:FLTImagePickerApiGetCodec()]; + codec:FLTImagePickerApiGetCodec() ]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickVideoWithSource:maxDuration:completion:)], - @"FLTImagePickerApi api (%@) doesn't respond to " - @"@selector(pickVideoWithSource:maxDuration:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(pickVideoWithSource:maxDuration:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to @selector(pickVideoWithSource:maxDuration:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTSourceSpecification *arg_source = GetNullableObjectAtIndex(args, 0); NSNumber *arg_maxDurationSeconds = GetNullableObjectAtIndex(args, 1); - [api pickVideoWithSource:arg_source - maxDuration:arg_maxDurationSeconds - completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api pickVideoWithSource:arg_source maxDuration:arg_maxDurationSeconds completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; - } else { + } + else { [channel setMessageHandler:nil]; } } diff --git a/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart b/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart index 3d1413cf0cce..a5575cf5ddfa 100644 --- a/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart +++ b/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart @@ -51,14 +51,28 @@ class ImagePickerIOS extends ImagePickerPlatform { }) async { final String? path = await _pickImageAsPath( source: source, - maxWidth: maxWidth, - maxHeight: maxHeight, - imageQuality: imageQuality, - preferredCameraDevice: preferredCameraDevice, + options: ImagePickerOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice, + ), ); return path != null ? PickedFile(path) : null; } + @override + Future getImageFromSource({ + required ImageSource source, + ImagePickerOptions options = const ImagePickerOptions(), + }) async { + final String? path = await _pickImageAsPath( + source: source, + options: options, + ); + return path != null ? XFile(path) : null; + } + @override Future?> pickMultiImage({ double? maxWidth, @@ -104,16 +118,16 @@ class ImagePickerIOS extends ImagePickerPlatform { Future _pickImageAsPath({ required ImageSource source, - double? maxWidth, - double? maxHeight, - int? imageQuality, - CameraDevice preferredCameraDevice = CameraDevice.rear, + ImagePickerOptions options = const ImagePickerOptions(), }) { + final int? imageQuality = options.imageQuality; if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { throw ArgumentError.value( imageQuality, 'imageQuality', 'must be between 0 and 100'); } + final double? maxHeight = options.maxHeight; + final double? maxWidth = options.maxWidth; if (maxWidth != null && maxWidth < 0) { throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative'); } @@ -124,10 +138,12 @@ class ImagePickerIOS extends ImagePickerPlatform { return _hostApi.pickImage( SourceSpecification( - type: _convertSource(source), - camera: _convertCamera(preferredCameraDevice)), + type: _convertSource(source), + camera: _convertCamera(options.preferredCameraDevice), + ), MaxSize(width: maxWidth, height: maxHeight), imageQuality, + options.requestFullMetadata, ); } @@ -167,10 +183,12 @@ class ImagePickerIOS extends ImagePickerPlatform { }) async { final String? path = await _pickImageAsPath( source: source, - maxWidth: maxWidth, - maxHeight: maxHeight, - imageQuality: imageQuality, - preferredCameraDevice: preferredCameraDevice, + options: ImagePickerOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice, + ), ); return path != null ? XFile(path) : null; } diff --git a/packages/image_picker/image_picker_ios/lib/src/messages.g.dart b/packages/image_picker/image_picker_ios/lib/src/messages.g.dart index 0c5859e80ac9..842e2339d496 100644 --- a/packages/image_picker/image_picker_ios/lib/src/messages.g.dart +++ b/packages/image_picker/image_picker_ios/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v3.0.2), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name // @dart = 2.12 @@ -65,7 +65,8 @@ class SourceSpecification { static SourceSpecification decode(Object message) { final Map pigeonMap = message as Map; return SourceSpecification( - type: SourceType.values[pigeonMap['type']! as int], + type: SourceType.values[pigeonMap['type']! as int] +, camera: pigeonMap['camera'] != null ? SourceCamera.values[pigeonMap['camera']! as int] : null, @@ -80,25 +81,27 @@ class _ImagePickerApiCodec extends StandardMessageCodec { if (value is MaxSize) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is SourceSpecification) { + } else + if (value is SourceSpecification) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else { + } else +{ super.writeValue(buffer, value); } } - @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return MaxSize.decode(readValue(buffer)!); - - case 129: + + case 129: return SourceSpecification.decode(readValue(buffer)!); - - default: + + default: return super.readValueOfType(type, buffer); + } } } @@ -107,29 +110,24 @@ class ImagePickerApi { /// Constructor for [ImagePickerApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - ImagePickerApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; + ImagePickerApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; static const MessageCodec codec = _ImagePickerApiCodec(); - Future pickImage(SourceSpecification arg_source, MaxSize arg_maxSize, - int? arg_imageQuality) async { + Future pickImage(SourceSpecification arg_source, MaxSize arg_maxSize, int? arg_imageQuality, bool arg_requestFullMetadata) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickImage', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.ImagePickerApi.pickImage', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_source, arg_maxSize, arg_imageQuality]) - as Map?; + await channel.send([arg_source, arg_maxSize, arg_imageQuality, arg_requestFullMetadata]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -140,22 +138,18 @@ class ImagePickerApi { } } - Future?> pickMultiImage( - MaxSize arg_maxSize, int? arg_imageQuality) async { + Future?> pickMultiImage(MaxSize arg_maxSize, int? arg_imageQuality) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_maxSize, arg_imageQuality]) - as Map?; + await channel.send([arg_maxSize, arg_imageQuality]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -166,22 +160,18 @@ class ImagePickerApi { } } - Future pickVideo( - SourceSpecification arg_source, int? arg_maxDurationSeconds) async { + Future pickVideo(SourceSpecification arg_source, int? arg_maxDurationSeconds) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickVideo', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.ImagePickerApi.pickVideo', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_source, arg_maxDurationSeconds]) - as Map?; + await channel.send([arg_source, arg_maxDurationSeconds]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, diff --git a/packages/image_picker/image_picker_ios/pigeons/messages.dart b/packages/image_picker/image_picker_ios/pigeons/messages.dart index 94ac034606e9..f6bff83959cf 100644 --- a/packages/image_picker/image_picker_ios/pigeons/messages.dart +++ b/packages/image_picker/image_picker_ios/pigeons/messages.dart @@ -35,9 +35,9 @@ class SourceSpecification { @HostApi(dartHostTestHandler: 'TestHostImagePickerApi') abstract class ImagePickerApi { @async - @ObjCSelector('pickImageWithSource:maxSize:quality:') - String? pickImage( - SourceSpecification source, MaxSize maxSize, int? imageQuality); + @ObjCSelector('pickImageWithSource:maxSize:quality:fullMetadata:') + String? pickImage(SourceSpecification source, MaxSize maxSize, + int? imageQuality, bool requestFullMetadata); @async @ObjCSelector('pickMultiImageWithMaxSize:quality:') List? pickMultiImage(MaxSize maxSize, int? imageQuality); diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index d1de0a14ea69..58434b4d2996 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_ios description: iOS implementation of the video_picker plugin. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5+2 +version: 0.8.6 environment: sdk: ">=2.14.0 <3.0.0" @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - image_picker_platform_interface: ^2.3.0 + image_picker_platform_interface: ^2.5.0 dev_dependencies: flutter_test: diff --git a/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart b/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart index 09517f1ef96b..14d1d0be99e6 100644 --- a/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart +++ b/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart @@ -39,7 +39,11 @@ class _ApiLogger implements TestHostImagePickerApi { @override Future pickImage( - SourceSpecification source, MaxSize maxSize, int? imageQuality) async { + SourceSpecification source, + MaxSize maxSize, + int? imageQuality, + bool requestFullMetadata, + ) async { // Flatten arguments for easy comparison. calls.add(_LoggedMethodCall('pickImage', arguments: { 'source': source.type, @@ -47,6 +51,7 @@ class _ApiLogger implements TestHostImagePickerApi { 'maxWidth': maxSize.width, 'maxHeight': maxSize.height, 'imageQuality': imageQuality, + 'requestFullMetadata': requestFullMetadata, })); return returnValue as String?; } @@ -103,14 +108,16 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.gallery, 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), ], ); @@ -156,49 +163,56 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': 70, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': 70, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': 70, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), ], ); @@ -257,6 +271,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), ], ); @@ -276,6 +291,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': SourceCamera.front, + 'requestFullMetadata': true, }), ], ); @@ -524,14 +540,16 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.gallery, 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), ], ); @@ -577,49 +595,56 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': 70, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': 70, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': 70, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), ], ); @@ -678,6 +703,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), ], ); @@ -697,6 +723,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': SourceCamera.front, + 'requestFullMetadata': true, }), ], ); @@ -934,4 +961,276 @@ void main() { ); }); }); + + group('#getImageFromSource', () { + test('passes the image source argument correctly', () async { + await picker.getImageFromSource(source: ImageSource.camera); + await picker.getImageFromSource(source: ImageSource.gallery); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.gallery, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + await picker.getImageFromSource(source: ImageSource.camera); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxWidth: 10.0), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxHeight: 10.0), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxWidth: 10.0, + maxHeight: 20.0, + ), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxWidth: 10.0, + imageQuality: 70, + ), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxHeight: 10.0, + imageQuality: 70, + ), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ), + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('does not accept a invalid imageQuality argument', () { + expect( + () => picker.getImageFromSource( + source: ImageSource.gallery, + options: const ImagePickerOptions(imageQuality: -1), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.gallery, + options: const ImagePickerOptions(imageQuality: 101), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(imageQuality: -1), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(imageQuality: 101), + ), + throwsArgumentError, + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxWidth: -1.0), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxHeight: -1.0), + ), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + log.returnValue = null; + + expect( + await picker.getImageFromSource(source: ImageSource.gallery), isNull); + expect( + await picker.getImageFromSource(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.getImageFromSource(source: ImageSource.camera); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.getImageFromSource( + source: ImageSource.camera, + options: + const ImagePickerOptions(preferredCameraDevice: CameraDevice.front), + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.front, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('Request full metadata argument defaults to true', () async { + await picker.getImageFromSource(source: ImageSource.gallery); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.gallery, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('passes the request full metadata argument correctly', () async { + await picker.getImageFromSource( + source: ImageSource.gallery, + options: const ImagePickerOptions(requestFullMetadata: false), + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.gallery, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': false, + }), + ], + ); + }); + }); } diff --git a/packages/image_picker/image_picker_ios/test/test_api.dart b/packages/image_picker/image_picker_ios/test/test_api.dart index d22a26b2489b..8e3c1f1fd941 100644 --- a/packages/image_picker/image_picker_ios/test/test_api.dart +++ b/packages/image_picker/image_picker_ios/test/test_api.dart @@ -1,21 +1,21 @@ // 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. -// Autogenerated from Pigeon (v3.0.2), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis // ignore_for_file: avoid_relative_lib_imports // @dart = 2.12 import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; - -// Manually changed due to https://github.com/flutter/flutter/issues/97744 import 'package:image_picker_ios/src/messages.g.dart'; class _TestHostImagePickerApiCodec extends StandardMessageCodec { const _TestHostImagePickerApiCodec(); + @override void writeValue(WriteBuffer buffer, Object? value) { if (value is MaxSize) { @@ -47,11 +47,14 @@ class _TestHostImagePickerApiCodec extends StandardMessageCodec { abstract class TestHostImagePickerApi { static const MessageCodec codec = _TestHostImagePickerApiCodec(); - Future pickImage( - SourceSpecification source, MaxSize maxSize, int? imageQuality); + Future pickImage(SourceSpecification source, MaxSize maxSize, + int? imageQuality, bool requestFullMetadata); + Future?> pickMultiImage(MaxSize maxSize, int? imageQuality); + Future pickVideo( SourceSpecification source, int? maxDurationSeconds); + static void setup(TestHostImagePickerApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -73,8 +76,11 @@ abstract class TestHostImagePickerApi { assert(arg_maxSize != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null MaxSize.'); final int? arg_imageQuality = (args[2] as int?); - final String? output = - await api.pickImage(arg_source!, arg_maxSize!, arg_imageQuality); + final bool? arg_requestFullMetadata = (args[3] as bool?); + assert(arg_requestFullMetadata != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null bool.'); + final String? output = await api.pickImage(arg_source!, arg_maxSize!, + arg_imageQuality, arg_requestFullMetadata!); return {'result': output}; }); } From bf73c7327bd3194ffbd78573c78061b10642193d Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Fri, 13 May 2022 11:33:26 +0200 Subject: [PATCH 02/11] Fixed line formatting --- .../ios/RunnerTests/ImagePickerPluginTests.m | 7 +- .../ios/Classes/FLTImagePickerPlugin.m | 2 +- .../image_picker_ios/ios/Classes/messages.g.h | 28 ++-- .../image_picker_ios/ios/Classes/messages.g.m | 139 ++++++++++-------- .../image_picker_ios/lib/src/messages.g.dart | 65 ++++---- 5 files changed, 135 insertions(+), 106 deletions(-) diff --git a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m index 3e99c19b6bde..e2311904f286 100644 --- a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m @@ -54,7 +54,7 @@ - (void)testPluginPickImageDeviceBack { camera:FLTSourceCameraRear] maxSize:[[FLTMaxSize alloc] init] quality:nil - fullMetadata:@(YES) + fullMetadata:@(YES) completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ }]; @@ -87,7 +87,7 @@ - (void)testPluginPickImageDeviceFront { camera:FLTSourceCameraFront] maxSize:[[FLTMaxSize alloc] init] quality:nil - fullMetadata:@(YES) + fullMetadata:@(YES) completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ }]; @@ -200,7 +200,6 @@ - (void)testPickImageWithoutFullMetadataPreiOS14 { [mockUIImagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]); } - #pragma mark - Test camera devices, no op on simulators - (void)testPluginPickImageDeviceCancelClickMultipleTimes { @@ -215,7 +214,7 @@ - (void)testPluginPickImageDeviceCancelClickMultipleTimes { camera:FLTSourceCameraRear] maxSize:[[FLTMaxSize alloc] init] quality:nil - fullMetadata:@(YES) + fullMetadata:@(YES) completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ }]; diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m index d1e8ce417c71..55127d9e010d 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m @@ -166,7 +166,7 @@ - (void)launchUIImagePickerWithSource:(nonnull FLTSourceSpecification *)source - (void)pickImageWithSource:(nonnull FLTSourceSpecification *)source maxSize:(nonnull FLTMaxSize *)maxSize quality:(nullable NSNumber *)imageQuality - fullMetadata:(NSNumber *)fullMetadata + fullMetadata:(NSNumber *)fullMetadata completion: (nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { [self cancelInProgressCall]; diff --git a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h index aa9f4eee95be..0d3e96c842bb 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h +++ b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h @@ -25,17 +25,15 @@ typedef NS_ENUM(NSUInteger, FLTSourceType) { @class FLTSourceSpecification; @interface FLTMaxSize : NSObject -+ (instancetype)makeWithWidth:(nullable NSNumber *)width - height:(nullable NSNumber *)height; -@property(nonatomic, strong, nullable) NSNumber * width; -@property(nonatomic, strong, nullable) NSNumber * height; ++ (instancetype)makeWithWidth:(nullable NSNumber *)width height:(nullable NSNumber *)height; +@property(nonatomic, strong, nullable) NSNumber *width; +@property(nonatomic, strong, nullable) NSNumber *height; @end @interface FLTSourceSpecification : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithType:(FLTSourceType)type - camera:(FLTSourceCamera)camera; ++ (instancetype)makeWithType:(FLTSourceType)type camera:(FLTSourceCamera)camera; @property(nonatomic, assign) FLTSourceType type; @property(nonatomic, assign) FLTSourceCamera camera; @end @@ -44,11 +42,21 @@ typedef NS_ENUM(NSUInteger, FLTSourceType) { NSObject *FLTImagePickerApiGetCodec(void); @protocol FLTImagePickerApi -- (void)pickImageWithSource:(FLTSourceSpecification *)source maxSize:(FLTMaxSize *)maxSize quality:(nullable NSNumber *)imageQuality fullMetadata:(NSNumber *)requestFullMetadata completion:(void(^)(NSString *_Nullable, FlutterError *_Nullable))completion; -- (void)pickMultiImageWithMaxSize:(FLTMaxSize *)maxSize quality:(nullable NSNumber *)imageQuality completion:(void(^)(NSArray *_Nullable, FlutterError *_Nullable))completion; -- (void)pickVideoWithSource:(FLTSourceSpecification *)source maxDuration:(nullable NSNumber *)maxDurationSeconds completion:(void(^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)pickImageWithSource:(FLTSourceSpecification *)source + maxSize:(FLTMaxSize *)maxSize + quality:(nullable NSNumber *)imageQuality + fullMetadata:(NSNumber *)requestFullMetadata + completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)pickMultiImageWithMaxSize:(FLTMaxSize *)maxSize + quality:(nullable NSNumber *)imageQuality + completion:(void (^)(NSArray *_Nullable, + FlutterError *_Nullable))completion; +- (void)pickVideoWithSource:(FLTSourceSpecification *)source + maxDuration:(nullable NSNumber *)maxDurationSeconds + completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; @end -extern void FLTImagePickerApiSetup(id binaryMessenger, NSObject *_Nullable api); +extern void FLTImagePickerApiSetup(id binaryMessenger, + NSObject *_Nullable api); NS_ASSUME_NONNULL_END diff --git a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m index c97e65279252..b13601480aea 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m @@ -14,26 +14,25 @@ NSDictionary *errorDict = (NSDictionary *)[NSNull null]; if (error) { errorDict = @{ - @"code": (error.code ? error.code : [NSNull null]), - @"message": (error.message ? error.message : [NSNull null]), - @"details": (error.details ? error.details : [NSNull null]), - }; + @"code" : (error.code ? error.code : [NSNull null]), + @"message" : (error.message ? error.message : [NSNull null]), + @"details" : (error.details ? error.details : [NSNull null]), + }; } return @{ - @"result": (result ? result : [NSNull null]), - @"error": errorDict, - }; + @"result" : (result ? result : [NSNull null]), + @"error" : errorDict, + }; } -static id GetNullableObject(NSDictionary* dict, id key) { +static id GetNullableObject(NSDictionary *dict, id key) { id result = dict[key]; return (result == [NSNull null]) ? nil : result; } -static id GetNullableObjectAtIndex(NSArray* array, NSInteger key) { +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { id result = array[key]; return (result == [NSNull null]) ? nil : result; } - @interface FLTMaxSize () + (FLTMaxSize *)fromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @@ -44,9 +43,8 @@ - (NSDictionary *)toMap; @end @implementation FLTMaxSize -+ (instancetype)makeWithWidth:(nullable NSNumber *)width - height:(nullable NSNumber *)height { - FLTMaxSize* pigeonResult = [[FLTMaxSize alloc] init]; ++ (instancetype)makeWithWidth:(nullable NSNumber *)width height:(nullable NSNumber *)height { + FLTMaxSize *pigeonResult = [[FLTMaxSize alloc] init]; pigeonResult.width = width; pigeonResult.height = height; return pigeonResult; @@ -58,14 +56,15 @@ + (FLTMaxSize *)fromMap:(NSDictionary *)dict { return pigeonResult; } - (NSDictionary *)toMap { - return [NSDictionary dictionaryWithObjectsAndKeys:(self.width ? self.width : [NSNull null]), @"width", (self.height ? self.height : [NSNull null]), @"height", nil]; + return [NSDictionary + dictionaryWithObjectsAndKeys:(self.width ? self.width : [NSNull null]), @"width", + (self.height ? self.height : [NSNull null]), @"height", nil]; } @end @implementation FLTSourceSpecification -+ (instancetype)makeWithType:(FLTSourceType)type - camera:(FLTSourceCamera)camera { - FLTSourceSpecification* pigeonResult = [[FLTSourceSpecification alloc] init]; ++ (instancetype)makeWithType:(FLTSourceType)type camera:(FLTSourceCamera)camera { + FLTSourceSpecification *pigeonResult = [[FLTSourceSpecification alloc] init]; pigeonResult.type = type; pigeonResult.camera = camera; return pigeonResult; @@ -77,25 +76,24 @@ + (FLTSourceSpecification *)fromMap:(NSDictionary *)dict { return pigeonResult; } - (NSDictionary *)toMap { - return [NSDictionary dictionaryWithObjectsAndKeys:@(self.type), @"type", @(self.camera), @"camera", nil]; + return [NSDictionary + dictionaryWithObjectsAndKeys:@(self.type), @"type", @(self.camera), @"camera", nil]; } @end @interface FLTImagePickerApiCodecReader : FlutterStandardReader @end @implementation FLTImagePickerApiCodecReader -- (nullable id)readValueOfType:(UInt8)type -{ +- (nullable id)readValueOfType:(UInt8)type { switch (type) { - case 128: + case 128: return [FLTMaxSize fromMap:[self readValue]]; - - case 129: + + case 129: return [FLTSourceSpecification fromMap:[self readValue]]; - - default: + + default: return [super readValueOfType:type]; - } } @end @@ -103,17 +101,14 @@ - (nullable id)readValueOfType:(UInt8)type @interface FLTImagePickerApiCodecWriter : FlutterStandardWriter @end @implementation FLTImagePickerApiCodecWriter -- (void)writeValue:(id)value -{ +- (void)writeValue:(id)value { if ([value isKindOfClass:[FLTMaxSize class]]) { [self writeByte:128]; [self writeValue:[value toMap]]; - } else - if ([value isKindOfClass:[FLTSourceSpecification class]]) { + } else if ([value isKindOfClass:[FLTSourceSpecification class]]) { [self writeByte:129]; [self writeValue:[value toMap]]; - } else -{ + } else { [super writeValue:value]; } } @@ -134,76 +129,90 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { static dispatch_once_t sPred = 0; static FlutterStandardMessageCodec *sSharedObject = nil; dispatch_once(&sPred, ^{ - FLTImagePickerApiCodecReaderWriter *readerWriter = [[FLTImagePickerApiCodecReaderWriter alloc] init]; + FLTImagePickerApiCodecReaderWriter *readerWriter = + [[FLTImagePickerApiCodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } - -void FLTImagePickerApiSetup(id binaryMessenger, NSObject *api) { +void FLTImagePickerApiSetup(id binaryMessenger, + NSObject *api) { { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickImage" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickImage" binaryMessenger:binaryMessenger - codec:FLTImagePickerApiGetCodec() ]; + codec:FLTImagePickerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickImageWithSource:maxSize:quality:fullMetadata:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to @selector(pickImageWithSource:maxSize:quality:fullMetadata:completion:)", api); + NSCAssert([api respondsToSelector:@selector + (pickImageWithSource:maxSize:quality:fullMetadata:completion:)], + @"FLTImagePickerApi api (%@) doesn't respond to " + @"@selector(pickImageWithSource:maxSize:quality:fullMetadata:completion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTSourceSpecification *arg_source = GetNullableObjectAtIndex(args, 0); FLTMaxSize *arg_maxSize = GetNullableObjectAtIndex(args, 1); NSNumber *arg_imageQuality = GetNullableObjectAtIndex(args, 2); NSNumber *arg_requestFullMetadata = GetNullableObjectAtIndex(args, 3); - [api pickImageWithSource:arg_source maxSize:arg_maxSize quality:arg_imageQuality fullMetadata:arg_requestFullMetadata completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api pickImageWithSource:arg_source + maxSize:arg_maxSize + quality:arg_imageQuality + fullMetadata:arg_requestFullMetadata + completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; - } - else { + } else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickMultiImage" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickMultiImage" binaryMessenger:binaryMessenger - codec:FLTImagePickerApiGetCodec() ]; + codec:FLTImagePickerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickMultiImageWithMaxSize:quality:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to @selector(pickMultiImageWithMaxSize:quality:completion:)", api); + NSCAssert([api respondsToSelector:@selector(pickMultiImageWithMaxSize:quality:completion:)], + @"FLTImagePickerApi api (%@) doesn't respond to " + @"@selector(pickMultiImageWithMaxSize:quality:completion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTMaxSize *arg_maxSize = GetNullableObjectAtIndex(args, 0); NSNumber *arg_imageQuality = GetNullableObjectAtIndex(args, 1); - [api pickMultiImageWithMaxSize:arg_maxSize quality:arg_imageQuality completion:^(NSArray *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api pickMultiImageWithMaxSize:arg_maxSize + quality:arg_imageQuality + completion:^(NSArray *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; - } - else { + } else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickVideo" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickVideo" binaryMessenger:binaryMessenger - codec:FLTImagePickerApiGetCodec() ]; + codec:FLTImagePickerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickVideoWithSource:maxDuration:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to @selector(pickVideoWithSource:maxDuration:completion:)", api); + NSCAssert([api respondsToSelector:@selector(pickVideoWithSource:maxDuration:completion:)], + @"FLTImagePickerApi api (%@) doesn't respond to " + @"@selector(pickVideoWithSource:maxDuration:completion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTSourceSpecification *arg_source = GetNullableObjectAtIndex(args, 0); NSNumber *arg_maxDurationSeconds = GetNullableObjectAtIndex(args, 1); - [api pickVideoWithSource:arg_source maxDuration:arg_maxDurationSeconds completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api pickVideoWithSource:arg_source + maxDuration:arg_maxDurationSeconds + completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; - } - else { + } else { [channel setMessageHandler:nil]; } } diff --git a/packages/image_picker/image_picker_ios/lib/src/messages.g.dart b/packages/image_picker/image_picker_ios/lib/src/messages.g.dart index 842e2339d496..70509b062fd6 100644 --- a/packages/image_picker/image_picker_ios/lib/src/messages.g.dart +++ b/packages/image_picker/image_picker_ios/lib/src/messages.g.dart @@ -65,8 +65,7 @@ class SourceSpecification { static SourceSpecification decode(Object message) { final Map pigeonMap = message as Map; return SourceSpecification( - type: SourceType.values[pigeonMap['type']! as int] -, + type: SourceType.values[pigeonMap['type']! as int], camera: pigeonMap['camera'] != null ? SourceCamera.values[pigeonMap['camera']! as int] : null, @@ -81,27 +80,25 @@ class _ImagePickerApiCodec extends StandardMessageCodec { if (value is MaxSize) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else - if (value is SourceSpecification) { + } else if (value is SourceSpecification) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else -{ + } else { super.writeValue(buffer, value); } } + @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return MaxSize.decode(readValue(buffer)!); - - case 129: + + case 129: return SourceSpecification.decode(readValue(buffer)!); - - default: + + default: return super.readValueOfType(type, buffer); - } } } @@ -110,24 +107,32 @@ class ImagePickerApi { /// Constructor for [ImagePickerApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - ImagePickerApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; + ImagePickerApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; static const MessageCodec codec = _ImagePickerApiCodec(); - Future pickImage(SourceSpecification arg_source, MaxSize arg_maxSize, int? arg_imageQuality, bool arg_requestFullMetadata) async { + Future pickImage(SourceSpecification arg_source, MaxSize arg_maxSize, + int? arg_imageQuality, bool arg_requestFullMetadata) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickImage', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_source, arg_maxSize, arg_imageQuality, arg_requestFullMetadata]) as Map?; + 'dev.flutter.pigeon.ImagePickerApi.pickImage', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel.send([ + arg_source, + arg_maxSize, + arg_imageQuality, + arg_requestFullMetadata + ]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -138,18 +143,22 @@ class ImagePickerApi { } } - Future?> pickMultiImage(MaxSize arg_maxSize, int? arg_imageQuality) async { + Future?> pickMultiImage( + MaxSize arg_maxSize, int? arg_imageQuality) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec, binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec, + binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_maxSize, arg_imageQuality]) as Map?; + await channel.send([arg_maxSize, arg_imageQuality]) + as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -160,18 +169,22 @@ class ImagePickerApi { } } - Future pickVideo(SourceSpecification arg_source, int? arg_maxDurationSeconds) async { + Future pickVideo( + SourceSpecification arg_source, int? arg_maxDurationSeconds) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickVideo', codec, binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.ImagePickerApi.pickVideo', codec, + binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_source, arg_maxDurationSeconds]) as Map?; + await channel.send([arg_source, arg_maxDurationSeconds]) + as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, From 3b3435f6dc75e4099f116998f7c060c8ae3a75d9 Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Wed, 25 May 2022 13:04:15 +0200 Subject: [PATCH 03/11] PR remarks --- .../ios/Classes/FLTImagePickerPlugin.m | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m index 32b6d562ca58..139f63b15b76 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m @@ -119,8 +119,7 @@ - (void)launchPHPickerWithContext:(nonnull FLTImagePickerMethodCallContext *)con _pickerViewController.presentationController.delegate = self; self.callContext = context; - BOOL requestFullMetadata = context.requestFullMetadata; - if (requestFullMetadata) { + if (context.requestFullMetadata) { [self checkPhotoAuthorizationForAccessLevel]; } else { [self showPhotoLibraryWithPHPicker:_pickerViewController]; @@ -134,7 +133,6 @@ - (void)launchUIImagePickerWithSource:(nonnull FLTSourceSpecification *)source imagePickerController.delegate = self; imagePickerController.mediaTypes = @[ (NSString *)kUTTypeImage ]; self.callContext = context; - BOOL requestFullMetadata = context.requestFullMetadata; switch (source.type) { case FLTSourceTypeCamera: @@ -143,7 +141,7 @@ - (void)launchUIImagePickerWithSource:(nonnull FLTSourceSpecification *)source break; case FLTSourceTypeGallery: if (@available(iOS 11, *)) { - if (requestFullMetadata) { + if (context.requestFullMetadata) { [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; } else { [self showPhotoLibraryWithImagePicker:imagePickerController]; @@ -244,7 +242,6 @@ - (void)pickVideoWithSource:(nonnull FLTSourceSpecification *)source } self.callContext = context; - BOOL requestFullMetadata = context.requestFullMetadata; switch (source.type) { case FLTSourceTypeCamera: @@ -252,7 +249,7 @@ - (void)pickVideoWithSource:(nonnull FLTSourceSpecification *)source camera:[self cameraDeviceForSource:source]]; break; case FLTSourceTypeGallery: - if (requestFullMetadata) { + if (context.requestFullMetadata) { [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; } else { [self showPhotoLibraryWithImagePicker:imagePickerController]; @@ -575,11 +572,10 @@ - (void)imagePickerController:(UIImagePickerController *)picker NSNumber *maxHeight = self.callContext.maxSize.height; NSNumber *imageQuality = self.callContext.imageQuality; NSNumber *desiredImageQuality = [self getDesiredImageQuality:imageQuality]; - BOOL requestFullMetadata = _callContext.requestFullMetadata; PHAsset *originalAsset; - if (requestFullMetadata) { - // Full metadata are available only in PHAsset, which requires gallery permission + if (_callContext.requestFullMetadata) { + // Full metadata are available only in PHAsset, which requires gallery permission. originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromImagePickerInfo:info]; } From 420a4c4b5dff6f26392beb224b9538d41840cb2f Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Wed, 25 May 2022 13:43:47 +0200 Subject: [PATCH 04/11] PR remarks --- .../ios/RunnerTests/ImagePickerPluginTests.m | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m index e2311904f286..a49a6682923a 100644 --- a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m @@ -179,25 +179,22 @@ - (void)testPickMultiImageShouldUseUIImagePickerControllerOnPreiOS14 { [mockUIImagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]); } -- (void)testPickImageWithoutFullMetadataPreiOS14 { - if (@available(iOS 14, *)) { - return; - } +- (void)testPickImageWithoutFullMetadata API_AVAILABLE(ios(11)) { id mockUIImagePicker = OCMClassMock([UIImagePickerController class]); + id photoLibrary = OCMClassMock([PHPhotoLibrary class]); + FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; [plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]]; - FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickImage" - arguments:@{ - @"source" : @(1), - @"requestFullMetadata" : @(NO), - }]; - [plugin handleMethodCall:call - result:^(id _Nullable r){ - }]; + [plugin pickImageWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeGallery + camera:FLTSourceCameraFront] + maxSize:[[FLTMaxSize alloc] init] + quality:nil + fullMetadata:@(NO) + completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ + }]; - OCMVerify(times(1), - [mockUIImagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]); + OCMVerify(times(0), [photoLibrary authorizationStatus]); } #pragma mark - Test camera devices, no op on simulators From b9c4cef93acbd2729f0ce325a80c8a9715915509 Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Fri, 29 Jul 2022 12:06:10 +0200 Subject: [PATCH 05/11] Updated iOS package with new interface method implementation --- .../image_picker_ios/ios/Classes/messages.g.h | 28 ++-- .../image_picker_ios/ios/Classes/messages.g.m | 140 +++++++++--------- .../lib/image_picker_ios.dart | 43 ++++-- .../image_picker_ios/lib/src/messages.g.dart | 65 ++++---- .../image_picker_ios/pigeons/messages.dart | 5 +- .../image_picker_ios/pubspec.yaml | 4 + .../image_picker_ios/test/test_api.dart | 90 +++++------ 7 files changed, 177 insertions(+), 198 deletions(-) diff --git a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h index 0d3e96c842bb..5f60c00fe2a5 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h +++ b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h @@ -25,15 +25,17 @@ typedef NS_ENUM(NSUInteger, FLTSourceType) { @class FLTSourceSpecification; @interface FLTMaxSize : NSObject -+ (instancetype)makeWithWidth:(nullable NSNumber *)width height:(nullable NSNumber *)height; -@property(nonatomic, strong, nullable) NSNumber *width; -@property(nonatomic, strong, nullable) NSNumber *height; ++ (instancetype)makeWithWidth:(nullable NSNumber *)width + height:(nullable NSNumber *)height; +@property(nonatomic, strong, nullable) NSNumber * width; +@property(nonatomic, strong, nullable) NSNumber * height; @end @interface FLTSourceSpecification : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithType:(FLTSourceType)type camera:(FLTSourceCamera)camera; ++ (instancetype)makeWithType:(FLTSourceType)type + camera:(FLTSourceCamera)camera; @property(nonatomic, assign) FLTSourceType type; @property(nonatomic, assign) FLTSourceCamera camera; @end @@ -42,21 +44,11 @@ typedef NS_ENUM(NSUInteger, FLTSourceType) { NSObject *FLTImagePickerApiGetCodec(void); @protocol FLTImagePickerApi -- (void)pickImageWithSource:(FLTSourceSpecification *)source - maxSize:(FLTMaxSize *)maxSize - quality:(nullable NSNumber *)imageQuality - fullMetadata:(NSNumber *)requestFullMetadata - completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; -- (void)pickMultiImageWithMaxSize:(FLTMaxSize *)maxSize - quality:(nullable NSNumber *)imageQuality - completion:(void (^)(NSArray *_Nullable, - FlutterError *_Nullable))completion; -- (void)pickVideoWithSource:(FLTSourceSpecification *)source - maxDuration:(nullable NSNumber *)maxDurationSeconds - completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)pickImageWithSource:(FLTSourceSpecification *)source maxSize:(FLTMaxSize *)maxSize quality:(nullable NSNumber *)imageQuality fullMetadata:(NSNumber *)requestFullMetadata completion:(void(^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)pickMultiImageWithMaxSize:(FLTMaxSize *)maxSize quality:(nullable NSNumber *)imageQuality fullMetadata:(NSNumber *)requestFullMetadata completion:(void(^)(NSArray *_Nullable, FlutterError *_Nullable))completion; +- (void)pickVideoWithSource:(FLTSourceSpecification *)source maxDuration:(nullable NSNumber *)maxDurationSeconds completion:(void(^)(NSString *_Nullable, FlutterError *_Nullable))completion; @end -extern void FLTImagePickerApiSetup(id binaryMessenger, - NSObject *_Nullable api); +extern void FLTImagePickerApiSetup(id binaryMessenger, NSObject *_Nullable api); NS_ASSUME_NONNULL_END diff --git a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m index b13601480aea..977af120458d 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m @@ -14,25 +14,26 @@ NSDictionary *errorDict = (NSDictionary *)[NSNull null]; if (error) { errorDict = @{ - @"code" : (error.code ? error.code : [NSNull null]), - @"message" : (error.message ? error.message : [NSNull null]), - @"details" : (error.details ? error.details : [NSNull null]), - }; + @"code": (error.code ? error.code : [NSNull null]), + @"message": (error.message ? error.message : [NSNull null]), + @"details": (error.details ? error.details : [NSNull null]), + }; } return @{ - @"result" : (result ? result : [NSNull null]), - @"error" : errorDict, - }; + @"result": (result ? result : [NSNull null]), + @"error": errorDict, + }; } -static id GetNullableObject(NSDictionary *dict, id key) { +static id GetNullableObject(NSDictionary* dict, id key) { id result = dict[key]; return (result == [NSNull null]) ? nil : result; } -static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { +static id GetNullableObjectAtIndex(NSArray* array, NSInteger key) { id result = array[key]; return (result == [NSNull null]) ? nil : result; } + @interface FLTMaxSize () + (FLTMaxSize *)fromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @@ -43,8 +44,9 @@ - (NSDictionary *)toMap; @end @implementation FLTMaxSize -+ (instancetype)makeWithWidth:(nullable NSNumber *)width height:(nullable NSNumber *)height { - FLTMaxSize *pigeonResult = [[FLTMaxSize alloc] init]; ++ (instancetype)makeWithWidth:(nullable NSNumber *)width + height:(nullable NSNumber *)height { + FLTMaxSize* pigeonResult = [[FLTMaxSize alloc] init]; pigeonResult.width = width; pigeonResult.height = height; return pigeonResult; @@ -56,15 +58,14 @@ + (FLTMaxSize *)fromMap:(NSDictionary *)dict { return pigeonResult; } - (NSDictionary *)toMap { - return [NSDictionary - dictionaryWithObjectsAndKeys:(self.width ? self.width : [NSNull null]), @"width", - (self.height ? self.height : [NSNull null]), @"height", nil]; + return [NSDictionary dictionaryWithObjectsAndKeys:(self.width ? self.width : [NSNull null]), @"width", (self.height ? self.height : [NSNull null]), @"height", nil]; } @end @implementation FLTSourceSpecification -+ (instancetype)makeWithType:(FLTSourceType)type camera:(FLTSourceCamera)camera { - FLTSourceSpecification *pigeonResult = [[FLTSourceSpecification alloc] init]; ++ (instancetype)makeWithType:(FLTSourceType)type + camera:(FLTSourceCamera)camera { + FLTSourceSpecification* pigeonResult = [[FLTSourceSpecification alloc] init]; pigeonResult.type = type; pigeonResult.camera = camera; return pigeonResult; @@ -76,24 +77,25 @@ + (FLTSourceSpecification *)fromMap:(NSDictionary *)dict { return pigeonResult; } - (NSDictionary *)toMap { - return [NSDictionary - dictionaryWithObjectsAndKeys:@(self.type), @"type", @(self.camera), @"camera", nil]; + return [NSDictionary dictionaryWithObjectsAndKeys:@(self.type), @"type", @(self.camera), @"camera", nil]; } @end @interface FLTImagePickerApiCodecReader : FlutterStandardReader @end @implementation FLTImagePickerApiCodecReader -- (nullable id)readValueOfType:(UInt8)type { +- (nullable id)readValueOfType:(UInt8)type +{ switch (type) { - case 128: + case 128: return [FLTMaxSize fromMap:[self readValue]]; - - case 129: + + case 129: return [FLTSourceSpecification fromMap:[self readValue]]; - - default: + + default: return [super readValueOfType:type]; + } } @end @@ -101,14 +103,17 @@ - (nullable id)readValueOfType:(UInt8)type { @interface FLTImagePickerApiCodecWriter : FlutterStandardWriter @end @implementation FLTImagePickerApiCodecWriter -- (void)writeValue:(id)value { +- (void)writeValue:(id)value +{ if ([value isKindOfClass:[FLTMaxSize class]]) { [self writeByte:128]; [self writeValue:[value toMap]]; - } else if ([value isKindOfClass:[FLTSourceSpecification class]]) { + } else + if ([value isKindOfClass:[FLTSourceSpecification class]]) { [self writeByte:129]; [self writeValue:[value toMap]]; - } else { + } else +{ [super writeValue:value]; } } @@ -129,90 +134,77 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { static dispatch_once_t sPred = 0; static FlutterStandardMessageCodec *sSharedObject = nil; dispatch_once(&sPred, ^{ - FLTImagePickerApiCodecReaderWriter *readerWriter = - [[FLTImagePickerApiCodecReaderWriter alloc] init]; + FLTImagePickerApiCodecReaderWriter *readerWriter = [[FLTImagePickerApiCodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } -void FLTImagePickerApiSetup(id binaryMessenger, - NSObject *api) { + +void FLTImagePickerApiSetup(id binaryMessenger, NSObject *api) { { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickImage" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickImage" binaryMessenger:binaryMessenger - codec:FLTImagePickerApiGetCodec()]; + codec:FLTImagePickerApiGetCodec() ]; if (api) { - NSCAssert([api respondsToSelector:@selector - (pickImageWithSource:maxSize:quality:fullMetadata:completion:)], - @"FLTImagePickerApi api (%@) doesn't respond to " - @"@selector(pickImageWithSource:maxSize:quality:fullMetadata:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(pickImageWithSource:maxSize:quality:fullMetadata:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to @selector(pickImageWithSource:maxSize:quality:fullMetadata:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTSourceSpecification *arg_source = GetNullableObjectAtIndex(args, 0); FLTMaxSize *arg_maxSize = GetNullableObjectAtIndex(args, 1); NSNumber *arg_imageQuality = GetNullableObjectAtIndex(args, 2); NSNumber *arg_requestFullMetadata = GetNullableObjectAtIndex(args, 3); - [api pickImageWithSource:arg_source - maxSize:arg_maxSize - quality:arg_imageQuality - fullMetadata:arg_requestFullMetadata - completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api pickImageWithSource:arg_source maxSize:arg_maxSize quality:arg_imageQuality fullMetadata:arg_requestFullMetadata completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; - } else { + } + else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickMultiImage" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickMultiImage" binaryMessenger:binaryMessenger - codec:FLTImagePickerApiGetCodec()]; + codec:FLTImagePickerApiGetCodec() ]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickMultiImageWithMaxSize:quality:completion:)], - @"FLTImagePickerApi api (%@) doesn't respond to " - @"@selector(pickMultiImageWithMaxSize:quality:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(pickMultiImageWithMaxSize:quality:fullMetadata:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to @selector(pickMultiImageWithMaxSize:quality:fullMetadata:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTMaxSize *arg_maxSize = GetNullableObjectAtIndex(args, 0); NSNumber *arg_imageQuality = GetNullableObjectAtIndex(args, 1); - [api pickMultiImageWithMaxSize:arg_maxSize - quality:arg_imageQuality - completion:^(NSArray *_Nullable output, - FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + NSNumber *arg_requestFullMetadata = GetNullableObjectAtIndex(args, 2); + [api pickMultiImageWithMaxSize:arg_maxSize quality:arg_imageQuality fullMetadata:arg_requestFullMetadata completion:^(NSArray *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; - } else { + } + else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickVideo" + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickVideo" binaryMessenger:binaryMessenger - codec:FLTImagePickerApiGetCodec()]; + codec:FLTImagePickerApiGetCodec() ]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickVideoWithSource:maxDuration:completion:)], - @"FLTImagePickerApi api (%@) doesn't respond to " - @"@selector(pickVideoWithSource:maxDuration:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(pickVideoWithSource:maxDuration:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to @selector(pickVideoWithSource:maxDuration:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTSourceSpecification *arg_source = GetNullableObjectAtIndex(args, 0); NSNumber *arg_maxDurationSeconds = GetNullableObjectAtIndex(args, 1); - [api pickVideoWithSource:arg_source - maxDuration:arg_maxDurationSeconds - completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api pickVideoWithSource:arg_source maxDuration:arg_maxDurationSeconds completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; - } else { + } + else { [channel setMessageHandler:nil]; } } diff --git a/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart b/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart index a5575cf5ddfa..aab18cde3f30 100644 --- a/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart +++ b/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart @@ -80,9 +80,13 @@ class ImagePickerIOS extends ImagePickerPlatform { int? imageQuality, }) async { final List? paths = await _pickMultiImageAsPath( - maxWidth: maxWidth, - maxHeight: maxHeight, - imageQuality: imageQuality, + options: MultiImagePickerOptions( + imageOptions: ImageOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + ), + ), ); if (paths == null) { return null; @@ -91,20 +95,33 @@ class ImagePickerIOS extends ImagePickerPlatform { return paths.map((dynamic path) => PickedFile(path as String)).toList(); } + @override + Future> getMultiImageWithOptions({ + MultiImagePickerOptions options = const MultiImagePickerOptions(), + }) async { + final List? paths = await _pickMultiImageAsPath(options: options); + if (paths == null) { + return []; + } + + return paths.map((String path) => XFile(path)).toList(); + } + Future?> _pickMultiImageAsPath({ - double? maxWidth, - double? maxHeight, - int? imageQuality, + MultiImagePickerOptions options = const MultiImagePickerOptions(), }) async { + final int? imageQuality = options.imageOptions.imageQuality; if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { throw ArgumentError.value( imageQuality, 'imageQuality', 'must be between 0 and 100'); } + final double? maxWidth = options.imageOptions.maxWidth; if (maxWidth != null && maxWidth < 0) { throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative'); } + final double? maxHeight = options.imageOptions.maxHeight; if (maxHeight != null && maxHeight < 0) { throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative'); } @@ -112,7 +129,9 @@ class ImagePickerIOS extends ImagePickerPlatform { // TODO(stuartmorgan): Remove the cast once Pigeon supports non-nullable // generics, https://github.com/flutter/flutter/issues/97848 return (await _hostApi.pickMultiImage( - MaxSize(width: maxWidth, height: maxHeight), imageQuality)) + MaxSize(width: maxWidth, height: maxHeight), + imageQuality, + options.imageOptions.requestFullMetadata)) ?.cast(); } @@ -200,9 +219,13 @@ class ImagePickerIOS extends ImagePickerPlatform { int? imageQuality, }) async { final List? paths = await _pickMultiImageAsPath( - maxWidth: maxWidth, - maxHeight: maxHeight, - imageQuality: imageQuality, + options: MultiImagePickerOptions( + imageOptions: ImageOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + ), + ), ); if (paths == null) { return null; diff --git a/packages/image_picker/image_picker_ios/lib/src/messages.g.dart b/packages/image_picker/image_picker_ios/lib/src/messages.g.dart index 70509b062fd6..cd90131b8207 100644 --- a/packages/image_picker/image_picker_ios/lib/src/messages.g.dart +++ b/packages/image_picker/image_picker_ios/lib/src/messages.g.dart @@ -65,7 +65,8 @@ class SourceSpecification { static SourceSpecification decode(Object message) { final Map pigeonMap = message as Map; return SourceSpecification( - type: SourceType.values[pigeonMap['type']! as int], + type: SourceType.values[pigeonMap['type']! as int] +, camera: pigeonMap['camera'] != null ? SourceCamera.values[pigeonMap['camera']! as int] : null, @@ -80,25 +81,27 @@ class _ImagePickerApiCodec extends StandardMessageCodec { if (value is MaxSize) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is SourceSpecification) { + } else + if (value is SourceSpecification) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else { + } else +{ super.writeValue(buffer, value); } } - @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return MaxSize.decode(readValue(buffer)!); - - case 129: + + case 129: return SourceSpecification.decode(readValue(buffer)!); - - default: + + default: return super.readValueOfType(type, buffer); + } } } @@ -107,32 +110,24 @@ class ImagePickerApi { /// Constructor for [ImagePickerApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - ImagePickerApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; + ImagePickerApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; static const MessageCodec codec = _ImagePickerApiCodec(); - Future pickImage(SourceSpecification arg_source, MaxSize arg_maxSize, - int? arg_imageQuality, bool arg_requestFullMetadata) async { + Future pickImage(SourceSpecification arg_source, MaxSize arg_maxSize, int? arg_imageQuality, bool arg_requestFullMetadata) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickImage', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel.send([ - arg_source, - arg_maxSize, - arg_imageQuality, - arg_requestFullMetadata - ]) as Map?; + 'dev.flutter.pigeon.ImagePickerApi.pickImage', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_source, arg_maxSize, arg_imageQuality, arg_requestFullMetadata]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -143,22 +138,18 @@ class ImagePickerApi { } } - Future?> pickMultiImage( - MaxSize arg_maxSize, int? arg_imageQuality) async { + Future?> pickMultiImage(MaxSize arg_maxSize, int? arg_imageQuality, bool arg_requestFullMetadata) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_maxSize, arg_imageQuality]) - as Map?; + await channel.send([arg_maxSize, arg_imageQuality, arg_requestFullMetadata]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -169,22 +160,18 @@ class ImagePickerApi { } } - Future pickVideo( - SourceSpecification arg_source, int? arg_maxDurationSeconds) async { + Future pickVideo(SourceSpecification arg_source, int? arg_maxDurationSeconds) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickVideo', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.ImagePickerApi.pickVideo', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_source, arg_maxDurationSeconds]) - as Map?; + await channel.send([arg_source, arg_maxDurationSeconds]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, diff --git a/packages/image_picker/image_picker_ios/pigeons/messages.dart b/packages/image_picker/image_picker_ios/pigeons/messages.dart index f6bff83959cf..dd8a0f0c0834 100644 --- a/packages/image_picker/image_picker_ios/pigeons/messages.dart +++ b/packages/image_picker/image_picker_ios/pigeons/messages.dart @@ -39,8 +39,9 @@ abstract class ImagePickerApi { String? pickImage(SourceSpecification source, MaxSize maxSize, int? imageQuality, bool requestFullMetadata); @async - @ObjCSelector('pickMultiImageWithMaxSize:quality:') - List? pickMultiImage(MaxSize maxSize, int? imageQuality); + @ObjCSelector('pickMultiImageWithMaxSize:quality:fullMetadata:') + List? pickMultiImage( + MaxSize maxSize, int? imageQuality, bool requestFullMetadata); @async @ObjCSelector('pickVideoWithSource:maxDuration:') String? pickVideo(SourceSpecification source, int? maxDurationSeconds); diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index 4175749a2d70..543e23dfa840 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -26,3 +26,7 @@ dev_dependencies: sdk: flutter mockito: ^5.0.0 pigeon: ^3.0.2 + +dependency_overrides: + image_picker_platform_interface: + path: ../image_picker_platform_interface \ No newline at end of file diff --git a/packages/image_picker/image_picker_ios/test/test_api.dart b/packages/image_picker/image_picker_ios/test/test_api.dart index 980c3bc0aff1..fe60db233358 100644 --- a/packages/image_picker/image_picker_ios/test/test_api.dart +++ b/packages/image_picker/image_picker_ios/test/test_api.dart @@ -6,125 +6,105 @@ // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis // ignore_for_file: avoid_relative_lib_imports // @dart = 2.12 +import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; -// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) -// ignore: unnecessary_import import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:image_picker_ios/src/messages.g.dart'; + +import '../lib/src/messages.g.dart'; class _TestHostImagePickerApiCodec extends StandardMessageCodec { const _TestHostImagePickerApiCodec(); - @override void writeValue(WriteBuffer buffer, Object? value) { if (value is MaxSize) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is SourceSpecification) { + } else + if (value is SourceSpecification) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else { + } else +{ super.writeValue(buffer, value); } } - @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return MaxSize.decode(readValue(buffer)!); - - case 129: + + case 129: return SourceSpecification.decode(readValue(buffer)!); - - default: + + default: return super.readValueOfType(type, buffer); + } } } - abstract class TestHostImagePickerApi { static const MessageCodec codec = _TestHostImagePickerApiCodec(); - Future pickImage(SourceSpecification source, MaxSize maxSize, - int? imageQuality, bool requestFullMetadata); - - Future?> pickMultiImage(MaxSize maxSize, int? imageQuality); - - Future pickVideo( - SourceSpecification source, int? maxDurationSeconds); - - static void setup(TestHostImagePickerApi? api, - {BinaryMessenger? binaryMessenger}) { + Future pickImage(SourceSpecification source, MaxSize maxSize, int? imageQuality, bool requestFullMetadata); + Future?> pickMultiImage(MaxSize maxSize, int? imageQuality, bool requestFullMetadata); + Future pickVideo(SourceSpecification source, int? maxDurationSeconds); + static void setup(TestHostImagePickerApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickImage', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.ImagePickerApi.pickImage', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null.'); final List args = (message as List?)!; - final SourceSpecification? arg_source = - (args[0] as SourceSpecification?); - assert(arg_source != null, - 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null SourceSpecification.'); + final SourceSpecification? arg_source = (args[0] as SourceSpecification?); + assert(arg_source != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null SourceSpecification.'); final MaxSize? arg_maxSize = (args[1] as MaxSize?); - assert(arg_maxSize != null, - 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null MaxSize.'); + assert(arg_maxSize != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null MaxSize.'); final int? arg_imageQuality = (args[2] as int?); final bool? arg_requestFullMetadata = (args[3] as bool?); - assert(arg_requestFullMetadata != null, - 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null bool.'); - final String? output = await api.pickImage(arg_source!, arg_maxSize!, - arg_imageQuality, arg_requestFullMetadata!); + assert(arg_requestFullMetadata != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null bool.'); + final String? output = await api.pickImage(arg_source!, arg_maxSize!, arg_imageQuality, arg_requestFullMetadata!); return {'result': output}; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null.'); final List args = (message as List?)!; final MaxSize? arg_maxSize = (args[0] as MaxSize?); - assert(arg_maxSize != null, - 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null, expected non-null MaxSize.'); + assert(arg_maxSize != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null, expected non-null MaxSize.'); final int? arg_imageQuality = (args[1] as int?); - final List? output = - await api.pickMultiImage(arg_maxSize!, arg_imageQuality); + final bool? arg_requestFullMetadata = (args[2] as bool?); + assert(arg_requestFullMetadata != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null, expected non-null bool.'); + final List? output = await api.pickMultiImage(arg_maxSize!, arg_imageQuality, arg_requestFullMetadata!); return {'result': output}; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickVideo', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.ImagePickerApi.pickVideo', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.ImagePickerApi.pickVideo was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickVideo was null.'); final List args = (message as List?)!; - final SourceSpecification? arg_source = - (args[0] as SourceSpecification?); - assert(arg_source != null, - 'Argument for dev.flutter.pigeon.ImagePickerApi.pickVideo was null, expected non-null SourceSpecification.'); + final SourceSpecification? arg_source = (args[0] as SourceSpecification?); + assert(arg_source != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickVideo was null, expected non-null SourceSpecification.'); final int? arg_maxDurationSeconds = (args[1] as int?); - final String? output = - await api.pickVideo(arg_source!, arg_maxDurationSeconds); + final String? output = await api.pickVideo(arg_source!, arg_maxDurationSeconds); return {'result': output}; }); } From b3d81e3edfb6e91e24af899122a5b75cc11b4c1c Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Fri, 29 Jul 2022 12:58:24 +0200 Subject: [PATCH 06/11] Implemented native part of picking multi images without full metadata --- .../ios/RunnerTests/ImagePickerPluginTests.m | 18 ++ .../image_picker_ios/example/pubspec.yaml | 6 +- .../ios/Classes/FLTImagePickerPlugin.m | 2 + .../image_picker_ios/ios/Classes/messages.g.h | 29 ++- .../image_picker_ios/ios/Classes/messages.g.m | 141 ++++++----- .../image_picker_ios/lib/src/messages.g.dart | 67 +++--- .../test/image_picker_ios_test.dart | 224 +++++++++++++++++- .../image_picker_ios/test/test_api.dart | 87 ++++--- 8 files changed, 436 insertions(+), 138 deletions(-) diff --git a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m index a49a6682923a..e93e203c71ea 100644 --- a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m @@ -172,6 +172,7 @@ - (void)testPickMultiImageShouldUseUIImagePickerControllerOnPreiOS14 { [plugin pickMultiImageWithMaxSize:[FLTMaxSize makeWithWidth:@(100) height:@(200)] quality:@(50) + fullMetadata:@(YES) completion:^(NSArray *_Nullable result, FlutterError *_Nullable error){ }]; @@ -197,6 +198,23 @@ - (void)testPickImageWithoutFullMetadata API_AVAILABLE(ios(11)) { OCMVerify(times(0), [photoLibrary authorizationStatus]); } +- (void)testPickMultiImageWithoutFullMetadata API_AVAILABLE(ios(11)) { + id mockUIImagePicker = OCMClassMock([UIImagePickerController class]); + id photoLibrary = OCMClassMock([PHPhotoLibrary class]); + + FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + [plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]]; + + [plugin pickMultiImageWithMaxSize:[[FLTMaxSize alloc] init] + quality:nil + fullMetadata:@(NO) + completion:^(NSArray *_Nullable result, + FlutterError *_Nullable error){ + }]; + + OCMVerify(times(0), [photoLibrary authorizationStatus]); +} + #pragma mark - Test camera devices, no op on simulators - (void)testPluginPickImageDeviceCancelClickMultipleTimes { diff --git a/packages/image_picker/image_picker_ios/example/pubspec.yaml b/packages/image_picker/image_picker_ios/example/pubspec.yaml index 40d5a60cad5b..895bf04d5775 100755 --- a/packages/image_picker/image_picker_ios/example/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/example/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - image_picker_platform_interface: ^2.5.0 + image_picker_platform_interface: ^2.6.0 video_player: ^2.1.4 dev_dependencies: @@ -27,3 +27,7 @@ dev_dependencies: flutter: uses-material-design: true + +dependency_overrides: + image_picker_platform_interface: + path: ../../image_picker_platform_interface \ No newline at end of file diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m index 139f63b15b76..97725e56a83e 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m @@ -195,12 +195,14 @@ - (void)pickImageWithSource:(nonnull FLTSourceSpecification *)source - (void)pickMultiImageWithMaxSize:(nonnull FLTMaxSize *)maxSize quality:(nullable NSNumber *)imageQuality + fullMetadata:(NSNumber *)fullMetadata completion:(nonnull void (^)(NSArray *_Nullable, FlutterError *_Nullable))completion { FLTImagePickerMethodCallContext *context = [[FLTImagePickerMethodCallContext alloc] initWithResult:completion]; context.maxSize = maxSize; context.imageQuality = imageQuality; + context.requestFullMetadata = [fullMetadata boolValue]; if (@available(iOS 14, *)) { [self launchPHPickerWithContext:context]; diff --git a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h index 5f60c00fe2a5..c87bda59d8fb 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h +++ b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h @@ -25,17 +25,15 @@ typedef NS_ENUM(NSUInteger, FLTSourceType) { @class FLTSourceSpecification; @interface FLTMaxSize : NSObject -+ (instancetype)makeWithWidth:(nullable NSNumber *)width - height:(nullable NSNumber *)height; -@property(nonatomic, strong, nullable) NSNumber * width; -@property(nonatomic, strong, nullable) NSNumber * height; ++ (instancetype)makeWithWidth:(nullable NSNumber *)width height:(nullable NSNumber *)height; +@property(nonatomic, strong, nullable) NSNumber *width; +@property(nonatomic, strong, nullable) NSNumber *height; @end @interface FLTSourceSpecification : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithType:(FLTSourceType)type - camera:(FLTSourceCamera)camera; ++ (instancetype)makeWithType:(FLTSourceType)type camera:(FLTSourceCamera)camera; @property(nonatomic, assign) FLTSourceType type; @property(nonatomic, assign) FLTSourceCamera camera; @end @@ -44,11 +42,22 @@ typedef NS_ENUM(NSUInteger, FLTSourceType) { NSObject *FLTImagePickerApiGetCodec(void); @protocol FLTImagePickerApi -- (void)pickImageWithSource:(FLTSourceSpecification *)source maxSize:(FLTMaxSize *)maxSize quality:(nullable NSNumber *)imageQuality fullMetadata:(NSNumber *)requestFullMetadata completion:(void(^)(NSString *_Nullable, FlutterError *_Nullable))completion; -- (void)pickMultiImageWithMaxSize:(FLTMaxSize *)maxSize quality:(nullable NSNumber *)imageQuality fullMetadata:(NSNumber *)requestFullMetadata completion:(void(^)(NSArray *_Nullable, FlutterError *_Nullable))completion; -- (void)pickVideoWithSource:(FLTSourceSpecification *)source maxDuration:(nullable NSNumber *)maxDurationSeconds completion:(void(^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)pickImageWithSource:(FLTSourceSpecification *)source + maxSize:(FLTMaxSize *)maxSize + quality:(nullable NSNumber *)imageQuality + fullMetadata:(NSNumber *)requestFullMetadata + completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)pickMultiImageWithMaxSize:(FLTMaxSize *)maxSize + quality:(nullable NSNumber *)imageQuality + fullMetadata:(NSNumber *)requestFullMetadata + completion:(void (^)(NSArray *_Nullable, + FlutterError *_Nullable))completion; +- (void)pickVideoWithSource:(FLTSourceSpecification *)source + maxDuration:(nullable NSNumber *)maxDurationSeconds + completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; @end -extern void FLTImagePickerApiSetup(id binaryMessenger, NSObject *_Nullable api); +extern void FLTImagePickerApiSetup(id binaryMessenger, + NSObject *_Nullable api); NS_ASSUME_NONNULL_END diff --git a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m index 977af120458d..71a5b5140417 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m @@ -14,26 +14,25 @@ NSDictionary *errorDict = (NSDictionary *)[NSNull null]; if (error) { errorDict = @{ - @"code": (error.code ? error.code : [NSNull null]), - @"message": (error.message ? error.message : [NSNull null]), - @"details": (error.details ? error.details : [NSNull null]), - }; + @"code" : (error.code ? error.code : [NSNull null]), + @"message" : (error.message ? error.message : [NSNull null]), + @"details" : (error.details ? error.details : [NSNull null]), + }; } return @{ - @"result": (result ? result : [NSNull null]), - @"error": errorDict, - }; + @"result" : (result ? result : [NSNull null]), + @"error" : errorDict, + }; } -static id GetNullableObject(NSDictionary* dict, id key) { +static id GetNullableObject(NSDictionary *dict, id key) { id result = dict[key]; return (result == [NSNull null]) ? nil : result; } -static id GetNullableObjectAtIndex(NSArray* array, NSInteger key) { +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { id result = array[key]; return (result == [NSNull null]) ? nil : result; } - @interface FLTMaxSize () + (FLTMaxSize *)fromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @@ -44,9 +43,8 @@ - (NSDictionary *)toMap; @end @implementation FLTMaxSize -+ (instancetype)makeWithWidth:(nullable NSNumber *)width - height:(nullable NSNumber *)height { - FLTMaxSize* pigeonResult = [[FLTMaxSize alloc] init]; ++ (instancetype)makeWithWidth:(nullable NSNumber *)width height:(nullable NSNumber *)height { + FLTMaxSize *pigeonResult = [[FLTMaxSize alloc] init]; pigeonResult.width = width; pigeonResult.height = height; return pigeonResult; @@ -58,14 +56,15 @@ + (FLTMaxSize *)fromMap:(NSDictionary *)dict { return pigeonResult; } - (NSDictionary *)toMap { - return [NSDictionary dictionaryWithObjectsAndKeys:(self.width ? self.width : [NSNull null]), @"width", (self.height ? self.height : [NSNull null]), @"height", nil]; + return [NSDictionary + dictionaryWithObjectsAndKeys:(self.width ? self.width : [NSNull null]), @"width", + (self.height ? self.height : [NSNull null]), @"height", nil]; } @end @implementation FLTSourceSpecification -+ (instancetype)makeWithType:(FLTSourceType)type - camera:(FLTSourceCamera)camera { - FLTSourceSpecification* pigeonResult = [[FLTSourceSpecification alloc] init]; ++ (instancetype)makeWithType:(FLTSourceType)type camera:(FLTSourceCamera)camera { + FLTSourceSpecification *pigeonResult = [[FLTSourceSpecification alloc] init]; pigeonResult.type = type; pigeonResult.camera = camera; return pigeonResult; @@ -77,25 +76,24 @@ + (FLTSourceSpecification *)fromMap:(NSDictionary *)dict { return pigeonResult; } - (NSDictionary *)toMap { - return [NSDictionary dictionaryWithObjectsAndKeys:@(self.type), @"type", @(self.camera), @"camera", nil]; + return [NSDictionary + dictionaryWithObjectsAndKeys:@(self.type), @"type", @(self.camera), @"camera", nil]; } @end @interface FLTImagePickerApiCodecReader : FlutterStandardReader @end @implementation FLTImagePickerApiCodecReader -- (nullable id)readValueOfType:(UInt8)type -{ +- (nullable id)readValueOfType:(UInt8)type { switch (type) { - case 128: + case 128: return [FLTMaxSize fromMap:[self readValue]]; - - case 129: + + case 129: return [FLTSourceSpecification fromMap:[self readValue]]; - - default: + + default: return [super readValueOfType:type]; - } } @end @@ -103,17 +101,14 @@ - (nullable id)readValueOfType:(UInt8)type @interface FLTImagePickerApiCodecWriter : FlutterStandardWriter @end @implementation FLTImagePickerApiCodecWriter -- (void)writeValue:(id)value -{ +- (void)writeValue:(id)value { if ([value isKindOfClass:[FLTMaxSize class]]) { [self writeByte:128]; [self writeValue:[value toMap]]; - } else - if ([value isKindOfClass:[FLTSourceSpecification class]]) { + } else if ([value isKindOfClass:[FLTSourceSpecification class]]) { [self writeByte:129]; [self writeValue:[value toMap]]; - } else -{ + } else { [super writeValue:value]; } } @@ -134,77 +129,93 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { static dispatch_once_t sPred = 0; static FlutterStandardMessageCodec *sSharedObject = nil; dispatch_once(&sPred, ^{ - FLTImagePickerApiCodecReaderWriter *readerWriter = [[FLTImagePickerApiCodecReaderWriter alloc] init]; + FLTImagePickerApiCodecReaderWriter *readerWriter = + [[FLTImagePickerApiCodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } - -void FLTImagePickerApiSetup(id binaryMessenger, NSObject *api) { +void FLTImagePickerApiSetup(id binaryMessenger, + NSObject *api) { { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickImage" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickImage" binaryMessenger:binaryMessenger - codec:FLTImagePickerApiGetCodec() ]; + codec:FLTImagePickerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickImageWithSource:maxSize:quality:fullMetadata:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to @selector(pickImageWithSource:maxSize:quality:fullMetadata:completion:)", api); + NSCAssert([api respondsToSelector:@selector + (pickImageWithSource:maxSize:quality:fullMetadata:completion:)], + @"FLTImagePickerApi api (%@) doesn't respond to " + @"@selector(pickImageWithSource:maxSize:quality:fullMetadata:completion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTSourceSpecification *arg_source = GetNullableObjectAtIndex(args, 0); FLTMaxSize *arg_maxSize = GetNullableObjectAtIndex(args, 1); NSNumber *arg_imageQuality = GetNullableObjectAtIndex(args, 2); NSNumber *arg_requestFullMetadata = GetNullableObjectAtIndex(args, 3); - [api pickImageWithSource:arg_source maxSize:arg_maxSize quality:arg_imageQuality fullMetadata:arg_requestFullMetadata completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api pickImageWithSource:arg_source + maxSize:arg_maxSize + quality:arg_imageQuality + fullMetadata:arg_requestFullMetadata + completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; - } - else { + } else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickMultiImage" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickMultiImage" binaryMessenger:binaryMessenger - codec:FLTImagePickerApiGetCodec() ]; + codec:FLTImagePickerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickMultiImageWithMaxSize:quality:fullMetadata:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to @selector(pickMultiImageWithMaxSize:quality:fullMetadata:completion:)", api); + NSCAssert([api respondsToSelector:@selector + (pickMultiImageWithMaxSize:quality:fullMetadata:completion:)], + @"FLTImagePickerApi api (%@) doesn't respond to " + @"@selector(pickMultiImageWithMaxSize:quality:fullMetadata:completion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTMaxSize *arg_maxSize = GetNullableObjectAtIndex(args, 0); NSNumber *arg_imageQuality = GetNullableObjectAtIndex(args, 1); NSNumber *arg_requestFullMetadata = GetNullableObjectAtIndex(args, 2); - [api pickMultiImageWithMaxSize:arg_maxSize quality:arg_imageQuality fullMetadata:arg_requestFullMetadata completion:^(NSArray *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api pickMultiImageWithMaxSize:arg_maxSize + quality:arg_imageQuality + fullMetadata:arg_requestFullMetadata + completion:^(NSArray *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; - } - else { + } else { [channel setMessageHandler:nil]; } } { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickVideo" + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickVideo" binaryMessenger:binaryMessenger - codec:FLTImagePickerApiGetCodec() ]; + codec:FLTImagePickerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickVideoWithSource:maxDuration:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to @selector(pickVideoWithSource:maxDuration:completion:)", api); + NSCAssert([api respondsToSelector:@selector(pickVideoWithSource:maxDuration:completion:)], + @"FLTImagePickerApi api (%@) doesn't respond to " + @"@selector(pickVideoWithSource:maxDuration:completion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTSourceSpecification *arg_source = GetNullableObjectAtIndex(args, 0); NSNumber *arg_maxDurationSeconds = GetNullableObjectAtIndex(args, 1); - [api pickVideoWithSource:arg_source maxDuration:arg_maxDurationSeconds completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; + [api pickVideoWithSource:arg_source + maxDuration:arg_maxDurationSeconds + completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; }]; - } - else { + } else { [channel setMessageHandler:nil]; } } diff --git a/packages/image_picker/image_picker_ios/lib/src/messages.g.dart b/packages/image_picker/image_picker_ios/lib/src/messages.g.dart index cd90131b8207..5f8768ba8cc1 100644 --- a/packages/image_picker/image_picker_ios/lib/src/messages.g.dart +++ b/packages/image_picker/image_picker_ios/lib/src/messages.g.dart @@ -65,8 +65,7 @@ class SourceSpecification { static SourceSpecification decode(Object message) { final Map pigeonMap = message as Map; return SourceSpecification( - type: SourceType.values[pigeonMap['type']! as int] -, + type: SourceType.values[pigeonMap['type']! as int], camera: pigeonMap['camera'] != null ? SourceCamera.values[pigeonMap['camera']! as int] : null, @@ -81,27 +80,25 @@ class _ImagePickerApiCodec extends StandardMessageCodec { if (value is MaxSize) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else - if (value is SourceSpecification) { + } else if (value is SourceSpecification) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else -{ + } else { super.writeValue(buffer, value); } } + @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return MaxSize.decode(readValue(buffer)!); - - case 129: + + case 129: return SourceSpecification.decode(readValue(buffer)!); - - default: + + default: return super.readValueOfType(type, buffer); - } } } @@ -110,24 +107,32 @@ class ImagePickerApi { /// Constructor for [ImagePickerApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - ImagePickerApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; + ImagePickerApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; static const MessageCodec codec = _ImagePickerApiCodec(); - Future pickImage(SourceSpecification arg_source, MaxSize arg_maxSize, int? arg_imageQuality, bool arg_requestFullMetadata) async { + Future pickImage(SourceSpecification arg_source, MaxSize arg_maxSize, + int? arg_imageQuality, bool arg_requestFullMetadata) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickImage', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_source, arg_maxSize, arg_imageQuality, arg_requestFullMetadata]) as Map?; + 'dev.flutter.pigeon.ImagePickerApi.pickImage', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel.send([ + arg_source, + arg_maxSize, + arg_imageQuality, + arg_requestFullMetadata + ]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -138,18 +143,22 @@ class ImagePickerApi { } } - Future?> pickMultiImage(MaxSize arg_maxSize, int? arg_imageQuality, bool arg_requestFullMetadata) async { + Future?> pickMultiImage(MaxSize arg_maxSize, + int? arg_imageQuality, bool arg_requestFullMetadata) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_maxSize, arg_imageQuality, arg_requestFullMetadata]) as Map?; + 'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel.send( + [arg_maxSize, arg_imageQuality, arg_requestFullMetadata]) + as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -160,18 +169,22 @@ class ImagePickerApi { } } - Future pickVideo(SourceSpecification arg_source, int? arg_maxDurationSeconds) async { + Future pickVideo( + SourceSpecification arg_source, int? arg_maxDurationSeconds) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickVideo', codec, binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.ImagePickerApi.pickVideo', codec, + binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_source, arg_maxDurationSeconds]) as Map?; + await channel.send([arg_source, arg_maxDurationSeconds]) + as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = (replyMap['error'] as Map?)!; + final Map error = + (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, diff --git a/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart b/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart index 14d1d0be99e6..b20025770ad1 100644 --- a/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart +++ b/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart @@ -58,11 +58,15 @@ class _ApiLogger implements TestHostImagePickerApi { @override Future?> pickMultiImage( - MaxSize maxSize, int? imageQuality) async { + MaxSize maxSize, + int? imageQuality, + bool requestFullMetadata, + ) async { calls.add(_LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': maxSize.width, 'maxHeight': maxSize.height, 'imageQuality': imageQuality, + 'requestFullMetadata': requestFullMetadata, })); return returnValue as List?; } @@ -311,6 +315,7 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), ], ); @@ -351,42 +356,49 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': null, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': null, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': 70, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': 70, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': 70, + 'requestFullMetadata': true, }), ], ); @@ -743,6 +755,7 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), ], ); @@ -783,42 +796,49 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': null, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': null, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': 70, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': 70, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': 70, + 'requestFullMetadata': true, }), ], ); @@ -1233,4 +1253,206 @@ void main() { ); }); }); + + group('#getMultiImageWithOptions', () { + test('calls the method correctly', () async { + log.returnValue = ['0', '1']; + await picker.getMultiImageWithOptions(); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + log.returnValue = ['0', '1']; + await picker.getMultiImageWithOptions(); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxWidth: 10.0), + ), + ); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxHeight: 10.0), + ), + ); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxWidth: 10.0, maxHeight: 20.0), + ), + ); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxWidth: 10.0, imageQuality: 70), + ), + ); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxHeight: 10.0, imageQuality: 70), + ), + ); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ), + ), + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('does not accept a negative width or height argument', () { + log.returnValue = ['0', '1']; + expect( + () => picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxWidth: -1.0), + ), + ), + throwsArgumentError, + ); + + expect( + () => picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxHeight: -1.0), + ), + ), + throwsArgumentError, + ); + }); + + test('does not accept a invalid imageQuality argument', () { + log.returnValue = ['0', '1']; + expect( + () => picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(imageQuality: -1), + ), + ), + throwsArgumentError, + ); + + expect( + () => picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(imageQuality: 101), + ), + ), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + log.returnValue = null; + + expect(await picker.getMultiImageWithOptions(), isEmpty); + }); + + test('Request full metadata argument defaults to true', () async { + log.returnValue = ['0', '1']; + await picker.getMultiImageWithOptions(); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('Passes the request full metadata argument correctly', () async { + log.returnValue = ['0', '1']; + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(requestFullMetadata: false), + ), + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'requestFullMetadata': false, + }), + ], + ); + }); + }); } diff --git a/packages/image_picker/image_picker_ios/test/test_api.dart b/packages/image_picker/image_picker_ios/test/test_api.dart index fe60db233358..49859a849b71 100644 --- a/packages/image_picker/image_picker_ios/test/test_api.dart +++ b/packages/image_picker/image_picker_ios/test/test_api.dart @@ -6,13 +6,12 @@ // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis // ignore_for_file: avoid_relative_lib_imports // @dart = 2.12 -import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; - -import '../lib/src/messages.g.dart'; +import 'package:image_picker_ios/src/messages.g.dart'; class _TestHostImagePickerApiCodec extends StandardMessageCodec { const _TestHostImagePickerApiCodec(); @@ -21,90 +20,110 @@ class _TestHostImagePickerApiCodec extends StandardMessageCodec { if (value is MaxSize) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else - if (value is SourceSpecification) { + } else if (value is SourceSpecification) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else -{ + } else { super.writeValue(buffer, value); } } + @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return MaxSize.decode(readValue(buffer)!); - - case 129: + + case 129: return SourceSpecification.decode(readValue(buffer)!); - - default: + + default: return super.readValueOfType(type, buffer); - } } } + abstract class TestHostImagePickerApi { static const MessageCodec codec = _TestHostImagePickerApiCodec(); - Future pickImage(SourceSpecification source, MaxSize maxSize, int? imageQuality, bool requestFullMetadata); - Future?> pickMultiImage(MaxSize maxSize, int? imageQuality, bool requestFullMetadata); - Future pickVideo(SourceSpecification source, int? maxDurationSeconds); - static void setup(TestHostImagePickerApi? api, {BinaryMessenger? binaryMessenger}) { + Future pickImage(SourceSpecification source, MaxSize maxSize, + int? imageQuality, bool requestFullMetadata); + Future?> pickMultiImage( + MaxSize maxSize, int? imageQuality, bool requestFullMetadata); + Future pickVideo( + SourceSpecification source, int? maxDurationSeconds); + static void setup(TestHostImagePickerApi? api, + {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickImage', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.ImagePickerApi.pickImage', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null.'); final List args = (message as List?)!; - final SourceSpecification? arg_source = (args[0] as SourceSpecification?); - assert(arg_source != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null SourceSpecification.'); + final SourceSpecification? arg_source = + (args[0] as SourceSpecification?); + assert(arg_source != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null SourceSpecification.'); final MaxSize? arg_maxSize = (args[1] as MaxSize?); - assert(arg_maxSize != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null MaxSize.'); + assert(arg_maxSize != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null MaxSize.'); final int? arg_imageQuality = (args[2] as int?); final bool? arg_requestFullMetadata = (args[3] as bool?); - assert(arg_requestFullMetadata != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null bool.'); - final String? output = await api.pickImage(arg_source!, arg_maxSize!, arg_imageQuality, arg_requestFullMetadata!); + assert(arg_requestFullMetadata != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null bool.'); + final String? output = await api.pickImage(arg_source!, arg_maxSize!, + arg_imageQuality, arg_requestFullMetadata!); return {'result': output}; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null.'); final List args = (message as List?)!; final MaxSize? arg_maxSize = (args[0] as MaxSize?); - assert(arg_maxSize != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null, expected non-null MaxSize.'); + assert(arg_maxSize != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null, expected non-null MaxSize.'); final int? arg_imageQuality = (args[1] as int?); final bool? arg_requestFullMetadata = (args[2] as bool?); - assert(arg_requestFullMetadata != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null, expected non-null bool.'); - final List? output = await api.pickMultiImage(arg_maxSize!, arg_imageQuality, arg_requestFullMetadata!); + assert(arg_requestFullMetadata != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null, expected non-null bool.'); + final List? output = await api.pickMultiImage( + arg_maxSize!, arg_imageQuality, arg_requestFullMetadata!); return {'result': output}; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ImagePickerApi.pickVideo', codec, binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.ImagePickerApi.pickVideo', codec, + binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickVideo was null.'); + assert(message != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickVideo was null.'); final List args = (message as List?)!; - final SourceSpecification? arg_source = (args[0] as SourceSpecification?); - assert(arg_source != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickVideo was null, expected non-null SourceSpecification.'); + final SourceSpecification? arg_source = + (args[0] as SourceSpecification?); + assert(arg_source != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickVideo was null, expected non-null SourceSpecification.'); final int? arg_maxDurationSeconds = (args[1] as int?); - final String? output = await api.pickVideo(arg_source!, arg_maxDurationSeconds); + final String? output = + await api.pickVideo(arg_source!, arg_maxDurationSeconds); return {'result': output}; }); } From 4a41df12ffd2f90682471c4dc35ed5b38da0ca94 Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Wed, 3 Aug 2022 15:25:51 +0200 Subject: [PATCH 07/11] Updated versions of the platform interface in iOS implementation --- .../image_picker/image_picker_ios/example/pubspec.yaml | 8 ++------ packages/image_picker/image_picker_ios/pubspec.yaml | 6 +----- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/packages/image_picker/image_picker_ios/example/pubspec.yaml b/packages/image_picker/image_picker_ios/example/pubspec.yaml index 895bf04d5775..996907cc83de 100755 --- a/packages/image_picker/image_picker_ios/example/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/example/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - image_picker_platform_interface: ^2.6.0 + image_picker_platform_interface: ^2.6.1 video_player: ^2.1.4 dev_dependencies: @@ -26,8 +26,4 @@ dev_dependencies: sdk: flutter flutter: - uses-material-design: true - -dependency_overrides: - image_picker_platform_interface: - path: ../../image_picker_platform_interface \ No newline at end of file + uses-material-design: true \ No newline at end of file diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index 543e23dfa840..3dd914929657 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -19,14 +19,10 @@ flutter: dependencies: flutter: sdk: flutter - image_picker_platform_interface: ^2.5.0 + image_picker_platform_interface: ^2.6.1 dev_dependencies: flutter_test: sdk: flutter mockito: ^5.0.0 pigeon: ^3.0.2 - -dependency_overrides: - image_picker_platform_interface: - path: ../image_picker_platform_interface \ No newline at end of file From 932ae7cb3ed3f08f94beafe81edae88b6a357754 Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Wed, 3 Aug 2022 16:46:46 +0200 Subject: [PATCH 08/11] Fixed import for generated messages file --- .../image_picker/image_picker_ios/lib/image_picker_ios.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart b/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart index aab18cde3f30..5dfcb16955ac 100644 --- a/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart +++ b/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart @@ -2,12 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - +import 'package:image_picker_ios/src/messages.g.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; -import 'src/messages.g.dart'; - // Converts an [ImageSource] to the corresponding Pigeon API enum value. SourceType _convertSource(ImageSource source) { switch (source) { From 24248286b4b96d95add79f84d97ef049016177f9 Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Wed, 3 Aug 2022 16:52:40 +0200 Subject: [PATCH 09/11] Restored accidentally removed comments from generated test API file --- packages/image_picker/image_picker_ios/test/test_api.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/image_picker/image_picker_ios/test/test_api.dart b/packages/image_picker/image_picker_ios/test/test_api.dart index 49859a849b71..1e44f600f57d 100644 --- a/packages/image_picker/image_picker_ios/test/test_api.dart +++ b/packages/image_picker/image_picker_ios/test/test_api.dart @@ -8,9 +8,13 @@ // @dart = 2.12 import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; + +// Manually changed due to https://github.com/flutter/flutter/issues/97744 import 'package:image_picker_ios/src/messages.g.dart'; class _TestHostImagePickerApiCodec extends StandardMessageCodec { From 2464c1329846212aa039c8443b35d4a5fcdf04be Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Fri, 5 Aug 2022 10:16:32 +0200 Subject: [PATCH 10/11] PR remarks --- .../ios/RunnerTests/ImagePickerPluginTests.m | 24 +++++++++---------- .../image_picker_ios/example/pubspec.yaml | 2 +- .../ios/Classes/FLTImagePickerPlugin.m | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m index e93e203c71ea..320582b0f8a3 100644 --- a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m @@ -46,7 +46,7 @@ - (void)testPluginPickImageDeviceBack { .andReturn(AVAuthorizationStatusAuthorized); // Run test - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; @@ -79,7 +79,7 @@ - (void)testPluginPickImageDeviceFront { .andReturn(AVAuthorizationStatusAuthorized); // Run test - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; @@ -112,7 +112,7 @@ - (void)testPluginPickVideoDeviceBack { .andReturn(AVAuthorizationStatusAuthorized); // Run test - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; @@ -144,7 +144,7 @@ - (void)testPluginPickVideoDeviceFront { .andReturn(AVAuthorizationStatusAuthorized); // Run test - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; @@ -167,7 +167,7 @@ - (void)testPickMultiImageShouldUseUIImagePickerControllerOnPreiOS14 { OCMStub(ClassMethod([photoLibrary authorizationStatus])) .andReturn(PHAuthorizationStatusAuthorized); - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; [plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]]; [plugin pickMultiImageWithMaxSize:[FLTMaxSize makeWithWidth:@(100) height:@(200)] @@ -184,7 +184,7 @@ - (void)testPickImageWithoutFullMetadata API_AVAILABLE(ios(11)) { id mockUIImagePicker = OCMClassMock([UIImagePickerController class]); id photoLibrary = OCMClassMock([PHPhotoLibrary class]); - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; [plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]]; [plugin pickImageWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeGallery @@ -202,7 +202,7 @@ - (void)testPickMultiImageWithoutFullMetadata API_AVAILABLE(ios(11)) { id mockUIImagePicker = OCMClassMock([UIImagePickerController class]); id photoLibrary = OCMClassMock([PHPhotoLibrary class]); - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; [plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]]; [plugin pickMultiImageWithMaxSize:[[FLTMaxSize alloc] init] @@ -221,7 +221,7 @@ - (void)testPluginPickImageDeviceCancelClickMultipleTimes { if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { return; } - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; plugin.imagePickerControllerOverrides = @[ controller ]; @@ -241,7 +241,7 @@ - (void)testPluginPickImageDeviceCancelClickMultipleTimes { #pragma mark - Test video duration - (void)testPickingVideoWithDuration { - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; @@ -262,12 +262,12 @@ - (void)testViewController { UIViewController *vc2 = [UIViewController new]; vc1.mockPresented = vc2; - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; XCTAssertEqual([plugin viewControllerWithWindow:window], vc2); } - (void)testPluginMultiImagePathHasNullItem { - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; dispatch_semaphore_t resultSemaphore = dispatch_semaphore_create(0); __block FlutterError *pickImageResult = nil; @@ -284,7 +284,7 @@ - (void)testPluginMultiImagePathHasNullItem { } - (void)testPluginMultiImagePathHasItem { - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; NSArray *pathList = @[ @"test" ]; dispatch_semaphore_t resultSemaphore = dispatch_semaphore_create(0); diff --git a/packages/image_picker/image_picker_ios/example/pubspec.yaml b/packages/image_picker/image_picker_ios/example/pubspec.yaml index 996907cc83de..c7f2ee4ed727 100755 --- a/packages/image_picker/image_picker_ios/example/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/example/pubspec.yaml @@ -26,4 +26,4 @@ dev_dependencies: sdk: flutter flutter: - uses-material-design: true \ No newline at end of file + uses-material-design: true diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m index 97725e56a83e..953efd8bb8eb 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m @@ -56,7 +56,7 @@ @interface FLTImagePickerPlugin () *)registrar { - FLTImagePickerPlugin *instance = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *instance = [[FLTImagePickerPlugin alloc] init]; FLTImagePickerApiSetup(registrar.messenger, instance); } From 09a264a9a76a503355dd49ee62b8ab8a7b611ec6 Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Thu, 25 Aug 2022 17:02:11 +0200 Subject: [PATCH 11/11] PR remarks --- .../image_picker_ios/ios/Classes/FLTImagePickerPlugin.m | 6 +----- .../image_picker/image_picker_ios/lib/image_picker_ios.dart | 4 +++- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m index 953efd8bb8eb..fa1bb6650501 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m @@ -251,11 +251,7 @@ - (void)pickVideoWithSource:(nonnull FLTSourceSpecification *)source camera:[self cameraDeviceForSource:source]]; break; case FLTSourceTypeGallery: - if (context.requestFullMetadata) { - [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; - } else { - [self showPhotoLibraryWithImagePicker:imagePickerController]; - } + [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; break; default: [self sendCallResultWithError:[FlutterError errorWithCode:@"invalid_source" diff --git a/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart b/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart index 5dfcb16955ac..fbc356f212b8 100644 --- a/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart +++ b/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart @@ -1,10 +1,12 @@ // 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 'dart:async'; -import 'package:image_picker_ios/src/messages.g.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'src/messages.g.dart'; + // Converts an [ImageSource] to the corresponding Pigeon API enum value. SourceType _convertSource(ImageSource source) { switch (source) {