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

[image_picker] add forceFullMetadata for iOS #3264

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/image_picker/image_picker/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 0.8.4

* Add `forceFullMetadata` option to `pickImage`.
* To keep this non-breaking `forceFullMetadata` defaults to `true`, so the plugin tries
to get the full image metadata which may require extra permission requests on certain platforms.
* If `forceFullMetadata` is set to `false`, the plugin fetches the image in a way that reduces
permission requests from the platform (e.g. on iOS the plugin won’t ask for the `NSPhotoLibraryUsageDescription` permission).

## 0.8.3+3

* Fix pickImage not returning a value on iOS when dismissing PHPicker sheet by swiping.
Expand Down
115 changes: 65 additions & 50 deletions packages/image_picker/image_picker/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ class _MyHomePageState extends State<MyHomePage> {
source: source, maxDuration: const Duration(seconds: 10));
await _playVideo(file);
} else if (isMultiImage) {
await _displayPickImageDialog(context!,
(double? maxWidth, double? maxHeight, int? quality) async {
await _displayPickImageDialog(context!, (double? maxWidth,
double? maxHeight, int? quality, bool forceFullMetadata) async {
try {
final pickedFileList = await _picker.pickMultiImage(
maxWidth: maxWidth,
Expand All @@ -106,14 +106,15 @@ class _MyHomePageState extends State<MyHomePage> {
}
});
} else {
await _displayPickImageDialog(context!,
(double? maxWidth, double? maxHeight, int? quality) async {
await _displayPickImageDialog(context!, (double? maxWidth,
double? maxHeight, int? quality, bool forceFullMetadata) async {
try {
final pickedFile = await _picker.pickImage(
source: source,
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: quality,
forceFullMetadata: forceFullMetadata,
);
setState(() {
_imageFile = pickedFile;
Expand Down Expand Up @@ -358,60 +359,74 @@ class _MyHomePageState extends State<MyHomePage> {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Add optional parameters'),
content: Column(
children: <Widget>[
TextField(
controller: maxWidthController,
keyboardType: TextInputType.numberWithOptions(decimal: true),
decoration:
InputDecoration(hintText: "Enter maxWidth if desired"),
),
TextField(
controller: maxHeightController,
keyboardType: TextInputType.numberWithOptions(decimal: true),
decoration:
InputDecoration(hintText: "Enter maxHeight if desired"),
),
TextField(
controller: qualityController,
keyboardType: TextInputType.number,
decoration:
InputDecoration(hintText: "Enter quality if desired"),
),
],
),
actions: <Widget>[
TextButton(
child: const Text('CANCEL'),
onPressed: () {
Navigator.of(context).pop();
},
bool forceFullMetadata = true;
return StatefulBuilder(builder: (context, setState) {
return AlertDialog(
title: Text('Add optional parameters'),
content: Column(
children: <Widget>[
TextField(
controller: maxWidthController,
keyboardType:
TextInputType.numberWithOptions(decimal: true),
decoration:
InputDecoration(hintText: "Enter maxWidth if desired"),
),
TextField(
controller: maxHeightController,
keyboardType:
TextInputType.numberWithOptions(decimal: true),
decoration:
InputDecoration(hintText: "Enter maxHeight if desired"),
),
TextField(
controller: qualityController,
keyboardType: TextInputType.number,
decoration:
InputDecoration(hintText: "Enter quality if desired"),
),
CheckboxListTile(
value: forceFullMetadata,
onChanged: (bool? value) {
setState(() {
forceFullMetadata = value ?? false;
});
},
title: Text("Force full metadata"),
)
],
),
TextButton(
child: const Text('PICK'),
actions: <Widget>[
TextButton(
child: const Text('CANCEL'),
onPressed: () {
double? width = maxWidthController.text.isNotEmpty
? double.parse(maxWidthController.text)
: null;
double? height = maxHeightController.text.isNotEmpty
? double.parse(maxHeightController.text)
: null;
int? quality = qualityController.text.isNotEmpty
? int.parse(qualityController.text)
: null;
onPick(width, height, quality);
Navigator.of(context).pop();
}),
],
);
},
),
TextButton(
child: const Text('PICK'),
onPressed: () {
double? width = maxWidthController.text.isNotEmpty
? double.parse(maxWidthController.text)
: null;
double? height = maxHeightController.text.isNotEmpty
? double.parse(maxHeightController.text)
: null;
int? quality = qualityController.text.isNotEmpty
? int.parse(qualityController.text)
: null;
onPick(width, height, quality, forceFullMetadata);
Navigator.of(context).pop();
}),
],
);
});
});
}
}

typedef void OnPickImageCallback(
double? maxWidth, double? maxHeight, int? quality);
double? maxWidth, double? maxHeight, int? quality, bool forceFullMetadata);

class AspectRatioVideo extends StatefulWidget {
AspectRatioVideo(this.controller);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,12 @@ - (void)pickImageWithPHPicker:(int)maxImagesAllowed API_AVAILABLE(ios(14)) {

self.maxImagesAllowed = maxImagesAllowed;

[self checkPhotoAuthorizationForAccessLevel];
BOOL usePhaAsset = [[_arguments objectForKey:@"forceFullMetadata"] boolValue];
if (usePhaAsset) {
[self checkPhotoAuthorizationForAccessLevel];
return;
}
[self showPhotoLibrary:PHPickerClassType];
}

- (void)pickImageWithUIImagePicker {
Expand All @@ -107,6 +112,7 @@ - (void)pickImageWithUIImagePicker {
_imagePickerController.mediaTypes = @[ (NSString *)kUTTypeImage ];

int imageSource = [[_arguments objectForKey:@"source"] intValue];
BOOL usePhaAsset = [[_arguments objectForKey:@"forceFullMetadata"] boolValue];

self.maxImagesAllowed = 1;

Expand All @@ -115,7 +121,11 @@ - (void)pickImageWithUIImagePicker {
[self checkCameraAuthorization];
break;
case SOURCE_GALLERY:
[self checkPhotoAuthorization];
if (usePhaAsset) {
[self checkPhotoAuthorization];
break;
}
[self showPhotoLibrary:UIImagePickerClassType];
break;
default:
self.result([FlutterError errorWithCode:@"invalid_source"
Expand All @@ -132,13 +142,14 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
details:nil]);
self.result = nil;
}
BOOL usePhaAsset = [[_arguments objectForKey:@"forceFullMetadata"] boolValue];

if ([@"pickImage" isEqualToString:call.method]) {
self.result = result;
_arguments = call.arguments;
int imageSource = [[_arguments objectForKey:@"source"] intValue];

if (imageSource == SOURCE_GALLERY) { // Capture is not possible with PHPicker
if (usePhaAsset && imageSource == SOURCE_GALLERY) { // Capture is not possible with PHPicker
if (@available(iOS 14, *)) {
// PHPicker is used
[self pickImageWithPHPicker:1];
Expand Down Expand Up @@ -171,6 +182,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
_arguments = call.arguments;

int imageSource = [[_arguments objectForKey:@"source"] intValue];
BOOL usePhaAsset = [[_arguments objectForKey:@"forceFullMetadata"] boolValue];
if ([[_arguments objectForKey:@"maxDuration"] isKindOfClass:[NSNumber class]]) {
NSTimeInterval max = [[_arguments objectForKey:@"maxDuration"] doubleValue];
_imagePickerController.videoMaximumDuration = max;
Expand All @@ -181,7 +193,11 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
[self checkCameraAuthorization];
break;
case SOURCE_GALLERY:
[self checkPhotoAuthorization];
if (usePhaAsset) {
[self checkPhotoAuthorization];
break;
}
[self showPhotoLibrary:UIImagePickerClassType];
break;
default:
result([FlutterError errorWithCode:@"invalid_source"
Expand Down Expand Up @@ -484,8 +500,12 @@ - (void)imagePickerController:(UIImagePickerController *)picker
NSNumber *maxHeight = [_arguments objectForKey:@"maxHeight"];
NSNumber *imageQuality = [_arguments objectForKey:@"imageQuality"];
NSNumber *desiredImageQuality = [self getDesiredImageQuality:imageQuality];
BOOL usePhaAsset = [[_arguments objectForKey:@"forceFullMetadata"] boolValue];

PHAsset *originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromImagePickerInfo:info];
PHAsset *originalAsset;
if (usePhaAsset) {
originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromImagePickerInfo:info];
}

if (maxWidth != (id)[NSNull null] || maxHeight != (id)[NSNull null]) {
image = [FLTImagePickerImageUtil scaledImage:image
Expand Down
14 changes: 14 additions & 0 deletions packages/image_picker/image_picker/lib/image_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ class ImagePicker {
/// image types such as JPEG and on Android PNG and WebP, too. If compression is not supported for the image that is picked,
/// a warning message will be logged.
///
/// `forceFullMetadata` defaults to `true`, so the plugin tries to get the full image metadata which may require
/// extra permission requests on certain platforms.
/// If `forceFullMetadata` is set to `false`, the plugin fetches the image in a way that reduces permission requests
/// from the platform (e.g. on iOS the plugin won’t ask for the `NSPhotoLibraryUsageDescription` permission).
///
/// Use `preferredCameraDevice` to specify the camera to use when the `source` is [ImageSource.camera].
/// The `preferredCameraDevice` is ignored when `source` is [ImageSource.gallery]. It is also ignored if the chosen camera is not supported on the device.
/// Defaults to [CameraDevice.rear]. Note that Android has no documented parameter for an intent to specify if
Expand All @@ -65,13 +70,15 @@ class ImagePicker {
double? maxWidth,
double? maxHeight,
int? imageQuality,
bool forceFullMetadata = true,
CameraDevice preferredCameraDevice = CameraDevice.rear,
}) {
return platform.pickImage(
source: source,
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: imageQuality,
forceFullMetadata: forceFullMetadata,
preferredCameraDevice: preferredCameraDevice,
);
}
Expand Down Expand Up @@ -185,6 +192,11 @@ class ImagePicker {
/// image types such as JPEG and on Android PNG and WebP, too. If compression is not supported for the image that is picked,
/// a warning message will be logged.
///
/// `forceFullMetadata` defaults to `true`, so the plugin tries to get the full image metadata which may require
/// extra permission requests on certain platforms.
/// If `forceFullMetadata` is set to `false`, the plugin fetches the image in a way that reduces permission requests
/// from the platform (e.g. on iOS the plugin won’t ask for the `NSPhotoLibraryUsageDescription` permission).
///
/// Use `preferredCameraDevice` to specify the camera to use when the `source` is [ImageSource.camera].
/// The `preferredCameraDevice` is ignored when `source` is [ImageSource.gallery]. It is also ignored if the chosen camera is not supported on the device.
/// Defaults to [CameraDevice.rear]. Note that Android has no documented parameter for an intent to specify if
Expand All @@ -205,13 +217,15 @@ class ImagePicker {
double? maxWidth,
double? maxHeight,
int? imageQuality,
bool forceFullMetadata = true,
CameraDevice preferredCameraDevice = CameraDevice.rear,
}) {
return platform.getImage(
source: source,
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: imageQuality,
forceFullMetadata: forceFullMetadata,
preferredCameraDevice: preferredCameraDevice,
);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/image_picker/image_picker/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image
library, and taking new pictures with the camera.
repository: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22
version: 0.8.3+3
version: 0.8.4

environment:
sdk: ">=2.12.0 <3.0.0"
Expand Down
Loading