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

[image_picker] add requestFullMetadata for iOS (optional permissions) #4638

Closed
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
5d3a9aa
Added new image picking method with flag for controlling metadata
PiotrMitkowski Jan 3, 2022
4195dca
Updated tests with cases for the new method
PiotrMitkowski Jan 3, 2022
7d255de
Updated changelog in platform interface and app-facing package to mat…
PiotrMitkowski Jan 3, 2022
3fd7383
Updated app-facing package to call new method from platform interface
PiotrMitkowski Jan 3, 2022
686bc22
Merge branch 'master' of github.com:flutter/plugins into ios-optional…
PiotrMitkowski Jan 12, 2022
1f73191
Fixed version in changelog file
PiotrMitkowski Jan 12, 2022
24d0679
Merge branch 'main' into ios-optional-permissions
PiotrMitkowski Jan 18, 2022
1ba0c73
Merge branch 'main' into ios-optional-permissions
PiotrMitkowski Jan 18, 2022
1084763
PR remarks
PiotrMitkowski Jan 24, 2022
a89ad0a
Renamed flag to better specify its purpose
PiotrMitkowski Jan 24, 2022
8ff7492
Renamed flag in remaining occurences
PiotrMitkowski Jan 24, 2022
193bbe9
Merge branch 'main' into ios-optional-permissions
PiotrMitkowski Jan 24, 2022
95eef20
Fixed formatting and originalAsset assignment
PiotrMitkowski Jan 24, 2022
e10b24e
Merge remote-tracking branch 'origin/ios-optional-permissions' into i…
PiotrMitkowski Jan 24, 2022
f2c2e68
Resolved merge conflicts
PiotrMitkowski Feb 1, 2022
f5f94b2
PR remarks
PiotrMitkowski Feb 14, 2022
97efdca
Merge branch 'main' of github.com:flutter/plugins into ios-optional-p…
PiotrMitkowski Feb 14, 2022
bd3dfeb
Fixed issues after merge
PiotrMitkowski Feb 14, 2022
b99ba07
Added a test for picking image without full metadata
PiotrMitkowski Feb 14, 2022
3d6c45c
Fixed code formatting
PiotrMitkowski Feb 14, 2022
b3e9166
Merge branch 'main' of github.com:flutter/plugins into ios-optional-p…
PiotrMitkowski Apr 7, 2022
bf3f55c
PR remarks
PiotrMitkowski Apr 7, 2022
742b259
Fixed linter warnings
PiotrMitkowski Apr 7, 2022
d41b390
Added dependency overrides for missing modules
PiotrMitkowski Apr 7, 2022
ead2f62
Fixed formatting
PiotrMitkowski Apr 7, 2022
7b6ace9
Added options class for handling image picking settings
PiotrMitkowski Apr 8, 2022
0f43653
Added missing license comment
PiotrMitkowski Apr 8, 2022
c6dcc49
Merge branch 'main' of github.com:flutter/plugins into ios-optional-p…
PiotrMitkowski Apr 13, 2022
9ba71b9
Merge branch 'main' of github.com:flutter/plugins into ios-optional-p…
PiotrMitkowski Apr 14, 2022
c1fc9da
PR remarks
PiotrMitkowski Apr 15, 2022
7f958a6
pbxproj version revert
PiotrMitkowski Apr 15, 2022
c825d09
PR remarks
PiotrMitkowski Apr 27, 2022
a2ca361
Merge branch 'main' of github.com:flutter/plugins into ios-optional-p…
PiotrMitkowski Apr 27, 2022
0120c47
Resolved merge conflicts
PiotrMitkowski Apr 27, 2022
0827c80
Generated Pigeon files for new API and metadata flag
PiotrMitkowski Apr 27, 2022
994e45f
Switched to use options object in private picking method
PiotrMitkowski Apr 28, 2022
9b39e31
Merge branch 'main' of github.com:flutter/plugins into ios-optional-p…
PiotrMitkowski Apr 28, 2022
933254e
Fixed failing unit tests
PiotrMitkowski Apr 28, 2022
66f14b2
Added missing tests for a new method and request full metadata parameter
PiotrMitkowski Apr 28, 2022
47098ec
Fixed failing native tests
PiotrMitkowski Apr 28, 2022
0a811d8
PR remarks
PiotrMitkowski Apr 29, 2022
0789a7b
Fixed linting
PiotrMitkowski Apr 29, 2022
c5966d4
Removed dependency overrides
PiotrMitkowski May 12, 2022
6f32405
Merge branch 'main' of github.com:flutter/plugins into ios-optional-p…
PiotrMitkowski May 12, 2022
217237b
PR remarks
PiotrMitkowski May 12, 2022
7a4d250
Fixed linter warnings and failing tests
PiotrMitkowski May 12, 2022
4ee04cb
Removed remaining dependency override
PiotrMitkowski May 12, 2022
b3fb220
Enforcing proper platform interface version having required API changes
PiotrMitkowski May 13, 2022
4d884de
Merge branch 'main' of github.com:flutter/plugins into ios-optional-p…
PiotrMitkowski May 13, 2022
c2240fa
Fixed failing test in app-facing plugin
PiotrMitkowski May 13, 2022
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
4 changes: 2 additions & 2 deletions packages/image_picker/image_picker/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## NEXT

