diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index c1450136b2e6..33e2b61b0e88 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.8.6 +* Adds `requestFullMetadata` option to `pickImage`, so images on iOS can be picked without `Photo Library Usage` permission. * Updates minimum Flutter version to 2.10. ## 0.8.5+6 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..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 ]]; @@ -54,6 +54,7 @@ - (void)testPluginPickImageDeviceBack { camera:FLTSourceCameraRear] maxSize:[[FLTMaxSize alloc] init] quality:nil + fullMetadata:@(YES) completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ }]; @@ -78,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 ]]; @@ -86,6 +87,7 @@ - (void)testPluginPickImageDeviceFront { camera:FLTSourceCameraFront] maxSize:[[FLTMaxSize alloc] init] quality:nil + fullMetadata:@(YES) completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ }]; @@ -110,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 ]]; @@ -142,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 ]]; @@ -165,11 +167,12 @@ - (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)] quality:@(50) + fullMetadata:@(YES) completion:^(NSArray *_Nullable result, FlutterError *_Nullable error){ }]; @@ -177,13 +180,48 @@ - (void)testPickMultiImageShouldUseUIImagePickerControllerOnPreiOS14 { [mockUIImagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]); } +- (void)testPickImageWithoutFullMetadata API_AVAILABLE(ios(11)) { + id mockUIImagePicker = OCMClassMock([UIImagePickerController class]); + id photoLibrary = OCMClassMock([PHPhotoLibrary class]); + + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; + [plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]]; + + [plugin pickImageWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeGallery + camera:FLTSourceCameraFront] + maxSize:[[FLTMaxSize alloc] init] + quality:nil + fullMetadata:@(NO) + completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ + }]; + + OCMVerify(times(0), [photoLibrary authorizationStatus]); +} + +- (void)testPickMultiImageWithoutFullMetadata API_AVAILABLE(ios(11)) { + id mockUIImagePicker = OCMClassMock([UIImagePickerController class]); + id photoLibrary = OCMClassMock([PHPhotoLibrary class]); + + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; + [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 { if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { return; } - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; plugin.imagePickerControllerOverrides = @[ controller ]; @@ -191,6 +229,7 @@ - (void)testPluginPickImageDeviceCancelClickMultipleTimes { camera:FLTSourceCameraRear] maxSize:[[FLTMaxSize alloc] init] quality:nil + fullMetadata:@(YES) completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ }]; @@ -202,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 ]]; @@ -223,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; @@ -245,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 24b3af031436..bca58a553682 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.6.1 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 18d4ad2f054c..fa1bb6650501 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); } @@ -119,7 +119,11 @@ - (void)launchPHPickerWithContext:(nonnull FLTImagePickerMethodCallContext *)con _pickerViewController.presentationController.delegate = self; self.callContext = context; - [self checkPhotoAuthorizationForAccessLevel]; + if (context.requestFullMetadata) { + [self checkPhotoAuthorizationForAccessLevel]; + } else { + [self showPhotoLibraryWithPHPicker:_pickerViewController]; + } } - (void)launchUIImagePickerWithSource:(nonnull FLTSourceSpecification *)source @@ -136,7 +140,16 @@ - (void)launchUIImagePickerWithSource:(nonnull FLTSourceSpecification *)source camera:[self cameraDeviceForSource:source]]; break; case FLTSourceTypeGallery: - [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; + if (@available(iOS 11, *)) { + if (context.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 +164,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 +180,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, *)) { @@ -180,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]; @@ -554,7 +571,11 @@ - (void)imagePickerController:(UIImagePickerController *)picker NSNumber *imageQuality = self.callContext.imageQuality; NSNumber *desiredImageQuality = [self getDesiredImageQuality:imageQuality]; - PHAsset *originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromImagePickerInfo:info]; + PHAsset *originalAsset; + if (_callContext.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 64c20452987d..d73a54d245f6 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..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 @@ -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; @@ -45,9 +45,11 @@ NSObject *FLTImagePickerApiGetCodec(void); - (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 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..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 @@ -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 @@ -144,18 +144,21 @@ void FLTImagePickerApiSetup(id binaryMessenger, binaryMessenger:binaryMessenger codec:FLTImagePickerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickImageWithSource:maxSize:quality:completion:)], + NSCAssert([api respondsToSelector:@selector + (pickImageWithSource:maxSize:quality:fullMetadata:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to " - @"@selector(pickImageWithSource:maxSize:quality:completion:)", + @"@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)); }]; @@ -170,16 +173,19 @@ void FLTImagePickerApiSetup(id binaryMessenger, binaryMessenger:binaryMessenger codec:FLTImagePickerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickMultiImageWithMaxSize:quality:completion:)], + NSCAssert([api respondsToSelector:@selector + (pickMultiImageWithMaxSize:quality:fullMetadata:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to " - @"@selector(pickMultiImageWithMaxSize:quality:completion:)", + @"@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)); 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..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,7 +1,6 @@ // 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_platform_interface/image_picker_platform_interface.dart'; @@ -51,14 +50,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, @@ -66,9 +79,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; @@ -77,20 +94,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'); } @@ -98,22 +128,24 @@ 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(); } 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 +156,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 +201,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; } @@ -182,9 +218,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 0c5859e80ac9..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 @@ -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 @@ -115,13 +115,16 @@ class ImagePickerApi { static const MessageCodec codec = _ImagePickerApiCodec(); Future pickImage(SourceSpecification arg_source, MaxSize arg_maxSize, - int? arg_imageQuality) async { + 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]) - as Map?; + final Map? replyMap = await channel.send([ + arg_source, + arg_maxSize, + arg_imageQuality, + arg_requestFullMetadata + ]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -140,14 +143,14 @@ 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); - final Map? replyMap = - await channel.send([arg_maxSize, arg_imageQuality]) - as Map?; + final Map? replyMap = await channel.send( + [arg_maxSize, arg_imageQuality, arg_requestFullMetadata]) + as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', diff --git a/packages/image_picker/image_picker_ios/pigeons/messages.dart b/packages/image_picker/image_picker_ios/pigeons/messages.dart index 94ac034606e9..dd8a0f0c0834 100644 --- a/packages/image_picker/image_picker_ios/pigeons/messages.dart +++ b/packages/image_picker/image_picker_ios/pigeons/messages.dart @@ -35,12 +35,13 @@ 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); + @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 dc695138dd7f..5c30bf9d8c35 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 image_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+6 +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.6.1 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..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 @@ -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,17 +51,22 @@ class _ApiLogger implements TestHostImagePickerApi { 'maxWidth': maxSize.width, 'maxHeight': maxSize.height, 'imageQuality': imageQuality, + 'requestFullMetadata': requestFullMetadata, })); return returnValue as String?; } @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?; } @@ -103,14 +112,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 +167,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 +275,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), ], ); @@ -276,6 +295,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': SourceCamera.front, + 'requestFullMetadata': true, }), ], ); @@ -295,6 +315,7 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), ], ); @@ -335,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, }), ], ); @@ -524,14 +552,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 +607,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 +715,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), ], ); @@ -697,6 +735,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': SourceCamera.front, + 'requestFullMetadata': true, }), ], ); @@ -716,6 +755,7 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), ], ); @@ -756,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, }), ], ); @@ -934,4 +981,478 @@ 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, + }), + ], + ); + }); + }); + + 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 8b20f2129d89..1e44f600f57d 100644 --- a/packages/image_picker/image_picker_ios/test/test_api.dart +++ b/packages/image_picker/image_picker_ios/test/test_api.dart @@ -1,12 +1,13 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// 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; + // 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; @@ -49,9 +50,10 @@ class _TestHostImagePickerApiCodec extends StandardMessageCodec { abstract class TestHostImagePickerApi { static const MessageCodec codec = _TestHostImagePickerApiCodec(); - Future pickImage( - SourceSpecification source, MaxSize maxSize, int? imageQuality); - Future?> pickMultiImage(MaxSize maxSize, int? imageQuality); + 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, @@ -75,8 +77,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}; }); } @@ -96,8 +101,11 @@ abstract class TestHostImagePickerApi { 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}; }); }