## 0.8.5
* Updates Android compileSdkVersion to 31.
* Fix iOS RunnerUITests search paths.
* Adds `forceFullMetadata` option to `pickImage`, so images on iOS can be picked without `Photo Library Usage` permission.

## 0.8.4+4

Expand Down
11 changes: 10 additions & 1 deletion packages/image_picker/image_picker/lib/image_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,13 @@ class ImagePicker {
/// the front or rear camera should be opened, this function is not guaranteed
/// to work on an Android device.
///
/// Use `forceFullMetadata` (defaults to `true`) to control, how much additional information the plugin tries to get on iOS.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: remove the comma after "control".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This shouldn't specifically say it's for iOS, as it might apply to other platforms in the future. I would remove iOS here, and add it to the sentence below (e.g., "... which may require extra permission requests on some platforms, such as iOS").

/// If `forceFullMetadata` is set to `true`, the plugin tries to get the full image metadata which may require
/// extra permission requests.
/// If `forceFullMetadata` is set to `false`, the plugin fetches the image in a way that reduces permission requests
/// from the iOS platform (it won’t ask for the `Photo Library Usage` permission).
///
///
/// In Android, the MainActivity can be destroyed for various reasons. If that happens, the result will be lost
/// in this call. You can then call [retrieveLostData] when your app relaunches to retrieve the lost data.
///
Expand All @@ -206,13 +213,15 @@ class ImagePicker {
double? maxHeight,
int? imageQuality,
CameraDevice preferredCameraDevice = CameraDevice.rear,
bool forceFullMetadata = true,
}) {
return platform.getImage(
return platform.getImageFromSource(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to require the version of the platform interface that has this method in pubspec.yaml.

source: source,
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: imageQuality,
preferredCameraDevice: preferredCameraDevice,
forceFullMetadata: forceFullMetadata,
);
}

Expand Down
8 changes: 7 additions & 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.4+4
version: 0.8.5

environment:
sdk: ">=2.14.0 <3.0.0"
Expand Down Expand Up @@ -33,3 +33,9 @@ dev_dependencies:
mockito: ^5.0.0
pedantic: ^1.10.0
plugin_platform_interface: ^2.0.0

# FOR TESTING ONLY. DO NOT MERGE.
dependency_overrides:
image_picker_platform_interface:
path:
../image_picker_platform_interface
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,16 @@ void main() {
'maxWidth': null,
'maxHeight': null,
'imageQuality': null,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
isMethodCall('pickImage', arguments: <String, dynamic>{
'source': 1,
'maxWidth': null,
'maxHeight': null,
'imageQuality': null,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
],
);
Expand Down Expand Up @@ -103,49 +105,56 @@ void main() {
'maxWidth': null,
'maxHeight': null,
'imageQuality': null,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
isMethodCall('pickImage', arguments: <String, dynamic>{
'source': 0,
'maxWidth': 10.0,
'maxHeight': null,
'imageQuality': null,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
isMethodCall('pickImage', arguments: <String, dynamic>{
'source': 0,
'maxWidth': null,
'maxHeight': 10.0,
'imageQuality': null,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
isMethodCall('pickImage', arguments: <String, dynamic>{
'source': 0,
'maxWidth': 10.0,
'maxHeight': 20.0,
'imageQuality': null,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
isMethodCall('pickImage', arguments: <String, dynamic>{
'source': 0,
'maxWidth': 10.0,
'maxHeight': null,
'imageQuality': 70,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
isMethodCall('pickImage', arguments: <String, dynamic>{
'source': 0,
'maxWidth': null,
'maxHeight': 10.0,
'imageQuality': 70,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
isMethodCall('pickImage', arguments: <String, dynamic>{
'source': 0,
'maxWidth': 10.0,
'maxHeight': 20.0,
'imageQuality': 70,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
],
);
Expand Down Expand Up @@ -182,6 +191,7 @@ void main() {
'maxHeight': null,
'imageQuality': null,
'cameraDevice': 0,
'forceFullMetadata': true,
}),
],
);
Expand All @@ -201,6 +211,7 @@ void main() {
'maxHeight': null,
'imageQuality': null,
'cameraDevice': 1,
'forceFullMetadata': true,
}),
],
);
Expand Down
48 changes: 39 additions & 9 deletions packages/image_picker/image_picker/test/image_picker_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,16 @@ void main() {
'maxWidth': null,
'maxHeight': null,
'imageQuality': null,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
isMethodCall('pickImage', arguments: <String, dynamic>{
'source': 1,
'maxWidth': null,
'maxHeight': null,
'imageQuality': null,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
],
);
Expand Down Expand Up @@ -98,49 +100,56 @@ void main() {
'maxWidth': null,
'maxHeight': null,
'imageQuality': null,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
isMethodCall('pickImage', arguments: <String, dynamic>{
'source': 0,
'maxWidth': 10.0,
'maxHeight': null,
'imageQuality': null,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
isMethodCall('pickImage', arguments: <String, dynamic>{
'source': 0,
'maxWidth': null,
'maxHeight': 10.0,
'imageQuality': null,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
isMethodCall('pickImage', arguments: <String, dynamic>{
'source': 0,
'maxWidth': 10.0,
'maxHeight': 20.0,
'imageQuality': null,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
isMethodCall('pickImage', arguments: <String, dynamic>{
'source': 0,
'maxWidth': 10.0,
'maxHeight': null,
'imageQuality': 70,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
isMethodCall('pickImage', arguments: <String, dynamic>{
'source': 0,
'maxWidth': null,
'maxHeight': 10.0,
'imageQuality': 70,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
isMethodCall('pickImage', arguments: <String, dynamic>{
'source': 0,
'maxWidth': 10.0,
'maxHeight': 20.0,
'imageQuality': 70,
'cameraDevice': 0
'cameraDevice': 0,
'forceFullMetadata': true,
}),
],
);
Expand Down Expand Up @@ -177,6 +186,7 @@ void main() {
'maxHeight': null,
'imageQuality': null,
'cameraDevice': 0,
'forceFullMetadata': true,
}),
],
);
Expand All @@ -196,6 +206,26 @@ void main() {
'maxHeight': null,
'imageQuality': null,
'cameraDevice': 1,
'forceFullMetadata': true,
}),
],
);
});

test('passes the full metadata argument correctly', () async {
await picker.pickImage(
source: ImageSource.camera, forceFullMetadata: false);

expect(
log,
<Matcher>[
isMethodCall('pickImage', arguments: <String, dynamic>{
'source': 0,
'maxWidth': null,
'maxHeight': null,
'imageQuality': null,
'cameraDevice': 0,
'forceFullMetadata': false,
}),
],
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 2.5.0

* Re-adding `forceFullMetadata` option, but as a parameter of a new `pickImageFromSource`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-adds

(Per style guide)

method (non-breaking change approach).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to explain in the changelog that it's non-breaking; that's indicated by the version.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I only wanted to explicitly show the difference from previous implementation


## 2.4.3

* Removes dependency on `meta`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform {
double? maxHeight,
int? imageQuality,
CameraDevice preferredCameraDevice = CameraDevice.rear,
bool forceFullMetadata = true,
}) {
if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) {
throw ArgumentError.value(
Expand All @@ -106,7 +107,8 @@ class MethodChannelImagePicker extends ImagePickerPlatform {
'maxWidth': maxWidth,
'maxHeight': maxHeight,
'imageQuality': imageQuality,
'cameraDevice': preferredCameraDevice.index
'cameraDevice': preferredCameraDevice.index,
'forceFullMetadata': forceFullMetadata,
},
);
}
Expand Down Expand Up @@ -270,4 +272,24 @@ class MethodChannelImagePicker extends ImagePickerPlatform {
files: pickedFileList,
);
}

@override
Future<XFile?> getImageFromSource({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put this next to getImage

required ImageSource source,
double? maxWidth,
double? maxHeight,
int? imageQuality,
CameraDevice preferredCameraDevice = CameraDevice.rear,
bool forceFullMetadata = true,
}) async {
String? path = await _getImagePath(
source: source,
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: imageQuality,
preferredCameraDevice: preferredCameraDevice,
forceFullMetadata: forceFullMetadata,
);
return path != null ? XFile(path) : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ abstract class ImagePickerPlatform extends PlatformInterface {
throw UnimplementedError('retrieveLostData() has not been implemented.');
}

/// This method is deprecated in favor of [getImageFromSource] and will be removed in a future update
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Missing final period.

///
/// Returns an [XFile] with the image that was picked.
///
/// The `source` argument controls where the image comes from. This can
Expand Down Expand Up @@ -251,4 +253,54 @@ abstract class ImagePickerPlatform extends PlatformInterface {
Future<LostDataResponse> getLostData() {
throw UnimplementedError('getLostData() has not been implemented.');
}

/// Returns an [XFile] with the image that was picked.
///
/// The `source` argument controls where the image comes from. This can
/// be either [ImageSource.camera] or [ImageSource.gallery].
///
/// Where iOS supports HEIC images, Android 8 and below doesn't. Android 9 and above only support HEIC images if used
/// in addition to a size modification, of which the usage is explained below.
///
/// If specified, the image will be at most `maxWidth` wide and
/// `maxHeight` tall. Otherwise the image will be returned at it's
/// original width and height.
///
/// The `imageQuality` argument modifies the quality of the image, ranging from 0-100
/// where 100 is the original/max quality. If `imageQuality` is null, the image with
/// the original quality will be returned. Compression is only supported for certain
/// image types such as JPEG. If compression is not supported for the image that is picked,
/// a warning message will be logged.
///
/// 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
/// the front or rear camera should be opened, this function is not guaranteed
/// to work on an Android device.
///
/// `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).
///
/// In Android, the MainActivity can be destroyed for various reasons. If that happens, the result will be lost
/// in this call. You can then call [getLostData] when your app relaunches to retrieve the lost data.
///
/// If no images were picked, the return value is null.
Future<XFile?> getImageFromSource({
required ImageSource source,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason behind making source not a part of ImagePickerOptions? Can we also move source to ImagePickerOptions?

Copy link
Contributor Author

@PiotrMitkowski PiotrMitkowski Apr 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This parameter is needed whenever the picking method is called. All parameters inside ImagePickerOptions are more optional, so the whole options object can be skipped if a caller doesn't need any specific adjustments. It was @stuartmorgan's suggestion in this PR that I've agreed with, as calling the picking method doesn't require creating the options object for every use

double? maxWidth,
double? maxHeight,
int? imageQuality,
CameraDevice preferredCameraDevice = CameraDevice.rear,
bool forceFullMetadata = true,
}) {
return getImage(
source: source,
maxHeight: maxHeight,
maxWidth: maxWidth,
imageQuality: imageQuality,
preferredCameraDevice: preferredCameraDevice,
);
}
}
Loading