From 4b3c599ca0ee2a53ca8ab92c2c0a68b724ef9468 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Mon, 21 Jun 2021 14:51:50 +0200 Subject: [PATCH 01/25] Migrate image picker platform interface to cross_file --- .../CHANGELOG.md | 8 +++ .../lib/image_picker_platform_interface.dart | 1 + .../method_channel_image_picker.dart | 17 ++--- .../image_picker_platform.dart | 16 ++--- .../types/{picked_file => }/lost_data.dart | 3 +- .../lib/src/types/picked_file/base.dart | 62 ------------------- .../lib/src/types/picked_file/html.dart | 49 --------------- .../lib/src/types/picked_file/io.dart | 41 ------------ .../src/types/picked_file/picked_file.dart | 8 --- .../src/types/picked_file/unsupported.dart | 18 ------ .../lib/src/types/types.dart | 2 +- .../pubspec.yaml | 3 +- .../test/picked_file_html_test.dart | 38 ------------ .../test/picked_file_io_test.dart | 42 ------------- 14 files changed, 32 insertions(+), 276 deletions(-) rename packages/image_picker/image_picker_platform_interface/lib/src/types/{picked_file => }/lost_data.dart (96%) delete mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart delete mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart delete mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart delete mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/picked_file.dart delete mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart delete mode 100644 packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart delete mode 100644 packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index e2def7243592..36188838cc2d 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,3 +1,11 @@ +## 3.0.0 + +* Breaking changes: + * pickImage now returns XFile instead of PickedFile + * pickVideo now returns XFile instead of PickedFile + * changed LostData file parameter type to XFile + * removed PickedFile class and its tests + ## 2.1.0 * Add `pickMultiImage` method. diff --git a/packages/image_picker/image_picker_platform_interface/lib/image_picker_platform_interface.dart b/packages/image_picker/image_picker_platform_interface/lib/image_picker_platform_interface.dart index b384e3845c4b..133c05ecfebf 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/image_picker_platform_interface.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/image_picker_platform_interface.dart @@ -4,3 +4,4 @@ export 'package:image_picker_platform_interface/src/platform_interface/image_picker_platform.dart'; export 'package:image_picker_platform_interface/src/types/types.dart'; +export 'package:cross_file/cross_file.dart'; diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart index e0f46457a8b8..9f36d5b07d03 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; +import 'package:image_picker_platform_interface/src/types/lost_data.dart'; import 'package:meta/meta.dart' show visibleForTesting; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; @@ -19,7 +20,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { MethodChannel get channel => _channel; @override - Future pickImage({ + Future pickImage({ required ImageSource source, double? maxWidth, double? maxHeight, @@ -33,11 +34,11 @@ class MethodChannelImagePicker extends ImagePickerPlatform { imageQuality: imageQuality, preferredCameraDevice: preferredCameraDevice, ); - return path != null ? PickedFile(path) : null; + return path != null ? XFile(path) : null; } @override - Future?> pickMultiImage({ + Future?> pickMultiImage({ double? maxWidth, double? maxHeight, int? imageQuality, @@ -49,9 +50,9 @@ class MethodChannelImagePicker extends ImagePickerPlatform { ); if (paths == null) return null; - final List files = []; + final List files = []; for (final path in paths) { - files.add(PickedFile(path)); + files.add(XFile(path)); } return files; } @@ -117,7 +118,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { } @override - Future pickVideo({ + Future pickVideo({ required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, @@ -127,7 +128,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { maxDuration: maxDuration, preferredCameraDevice: preferredCameraDevice, ); - return path != null ? PickedFile(path) : null; + return path != null ? XFile(path) : null; } Future _pickVideoPath({ @@ -175,7 +176,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { final String? path = result['path']; return LostData( - file: path != null ? PickedFile(path) : null, + file: path != null ? XFile(path) : null, exception: exception, type: retrieveType, ); diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart index 32af7747185a..5f35c5300c94 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart @@ -3,6 +3,8 @@ // found in the LICENSE file. import 'dart:async'; +import 'package:cross_file/cross_file.dart'; +import 'package:image_picker_platform_interface/src/types/lost_data.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; @@ -40,7 +42,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { // Next version of the API. - /// Returns a [PickedFile] with the image that was picked. + /// Returns a [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]. @@ -68,7 +70,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { /// in this call. You can then call [retrieveLostData] when your app relaunches to retrieve the lost data. /// /// If no images were picked, the return value is null. - Future pickImage({ + Future pickImage({ required ImageSource source, double? maxWidth, double? maxHeight, @@ -78,7 +80,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { throw UnimplementedError('pickImage() has not been implemented.'); } - /// Returns a [List] with the images that were picked. + /// Returns a [List] with the images that were picked. /// /// The images come from the [ImageSource.gallery]. /// @@ -96,7 +98,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { /// a warning message will be logged. /// /// If no images were picked, the return value is null. - Future?> pickMultiImage({ + Future?> pickMultiImage({ double? maxWidth, double? maxHeight, int? imageQuality, @@ -104,7 +106,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { throw UnimplementedError('pickMultiImage() has not been implemented.'); } - /// Returns a [PickedFile] containing the video that was picked. + /// Returns a [XFile] containing the video that was picked. /// /// The [source] argument controls where the video comes from. This can /// be either [ImageSource.camera] or [ImageSource.gallery]. @@ -120,7 +122,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { /// in this call. You can then call [retrieveLostData] when your app relaunches to retrieve the lost data. /// /// If no images were picked, the return value is null. - Future pickVideo({ + Future pickVideo({ required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, @@ -128,7 +130,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { throw UnimplementedError('pickVideo() has not been implemented.'); } - /// Retrieve the lost [PickedFile] file when [pickImage] or [pickVideo] failed because the MainActivity is destroyed. (Android only) + /// Retrieve the lost [XFile] file when [pickImage] or [pickVideo] failed because the MainActivity is destroyed. (Android only) /// /// Image or video can be lost if the MainActivity is destroyed. And there is no guarantee that the MainActivity is always alive. /// Call this method to retrieve the lost data and process the data according to your APP's business logic. diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data.dart similarity index 96% rename from packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart rename to packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data.dart index 64f6a1f27538..a270eece235e 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:cross_file/cross_file.dart'; import 'package:flutter/services.dart'; import 'package:image_picker_platform_interface/src/types/types.dart'; @@ -31,7 +32,7 @@ class LostData { /// The file that was lost in a previous [pickImage] or [pickVideo] call due to MainActivity being destroyed. /// /// Can be null if [exception] exists. - final PickedFile? file; + final XFile? file; /// The exception of the last [pickImage] or [pickVideo]. /// diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart deleted file mode 100644 index de259e0611dd..000000000000 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart +++ /dev/null @@ -1,62 +0,0 @@ -// 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:convert'; -import 'dart:typed_data'; - -import 'package:meta/meta.dart'; - -/// The interface for a PickedFile. -/// -/// A PickedFile is a container that wraps the path of a selected -/// file by the user and (in some platforms, like web) the bytes -/// with the contents of the file. -/// -/// This class is a very limited subset of dart:io [File], so all -/// the methods should seem familiar. -@immutable -abstract class PickedFileBase { - /// Construct a PickedFile - PickedFileBase(String path); - - /// Get the path of the picked file. - /// - /// This should only be used as a backwards-compatibility clutch - /// for mobile apps, or cosmetic reasons only (to show the user - /// the path they've picked). - /// - /// Accessing the data contained in the picked file by its path - /// is platform-dependant (and won't work on web), so use the - /// byte getters in the PickedFile instance instead. - String get path { - throw UnimplementedError('.path has not been implemented.'); - } - - /// Synchronously read the entire file contents as a string using the given [Encoding]. - /// - /// By default, `encoding` is [utf8]. - /// - /// Throws Exception if the operation fails. - Future readAsString({Encoding encoding = utf8}) { - throw UnimplementedError('readAsString() has not been implemented.'); - } - - /// Synchronously read the entire file contents as a list of bytes. - /// - /// Throws Exception if the operation fails. - Future readAsBytes() { - throw UnimplementedError('readAsBytes() has not been implemented.'); - } - - /// Create a new independent [Stream] for the contents of this file. - /// - /// If `start` is present, the file will be read from byte-offset `start`. Otherwise from the beginning (index 0). - /// - /// If `end` is present, only up to byte-index `end` will be read. Otherwise, until end of file. - /// - /// In order to make sure that system resources are freed, the stream must be read to completion or the subscription on the stream must be cancelled. - Stream openRead([int? start, int? end]) { - throw UnimplementedError('openRead() has not been implemented.'); - } -} diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart deleted file mode 100644 index 24e1931008b6..000000000000 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart +++ /dev/null @@ -1,49 +0,0 @@ -// 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:convert'; -import 'dart:typed_data'; - -import 'package:http/http.dart' as http show readBytes; - -import './base.dart'; - -/// A PickedFile that works on web. -/// -/// It wraps the bytes of a selected file. -class PickedFile extends PickedFileBase { - final String path; - final Uint8List? _initBytes; - - /// Construct a PickedFile object from its ObjectUrl. - /// - /// Optionally, this can be initialized with `bytes` - /// so no http requests are performed to retrieve files later. - PickedFile(this.path, {Uint8List? bytes}) - : _initBytes = bytes, - super(path); - - Future get _bytes async { - if (_initBytes != null) { - return Future.value(UnmodifiableUint8ListView(_initBytes!)); - } - return http.readBytes(Uri.parse(path)); - } - - @override - Future readAsString({Encoding encoding = utf8}) async { - return encoding.decode(await _bytes); - } - - @override - Future readAsBytes() async { - return Future.value(await _bytes); - } - - @override - Stream openRead([int? start, int? end]) async* { - final bytes = await _bytes; - yield bytes.sublist(start ?? 0, end ?? bytes.length); - } -} diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart deleted file mode 100644 index 7037b6b7121a..000000000000 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart +++ /dev/null @@ -1,41 +0,0 @@ -// 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:convert'; -import 'dart:io'; -import 'dart:typed_data'; - -import './base.dart'; - -/// A PickedFile backed by a dart:io File. -class PickedFile extends PickedFileBase { - final File _file; - - /// Construct a PickedFile object backed by a dart:io File. - PickedFile(String path) - : _file = File(path), - super(path); - - @override - String get path { - return _file.path; - } - - @override - Future readAsString({Encoding encoding = utf8}) { - return _file.readAsString(encoding: encoding); - } - - @override - Future readAsBytes() { - return _file.readAsBytes(); - } - - @override - Stream openRead([int? start, int? end]) { - return _file - .openRead(start ?? 0, end) - .map((chunk) => Uint8List.fromList(chunk)); - } -} diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/picked_file.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/picked_file.dart deleted file mode 100644 index c8c9e5a0ac79..000000000000 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/picked_file.dart +++ /dev/null @@ -1,8 +0,0 @@ -// 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. - -export 'lost_data.dart'; -export 'unsupported.dart' - if (dart.library.html) 'html.dart' - if (dart.library.io) 'io.dart'; diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart deleted file mode 100644 index ad3ed6a4f86a..000000000000 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart +++ /dev/null @@ -1,18 +0,0 @@ -// 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 './base.dart'; - -/// A PickedFile is a cross-platform, simplified File abstraction. -/// -/// It wraps the bytes of a selected file, and its (platform-dependant) path. -class PickedFile extends PickedFileBase { - /// Construct a PickedFile object, from its `bytes`. - /// - /// Optionally, you may pass a `path`. See caveats in [PickedFileBase.path]. - PickedFile(String path) : super(path) { - throw UnimplementedError( - 'PickedFile is not available in your current platform.'); - } -} diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart index 10e7745f2741..18d0ff81b76a 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart @@ -5,7 +5,7 @@ export 'camera_device.dart'; export 'image_source.dart'; export 'retrieve_type.dart'; -export 'picked_file/picked_file.dart'; +export 'lost_data.dart'; /// Denotes that an image is being picked. const String kTypeImage = 'image'; diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index 8e176a09a626..8243bb2112c5 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.1.0 +version: 3.0.0 environment: sdk: ">=2.12.0 <3.0.0" @@ -16,6 +16,7 @@ dependencies: http: ^0.13.0 meta: ^1.3.0 plugin_platform_interface: ^2.0.0 + cross_file: ^0.3.1+1 dev_dependencies: flutter_test: diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart deleted file mode 100644 index 7721f66148e0..000000000000 --- a/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart +++ /dev/null @@ -1,38 +0,0 @@ -// 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. - -@TestOn('chrome') // Uses web-only Flutter SDK - -import 'dart:convert'; -import 'dart:html' as html; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; - -final String expectedStringContents = 'Hello, world!'; -final List bytes = utf8.encode(expectedStringContents); -final html.File textFile = html.File([bytes], 'hello.txt'); -final String textFileUrl = html.Url.createObjectUrl(textFile); - -void main() { - group('Create with an objectUrl', () { - final pickedFile = PickedFile(textFileUrl); - - test('Can be read as a string', () async { - expect(await pickedFile.readAsString(), equals(expectedStringContents)); - }); - test('Can be read as bytes', () async { - expect(await pickedFile.readAsBytes(), equals(bytes)); - }); - - test('Can be read as a stream', () async { - expect(await pickedFile.openRead().first, equals(bytes)); - }); - - test('Stream can be sliced', () async { - expect( - await pickedFile.openRead(2, 5).first, equals(bytes.sublist(2, 5))); - }); - }); -} diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart deleted file mode 100644 index d366204c36bf..000000000000 --- a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart +++ /dev/null @@ -1,42 +0,0 @@ -// 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. - -@TestOn('vm') // Uses dart:io - -import 'dart:convert'; -import 'dart:io'; -import 'dart:typed_data'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; - -final pathPrefix = - Directory.current.path.endsWith('test') ? './assets/' : './test/assets/'; -final path = pathPrefix + 'hello.txt'; -final String expectedStringContents = 'Hello, world!'; -final Uint8List bytes = Uint8List.fromList(utf8.encode(expectedStringContents)); -final File textFile = File(path); -final String textFilePath = textFile.path; - -void main() { - group('Create with an objectUrl', () { - final PickedFile pickedFile = PickedFile(textFilePath); - - test('Can be read as a string', () async { - expect(await pickedFile.readAsString(), equals(expectedStringContents)); - }); - test('Can be read as bytes', () async { - expect(await pickedFile.readAsBytes(), equals(bytes)); - }); - - test('Can be read as a stream', () async { - expect(await pickedFile.openRead().first, equals(bytes)); - }); - - test('Stream can be sliced', () async { - expect( - await pickedFile.openRead(2, 5).first, equals(bytes.sublist(2, 5))); - }); - }); -} From c4c5de4af93fff75564d4b2b728e7964aff60500 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Mon, 21 Jun 2021 15:00:13 +0200 Subject: [PATCH 02/25] [image_picker] Implemented changes for cross_file migration --- .../image_picker/image_picker/CHANGELOG.md | 5 +++++ .../image_picker/example/lib/main.dart | 6 +++--- .../image_picker/lib/image_picker.dart | 18 ++++++++---------- .../image_picker/image_picker/pubspec.yaml | 2 +- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index d9127e24d2af..3c0333fbfc8b 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.9.0 + +* Breaking change: + * Changed all usages of PickedFile to XFile + ## 0.8.0+4 * Cleaned up the README example diff --git a/packages/image_picker/image_picker/example/lib/main.dart b/packages/image_picker/image_picker/example/lib/main.dart index 698de1d98898..c0a9471c4a41 100755 --- a/packages/image_picker/image_picker/example/lib/main.dart +++ b/packages/image_picker/image_picker/example/lib/main.dart @@ -36,7 +36,7 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - PickedFile? _imageFile; + XFile? _imageFile; dynamic _pickImageError; bool isVideo = false; VideoPlayerController? _controller; @@ -48,7 +48,7 @@ class _MyHomePageState extends State { final TextEditingController maxHeightController = TextEditingController(); final TextEditingController qualityController = TextEditingController(); - Future _playVideo(PickedFile? file) async { + Future _playVideo(XFile? file) async { if (file != null && mounted) { await _disposeVideoController(); late VideoPlayerController controller; @@ -78,7 +78,7 @@ class _MyHomePageState extends State { await _controller!.setVolume(0.0); } if (isVideo) { - final PickedFile? file = await _picker.getVideo( + final XFile? file = await _picker.getVideo( source: source, maxDuration: const Duration(seconds: 10)); await _playVideo(file); } else { diff --git a/packages/image_picker/image_picker/lib/image_picker.dart b/packages/image_picker/image_picker/lib/image_picker.dart index 77c26d40346a..1b1134ecd998 100755 --- a/packages/image_picker/image_picker/lib/image_picker.dart +++ b/packages/image_picker/image_picker/lib/image_picker.dart @@ -5,9 +5,7 @@ // ignore_for_file: deprecated_member_use, deprecated_member_use_from_same_package import 'dart:async'; - import 'package:flutter/foundation.dart'; - import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; export 'package:image_picker_platform_interface/image_picker_platform_interface.dart' @@ -17,7 +15,7 @@ export 'package:image_picker_platform_interface/image_picker_platform_interface. ImageSource, CameraDevice, LostData, - PickedFile, + XFile, RetrieveType; /// Provides an easy way to pick an image/video from the image library, @@ -27,9 +25,9 @@ class ImagePicker { @visibleForTesting static ImagePickerPlatform get platform => ImagePickerPlatform.instance; - /// Returns a [PickedFile] object wrapping the image that was picked. + /// Returns a [XFile] object wrapping the image that was picked. /// - /// The returned [PickedFile] is intended to be used within a single APP session. Do not save the file path and use it across sessions. + /// The returned [XFile] is intended to be used within a single APP session. Do not save the file path and use it across sessions. /// /// The `source` argument controls where the image comes from. This can /// be either [ImageSource.camera] or [ImageSource.gallery]. @@ -54,7 +52,7 @@ class ImagePicker { /// /// 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. - Future getImage({ + Future getImage({ required ImageSource source, double? maxWidth, double? maxHeight, @@ -70,9 +68,9 @@ class ImagePicker { ); } - /// Returns a [PickedFile] object wrapping the video that was picked. + /// Returns a [XFile] object wrapping the video that was picked. /// - /// The returned [PickedFile] is intended to be used within a single APP session. Do not save the file path and use it across sessions. + /// The returned [XFile] is intended to be used within a single APP session. Do not save the file path and use it across sessions. /// /// The [source] argument controls where the video comes from. This can /// be either [ImageSource.camera] or [ImageSource.gallery]. @@ -86,7 +84,7 @@ class ImagePicker { /// /// In Android, the MainActivity can be destroyed for various fo 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. - Future getVideo({ + Future getVideo({ required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, @@ -98,7 +96,7 @@ class ImagePicker { ); } - /// Retrieve the lost [PickedFile] when [selectImage] or [selectVideo] failed because the MainActivity is destroyed. (Android only) + /// Retrieve the lost [XFile] when [selectImage] or [selectVideo] failed because the MainActivity is destroyed. (Android only) /// /// Image or video can be lost if the MainActivity is destroyed. And there is no guarantee that the MainActivity is always alive. /// Call this method to retrieve the lost data and process the data according to your APP's business logic. diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 4dc7785111a4..46002ba3f93e 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -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.0+4 +version: 0.9.0 environment: sdk: ">=2.12.0 <3.0.0" From 44aa4c5531ea063364be99e62e8a5a427b1b4817 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Tue, 22 Jun 2021 16:46:29 +0200 Subject: [PATCH 03/25] [image_picker] migrated web implementation to cross_file --- .../image_picker_for_web/CHANGELOG.md | 5 +++++ .../lib/image_picker_for_web.dart | 18 +++++++++--------- .../image_picker_for_web/pubspec.yaml | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index 7b2c4077e28d..2ebe3350460b 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,3 +1,8 @@ +# 3.0.0 + +* Breaking change: + * Changed all usages of PickedFile to XFile + # 2.0.0 * Migrate to null safety. diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index 2fb66380e1d8..88114e0ba261 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -34,7 +34,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { ImagePickerPlatform.instance = ImagePickerPlugin(); } - /// Returns a [PickedFile] with the image that was picked. + /// Returns a [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]. @@ -47,7 +47,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// /// If no images were picked, the return value is null. @override - Future pickImage({ + Future pickImage({ required ImageSource source, double? maxWidth, double? maxHeight, @@ -58,7 +58,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { return pickFile(accept: _kAcceptImageMimeType, capture: capture); } - /// Returns a [PickedFile] containing the video that was picked. + /// Returns a [XFile] containing the video that was picked. /// /// The [source] argument controls where the video comes from. This can /// be either [ImageSource.camera] or [ImageSource.gallery]. @@ -71,7 +71,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// /// If no images were picked, the return value is null. @override - Future pickVideo({ + Future pickVideo({ required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, @@ -81,12 +81,12 @@ class ImagePickerPlugin extends ImagePickerPlatform { } /// Injects a file input with the specified accept+capture attributes, and - /// returns the PickedFile that the user selected locally. + /// returns the XFile that the user selected locally. /// /// `capture` is only supported in mobile browsers. /// See https://caniuse.com/#feat=html-media-capture @visibleForTesting - Future pickFile({ + Future pickFile({ String? accept, String? capture, }) { @@ -130,13 +130,13 @@ class ImagePickerPlugin extends ImagePickerPlatform { } /// Monitors an and returns the selected file. - Future _getSelectedFile(html.FileUploadInputElement input) { - final Completer _completer = Completer(); + Future _getSelectedFile(html.FileUploadInputElement input) { + final Completer _completer = Completer(); // Observe the input until we can return something input.onChange.first.then((event) { final objectUrl = _handleOnChangeEvent(event); if (!_completer.isCompleted && objectUrl != null) { - _completer.complete(PickedFile(objectUrl)); + _completer.complete(XFile(objectUrl)); } }); input.onError.first.then((event) { diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index 768f7e27ce77..c9940d773dcd 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker_for_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 2.0.0 +version: 3.0.0 environment: sdk: ">=2.12.0 <3.0.0" From e39fa1f5754e9e46f6ddcedcd0f678e75ebf3b91 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Wed, 23 Jun 2021 10:49:13 +0200 Subject: [PATCH 04/25] Reverted changes to web implementation to move to separate PR --- .../image_picker_for_web/CHANGELOG.md | 5 ----- .../lib/image_picker_for_web.dart | 18 +++++++++--------- .../image_picker_for_web/pubspec.yaml | 2 +- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index 2ebe3350460b..7b2c4077e28d 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,8 +1,3 @@ -# 3.0.0 - -* Breaking change: - * Changed all usages of PickedFile to XFile - # 2.0.0 * Migrate to null safety. diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index 88114e0ba261..2fb66380e1d8 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -34,7 +34,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { ImagePickerPlatform.instance = ImagePickerPlugin(); } - /// Returns a [XFile] with the image that was picked. + /// Returns a [PickedFile] with the image that was picked. /// /// The `source` argument controls where the image comes from. This can /// be either [ImageSource.camera] or [ImageSource.gallery]. @@ -47,7 +47,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// /// If no images were picked, the return value is null. @override - Future pickImage({ + Future pickImage({ required ImageSource source, double? maxWidth, double? maxHeight, @@ -58,7 +58,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { return pickFile(accept: _kAcceptImageMimeType, capture: capture); } - /// Returns a [XFile] containing the video that was picked. + /// Returns a [PickedFile] containing the video that was picked. /// /// The [source] argument controls where the video comes from. This can /// be either [ImageSource.camera] or [ImageSource.gallery]. @@ -71,7 +71,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// /// If no images were picked, the return value is null. @override - Future pickVideo({ + Future pickVideo({ required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, @@ -81,12 +81,12 @@ class ImagePickerPlugin extends ImagePickerPlatform { } /// Injects a file input with the specified accept+capture attributes, and - /// returns the XFile that the user selected locally. + /// returns the PickedFile that the user selected locally. /// /// `capture` is only supported in mobile browsers. /// See https://caniuse.com/#feat=html-media-capture @visibleForTesting - Future pickFile({ + Future pickFile({ String? accept, String? capture, }) { @@ -130,13 +130,13 @@ class ImagePickerPlugin extends ImagePickerPlatform { } /// Monitors an and returns the selected file. - Future _getSelectedFile(html.FileUploadInputElement input) { - final Completer _completer = Completer(); + Future _getSelectedFile(html.FileUploadInputElement input) { + final Completer _completer = Completer(); // Observe the input until we can return something input.onChange.first.then((event) { final objectUrl = _handleOnChangeEvent(event); if (!_completer.isCompleted && objectUrl != null) { - _completer.complete(XFile(objectUrl)); + _completer.complete(PickedFile(objectUrl)); } }); input.onError.first.then((event) { diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index c9940d773dcd..768f7e27ce77 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker_for_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 3.0.0 +version: 2.0.0 environment: sdk: ">=2.12.0 <3.0.0" From f4fbd0cf463564db9e97ec15f9c8510adc10855b Mon Sep 17 00:00:00 2001 From: BeMacized Date: Wed, 23 Jun 2021 10:51:36 +0200 Subject: [PATCH 05/25] [image_picker_for_web] Migration to cross_file --- .../image_picker_for_web/CHANGELOG.md | 5 +++++ .../lib/image_picker_for_web.dart | 18 +++++++++--------- .../image_picker_for_web/pubspec.yaml | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index 7b2c4077e28d..2ebe3350460b 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,3 +1,8 @@ +# 3.0.0 + +* Breaking change: + * Changed all usages of PickedFile to XFile + # 2.0.0 * Migrate to null safety. diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index 2fb66380e1d8..88114e0ba261 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -34,7 +34,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { ImagePickerPlatform.instance = ImagePickerPlugin(); } - /// Returns a [PickedFile] with the image that was picked. + /// Returns a [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]. @@ -47,7 +47,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// /// If no images were picked, the return value is null. @override - Future pickImage({ + Future pickImage({ required ImageSource source, double? maxWidth, double? maxHeight, @@ -58,7 +58,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { return pickFile(accept: _kAcceptImageMimeType, capture: capture); } - /// Returns a [PickedFile] containing the video that was picked. + /// Returns a [XFile] containing the video that was picked. /// /// The [source] argument controls where the video comes from. This can /// be either [ImageSource.camera] or [ImageSource.gallery]. @@ -71,7 +71,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// /// If no images were picked, the return value is null. @override - Future pickVideo({ + Future pickVideo({ required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, @@ -81,12 +81,12 @@ class ImagePickerPlugin extends ImagePickerPlatform { } /// Injects a file input with the specified accept+capture attributes, and - /// returns the PickedFile that the user selected locally. + /// returns the XFile that the user selected locally. /// /// `capture` is only supported in mobile browsers. /// See https://caniuse.com/#feat=html-media-capture @visibleForTesting - Future pickFile({ + Future pickFile({ String? accept, String? capture, }) { @@ -130,13 +130,13 @@ class ImagePickerPlugin extends ImagePickerPlatform { } /// Monitors an and returns the selected file. - Future _getSelectedFile(html.FileUploadInputElement input) { - final Completer _completer = Completer(); + Future _getSelectedFile(html.FileUploadInputElement input) { + final Completer _completer = Completer(); // Observe the input until we can return something input.onChange.first.then((event) { final objectUrl = _handleOnChangeEvent(event); if (!_completer.isCompleted && objectUrl != null) { - _completer.complete(PickedFile(objectUrl)); + _completer.complete(XFile(objectUrl)); } }); input.onError.first.then((event) { diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index 768f7e27ce77..c9940d773dcd 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker_for_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 2.0.0 +version: 3.0.0 environment: sdk: ">=2.12.0 <3.0.0" From 68e2ef837b458f01fb1cfbf48783404b3a4d2214 Mon Sep 17 00:00:00 2001 From: "Bodhi Mulders (BeMacized)" Date: Wed, 30 Jun 2021 18:47:40 +0200 Subject: [PATCH 06/25] Re-added old methods and marked them as deprecated. --- .../method_channel_image_picker.dart | 115 ++++- .../image_picker_platform.dart | 128 ++++- .../lib/src/types/lost_data_response.dart | 53 ++ .../lib/src/types/picked_file/base.dart | 62 +++ .../lib/src/types/picked_file/html.dart | 49 ++ .../lib/src/types/picked_file/io.dart | 41 ++ .../types/{ => picked_file}/lost_data.dart | 3 +- .../src/types/picked_file/picked_file.dart | 8 + .../src/types/picked_file/unsupported.dart | 18 + .../lib/src/types/types.dart | 3 +- .../new_method_channel_image_picker_test.dart | 473 +++++++++++++++++- .../test/picked_file_html_test.dart | 38 ++ .../test/picked_file_io_test.dart | 42 ++ 13 files changed, 1005 insertions(+), 28 deletions(-) create mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data_response.dart create mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart create mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart create mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart rename packages/image_picker/image_picker_platform_interface/lib/src/types/{ => picked_file}/lost_data.dart (96%) create mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/picked_file.dart create mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart create mode 100644 packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart create mode 100644 packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart index 9f36d5b07d03..e0c3f0d5cf52 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:image_picker_platform_interface/src/types/lost_data.dart'; import 'package:meta/meta.dart' show visibleForTesting; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; @@ -20,44 +19,44 @@ class MethodChannelImagePicker extends ImagePickerPlatform { MethodChannel get channel => _channel; @override - Future pickImage({ + Future pickImage({ required ImageSource source, double? maxWidth, double? maxHeight, int? imageQuality, CameraDevice preferredCameraDevice = CameraDevice.rear, }) async { - String? path = await _pickImagePath( + String? path = await _getImagePath( source: source, maxWidth: maxWidth, maxHeight: maxHeight, imageQuality: imageQuality, preferredCameraDevice: preferredCameraDevice, ); - return path != null ? XFile(path) : null; + return path != null ? PickedFile(path) : null; } @override - Future?> pickMultiImage({ + Future?> pickMultiImage({ double? maxWidth, double? maxHeight, int? imageQuality, }) async { - final List? paths = await _pickMultiImagePath( + final List? paths = await _getMultiImagePath( maxWidth: maxWidth, maxHeight: maxHeight, imageQuality: imageQuality, ); if (paths == null) return null; - final List files = []; + final List files = []; for (final path in paths) { - files.add(XFile(path)); + files.add(PickedFile(path)); } return files; } - Future?> _pickMultiImagePath({ + Future?> _getMultiImagePath({ double? maxWidth, double? maxHeight, int? imageQuality, @@ -85,7 +84,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { ); } - Future _pickImagePath({ + Future _getImagePath({ required ImageSource source, double? maxWidth, double? maxHeight, @@ -118,20 +117,20 @@ class MethodChannelImagePicker extends ImagePickerPlatform { } @override - Future pickVideo({ + Future pickVideo({ required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, }) async { - final String? path = await _pickVideoPath( + final String? path = await _getVideoPath( source: source, maxDuration: maxDuration, preferredCameraDevice: preferredCameraDevice, ); - return path != null ? XFile(path) : null; + return path != null ? PickedFile(path) : null; } - Future _pickVideoPath({ + Future _getVideoPath({ required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, @@ -176,6 +175,94 @@ class MethodChannelImagePicker extends ImagePickerPlatform { final String? path = result['path']; return LostData( + file: path != null ? PickedFile(path) : null, + exception: exception, + type: retrieveType, + ); + } + + @override + Future getImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) async { + String? path = await _getImagePath( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? XFile(path) : null; + } + + @override + Future?> getMultiImage({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) async { + final List? paths = await _getMultiImagePath( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + ); + if (paths == null) return null; + + final List files = []; + for (final path in paths) { + files.add(XFile(path)); + } + return files; + } + + @override + Future getVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) async { + final String? path = await _getVideoPath( + source: source, + maxDuration: maxDuration, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? XFile(path) : null; + } + + @override + Future getLostData() async { + final Map? result = + await _channel.invokeMapMethod('retrieve'); + + if (result == null) { + return LostDataResponse.empty(); + } + + assert(result.containsKey('path') ^ result.containsKey('errorCode')); + + final String? type = result['type']; + assert(type == kTypeImage || type == kTypeVideo); + + RetrieveType? retrieveType; + if (type == kTypeImage) { + retrieveType = RetrieveType.image; + } else if (type == kTypeVideo) { + retrieveType = RetrieveType.video; + } + + PlatformException? exception; + if (result.containsKey('errorCode')) { + exception = PlatformException( + code: result['errorCode'], message: result['errorMessage']); + } + + final String? path = result['path']; + + return LostDataResponse( file: path != null ? XFile(path) : null, exception: exception, type: retrieveType, diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart index 5f35c5300c94..b89e8c221abe 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart @@ -3,11 +3,9 @@ // found in the LICENSE file. import 'dart:async'; -import 'package:cross_file/cross_file.dart'; -import 'package:image_picker_platform_interface/src/types/lost_data.dart'; +import 'package:cross_file/cross_file.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - import 'package:image_picker_platform_interface/src/method_channel/method_channel_image_picker.dart'; import 'package:image_picker_platform_interface/src/types/types.dart'; @@ -42,7 +40,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { // Next version of the API. - /// Returns a [XFile] with the image that was picked. + /// Returns a [PickedFile] with the image that was picked. /// /// The `source` argument controls where the image comes from. This can /// be either [ImageSource.camera] or [ImageSource.gallery]. @@ -70,7 +68,8 @@ abstract class ImagePickerPlatform extends PlatformInterface { /// in this call. You can then call [retrieveLostData] when your app relaunches to retrieve the lost data. /// /// If no images were picked, the return value is null. - Future pickImage({ + @Deprecated('Switch to using getImage instead') + Future pickImage({ required ImageSource source, double? maxWidth, double? maxHeight, @@ -80,7 +79,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { throw UnimplementedError('pickImage() has not been implemented.'); } - /// Returns a [List] with the images that were picked. + /// Returns a [List] with the images that were picked. /// /// The images come from the [ImageSource.gallery]. /// @@ -98,7 +97,8 @@ abstract class ImagePickerPlatform extends PlatformInterface { /// a warning message will be logged. /// /// If no images were picked, the return value is null. - Future?> pickMultiImage({ + @Deprecated('Switch to using getMultiImage instead') + Future?> pickMultiImage({ double? maxWidth, double? maxHeight, int? imageQuality, @@ -106,7 +106,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { throw UnimplementedError('pickMultiImage() has not been implemented.'); } - /// Returns a [XFile] containing the video that was picked. + /// Returns a [PickedFile] containing the video that was picked. /// /// The [source] argument controls where the video comes from. This can /// be either [ImageSource.camera] or [ImageSource.gallery]. @@ -122,7 +122,8 @@ abstract class ImagePickerPlatform extends PlatformInterface { /// in this call. You can then call [retrieveLostData] when your app relaunches to retrieve the lost data. /// /// If no images were picked, the return value is null. - Future pickVideo({ + @Deprecated('Switch to using getVideo instead') + Future pickVideo({ required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, @@ -130,7 +131,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { throw UnimplementedError('pickVideo() has not been implemented.'); } - /// Retrieve the lost [XFile] file when [pickImage] or [pickVideo] failed because the MainActivity is destroyed. (Android only) + /// Retrieve the lost [PickedFile] file when [pickImage] or [pickVideo] failed because the MainActivity is destroyed. (Android only) /// /// Image or video can be lost if the MainActivity is destroyed. And there is no guarantee that the MainActivity is always alive. /// Call this method to retrieve the lost data and process the data according to your APP's business logic. @@ -146,4 +147,111 @@ abstract class ImagePickerPlatform extends PlatformInterface { Future retrieveLostData() { throw UnimplementedError('retrieveLostData() 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. + /// + /// 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. + /// + /// If no images were picked, the return value is null. + Future getImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) { + throw UnimplementedError('getImage() has not been implemented.'); + } + + /// Returns a [List] with the images that were picked. + /// + /// The images come from the [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 images, ranging from 0-100 + /// where 100 is the original/max quality. If `imageQuality` is null, the images 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. + /// + /// If no images were picked, the return value is null. + Future?> getMultiImage({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) { + throw UnimplementedError('getMultiImage() has not been implemented.'); + } + + /// Returns a [XFile] containing the video that was picked. + /// + /// The [source] argument controls where the video comes from. This can + /// be either [ImageSource.camera] or [ImageSource.gallery]. + /// + /// The [maxDuration] argument specifies the maximum duration of the captured video. If no [maxDuration] is specified, + /// the maximum duration will be infinite. + /// + /// 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]. + /// + /// In Android, the MainActivity can be destroyed for various fo 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. + /// + /// If no images were picked, the return value is null. + Future getVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) { + throw UnimplementedError('getVideo() has not been implemented.'); + } + + /// Retrieve the lost [XFile] file when [getImage], [getMultiImage] or [getVideo] failed because the MainActivity is + /// destroyed. (Android only) + /// + /// Image or video can be lost if the MainActivity is destroyed. And there is no guarantee that the MainActivity is + /// always alive. Call this method to retrieve the lost data and process the data according to your APP's business logic. + /// + /// Returns a [LostDataResponse] object if successfully retrieved the lost data. The [LostDataResponse] object can + /// represent either a successful image/video selection, or a failure. + /// + /// Calling this on a non-Android platform will throw [UnimplementedError] exception. + /// + /// See also: + /// * [LostDataResponse], for what's included in the response. + /// * [Android Activity Lifecycle](https://developer.android.com/reference/android/app/Activity.html), for more + /// information on MainActivity destruction. + Future getLostData() { + throw UnimplementedError('getLostData() has not been implemented.'); + } } diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data_response.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data_response.dart new file mode 100644 index 000000000000..576ad334bd35 --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data_response.dart @@ -0,0 +1,53 @@ +// 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 'package:cross_file/cross_file.dart'; +import 'package:flutter/services.dart'; +import 'package:image_picker_platform_interface/src/types/types.dart'; + +/// The response object of [ImagePicker.getLostData]. +/// +/// Only applies to Android. +/// See also: +/// * [ImagePicker.getLostData] for more details on retrieving lost data. +class LostDataResponse { + /// Creates an instance with the given [file], [exception], and [type]. Any of + /// the params may be null, but this is never considered to be empty. + LostDataResponse({this.file, this.exception, this.type}); + + /// Initializes an instance with all member params set to null and considered + /// to be empty. + LostDataResponse.empty() + : file = null, + exception = null, + type = null, + _empty = true; + + /// Whether it is an empty response. + /// + /// An empty response should have [file], [exception] and [type] to be null. + bool get isEmpty => _empty; + + /// The file that was lost in a previous [getImage], [getMultiImage] or [getVideo] call due to MainActivity being destroyed. + /// + /// Can be null if [exception] exists. + final XFile? file; + + /// The exception of the last [getImage], [getMultiImage] or [getVideo]. + /// + /// If the last [getImage], [getMultiImage] or [getVideo] threw some exception before the MainActivity destruction, + /// this variable keeps that exception. + /// You should handle this exception as if the [getImage], [getMultiImage] or [getVideo] got an exception when + /// the MainActivity was not destroyed. + /// + /// Note that it is not the exception that caused the destruction of the MainActivity. + final PlatformException? exception; + + /// Can either be [RetrieveType.image] or [RetrieveType.video]; + /// + /// If the lost data is empty, this will be null. + final RetrieveType? type; + + bool _empty = false; +} diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart new file mode 100644 index 000000000000..de259e0611dd --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart @@ -0,0 +1,62 @@ +// 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:convert'; +import 'dart:typed_data'; + +import 'package:meta/meta.dart'; + +/// The interface for a PickedFile. +/// +/// A PickedFile is a container that wraps the path of a selected +/// file by the user and (in some platforms, like web) the bytes +/// with the contents of the file. +/// +/// This class is a very limited subset of dart:io [File], so all +/// the methods should seem familiar. +@immutable +abstract class PickedFileBase { + /// Construct a PickedFile + PickedFileBase(String path); + + /// Get the path of the picked file. + /// + /// This should only be used as a backwards-compatibility clutch + /// for mobile apps, or cosmetic reasons only (to show the user + /// the path they've picked). + /// + /// Accessing the data contained in the picked file by its path + /// is platform-dependant (and won't work on web), so use the + /// byte getters in the PickedFile instance instead. + String get path { + throw UnimplementedError('.path has not been implemented.'); + } + + /// Synchronously read the entire file contents as a string using the given [Encoding]. + /// + /// By default, `encoding` is [utf8]. + /// + /// Throws Exception if the operation fails. + Future readAsString({Encoding encoding = utf8}) { + throw UnimplementedError('readAsString() has not been implemented.'); + } + + /// Synchronously read the entire file contents as a list of bytes. + /// + /// Throws Exception if the operation fails. + Future readAsBytes() { + throw UnimplementedError('readAsBytes() has not been implemented.'); + } + + /// Create a new independent [Stream] for the contents of this file. + /// + /// If `start` is present, the file will be read from byte-offset `start`. Otherwise from the beginning (index 0). + /// + /// If `end` is present, only up to byte-index `end` will be read. Otherwise, until end of file. + /// + /// In order to make sure that system resources are freed, the stream must be read to completion or the subscription on the stream must be cancelled. + Stream openRead([int? start, int? end]) { + throw UnimplementedError('openRead() has not been implemented.'); + } +} diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart new file mode 100644 index 000000000000..24e1931008b6 --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart @@ -0,0 +1,49 @@ +// 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:convert'; +import 'dart:typed_data'; + +import 'package:http/http.dart' as http show readBytes; + +import './base.dart'; + +/// A PickedFile that works on web. +/// +/// It wraps the bytes of a selected file. +class PickedFile extends PickedFileBase { + final String path; + final Uint8List? _initBytes; + + /// Construct a PickedFile object from its ObjectUrl. + /// + /// Optionally, this can be initialized with `bytes` + /// so no http requests are performed to retrieve files later. + PickedFile(this.path, {Uint8List? bytes}) + : _initBytes = bytes, + super(path); + + Future get _bytes async { + if (_initBytes != null) { + return Future.value(UnmodifiableUint8ListView(_initBytes!)); + } + return http.readBytes(Uri.parse(path)); + } + + @override + Future readAsString({Encoding encoding = utf8}) async { + return encoding.decode(await _bytes); + } + + @override + Future readAsBytes() async { + return Future.value(await _bytes); + } + + @override + Stream openRead([int? start, int? end]) async* { + final bytes = await _bytes; + yield bytes.sublist(start ?? 0, end ?? bytes.length); + } +} diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart new file mode 100644 index 000000000000..7037b6b7121a --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart @@ -0,0 +1,41 @@ +// 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:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import './base.dart'; + +/// A PickedFile backed by a dart:io File. +class PickedFile extends PickedFileBase { + final File _file; + + /// Construct a PickedFile object backed by a dart:io File. + PickedFile(String path) + : _file = File(path), + super(path); + + @override + String get path { + return _file.path; + } + + @override + Future readAsString({Encoding encoding = utf8}) { + return _file.readAsString(encoding: encoding); + } + + @override + Future readAsBytes() { + return _file.readAsBytes(); + } + + @override + Stream openRead([int? start, int? end]) { + return _file + .openRead(start ?? 0, end) + .map((chunk) => Uint8List.fromList(chunk)); + } +} diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart similarity index 96% rename from packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data.dart rename to packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart index a270eece235e..64f6a1f27538 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:cross_file/cross_file.dart'; import 'package:flutter/services.dart'; import 'package:image_picker_platform_interface/src/types/types.dart'; @@ -32,7 +31,7 @@ class LostData { /// The file that was lost in a previous [pickImage] or [pickVideo] call due to MainActivity being destroyed. /// /// Can be null if [exception] exists. - final XFile? file; + final PickedFile? file; /// The exception of the last [pickImage] or [pickVideo]. /// diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/picked_file.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/picked_file.dart new file mode 100644 index 000000000000..c8c9e5a0ac79 --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/picked_file.dart @@ -0,0 +1,8 @@ +// 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. + +export 'lost_data.dart'; +export 'unsupported.dart' + if (dart.library.html) 'html.dart' + if (dart.library.io) 'io.dart'; diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart new file mode 100644 index 000000000000..ad3ed6a4f86a --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart @@ -0,0 +1,18 @@ +// 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 './base.dart'; + +/// A PickedFile is a cross-platform, simplified File abstraction. +/// +/// It wraps the bytes of a selected file, and its (platform-dependant) path. +class PickedFile extends PickedFileBase { + /// Construct a PickedFile object, from its `bytes`. + /// + /// Optionally, you may pass a `path`. See caveats in [PickedFileBase.path]. + PickedFile(String path) : super(path) { + throw UnimplementedError( + 'PickedFile is not available in your current platform.'); + } +} diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart index 18d0ff81b76a..ad7cd3fbcaab 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart @@ -5,7 +5,8 @@ export 'camera_device.dart'; export 'image_source.dart'; export 'retrieve_type.dart'; -export 'lost_data.dart'; +export 'picked_file/picked_file.dart'; +export 'lost_data_response.dart'; /// Denotes that an image is being picked. const String kTypeImage = 'image'; diff --git a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart index 83ae6fac9071..070ffb22ff8d 100644 --- a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart @@ -342,7 +342,7 @@ void main() { }); }); - group('#pickVideoPath', () { + group('#pickVideo', () { test('passes the image source argument correctly', () async { await picker.pickVideo(source: ImageSource.camera); await picker.pickVideo(source: ImageSource.gallery); @@ -497,5 +497,476 @@ void main() { expect(picker.retrieveLostData(), throwsAssertionError); }); }); + + group('#getImage', () { + test('passes the image source argument correctly', () async { + await picker.getImage(source: ImageSource.camera); + await picker.getImage(source: ImageSource.gallery); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 1, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0 + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + await picker.getImage(source: ImageSource.camera); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + ); + await picker.getImage( + source: ImageSource.camera, + maxHeight: 10.0, + ); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + imageQuality: 70, + ); + await picker.getImage( + source: ImageSource.camera, + maxHeight: 10.0, + imageQuality: 70, + ); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'cameraDevice': 0 + }), + ], + ); + }); + + test('does not accept a invalid imageQuality argument', () { + expect( + () => picker.getImage(imageQuality: -1, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => + picker.getImage(imageQuality: 101, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => picker.getImage(imageQuality: -1, source: ImageSource.camera), + throwsArgumentError, + ); + + expect( + () => picker.getImage(imageQuality: 101, source: ImageSource.camera), + throwsArgumentError, + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.getImage(source: ImageSource.camera, maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.getImage(source: ImageSource.camera, maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + picker.channel + .setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.getImage(source: ImageSource.gallery), isNull); + expect(await picker.getImage(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.getImage(source: ImageSource.camera); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.getImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 1, + }), + ], + ); + }); + }); + + group('#getMultiImage', () { + test('calls the method correctly', () async { + returnValue = ['0', '1']; + await picker.getMultiImage(); + + expect( + log, + [ + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + returnValue = ['0', '1']; + await picker.getMultiImage(); + await picker.getMultiImage( + maxWidth: 10.0, + ); + await picker.getMultiImage( + maxHeight: 10.0, + ); + await picker.getMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.getMultiImage( + maxWidth: 10.0, + imageQuality: 70, + ); + await picker.getMultiImage( + maxHeight: 10.0, + imageQuality: 70, + ); + await picker.getMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect( + log, + [ + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + }), + ], + ); + }); + + test('does not accept a negative width or height argument', () { + returnValue = ['0', '1']; + expect( + () => picker.getMultiImage(maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.getMultiImage(maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('does not accept a invalid imageQuality argument', () { + returnValue = ['0', '1']; + expect( + () => picker.getMultiImage(imageQuality: -1), + throwsArgumentError, + ); + + expect( + () => picker.getMultiImage(imageQuality: 101), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + picker.channel + .setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.getMultiImage(), isNull); + expect(await picker.getMultiImage(), isNull); + }); + }); + + group('#getVideo', () { + test('passes the image source argument correctly', () async { + await picker.getVideo(source: ImageSource.camera); + await picker.getVideo(source: ImageSource.gallery); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'cameraDevice': 0, + 'maxDuration': null, + }), + isMethodCall('pickVideo', arguments: { + 'source': 1, + 'cameraDevice': 0, + 'maxDuration': null, + }), + ], + ); + }); + + test('passes the duration argument correctly', () async { + await picker.getVideo(source: ImageSource.camera); + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(seconds: 10), + ); + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(minutes: 1), + ); + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(hours: 1), + ); + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': null, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 10, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 60, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 3600, + 'cameraDevice': 0, + }), + ], + ); + }); + + test('handles a null video path response gracefully', () async { + picker.channel + .setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.getVideo(source: ImageSource.gallery), isNull); + expect(await picker.getVideo(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.getVideo(source: ImageSource.camera); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'cameraDevice': 0, + 'maxDuration': null, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.getVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front, + ); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': null, + 'cameraDevice': 1, + }), + ], + ); + }); + }); + + group('#getLostData', () { + test('getLostData get success response', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'image', + 'path': '/example/path', + }; + }); + // ignore: deprecated_member_use_from_same_package + final LostDataResponse response = await picker.getLostData(); + expect(response.type, RetrieveType.image); + expect(response.file, isNotNull); + expect(response.file!.path, '/example/path'); + }); + + test('getLostData get error response', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'video', + 'errorCode': 'test_error_code', + 'errorMessage': 'test_error_message', + }; + }); + // ignore: deprecated_member_use_from_same_package + final LostDataResponse response = await picker.getLostData(); + expect(response.type, RetrieveType.video); + expect(response.exception, isNotNull); + expect(response.exception!.code, 'test_error_code'); + expect(response.exception!.message, 'test_error_message'); + }); + + test('getLostData get null response', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + return null; + }); + expect((await picker.getLostData()).isEmpty, true); + }); + + test('getLostData get both path and error should throw', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'video', + 'errorCode': 'test_error_code', + 'errorMessage': 'test_error_message', + 'path': '/example/path', + }; + }); + expect(picker.getLostData(), throwsAssertionError); + }); + }); }); } diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart new file mode 100644 index 000000000000..0fa296ad4f2e --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart @@ -0,0 +1,38 @@ +// 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. + +@TestOn('chrome') // Uses web-only Flutter SDK + +import 'dart:convert'; +import 'dart:html' as html; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; + +final String expectedStringContents = 'Hello, world!'; +final List bytes = utf8.encode(expectedStringContents); +final html.File textFile = html.File([bytes], 'hello.txt'); +final String textFileUrl = html.Url.createObjectUrl(textFile); + +void main() { + group('Create with an objectUrl', () { + final pickedFile = PickedFile(textFileUrl); + + test('Can be read as a string', () async { + expect(await pickedFile.readAsString(), equals(expectedStringContents)); + }); + test('Can be read as bytes', () async { + expect(await pickedFile.readAsBytes(), equals(bytes)); + }); + + test('Can be read as a stream', () async { + expect(await pickedFile.openRead().first, equals(bytes)); + }); + + test('Stream can be sliced', () async { + expect( + await pickedFile.openRead(2, 5).first, equals(bytes.sublist(2, 5))); + }); + }); +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart new file mode 100644 index 000000000000..5e3edc454691 --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart @@ -0,0 +1,42 @@ +// 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. + +@TestOn('vm') // Uses dart:io + +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; + +final pathPrefix = +Directory.current.path.endsWith('test') ? './assets/' : './test/assets/'; +final path = pathPrefix + 'hello.txt'; +final String expectedStringContents = 'Hello, world!'; +final Uint8List bytes = Uint8List.fromList(utf8.encode(expectedStringContents)); +final File textFile = File(path); +final String textFilePath = textFile.path; + +void main() { + group('Create with an objectUrl', () { + final PickedFile pickedFile = PickedFile(textFilePath); + + test('Can be read as a string', () async { + expect(await pickedFile.readAsString(), equals(expectedStringContents)); + }); + test('Can be read as bytes', () async { + expect(await pickedFile.readAsBytes(), equals(bytes)); + }); + + test('Can be read as a stream', () async { + expect(await pickedFile.openRead().first, equals(bytes)); + }); + + test('Stream can be sliced', () async { + expect( + await pickedFile.openRead(2, 5).first, equals(bytes.sublist(2, 5))); + }); + }); +} \ No newline at end of file From a0ab00eae365e359cb9334b4233b1fab35a9266e Mon Sep 17 00:00:00 2001 From: "Bodhi Mulders (BeMacized)" Date: Wed, 30 Jun 2021 19:00:49 +0200 Subject: [PATCH 07/25] Update to match platform interface changes --- .../lib/image_picker_for_web.dart | 98 +++++++++++++++++-- .../test/image_picker_for_web_test.dart | 23 ++++- 2 files changed, 113 insertions(+), 8 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index 88114e0ba261..14d28ab70d07 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -34,7 +34,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { ImagePickerPlatform.instance = ImagePickerPlugin(); } - /// Returns a [XFile] with the image that was picked. + /// Returns a [PickedFile] with the image that was picked. /// /// The `source` argument controls where the image comes from. This can /// be either [ImageSource.camera] or [ImageSource.gallery]. @@ -47,7 +47,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// /// If no images were picked, the return value is null. @override - Future pickImage({ + Future pickImage({ required ImageSource source, double? maxWidth, double? maxHeight, @@ -58,7 +58,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { return pickFile(accept: _kAcceptImageMimeType, capture: capture); } - /// Returns a [XFile] containing the video that was picked. + /// Returns a [PickedFile] containing the video that was picked. /// /// The [source] argument controls where the video comes from. This can /// be either [ImageSource.camera] or [ImageSource.gallery]. @@ -71,7 +71,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// /// If no images were picked, the return value is null. @override - Future pickVideo({ + Future pickVideo({ required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, Duration? maxDuration, @@ -81,12 +81,13 @@ class ImagePickerPlugin extends ImagePickerPlatform { } /// Injects a file input with the specified accept+capture attributes, and - /// returns the XFile that the user selected locally. + /// returns the PickedFile that the user selected locally. /// /// `capture` is only supported in mobile browsers. /// See https://caniuse.com/#feat=html-media-capture @visibleForTesting - Future pickFile({ + @Deprecated("Switch to using getFile instead") + Future pickFile({ String? accept, String? capture, }) { @@ -96,6 +97,68 @@ class ImagePickerPlugin extends ImagePickerPlatform { return _getSelectedFile(input); } + /// 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]. + /// + /// Note that the `maxWidth`, `maxHeight` and `imageQuality` arguments are not supported on the web. If any of these arguments is supplied, it'll be silently ignored by the web version of the plugin. + /// + /// 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]. + /// + /// If no images were picked, the return value is null. + @override + Future getImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) { + String? capture = computeCaptureAttribute(source, preferredCameraDevice); + return getFile(accept: _kAcceptImageMimeType, capture: capture); + } + + /// Returns an [XFile] containing the video that was picked. + /// + /// The [source] argument controls where the video comes from. This can + /// be either [ImageSource.camera] or [ImageSource.gallery]. + /// + /// Note that the `maxDuration` argument is not supported on the web. If the argument is supplied, it'll be silently ignored by the web version of the plugin. + /// + /// 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]. + /// + /// If no images were picked, the return value is null. + @override + Future getVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) { + String? capture = computeCaptureAttribute(source, preferredCameraDevice); + return getFile(accept: _kAcceptVideoMimeType, capture: capture); + } + + /// Injects a file input with the specified accept+capture attributes, and + /// returns the PickedFile that the user selected locally. + /// + /// `capture` is only supported in mobile browsers. + /// See https://caniuse.com/#feat=html-media-capture + @visibleForTesting + Future getFile({ + String? accept, + String? capture, + }) { + html.FileUploadInputElement input = + createInputElement(accept, capture) as html.FileUploadInputElement; + _injectAndActivate(input); + return _getSelectedXFile(input); + } + // DOM methods /// Converts plugin configuration into a proper value for the `capture` attribute. @@ -130,7 +193,28 @@ class ImagePickerPlugin extends ImagePickerPlatform { } /// Monitors an and returns the selected file. - Future _getSelectedFile(html.FileUploadInputElement input) { + @Deprecated("Use _getSelectedXFile instead") + Future _getSelectedFile(html.FileUploadInputElement input) { + final Completer _completer = Completer(); + // Observe the input until we can return something + input.onChange.first.then((event) { + final objectUrl = _handleOnChangeEvent(event); + if (!_completer.isCompleted && objectUrl != null) { + _completer.complete(PickedFile(objectUrl)); + } + }); + input.onError.first.then((event) { + if (!_completer.isCompleted) { + _completer.completeError(event); + } + }); + // Note that we don't bother detaching from these streams, since the + // "input" gets re-created in the DOM every time the user needs to + // pick a file. + return _completer.future; + } + + Future _getSelectedXFile(html.FileUploadInputElement input) { final Completer _completer = Completer(); // Observe the input until we can return something input.onChange.first.then((event) { diff --git a/packages/image_picker/image_picker_for_web/test/image_picker_for_web_test.dart b/packages/image_picker/image_picker_for_web/test/image_picker_for_web_test.dart index fbdd1d38bee6..f70c273d03bc 100644 --- a/packages/image_picker/image_picker_for_web/test/image_picker_for_web_test.dart +++ b/packages/image_picker/image_picker_for_web/test/image_picker_for_web_test.dart @@ -24,7 +24,7 @@ void main() { plugin = ImagePickerPlugin(); }); - test('Can select a file', () async { + test('Can select a file (Deprecated)', () async { final mockInput = html.FileUploadInputElement(); final overrides = ImagePickerPluginTestOverrides() @@ -45,6 +45,27 @@ void main() { expect((await file).readAsBytes(), completion(isNotEmpty)); }); + test('Can select a file', () async { + final mockInput = html.FileUploadInputElement(); + + final overrides = ImagePickerPluginTestOverrides() + ..createInputElement = ((_, __) => mockInput) + ..getFileFromInput = ((_) => textFile); + + final plugin = ImagePickerPlugin(overrides: overrides); + + // Init the pick file dialog... + final file = plugin.getFile(); + + // Mock the browser behavior of selecting a file... + mockInput.dispatchEvent(html.Event('change')); + + // Now the file should be available + expect(file, completes); + // And readable + expect((await file).readAsBytes(), completion(isNotEmpty)); + }); + // There's no good way of detecting when the user has "aborted" the selection. test('computeCaptureAttribute', () { From a9871706a8dcfcc2f3ce49a403146fe95e88bb66 Mon Sep 17 00:00:00 2001 From: "Bodhi Mulders (BeMacized)" Date: Wed, 30 Jun 2021 19:26:40 +0200 Subject: [PATCH 08/25] Format & annotate deprecated classes. --- .../method_channel_image_picker.dart | 2 +- .../lib/src/types/picked_file/base.dart | 1 + .../lib/src/types/picked_file/html.dart | 1 + .../lib/src/types/picked_file/io.dart | 1 + .../lib/src/types/picked_file/lost_data.dart | 1 + .../src/types/picked_file/unsupported.dart | 1 + .../new_method_channel_image_picker_test.dart | 21 +++++++++---------- .../test/picked_file_html_test.dart | 2 +- .../test/picked_file_io_test.dart | 4 ++-- 9 files changed, 19 insertions(+), 15 deletions(-) diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart index e0c3f0d5cf52..02ad17907cea 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart @@ -236,7 +236,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { @override Future getLostData() async { final Map? result = - await _channel.invokeMapMethod('retrieve'); + await _channel.invokeMapMethod('retrieve'); if (result == null) { return LostDataResponse.empty(); diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart index de259e0611dd..0adaee63ceaa 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart @@ -16,6 +16,7 @@ import 'package:meta/meta.dart'; /// This class is a very limited subset of dart:io [File], so all /// the methods should seem familiar. @immutable +@Deprecated("Switch to using XFile instead") abstract class PickedFileBase { /// Construct a PickedFile PickedFileBase(String path); diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart index 24e1931008b6..c8218605e1bd 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart @@ -12,6 +12,7 @@ import './base.dart'; /// A PickedFile that works on web. /// /// It wraps the bytes of a selected file. +@Deprecated("Switch to using XFile instead") class PickedFile extends PickedFileBase { final String path; final Uint8List? _initBytes; diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart index 7037b6b7121a..b2905803ad0c 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart @@ -9,6 +9,7 @@ import 'dart:typed_data'; import './base.dart'; /// A PickedFile backed by a dart:io File. +@Deprecated("Switch to using XFile instead") class PickedFile extends PickedFileBase { final File _file; diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart index 64f6a1f27538..e29ae409d6d5 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart @@ -10,6 +10,7 @@ import 'package:image_picker_platform_interface/src/types/types.dart'; /// Only applies to Android. /// See also: /// * [ImagePicker.retrieveLostData] for more details on retrieving lost data. +@Deprecated("Switch to using LostDataResponse instead") class LostData { /// Creates an instance with the given [file], [exception], and [type]. Any of /// the params may be null, but this is never considered to be empty. diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart index ad3ed6a4f86a..b2fb6873de3b 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart @@ -7,6 +7,7 @@ import './base.dart'; /// A PickedFile is a cross-platform, simplified File abstraction. /// /// It wraps the bytes of a selected file, and its (platform-dependant) path. +@Deprecated("Switch to using XFile instead") class PickedFile extends PickedFileBase { /// Construct a PickedFile object, from its `bytes`. /// diff --git a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart index 070ffb22ff8d..800d4f7b12c5 100644 --- a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart @@ -614,35 +614,34 @@ void main() { test('does not accept a invalid imageQuality argument', () { expect( - () => picker.getImage(imageQuality: -1, source: ImageSource.gallery), + () => picker.getImage(imageQuality: -1, source: ImageSource.gallery), throwsArgumentError, ); expect( - () => - picker.getImage(imageQuality: 101, source: ImageSource.gallery), + () => picker.getImage(imageQuality: 101, source: ImageSource.gallery), throwsArgumentError, ); expect( - () => picker.getImage(imageQuality: -1, source: ImageSource.camera), + () => picker.getImage(imageQuality: -1, source: ImageSource.camera), throwsArgumentError, ); expect( - () => picker.getImage(imageQuality: 101, source: ImageSource.camera), + () => picker.getImage(imageQuality: 101, source: ImageSource.camera), throwsArgumentError, ); }); test('does not accept a negative width or height argument', () { expect( - () => picker.getImage(source: ImageSource.camera, maxWidth: -1.0), + () => picker.getImage(source: ImageSource.camera, maxWidth: -1.0), throwsArgumentError, ); expect( - () => picker.getImage(source: ImageSource.camera, maxHeight: -1.0), + () => picker.getImage(source: ImageSource.camera, maxHeight: -1.0), throwsArgumentError, ); }); @@ -781,12 +780,12 @@ void main() { test('does not accept a negative width or height argument', () { returnValue = ['0', '1']; expect( - () => picker.getMultiImage(maxWidth: -1.0), + () => picker.getMultiImage(maxWidth: -1.0), throwsArgumentError, ); expect( - () => picker.getMultiImage(maxHeight: -1.0), + () => picker.getMultiImage(maxHeight: -1.0), throwsArgumentError, ); }); @@ -794,12 +793,12 @@ void main() { test('does not accept a invalid imageQuality argument', () { returnValue = ['0', '1']; expect( - () => picker.getMultiImage(imageQuality: -1), + () => picker.getMultiImage(imageQuality: -1), throwsArgumentError, ); expect( - () => picker.getMultiImage(imageQuality: 101), + () => picker.getMultiImage(imageQuality: 101), throwsArgumentError, ); }); diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart index 0fa296ad4f2e..7721f66148e0 100644 --- a/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart @@ -35,4 +35,4 @@ void main() { await pickedFile.openRead(2, 5).first, equals(bytes.sublist(2, 5))); }); }); -} \ No newline at end of file +} diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart index 5e3edc454691..d366204c36bf 100644 --- a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart @@ -12,7 +12,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; final pathPrefix = -Directory.current.path.endsWith('test') ? './assets/' : './test/assets/'; + Directory.current.path.endsWith('test') ? './assets/' : './test/assets/'; final path = pathPrefix + 'hello.txt'; final String expectedStringContents = 'Hello, world!'; final Uint8List bytes = Uint8List.fromList(utf8.encode(expectedStringContents)); @@ -39,4 +39,4 @@ void main() { await pickedFile.openRead(2, 5).first, equals(bytes.sublist(2, 5))); }); }); -} \ No newline at end of file +} From 217949d98a93bc97e912c22c1f9d5c7406c63dac Mon Sep 17 00:00:00 2001 From: "Bodhi Mulders (BeMacized)" Date: Wed, 30 Jun 2021 19:28:36 +0200 Subject: [PATCH 09/25] Updated to match platform interface changes --- .../image_picker/example/lib/main.dart | 12 +- .../image_picker/lib/image_picker.dart | 152 ++++++- .../image_picker/test/image_picker_test.dart | 421 +++++++++++++++++- 3 files changed, 573 insertions(+), 12 deletions(-) diff --git a/packages/image_picker/image_picker/example/lib/main.dart b/packages/image_picker/image_picker/example/lib/main.dart index 390cdf764deb..2d5fd9aee4a7 100755 --- a/packages/image_picker/image_picker/example/lib/main.dart +++ b/packages/image_picker/image_picker/example/lib/main.dart @@ -36,9 +36,9 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - List? _imageFileList; + List? _imageFileList; - set _imageFile(PickedFile? value) { + set _imageFile(XFile? value) { _imageFileList = value == null ? null : [value]; } @@ -84,14 +84,14 @@ class _MyHomePageState extends State { await _controller!.setVolume(0.0); } if (isVideo) { - final XFile? file = await _picker.getVideo( + final XFile? file = await _picker.pickVideo( source: source, maxDuration: const Duration(seconds: 10)); await _playVideo(file); } else if (isMultiImage) { await _displayPickImageDialog(context!, (double? maxWidth, double? maxHeight, int? quality) async { try { - final pickedFileList = await _picker.getMultiImage( + final pickedFileList = await _picker.pickMultiImage( maxWidth: maxWidth, maxHeight: maxHeight, imageQuality: quality, @@ -109,7 +109,7 @@ class _MyHomePageState extends State { await _displayPickImageDialog(context!, (double? maxWidth, double? maxHeight, int? quality) async { try { - final pickedFile = await _picker.getImage( + final pickedFile = await _picker.pickImage( source: source, maxWidth: maxWidth, maxHeight: maxHeight, @@ -214,7 +214,7 @@ class _MyHomePageState extends State { } Future retrieveLostData() async { - final LostData response = await _picker.getLostData(); + final LostDataResponse response = await _picker.retrieveLostData(); if (response.isEmpty) { return; } diff --git a/packages/image_picker/image_picker/lib/image_picker.dart b/packages/image_picker/image_picker/lib/image_picker.dart index 8406a9d354ca..1769ab92f15c 100755 --- a/packages/image_picker/image_picker/lib/image_picker.dart +++ b/packages/image_picker/image_picker/lib/image_picker.dart @@ -15,6 +15,8 @@ export 'package:image_picker_platform_interface/image_picker_platform_interface. ImageSource, CameraDevice, LostData, + LostDataResponse, + PickedFile, XFile, RetrieveType; @@ -25,9 +27,9 @@ class ImagePicker { @visibleForTesting static ImagePickerPlatform get platform => ImagePickerPlatform.instance; - /// Returns a [XFile] object wrapping the image that was picked. + /// Returns a [PickedFile] object wrapping the image that was picked. /// - /// The returned [XFile] is intended to be used within a single APP session. Do not save the file path and use it across sessions. + /// The returned [PickedFile] is intended to be used within a single APP session. Do not save the file path and use it across sessions. /// /// The `source` argument controls where the image comes from. This can /// be either [ImageSource.camera] or [ImageSource.gallery]. @@ -59,6 +61,7 @@ class ImagePicker { /// the camera or photos gallery, no camera is available, plugin is already in use, /// temporary file could not be created (iOS only), plugin activity could not /// be allocated (Android only) or due to an unknown error. + @Deprecated('Switch to using pickImage instead') Future getImage({ required ImageSource source, double? maxWidth, @@ -99,6 +102,7 @@ class ImagePicker { /// be allocated (Android only) or due to an unknown error. /// /// See also [getImage] to allow users to only pick a single image. + @Deprecated('Switch to using pickMultiImage instead') Future?> getMultiImage({ double? maxWidth, double? maxHeight, @@ -113,7 +117,7 @@ class ImagePicker { /// Returns a [PickedFile] object wrapping the video that was picked. /// - /// The returned [XFile] is intended to be used within a single APP session. Do not save the file path and use it across sessions. + /// The returned [PickedFile] is intended to be used within a single APP session. Do not save the file path and use it across sessions. /// /// The [source] argument controls where the video comes from. This can /// be either [ImageSource.camera] or [ImageSource.gallery]. @@ -133,6 +137,7 @@ class ImagePicker { /// temporary file could not be created and video could not be cached (iOS only), /// plugin activity could not be allocated (Android only) or due to an unknown error. /// + @Deprecated('Switch to using pickVideo instead') Future getVideo({ required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, @@ -145,7 +150,7 @@ class ImagePicker { ); } - /// Retrieve the lost [XFile] when [selectImage] or [selectVideo] failed because the MainActivity is destroyed. (Android only) + /// Retrieve the lost [PickedFile] when [selectImage] or [selectVideo] failed because the MainActivity is destroyed. (Android only) /// /// Image or video can be lost if the MainActivity is destroyed. And there is no guarantee that the MainActivity is always alive. /// Call this method to retrieve the lost data and process the data according to your APP's business logic. @@ -158,7 +163,146 @@ class ImagePicker { /// See also: /// * [LostData], for what's included in the response. /// * [Android Activity Lifecycle](https://developer.android.com/reference/android/app/Activity.html), for more information on MainActivity destruction. + @Deprecated('Switch to using retrieveLostData instead') Future getLostData() { return platform.retrieveLostData(); } + + /// Returns an [XFile] object wrapping the image that was picked. + /// + /// The returned [XFile] is intended to be used within a single APP session. Do not save the file path and use it across sessions. + /// + /// 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 and on Android PNG and WebP, too. 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. + /// + /// 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. + /// + /// See also [pickMultiImage] to allow users to select multiple images at once. + /// + /// The method could throw [PlatformException] if the app does not have permission to access + /// the camera or photos gallery, no camera is available, plugin is already in use, + /// temporary file could not be created (iOS only), plugin activity could not + /// be allocated (Android only) or due to an unknown error. + Future pickImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) { + return platform.getImage( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice, + ); + } + + /// Returns a [List] object wrapping the images that were picked. + /// + /// The returned [List] is intended to be used within a single APP session. Do not save the file path and use it across sessions. + /// + /// 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. + /// + /// This method is not supported in iOS versions lower than 14. + /// + /// If specified, the images will be at most `maxWidth` wide and + /// `maxHeight` tall. Otherwise the images will be returned at it's + /// original width and height. + /// The `imageQuality` argument modifies the quality of the images, ranging from 0-100 + /// where 100 is the original/max quality. If `imageQuality` is null, the images with + /// the original quality will be returned. Compression is only supported for certain + /// 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. + /// + /// The method could throw [PlatformException] if the app does not have permission to access + /// the camera or photos gallery, no camera is available, plugin is already in use, + /// temporary file could not be created (iOS only), plugin activity could not + /// be allocated (Android only) or due to an unknown error. + /// + /// See also [pickImage] to allow users to only pick a single image. + Future?> pickMultiImage({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) { + return platform.getMultiImage( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + ); + } + + /// Returns an [XFile] object wrapping the video that was picked. + /// + /// The returned [XFile] is intended to be used within a single APP session. Do not save the file path and use it across sessions. + /// + /// The [source] argument controls where the video comes from. This can + /// be either [ImageSource.camera] or [ImageSource.gallery]. + /// + /// The [maxDuration] argument specifies the maximum duration of the captured video. If no [maxDuration] is specified, + /// the maximum duration will be infinite. + /// + /// 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]. + /// + /// In Android, the MainActivity can be destroyed for various fo 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. + /// + /// The method could throw [PlatformException] if the app does not have permission to access + /// the camera or photos gallery, no camera is available, plugin is already in use, + /// temporary file could not be created and video could not be cached (iOS only), + /// plugin activity could not be allocated (Android only) or due to an unknown error. + /// + Future pickVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) { + return platform.getVideo( + source: source, + preferredCameraDevice: preferredCameraDevice, + maxDuration: maxDuration, + ); + } + + /// Retrieve the lost [XFile] when [pickImage], [pickMultiImage] or [pickVideo] failed because the MainActivity + /// is destroyed. (Android only) + /// + /// Image or video can be lost if the MainActivity is destroyed. And there is no guarantee that the MainActivity is always alive. + /// Call this method to retrieve the lost data and process the data according to your APP's business logic. + /// + /// Returns a [LostDataResponse] object if successfully retrieved the lost data. The [LostDataResponse] object can \ + /// represent either a successful image/video selection, or a failure. + /// + /// Calling this on a non-Android platform will throw [UnimplementedError] exception. + /// + /// See also: + /// * [LostDataResponse], for what's included in the response. + /// * [Android Activity Lifecycle](https://developer.android.com/reference/android/app/Activity.html), for more information on MainActivity destruction. + Future retrieveLostData() { + return platform.getLostData(); + } } diff --git a/packages/image_picker/image_picker/test/image_picker_test.dart b/packages/image_picker/image_picker/test/image_picker_test.dart index d83b403d1d45..e5447ba7260b 100644 --- a/packages/image_picker/image_picker/test/image_picker_test.dart +++ b/packages/image_picker/image_picker/test/image_picker_test.dart @@ -29,7 +29,7 @@ void main() { ImagePickerPlatform.instance = savedPlatform; }); - group('#Single image/video', () { + group('#Single image/video (Deprecated)', () { setUp(() { channel.setMockMethodCallHandler((MethodCall methodCall) async { log.add(methodCall); @@ -350,7 +350,7 @@ void main() { }); }); - group('Multi images', () { + group('#Multi images (Deprecated)', () { setUp(() { channel.setMockMethodCallHandler((MethodCall methodCall) async { log.add(methodCall); @@ -445,6 +445,423 @@ void main() { }); }); }); + + group('#Single image/video', () { + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return ''; + }); + + log.clear(); + }); + + group('#pickImage', () { + test('passes the image source argument correctly', () async { + await picker.pickImage(source: ImageSource.camera); + await picker.pickImage(source: ImageSource.gallery); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 1, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0 + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + await picker.pickImage(source: ImageSource.camera); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + ); + await picker.pickImage( + source: ImageSource.camera, + maxHeight: 10.0, + ); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.pickImage( + source: ImageSource.camera, maxWidth: 10.0, imageQuality: 70); + await picker.pickImage( + source: ImageSource.camera, maxHeight: 10.0, imageQuality: 70); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'cameraDevice': 0 + }), + ], + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + picker.pickImage(source: ImageSource.camera, maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + picker.pickImage(source: ImageSource.camera, maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.pickImage(source: ImageSource.gallery), isNull); + expect(await picker.pickImage(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.pickImage(source: ImageSource.camera); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.pickImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 1, + }), + ], + ); + }); + }); + + group('#pickVideo', () { + test('passes the image source argument correctly', () async { + await picker.pickVideo(source: ImageSource.camera); + await picker.pickVideo(source: ImageSource.gallery); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'cameraDevice': 0, + 'maxDuration': null, + }), + isMethodCall('pickVideo', arguments: { + 'source': 1, + 'cameraDevice': 0, + 'maxDuration': null, + }), + ], + ); + }); + + test('passes the duration argument correctly', () async { + await picker.pickVideo(source: ImageSource.camera); + await picker.pickVideo( + source: ImageSource.camera, + maxDuration: const Duration(seconds: 10)); + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(minutes: 1)); + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(hours: 1)); + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': null, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 10, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 60, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 3600, + 'cameraDevice': 0, + }), + ], + ); + }); + + test('handles a null video path response gracefully', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.pickVideo(source: ImageSource.gallery), isNull); + expect(await picker.pickVideo(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.pickVideo(source: ImageSource.camera); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'cameraDevice': 0, + 'maxDuration': null, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.pickVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': null, + 'cameraDevice': 1, + }), + ], + ); + }); + }); + + group('#retrieveLostData', () { + test('retrieveLostData get success response', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'image', + 'path': '/example/path', + }; + }); + final LostDataResponse response = await picker.retrieveLostData(); + expect(response.type, RetrieveType.image); + expect(response.file!.path, '/example/path'); + }); + + test('retrieveLostData get error response', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'video', + 'errorCode': 'test_error_code', + 'errorMessage': 'test_error_message', + }; + }); + final LostDataResponse response = await picker.retrieveLostData(); + expect(response.type, RetrieveType.video); + expect(response.exception!.code, 'test_error_code'); + expect(response.exception!.message, 'test_error_message'); + }); + + test('retrieveLostData get null response', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return null; + }); + expect((await picker.retrieveLostData()).isEmpty, true); + }); + + test('retrieveLostData get both path and error should throw', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'video', + 'errorCode': 'test_error_code', + 'errorMessage': 'test_error_message', + 'path': '/example/path', + }; + }); + expect(picker.retrieveLostData(), throwsAssertionError); + }); + }); + }); + + group('#Multi images', () { + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return []; + }); + log.clear(); + }); + + group('#pickMultiImage', () { + test('passes the width and height arguments correctly', () async { + await picker.pickMultiImage(); + await picker.pickMultiImage( + maxWidth: 10.0, + ); + await picker.pickMultiImage( + maxHeight: 10.0, + ); + await picker.pickMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.pickMultiImage( + maxWidth: 10.0, + imageQuality: 70, + ); + await picker.pickMultiImage( + maxHeight: 10.0, + imageQuality: 70, + ); + await picker.pickMultiImage( + maxWidth: 10.0, maxHeight: 20.0, imageQuality: 70); + + expect( + log, + [ + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + }), + ], + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + picker.pickMultiImage(maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + picker.pickMultiImage(maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.pickMultiImage(), isNull); + expect(await picker.pickMultiImage(), isNull); + }); + }); + }); }); } From 3fa10b9e56425e2db69864fce445f109b98f1d5d Mon Sep 17 00:00:00 2001 From: "Bodhi Mulders (BeMacized)" Date: Wed, 30 Jun 2021 19:32:44 +0200 Subject: [PATCH 10/25] Updated pubspec and changelog. --- .../image_picker_platform_interface/CHANGELOG.md | 16 ++++++++-------- .../image_picker_platform_interface/pubspec.yaml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index 36188838cc2d..6abbd4771fc7 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,11 +1,11 @@ -## 3.0.0 - -* Breaking changes: - * pickImage now returns XFile instead of PickedFile - * pickVideo now returns XFile instead of PickedFile - * changed LostData file parameter type to XFile - * removed PickedFile class and its tests - +## 2.2.0 + +* pickImage has been deprecated in favor of getImage +* pickVideo has been deprecated in favor of getVideo +* pickMultiImage has been deprecated in favor of getMultiImage +* getImage, getVideo and getMultiImage functions have been added to return XFile instances instead of PickedFile instances. +* PickedFile and related classes have been marked deprecated. + ## 2.1.0 * Add `pickMultiImage` method. diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index 8243bb2112c5..0953e76f03ee 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 3.0.0 +version: 2.2.0 environment: sdk: ">=2.12.0 <3.0.0" From addfe99f0ae6eabc4e680a32d28128da80ae9322 Mon Sep 17 00:00:00 2001 From: "Bodhi Mulders (BeMacized)" Date: Wed, 30 Jun 2021 19:33:30 +0200 Subject: [PATCH 11/25] Updated platform interface dependency version --- packages/image_picker/image_picker_for_web/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index c9940d773dcd..e6af2b3c4e60 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -20,7 +20,7 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - image_picker_platform_interface: ^2.0.0 + image_picker_platform_interface: ^2.2.0 meta: ^1.3.0 dev_dependencies: From aefff739dad7130ff0130fd013f44bdd7d72190a Mon Sep 17 00:00:00 2001 From: "Bodhi Mulders (BeMacized)" Date: Wed, 30 Jun 2021 19:35:59 +0200 Subject: [PATCH 12/25] Updated changelog and pubspec version --- packages/image_picker/image_picker_for_web/CHANGELOG.md | 5 ++--- packages/image_picker/image_picker_for_web/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index 2ebe3350460b..d7d752276988 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,7 +1,6 @@ -# 3.0.0 +# 2.1.0 -* Breaking change: - * Changed all usages of PickedFile to XFile +* Added getImage, getVideo and getFile methods that return XFile instances rather than PickedFile instances. # 2.0.0 diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index e6af2b3c4e60..d9b9c5e5cb86 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker_for_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 3.0.0 +version: 2.1.0 environment: sdk: ">=2.12.0 <3.0.0" From f5a4163c9d7b794004a7b8f49510b24512506f77 Mon Sep 17 00:00:00 2001 From: "Bodhi Mulders (BeMacized)" Date: Wed, 30 Jun 2021 19:39:02 +0200 Subject: [PATCH 13/25] Updated pubspec version and changelog --- packages/image_picker/image_picker/CHANGELOG.md | 6 +++--- packages/image_picker/image_picker/pubspec.yaml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 216809e2bab8..50156987b887 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,7 +1,7 @@ -## 0.9.0 +## 0.8.2 -* Breaking change: - * Changed all usages of PickedFile to XFile +* Added pickImage, pickVideo, pickMultiImage and retrieveLostData methods +that return XFile instances rather than PickedFile instances. ## 0.8.1+2 diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index c15cb9d67105..85baf4e08cd8 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -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.9.0 +version: 0.8.2 environment: sdk: ">=2.12.0 <3.0.0" @@ -25,7 +25,7 @@ dependencies: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 image_picker_for_web: ^2.0.0 - image_picker_platform_interface: ^2.1.0 + image_picker_platform_interface: ^2.2.0 dev_dependencies: flutter_test: From 2c4cd91c2f3f1923516ffe330e67817eaf02fac0 Mon Sep 17 00:00:00 2001 From: "Bodhi Mulders (BeMacized)" Date: Fri, 2 Jul 2021 10:36:10 +0200 Subject: [PATCH 14/25] Fix analysis issues for deprecations --- .../method_channel_image_picker.dart | 4 ++ .../image_picker_platform.dart | 5 ++- .../new_method_channel_image_picker_test.dart | 45 +++++++++++++++++++ .../test/picked_file_html_test.dart | 1 + .../test/picked_file_io_test.dart | 1 + 5 files changed, 54 insertions(+), 2 deletions(-) diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart index 02ad17907cea..54197ccb732e 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart @@ -19,6 +19,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { MethodChannel get channel => _channel; @override + @Deprecated('Switch to using getImage instead') Future pickImage({ required ImageSource source, double? maxWidth, @@ -37,6 +38,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { } @override + @Deprecated('Switch to using getMultiImage instead') Future?> pickMultiImage({ double? maxWidth, double? maxHeight, @@ -117,6 +119,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { } @override + @Deprecated('Switch to using getVideo instead') Future pickVideo({ required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, @@ -146,6 +149,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { } @override + @Deprecated("Switch to using getLostData instead") Future retrieveLostData() async { final Map? result = await _channel.invokeMapMethod('retrieve'); diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart index b89e8c221abe..9b3b5d1cf180 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart @@ -144,6 +144,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { /// See also: /// * [LostData], for what's included in the response. /// * [Android Activity Lifecycle](https://developer.android.com/reference/android/app/Activity.html), for more information on MainActivity destruction. + @Deprecated("Switch to using getLostData instead") Future retrieveLostData() { throw UnimplementedError('retrieveLostData() has not been implemented.'); } @@ -173,7 +174,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { /// to work on an Android device. /// /// 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. + /// 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 getImage({ @@ -225,7 +226,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { /// Defaults to [CameraDevice.rear]. /// /// In Android, the MainActivity can be destroyed for various fo 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. + /// 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 getVideo({ diff --git a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart index 800d4f7b12c5..7850a3011f9c 100644 --- a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart @@ -29,7 +29,9 @@ void main() { group('#pickImage', () { test('passes the image source argument correctly', () async { + // ignore: deprecated_member_use_from_same_package await picker.pickImage(source: ImageSource.camera); + // ignore: deprecated_member_use_from_same_package await picker.pickImage(source: ImageSource.gallery); expect( @@ -54,30 +56,37 @@ void main() { }); test('passes the width and height arguments correctly', () async { + // ignore: deprecated_member_use_from_same_package await picker.pickImage(source: ImageSource.camera); + // ignore: deprecated_member_use_from_same_package await picker.pickImage( source: ImageSource.camera, maxWidth: 10.0, ); + // ignore: deprecated_member_use_from_same_package await picker.pickImage( source: ImageSource.camera, maxHeight: 10.0, ); + // ignore: deprecated_member_use_from_same_package await picker.pickImage( source: ImageSource.camera, maxWidth: 10.0, maxHeight: 20.0, ); + // ignore: deprecated_member_use_from_same_package await picker.pickImage( source: ImageSource.camera, maxWidth: 10.0, imageQuality: 70, ); + // ignore: deprecated_member_use_from_same_package await picker.pickImage( source: ImageSource.camera, maxHeight: 10.0, imageQuality: 70, ); + // ignore: deprecated_member_use_from_same_package await picker.pickImage( source: ImageSource.camera, maxWidth: 10.0, @@ -143,22 +152,26 @@ void main() { test('does not accept a invalid imageQuality argument', () { expect( + // ignore: deprecated_member_use_from_same_package () => picker.pickImage(imageQuality: -1, source: ImageSource.gallery), throwsArgumentError, ); expect( () => + // ignore: deprecated_member_use_from_same_package picker.pickImage(imageQuality: 101, source: ImageSource.gallery), throwsArgumentError, ); expect( + // ignore: deprecated_member_use_from_same_package () => picker.pickImage(imageQuality: -1, source: ImageSource.camera), throwsArgumentError, ); expect( + // ignore: deprecated_member_use_from_same_package () => picker.pickImage(imageQuality: 101, source: ImageSource.camera), throwsArgumentError, ); @@ -166,11 +179,13 @@ void main() { test('does not accept a negative width or height argument', () { expect( + // ignore: deprecated_member_use_from_same_package () => picker.pickImage(source: ImageSource.camera, maxWidth: -1.0), throwsArgumentError, ); expect( + // ignore: deprecated_member_use_from_same_package () => picker.pickImage(source: ImageSource.camera, maxHeight: -1.0), throwsArgumentError, ); @@ -180,11 +195,14 @@ void main() { picker.channel .setMockMethodCallHandler((MethodCall methodCall) => null); + // ignore: deprecated_member_use_from_same_package expect(await picker.pickImage(source: ImageSource.gallery), isNull); + // ignore: deprecated_member_use_from_same_package expect(await picker.pickImage(source: ImageSource.camera), isNull); }); test('camera position defaults to back', () async { + // ignore: deprecated_member_use_from_same_package await picker.pickImage(source: ImageSource.camera); expect( @@ -202,6 +220,7 @@ void main() { }); test('camera position can set to front', () async { + // ignore: deprecated_member_use_from_same_package await picker.pickImage( source: ImageSource.camera, preferredCameraDevice: CameraDevice.front); @@ -224,6 +243,7 @@ void main() { group('#pickMultiImage', () { test('calls the method correctly', () async { returnValue = ['0', '1']; + // ignore: deprecated_member_use_from_same_package await picker.pickMultiImage(); expect( @@ -240,25 +260,32 @@ void main() { test('passes the width and height arguments correctly', () async { returnValue = ['0', '1']; + // ignore: deprecated_member_use_from_same_package await picker.pickMultiImage(); + // ignore: deprecated_member_use_from_same_package await picker.pickMultiImage( maxWidth: 10.0, ); + // ignore: deprecated_member_use_from_same_package await picker.pickMultiImage( maxHeight: 10.0, ); + // ignore: deprecated_member_use_from_same_package await picker.pickMultiImage( maxWidth: 10.0, maxHeight: 20.0, ); + // ignore: deprecated_member_use_from_same_package await picker.pickMultiImage( maxWidth: 10.0, imageQuality: 70, ); + // ignore: deprecated_member_use_from_same_package await picker.pickMultiImage( maxHeight: 10.0, imageQuality: 70, ); + // ignore: deprecated_member_use_from_same_package await picker.pickMultiImage( maxWidth: 10.0, maxHeight: 20.0, @@ -310,11 +337,13 @@ void main() { test('does not accept a negative width or height argument', () { returnValue = ['0', '1']; expect( + // ignore: deprecated_member_use_from_same_package () => picker.pickMultiImage(maxWidth: -1.0), throwsArgumentError, ); expect( + // ignore: deprecated_member_use_from_same_package () => picker.pickMultiImage(maxHeight: -1.0), throwsArgumentError, ); @@ -323,11 +352,13 @@ void main() { test('does not accept a invalid imageQuality argument', () { returnValue = ['0', '1']; expect( + // ignore: deprecated_member_use_from_same_package () => picker.pickMultiImage(imageQuality: -1), throwsArgumentError, ); expect( + // ignore: deprecated_member_use_from_same_package () => picker.pickMultiImage(imageQuality: 101), throwsArgumentError, ); @@ -337,14 +368,18 @@ void main() { picker.channel .setMockMethodCallHandler((MethodCall methodCall) => null); + // ignore: deprecated_member_use_from_same_package expect(await picker.pickMultiImage(), isNull); + // ignore: deprecated_member_use_from_same_package expect(await picker.pickMultiImage(), isNull); }); }); group('#pickVideo', () { test('passes the image source argument correctly', () async { + // ignore: deprecated_member_use_from_same_package await picker.pickVideo(source: ImageSource.camera); + // ignore: deprecated_member_use_from_same_package await picker.pickVideo(source: ImageSource.gallery); expect( @@ -365,15 +400,19 @@ void main() { }); test('passes the duration argument correctly', () async { + // ignore: deprecated_member_use_from_same_package await picker.pickVideo(source: ImageSource.camera); + // ignore: deprecated_member_use_from_same_package await picker.pickVideo( source: ImageSource.camera, maxDuration: const Duration(seconds: 10), ); + // ignore: deprecated_member_use_from_same_package await picker.pickVideo( source: ImageSource.camera, maxDuration: const Duration(minutes: 1), ); + // ignore: deprecated_member_use_from_same_package await picker.pickVideo( source: ImageSource.camera, maxDuration: const Duration(hours: 1), @@ -409,11 +448,14 @@ void main() { picker.channel .setMockMethodCallHandler((MethodCall methodCall) => null); + // ignore: deprecated_member_use_from_same_package expect(await picker.pickVideo(source: ImageSource.gallery), isNull); + // ignore: deprecated_member_use_from_same_package expect(await picker.pickVideo(source: ImageSource.camera), isNull); }); test('camera position defaults to back', () async { + // ignore: deprecated_member_use_from_same_package await picker.pickVideo(source: ImageSource.camera); expect( @@ -429,6 +471,7 @@ void main() { }); test('camera position can set to front', () async { + // ignore: deprecated_member_use_from_same_package await picker.pickVideo( source: ImageSource.camera, preferredCameraDevice: CameraDevice.front, @@ -482,6 +525,7 @@ void main() { picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { return null; }); + // ignore: deprecated_member_use_from_same_package expect((await picker.retrieveLostData()).isEmpty, true); }); @@ -494,6 +538,7 @@ void main() { 'path': '/example/path', }; }); + // ignore: deprecated_member_use_from_same_package expect(picker.retrieveLostData(), throwsAssertionError); }); }); diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart index 7721f66148e0..42fea59a6f81 100644 --- a/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart @@ -17,6 +17,7 @@ final String textFileUrl = html.Url.createObjectUrl(textFile); void main() { group('Create with an objectUrl', () { + // ignore: deprecated_member_use_from_same_package final pickedFile = PickedFile(textFileUrl); test('Can be read as a string', () async { diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart index d366204c36bf..d4ccca84092e 100644 --- a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart @@ -21,6 +21,7 @@ final String textFilePath = textFile.path; void main() { group('Create with an objectUrl', () { + // ignore: deprecated_member_use_from_same_package final PickedFile pickedFile = PickedFile(textFilePath); test('Can be read as a string', () async { From 29e0c40c73a1b0388cfe00ee5c2716f927abb2c6 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Wed, 7 Jul 2021 18:53:02 -0700 Subject: [PATCH 15/25] Remove Deprecation tags so we don't break consumers of this package once it gets published. --- .../CHANGELOG.md | 11 +++-- .../method_channel_image_picker.dart | 4 -- .../image_picker_platform.dart | 4 -- .../lib/src/types/picked_file/base.dart | 1 - .../lib/src/types/picked_file/html.dart | 1 - .../lib/src/types/picked_file/io.dart | 1 - .../lib/src/types/picked_file/lost_data.dart | 1 - .../src/types/picked_file/unsupported.dart | 1 - .../new_method_channel_image_picker_test.dart | 49 ------------------- .../test/picked_file_html_test.dart | 1 - .../test/picked_file_io_test.dart | 1 - 11 files changed, 6 insertions(+), 69 deletions(-) diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index 6abbd4771fc7..bd56f0ca77a6 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,10 +1,11 @@ ## 2.2.0 -* pickImage has been deprecated in favor of getImage -* pickVideo has been deprecated in favor of getVideo -* pickMultiImage has been deprecated in favor of getMultiImage -* getImage, getVideo and getMultiImage functions have been added to return XFile instances instead of PickedFile instances. -* PickedFile and related classes have been marked deprecated. +* Added new methods that return `XFile` (from `package:cross_file`) + * `getImage` (will deprecate `pickImage`) + * `getVideo` (will deprecate `pickVideo`) + * `getMultiImage` (will deprecate `pickMultiImage`) + +_`PickedFile` will also be marked as deprecated in an upcoming release._ ## 2.1.0 diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart index 54197ccb732e..02ad17907cea 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart @@ -19,7 +19,6 @@ class MethodChannelImagePicker extends ImagePickerPlatform { MethodChannel get channel => _channel; @override - @Deprecated('Switch to using getImage instead') Future pickImage({ required ImageSource source, double? maxWidth, @@ -38,7 +37,6 @@ class MethodChannelImagePicker extends ImagePickerPlatform { } @override - @Deprecated('Switch to using getMultiImage instead') Future?> pickMultiImage({ double? maxWidth, double? maxHeight, @@ -119,7 +117,6 @@ class MethodChannelImagePicker extends ImagePickerPlatform { } @override - @Deprecated('Switch to using getVideo instead') Future pickVideo({ required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, @@ -149,7 +146,6 @@ class MethodChannelImagePicker extends ImagePickerPlatform { } @override - @Deprecated("Switch to using getLostData instead") Future retrieveLostData() async { final Map? result = await _channel.invokeMapMethod('retrieve'); diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart index 9b3b5d1cf180..8f9ab99eae06 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart @@ -68,7 +68,6 @@ abstract class ImagePickerPlatform extends PlatformInterface { /// in this call. You can then call [retrieveLostData] when your app relaunches to retrieve the lost data. /// /// If no images were picked, the return value is null. - @Deprecated('Switch to using getImage instead') Future pickImage({ required ImageSource source, double? maxWidth, @@ -97,7 +96,6 @@ abstract class ImagePickerPlatform extends PlatformInterface { /// a warning message will be logged. /// /// If no images were picked, the return value is null. - @Deprecated('Switch to using getMultiImage instead') Future?> pickMultiImage({ double? maxWidth, double? maxHeight, @@ -122,7 +120,6 @@ abstract class ImagePickerPlatform extends PlatformInterface { /// in this call. You can then call [retrieveLostData] when your app relaunches to retrieve the lost data. /// /// If no images were picked, the return value is null. - @Deprecated('Switch to using getVideo instead') Future pickVideo({ required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, @@ -144,7 +141,6 @@ abstract class ImagePickerPlatform extends PlatformInterface { /// See also: /// * [LostData], for what's included in the response. /// * [Android Activity Lifecycle](https://developer.android.com/reference/android/app/Activity.html), for more information on MainActivity destruction. - @Deprecated("Switch to using getLostData instead") Future retrieveLostData() { throw UnimplementedError('retrieveLostData() has not been implemented.'); } diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart index 0adaee63ceaa..de259e0611dd 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart @@ -16,7 +16,6 @@ import 'package:meta/meta.dart'; /// This class is a very limited subset of dart:io [File], so all /// the methods should seem familiar. @immutable -@Deprecated("Switch to using XFile instead") abstract class PickedFileBase { /// Construct a PickedFile PickedFileBase(String path); diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart index c8218605e1bd..24e1931008b6 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart @@ -12,7 +12,6 @@ import './base.dart'; /// A PickedFile that works on web. /// /// It wraps the bytes of a selected file. -@Deprecated("Switch to using XFile instead") class PickedFile extends PickedFileBase { final String path; final Uint8List? _initBytes; diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart index b2905803ad0c..7037b6b7121a 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart @@ -9,7 +9,6 @@ import 'dart:typed_data'; import './base.dart'; /// A PickedFile backed by a dart:io File. -@Deprecated("Switch to using XFile instead") class PickedFile extends PickedFileBase { final File _file; diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart index e29ae409d6d5..64f6a1f27538 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart @@ -10,7 +10,6 @@ import 'package:image_picker_platform_interface/src/types/types.dart'; /// Only applies to Android. /// See also: /// * [ImagePicker.retrieveLostData] for more details on retrieving lost data. -@Deprecated("Switch to using LostDataResponse instead") class LostData { /// Creates an instance with the given [file], [exception], and [type]. Any of /// the params may be null, but this is never considered to be empty. diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart index b2fb6873de3b..ad3ed6a4f86a 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart @@ -7,7 +7,6 @@ import './base.dart'; /// A PickedFile is a cross-platform, simplified File abstraction. /// /// It wraps the bytes of a selected file, and its (platform-dependant) path. -@Deprecated("Switch to using XFile instead") class PickedFile extends PickedFileBase { /// Construct a PickedFile object, from its `bytes`. /// diff --git a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart index 7850a3011f9c..e5321abc0121 100644 --- a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart @@ -29,9 +29,7 @@ void main() { group('#pickImage', () { test('passes the image source argument correctly', () async { - // ignore: deprecated_member_use_from_same_package await picker.pickImage(source: ImageSource.camera); - // ignore: deprecated_member_use_from_same_package await picker.pickImage(source: ImageSource.gallery); expect( @@ -56,37 +54,30 @@ void main() { }); test('passes the width and height arguments correctly', () async { - // ignore: deprecated_member_use_from_same_package await picker.pickImage(source: ImageSource.camera); - // ignore: deprecated_member_use_from_same_package await picker.pickImage( source: ImageSource.camera, maxWidth: 10.0, ); - // ignore: deprecated_member_use_from_same_package await picker.pickImage( source: ImageSource.camera, maxHeight: 10.0, ); - // ignore: deprecated_member_use_from_same_package await picker.pickImage( source: ImageSource.camera, maxWidth: 10.0, maxHeight: 20.0, ); - // ignore: deprecated_member_use_from_same_package await picker.pickImage( source: ImageSource.camera, maxWidth: 10.0, imageQuality: 70, ); - // ignore: deprecated_member_use_from_same_package await picker.pickImage( source: ImageSource.camera, maxHeight: 10.0, imageQuality: 70, ); - // ignore: deprecated_member_use_from_same_package await picker.pickImage( source: ImageSource.camera, maxWidth: 10.0, @@ -152,26 +143,22 @@ void main() { test('does not accept a invalid imageQuality argument', () { expect( - // ignore: deprecated_member_use_from_same_package () => picker.pickImage(imageQuality: -1, source: ImageSource.gallery), throwsArgumentError, ); expect( () => - // ignore: deprecated_member_use_from_same_package picker.pickImage(imageQuality: 101, source: ImageSource.gallery), throwsArgumentError, ); expect( - // ignore: deprecated_member_use_from_same_package () => picker.pickImage(imageQuality: -1, source: ImageSource.camera), throwsArgumentError, ); expect( - // ignore: deprecated_member_use_from_same_package () => picker.pickImage(imageQuality: 101, source: ImageSource.camera), throwsArgumentError, ); @@ -179,13 +166,11 @@ void main() { test('does not accept a negative width or height argument', () { expect( - // ignore: deprecated_member_use_from_same_package () => picker.pickImage(source: ImageSource.camera, maxWidth: -1.0), throwsArgumentError, ); expect( - // ignore: deprecated_member_use_from_same_package () => picker.pickImage(source: ImageSource.camera, maxHeight: -1.0), throwsArgumentError, ); @@ -195,14 +180,11 @@ void main() { picker.channel .setMockMethodCallHandler((MethodCall methodCall) => null); - // ignore: deprecated_member_use_from_same_package expect(await picker.pickImage(source: ImageSource.gallery), isNull); - // ignore: deprecated_member_use_from_same_package expect(await picker.pickImage(source: ImageSource.camera), isNull); }); test('camera position defaults to back', () async { - // ignore: deprecated_member_use_from_same_package await picker.pickImage(source: ImageSource.camera); expect( @@ -220,7 +202,6 @@ void main() { }); test('camera position can set to front', () async { - // ignore: deprecated_member_use_from_same_package await picker.pickImage( source: ImageSource.camera, preferredCameraDevice: CameraDevice.front); @@ -243,7 +224,6 @@ void main() { group('#pickMultiImage', () { test('calls the method correctly', () async { returnValue = ['0', '1']; - // ignore: deprecated_member_use_from_same_package await picker.pickMultiImage(); expect( @@ -260,32 +240,25 @@ void main() { test('passes the width and height arguments correctly', () async { returnValue = ['0', '1']; - // ignore: deprecated_member_use_from_same_package await picker.pickMultiImage(); - // ignore: deprecated_member_use_from_same_package await picker.pickMultiImage( maxWidth: 10.0, ); - // ignore: deprecated_member_use_from_same_package await picker.pickMultiImage( maxHeight: 10.0, ); - // ignore: deprecated_member_use_from_same_package await picker.pickMultiImage( maxWidth: 10.0, maxHeight: 20.0, ); - // ignore: deprecated_member_use_from_same_package await picker.pickMultiImage( maxWidth: 10.0, imageQuality: 70, ); - // ignore: deprecated_member_use_from_same_package await picker.pickMultiImage( maxHeight: 10.0, imageQuality: 70, ); - // ignore: deprecated_member_use_from_same_package await picker.pickMultiImage( maxWidth: 10.0, maxHeight: 20.0, @@ -337,13 +310,11 @@ void main() { test('does not accept a negative width or height argument', () { returnValue = ['0', '1']; expect( - // ignore: deprecated_member_use_from_same_package () => picker.pickMultiImage(maxWidth: -1.0), throwsArgumentError, ); expect( - // ignore: deprecated_member_use_from_same_package () => picker.pickMultiImage(maxHeight: -1.0), throwsArgumentError, ); @@ -352,13 +323,11 @@ void main() { test('does not accept a invalid imageQuality argument', () { returnValue = ['0', '1']; expect( - // ignore: deprecated_member_use_from_same_package () => picker.pickMultiImage(imageQuality: -1), throwsArgumentError, ); expect( - // ignore: deprecated_member_use_from_same_package () => picker.pickMultiImage(imageQuality: 101), throwsArgumentError, ); @@ -368,18 +337,14 @@ void main() { picker.channel .setMockMethodCallHandler((MethodCall methodCall) => null); - // ignore: deprecated_member_use_from_same_package expect(await picker.pickMultiImage(), isNull); - // ignore: deprecated_member_use_from_same_package expect(await picker.pickMultiImage(), isNull); }); }); group('#pickVideo', () { test('passes the image source argument correctly', () async { - // ignore: deprecated_member_use_from_same_package await picker.pickVideo(source: ImageSource.camera); - // ignore: deprecated_member_use_from_same_package await picker.pickVideo(source: ImageSource.gallery); expect( @@ -400,19 +365,15 @@ void main() { }); test('passes the duration argument correctly', () async { - // ignore: deprecated_member_use_from_same_package await picker.pickVideo(source: ImageSource.camera); - // ignore: deprecated_member_use_from_same_package await picker.pickVideo( source: ImageSource.camera, maxDuration: const Duration(seconds: 10), ); - // ignore: deprecated_member_use_from_same_package await picker.pickVideo( source: ImageSource.camera, maxDuration: const Duration(minutes: 1), ); - // ignore: deprecated_member_use_from_same_package await picker.pickVideo( source: ImageSource.camera, maxDuration: const Duration(hours: 1), @@ -448,14 +409,11 @@ void main() { picker.channel .setMockMethodCallHandler((MethodCall methodCall) => null); - // ignore: deprecated_member_use_from_same_package expect(await picker.pickVideo(source: ImageSource.gallery), isNull); - // ignore: deprecated_member_use_from_same_package expect(await picker.pickVideo(source: ImageSource.camera), isNull); }); test('camera position defaults to back', () async { - // ignore: deprecated_member_use_from_same_package await picker.pickVideo(source: ImageSource.camera); expect( @@ -471,7 +429,6 @@ void main() { }); test('camera position can set to front', () async { - // ignore: deprecated_member_use_from_same_package await picker.pickVideo( source: ImageSource.camera, preferredCameraDevice: CameraDevice.front, @@ -498,7 +455,6 @@ void main() { 'path': '/example/path', }; }); - // ignore: deprecated_member_use_from_same_package final LostData response = await picker.retrieveLostData(); expect(response.type, RetrieveType.image); expect(response.file, isNotNull); @@ -513,7 +469,6 @@ void main() { 'errorMessage': 'test_error_message', }; }); - // ignore: deprecated_member_use_from_same_package final LostData response = await picker.retrieveLostData(); expect(response.type, RetrieveType.video); expect(response.exception, isNotNull); @@ -525,7 +480,6 @@ void main() { picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { return null; }); - // ignore: deprecated_member_use_from_same_package expect((await picker.retrieveLostData()).isEmpty, true); }); @@ -538,7 +492,6 @@ void main() { 'path': '/example/path', }; }); - // ignore: deprecated_member_use_from_same_package expect(picker.retrieveLostData(), throwsAssertionError); }); }); @@ -970,7 +923,6 @@ void main() { 'path': '/example/path', }; }); - // ignore: deprecated_member_use_from_same_package final LostDataResponse response = await picker.getLostData(); expect(response.type, RetrieveType.image); expect(response.file, isNotNull); @@ -985,7 +937,6 @@ void main() { 'errorMessage': 'test_error_message', }; }); - // ignore: deprecated_member_use_from_same_package final LostDataResponse response = await picker.getLostData(); expect(response.type, RetrieveType.video); expect(response.exception, isNotNull); diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart index 42fea59a6f81..7721f66148e0 100644 --- a/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart @@ -17,7 +17,6 @@ final String textFileUrl = html.Url.createObjectUrl(textFile); void main() { group('Create with an objectUrl', () { - // ignore: deprecated_member_use_from_same_package final pickedFile = PickedFile(textFileUrl); test('Can be read as a string', () async { diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart index d4ccca84092e..d366204c36bf 100644 --- a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart @@ -21,7 +21,6 @@ final String textFilePath = textFile.path; void main() { group('Create with an objectUrl', () { - // ignore: deprecated_member_use_from_same_package final PickedFile pickedFile = PickedFile(textFilePath); test('Can be read as a string', () async { From 4615db1f8d15e764932a1a6e2cb3b7103c1795a5 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Thu, 8 Jul 2021 14:56:52 +0200 Subject: [PATCH 16/25] Temporary platform interface reference dependency --- packages/image_picker/image_picker_for_web/pubspec.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index d9b9c5e5cb86..9f4c68e02cef 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -20,8 +20,10 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - image_picker_platform_interface: ^2.2.0 meta: ^1.3.0 + # TODO: CHANGE TO VERSION REFERENCE WHEN NEW INTERFACE HAS BEEN PUBLISHED + image_picker_platform_interface: + path: ../image_picker_platform_interface dev_dependencies: flutter_test: From 84ee0a2d84c6ae62ae0ad3b238b83924508661d3 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Thu, 8 Jul 2021 15:01:14 +0200 Subject: [PATCH 17/25] Temporary dependency update --- packages/image_picker/image_picker/pubspec.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 85baf4e08cd8..b9701e049889 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -24,8 +24,11 @@ dependencies: flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 - image_picker_for_web: ^2.0.0 - image_picker_platform_interface: ^2.2.0 + # TODO: CHANGE TO VERSION REFERENCES WHEN NEW VERSION OF INTERFACE AND WEB IMPL ARE PUBLISHED + image_picker_for_web: + path: ../image_picker_for_web + image_picker_platform_interface: + path: ../image_picker_platform_interface dev_dependencies: flutter_test: From faa1a9941a74a2cb310870df12a66cc970d652f3 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Thu, 8 Jul 2021 15:15:08 +0200 Subject: [PATCH 18/25] Implement PR feedback --- .../method_channel_image_picker.dart | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart index 02ad17907cea..bb9e18e78d83 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart @@ -49,11 +49,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { ); if (paths == null) return null; - final List files = []; - for (final path in paths) { - files.add(PickedFile(path)); - } - return files; + return paths.map((path) => PickedFile(path)).toList(); } Future?> _getMultiImagePath({ @@ -154,7 +150,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { return LostData.empty(); } - assert(result.containsKey('path') ^ result.containsKey('errorCode')); + assert(result.containsKey('path') != result.containsKey('errorCode')); final String? type = result['type']; assert(type == kTypeImage || type == kTypeVideo); @@ -212,11 +208,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { ); if (paths == null) return null; - final List files = []; - for (final path in paths) { - files.add(XFile(path)); - } - return files; + return paths.map((path) => XFile(path)).toList(); } @override @@ -242,7 +234,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { return LostDataResponse.empty(); } - assert(result.containsKey('path') ^ result.containsKey('errorCode')); + assert(result.containsKey('path') != result.containsKey('errorCode')); final String? type = result['type']; assert(type == kTypeImage || type == kTypeVideo); From 756633b2c17974b3279fef622ccb6b7f3a7d53eb Mon Sep 17 00:00:00 2001 From: "Bodhi Mulders (BeMacized)" Date: Tue, 13 Jul 2021 13:55:24 +0200 Subject: [PATCH 19/25] Update platform interface and web implementation dependency references --- packages/image_picker/image_picker/CHANGELOG.md | 3 +-- packages/image_picker/image_picker/pubspec.yaml | 7 ++----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 290e23130599..c106d6355042 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,7 +1,6 @@ ## 0.8.2 -* Added pickImage, pickVideo, pickMultiImage and retrieveLostData methods -that return XFile instances rather than PickedFile instances. +* Added pickImage, pickVideo, pickMultiImage and retrieveLostData methods that return XFile instances. ## 0.8.1+3 diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index b9701e049889..e5ecfeb22232 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -24,11 +24,8 @@ dependencies: flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 - # TODO: CHANGE TO VERSION REFERENCES WHEN NEW VERSION OF INTERFACE AND WEB IMPL ARE PUBLISHED - image_picker_for_web: - path: ../image_picker_for_web - image_picker_platform_interface: - path: ../image_picker_platform_interface + image_picker_for_web: ^2.1.0 + image_picker_platform_interface: ^2.2.0 dev_dependencies: flutter_test: From c7fd236bdcf2d58926f0500539d9de0c63e740bb Mon Sep 17 00:00:00 2001 From: "Bodhi Mulders (BeMacized)" Date: Tue, 13 Jul 2021 14:29:08 +0200 Subject: [PATCH 20/25] Update documentation --- packages/image_picker/image_picker/README.md | 86 ++++++-------------- 1 file changed, 25 insertions(+), 61 deletions(-) diff --git a/packages/image_picker/image_picker/README.md b/packages/image_picker/image_picker/README.md index 3b3746d9f63e..f78107e167aa 100755 --- a/packages/image_picker/image_picker/README.md +++ b/packages/image_picker/image_picker/README.md @@ -12,7 +12,7 @@ First, add `image_picker` as a [dependency in your pubspec.yaml file](https://fl ### iOS Starting with version **0.8.1** the iOS implementation uses PHPicker to pick (multiple) images on iOS 14 or higher. -As a result of implementing PHPicker it becomes impossible to pick HEIC images on the iOS simulator in iOS 14+. This is a known issue. Please test this on a real device, or test with non-HEIC images until Apple solves this issue.[63426347 - Apple known issue](https://www.google.com/search?q=63426347+apple&sxsrf=ALeKk01YnTMid5S0PYvhL8GbgXJ40ZS[…]t=gws-wiz&ved=0ahUKEwjKh8XH_5HwAhWL_rsIHUmHDN8Q4dUDCA8&uact=5) +As a result of implementing PHPicker it becomes impossible to pick HEIC images on the iOS simulator in iOS 14+. This is a known issue. Please test this on a real device, or test with non-HEIC images until Apple solves this issue.[63426347 - Apple known issue](https://www.google.com/search?q=63426347+apple&sxsrf=ALeKk01YnTMid5S0PYvhL8GbgXJ40ZS[…]t=gws-wiz&ved=0ahUKEwjKh8XH_5HwAhWL_rsIHUmHDN8Q4dUDCA8&uact=5) Add the following keys to your _Info.plist_ file, located in `/ios/Runner/Info.plist`: @@ -37,7 +37,19 @@ If you require your picked image to be stored permanently, it is your responsibi import 'package:image_picker/image_picker.dart'; ... - final PickedFile? pickedFile = await picker.getImage(source: ImageSource.camera); + final ImagePicker _picker = ImagePicker(); + // Pick an image + final XFile? image = await _picker.pickImage(source: ImageSource.gallery); + // Capture a photo + final XFile? photo = await _picker.pickImage(source: ImageSource.camera); + // Pick a video + final XFile? image = await _picker.pickVideo(source: ImageSource.gallery); + // Capture a video + final XFile? photo = await _picker.pickVideo(source: ImageSource.camera); + // Pick multiple images + final List? images = await _picker.pickMultiImage(source: ImageSource.gallery); + // Pick multiple photos + final List? photos = await _picker.pickMultiImage(source: ImageSource.camera); ... ``` @@ -46,9 +58,9 @@ import 'package:image_picker/image_picker.dart'; Android system -- although very rarely -- sometimes kills the MainActivity after the image_picker finishes. When this happens, we lost the data selected from the image_picker. You can use `retrieveLostData` to retrieve the lost data in this situation. For example: ```dart -Future retrieveLostData() async { - final LostData response = - await picker.getLostData(); +Future getLostData() async { + final LostDataResponse response = + await picker.retrieveLostData(); if (response.isEmpty) { return; } @@ -57,7 +69,7 @@ Future retrieveLostData() async { if (response.type == RetrieveType.video) { _handleVideo(response.file); } else { - _handleImage(response.file); + _handleImage(response.file); } }); } else { @@ -68,65 +80,17 @@ Future retrieveLostData() async { There's no way to detect when this happens, so calling this method at the right place is essential. We recommend to wire this into some kind of start up check. Please refer to the example app to see how we used it. -On Android, `getLostData` will only get the last picked image when picking multiple images, see: [#84634](https://github.com/flutter/flutter/issues/84634). +On Android, `retrieveLostData` will only get the last picked image when picking multiple images, see: [#84634](https://github.com/flutter/flutter/issues/84634). -## Deprecation warnings in `pickImage`, `pickVideo` and `LostDataResponse` +## Migrating to 0.8.2+ -Starting with version **0.6.7** of the image_picker plugin, the API of the plugin changed slightly to allow for web implementations to exist. - -The **old methods that returned `dart:io` File objects were marked as deprecated**, and a new set of methods that return [`PickedFile` objects](https://pub.dev/documentation/image_picker_platform_interface/latest/image_picker_platform_interface/PickedFile-class.html) were introduced. - -### How to migrate from to ^0.6.7 - -#### Instantiate the `ImagePicker` - -The new ImagePicker API does not rely in static methods anymore, so the first thing you'll need to do is to create a new instance of the plugin where you need it: - -```dart -final _picker = ImagePicker(); -``` +Starting with version **0.8.2** of the image_picker plugin, new methods have been added for picking files that return `XFile` instances (from the [cross_file](https://pub.dev/packages/cross_file) package) rather than the plugin's own `PickedFile` instances. While the previous methods still exist, it is already recommended to start migrating over to their new equivalents. Eventually, `PickedFile` and the methods that return instances of it will be deprecated and removed. #### Call the new methods -The new methods **receive the same parameters as before**, but they **return a `PickedFile`, instead of a `File`**. The `LostDataResponse` class has been replaced by the [`LostData` class](https://pub.dev/documentation/image_picker_platform_interface/latest/image_picker_platform_interface/LostData-class.html). - | Old API | New API | |---------|---------| -| `File image = await ImagePicker.pickImage(...)` | `PickedFile image = await _picker.getImage(...)` | -| `File video = await ImagePicker.pickVideo(...)` | `PickedFile video = await _picker.getVideo(...)` | -| `LostDataResponse response = await ImagePicker.retrieveLostData()` | `LostData response = await _picker.getLostData()` | - -#### `PickedFile` to `File` - -If your app needs dart:io `File` objects to operate, you may transform `PickedFile` to `File` like so: - -```dart -final pickedFile = await _picker.getImage(...); -final File file = File(pickedFile.path); -``` - -You may also retrieve the bytes from the pickedFile directly if needed: - -```dart -final bytes = await pickedFile.readAsBytes(); -``` - -#### Getting ready for the web platform - -Note that on the web platform (`kIsWeb == true`), `File` is not available, so the `path` of the `PickedFile` will point to a network resource instead: - -```dart -if (kIsWeb) { - image = Image.network(pickedFile.path); -} else { - image = Image.file(File(pickedFile.path)); -} -``` - -Alternatively, the code may be unified at the expense of memory utilization: - -```dart -image = Image.memory(await pickedFile.readAsBytes()) -``` - -Take a look at the changes to the `example` app introduced in version 0.6.7 to see the migration steps applied there. +| `PickedFile image = await _picker.getImage(...)` | `XFile image = await _picker.pickImage(...)` | +| `List images = await _picker.getMultiImage(...)` | `List images = await _picker.pickMultiImage(...)` | +| `PickedFile video = await _picker.getVideo(...)` | `XFile video = await _picker.pickVideo(...)` | +| `LostData response = await _picker.getLostData()` | `LostDataResponse response = await _picker.retrieveLostData()` | \ No newline at end of file From 8c89c99c353824dcd2eb9157a08605d5f25175a6 Mon Sep 17 00:00:00 2001 From: "Bodhi Mulders (BeMacized)" Date: Tue, 13 Jul 2021 14:33:47 +0200 Subject: [PATCH 21/25] Fix merge issues --- packages/image_picker/image_picker/README.md | 2 +- .../image_picker_for_web_test.dart | 106 ++++++++++++++++++ .../lib/image_picker_for_web.dart | 2 - .../image_picker_for_web/pubspec.yaml | 3 - 4 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart diff --git a/packages/image_picker/image_picker/README.md b/packages/image_picker/image_picker/README.md index f78107e167aa..18fd96d890fd 100755 --- a/packages/image_picker/image_picker/README.md +++ b/packages/image_picker/image_picker/README.md @@ -69,7 +69,7 @@ Future getLostData() async { if (response.type == RetrieveType.video) { _handleVideo(response.file); } else { - _handleImage(response.file); + _handleImage(response.file); } }); } else { diff --git a/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart b/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart new file mode 100644 index 000000000000..1c6a10d3884e --- /dev/null +++ b/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart @@ -0,0 +1,106 @@ +// 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:convert'; +import 'dart:html' as html; +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:image_picker_for_web/image_picker_for_web.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'package:integration_test/integration_test.dart'; + +final String expectedStringContents = "Hello, world!"; +final Uint8List bytes = utf8.encode(expectedStringContents) as Uint8List; +final html.File textFile = html.File([bytes], "hello.txt"); + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + // Under test... + late ImagePickerPlugin plugin; + + setUp(() { + plugin = ImagePickerPlugin(); + }); + + testWidgets('Can select a file (Deprecated)', (WidgetTester tester) async { + final mockInput = html.FileUploadInputElement(); + + final overrides = ImagePickerPluginTestOverrides() + ..createInputElement = ((_, __) => mockInput) + ..getFileFromInput = ((_) => textFile); + + final plugin = ImagePickerPlugin(overrides: overrides); + + // Init the pick file dialog... + final file = plugin.pickFile(); + + // Mock the browser behavior of selecting a file... + mockInput.dispatchEvent(html.Event('change')); + + // Now the file should be available + expect(file, completes); + // And readable + expect((await file).readAsBytes(), completion(isNotEmpty)); + }); + + testWidgets('Can select a file', (WidgetTester tester) async { + final mockInput = html.FileUploadInputElement(); + + final overrides = ImagePickerPluginTestOverrides() + ..createInputElement = ((_, __) => mockInput) + ..getFileFromInput = ((_) => textFile); + + final plugin = ImagePickerPlugin(overrides: overrides); + + // Init the pick file dialog... + final file = plugin.getFile(); + + // Mock the browser behavior of selecting a file... + mockInput.dispatchEvent(html.Event('change')); + + // Now the file should be available + expect(file, completes); + // And readable + expect((await file).readAsBytes(), completion(isNotEmpty)); + }); + + // There's no good way of detecting when the user has "aborted" the selection. + + testWidgets('computeCaptureAttribute', (WidgetTester tester) async { + expect( + plugin.computeCaptureAttribute(ImageSource.gallery, CameraDevice.front), + isNull, + ); + expect( + plugin.computeCaptureAttribute(ImageSource.gallery, CameraDevice.rear), + isNull, + ); + expect( + plugin.computeCaptureAttribute(ImageSource.camera, CameraDevice.front), + 'user', + ); + expect( + plugin.computeCaptureAttribute(ImageSource.camera, CameraDevice.rear), + 'environment', + ); + }); + + group('createInputElement', () { + testWidgets('accept: any, capture: null', (WidgetTester tester) async { + html.Element input = plugin.createInputElement('any', null); + + expect(input.attributes, containsPair('accept', 'any')); + expect(input.attributes, isNot(contains('capture'))); + }); + + testWidgets('accept: any, capture: something', (WidgetTester tester) async { + html.Element input = plugin.createInputElement('any', 'something'); + + expect(input.attributes, containsPair('accept', 'any')); + expect(input.attributes, containsPair('capture', 'something')); + }); + }); +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index 76ee7c32a9b2..08ce801cafbe 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -86,7 +86,6 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// `capture` is only supported in mobile browsers. /// See https://caniuse.com/#feat=html-media-capture @visibleForTesting - @Deprecated("Switch to using getFile instead") Future pickFile({ String? accept, String? capture, @@ -193,7 +192,6 @@ class ImagePickerPlugin extends ImagePickerPlatform { } /// Monitors an and returns the selected file. - @Deprecated("Use _getSelectedXFile instead") Future _getSelectedFile(html.FileUploadInputElement input) { final Completer _completer = Completer(); // Observe the input until we can return something diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index 41749ecc19e2..d9b9c5e5cb86 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -22,9 +22,6 @@ dependencies: sdk: flutter image_picker_platform_interface: ^2.2.0 meta: ^1.3.0 - # TODO: CHANGE TO VERSION REFERENCE WHEN NEW INTERFACE HAS BEEN PUBLISHED - image_picker_platform_interface: - path: ../image_picker_platform_interface dev_dependencies: flutter_test: From 8073e7adb444dd188cea8b803eb8d1753a47861e Mon Sep 17 00:00:00 2001 From: "Bodhi Mulders (BeMacized)" Date: Tue, 13 Jul 2021 14:35:37 +0200 Subject: [PATCH 22/25] Fix merge issues --- .../example/integration_test/image_picker_for_web_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart b/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart index 1c6a10d3884e..c6d0b3b532ca 100644 --- a/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart +++ b/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart @@ -103,4 +103,4 @@ void main() { expect(input.attributes, containsPair('capture', 'something')); }); }); -} \ No newline at end of file +} From bda48dd26600c5a20d4f8902b9fb334b8aaf9e3a Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Tue, 13 Jul 2021 19:23:09 -0700 Subject: [PATCH 23/25] Move tests of deprecated methods to new file, and ignore warnings there. Leave new tests in a separate file. --- .../test/image_picker_deprecated_test.dart | 458 ++++++++++++++++++ .../image_picker/test/image_picker_test.dart | 421 +--------------- 2 files changed, 460 insertions(+), 419 deletions(-) create mode 100644 packages/image_picker/image_picker/test/image_picker_deprecated_test.dart diff --git a/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart b/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart new file mode 100644 index 000000000000..f295e3d02f66 --- /dev/null +++ b/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart @@ -0,0 +1,458 @@ +// 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. + +// ignore_for_file: deprecated_member_use_from_same_package + +// This file preserves the tests for the deprecated methods as they were before +// the migration. See image_picker_test.dart for the current tests. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'package:mockito/mockito.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$ImagePicker', () { + const MethodChannel channel = + MethodChannel('plugins.flutter.io/image_picker'); + + final List log = []; + + final picker = ImagePicker(); + + test('ImagePicker platform instance overrides the actual platform used', + () { + final ImagePickerPlatform savedPlatform = ImagePickerPlatform.instance; + final MockPlatform mockPlatform = MockPlatform(); + ImagePickerPlatform.instance = mockPlatform; + expect(ImagePicker.platform, mockPlatform); + ImagePickerPlatform.instance = savedPlatform; + }); + + group('#Single image/video', () { + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return ''; + }); + + log.clear(); + }); + + group('#pickImage', () { + test('passes the image source argument correctly', () async { + await picker.getImage(source: ImageSource.camera); + await picker.getImage(source: ImageSource.gallery); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 1, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0 + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + await picker.getImage(source: ImageSource.camera); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + ); + await picker.getImage( + source: ImageSource.camera, + maxHeight: 10.0, + ); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.getImage( + source: ImageSource.camera, maxWidth: 10.0, imageQuality: 70); + await picker.getImage( + source: ImageSource.camera, maxHeight: 10.0, imageQuality: 70); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'cameraDevice': 0 + }), + ], + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + picker.getImage(source: ImageSource.camera, maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + picker.getImage(source: ImageSource.camera, maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.getImage(source: ImageSource.gallery), isNull); + expect(await picker.getImage(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.getImage(source: ImageSource.camera); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.getImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 1, + }), + ], + ); + }); + }); + + group('#pickVideo', () { + test('passes the image source argument correctly', () async { + await picker.getVideo(source: ImageSource.camera); + await picker.getVideo(source: ImageSource.gallery); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'cameraDevice': 0, + 'maxDuration': null, + }), + isMethodCall('pickVideo', arguments: { + 'source': 1, + 'cameraDevice': 0, + 'maxDuration': null, + }), + ], + ); + }); + + test('passes the duration argument correctly', () async { + await picker.getVideo(source: ImageSource.camera); + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(seconds: 10)); + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(minutes: 1)); + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(hours: 1)); + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': null, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 10, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 60, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 3600, + 'cameraDevice': 0, + }), + ], + ); + }); + + test('handles a null video path response gracefully', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.getVideo(source: ImageSource.gallery), isNull); + expect(await picker.getVideo(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.getVideo(source: ImageSource.camera); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'cameraDevice': 0, + 'maxDuration': null, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.getVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': null, + 'cameraDevice': 1, + }), + ], + ); + }); + }); + + group('#retrieveLostData', () { + test('retrieveLostData get success response', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'image', + 'path': '/example/path', + }; + }); + final LostData response = await picker.getLostData(); + expect(response.type, RetrieveType.image); + expect(response.file!.path, '/example/path'); + }); + + test('retrieveLostData get error response', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'video', + 'errorCode': 'test_error_code', + 'errorMessage': 'test_error_message', + }; + }); + final LostData response = await picker.getLostData(); + expect(response.type, RetrieveType.video); + expect(response.exception!.code, 'test_error_code'); + expect(response.exception!.message, 'test_error_message'); + }); + + test('retrieveLostData get null response', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return null; + }); + expect((await picker.getLostData()).isEmpty, true); + }); + + test('retrieveLostData get both path and error should throw', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'video', + 'errorCode': 'test_error_code', + 'errorMessage': 'test_error_message', + 'path': '/example/path', + }; + }); + expect(picker.getLostData(), throwsAssertionError); + }); + }); + }); + + group('Multi images', () { + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return []; + }); + log.clear(); + }); + + group('#pickMultiImage', () { + test('passes the width and height arguments correctly', () async { + await picker.getMultiImage(); + await picker.getMultiImage( + maxWidth: 10.0, + ); + await picker.getMultiImage( + maxHeight: 10.0, + ); + await picker.getMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.getMultiImage( + maxWidth: 10.0, + imageQuality: 70, + ); + await picker.getMultiImage( + maxHeight: 10.0, + imageQuality: 70, + ); + await picker.getMultiImage( + maxWidth: 10.0, maxHeight: 20.0, imageQuality: 70); + + expect( + log, + [ + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + }), + ], + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + picker.getMultiImage(maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + picker.getMultiImage(maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.getMultiImage(), isNull); + expect(await picker.getMultiImage(), isNull); + }); + }); + }); + }); +} + +class MockPlatform extends Mock + with MockPlatformInterfaceMixin + implements ImagePickerPlatform {} diff --git a/packages/image_picker/image_picker/test/image_picker_test.dart b/packages/image_picker/image_picker/test/image_picker_test.dart index e5447ba7260b..960dfe6917ea 100644 --- a/packages/image_picker/image_picker/test/image_picker_test.dart +++ b/packages/image_picker/image_picker/test/image_picker_test.dart @@ -29,423 +29,6 @@ void main() { ImagePickerPlatform.instance = savedPlatform; }); - group('#Single image/video (Deprecated)', () { - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - return ''; - }); - - log.clear(); - }); - - group('#pickImage', () { - test('passes the image source argument correctly', () async { - await picker.getImage(source: ImageSource.camera); - await picker.getImage(source: ImageSource.gallery); - - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 1, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - ], - ); - }); - - test('passes the width and height arguments correctly', () async { - await picker.getImage(source: ImageSource.camera); - await picker.getImage( - source: ImageSource.camera, - maxWidth: 10.0, - ); - await picker.getImage( - source: ImageSource.camera, - maxHeight: 10.0, - ); - await picker.getImage( - source: ImageSource.camera, - maxWidth: 10.0, - maxHeight: 20.0, - ); - await picker.getImage( - source: ImageSource.camera, maxWidth: 10.0, imageQuality: 70); - await picker.getImage( - source: ImageSource.camera, maxHeight: 10.0, imageQuality: 70); - await picker.getImage( - source: ImageSource.camera, - maxWidth: 10.0, - maxHeight: 20.0, - imageQuality: 70); - - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': 70, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': 70, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': 70, - 'cameraDevice': 0 - }), - ], - ); - }); - - test('does not accept a negative width or height argument', () { - expect( - picker.getImage(source: ImageSource.camera, maxWidth: -1.0), - throwsArgumentError, - ); - - expect( - picker.getImage(source: ImageSource.camera, maxHeight: -1.0), - throwsArgumentError, - ); - }); - - test('handles a null image path response gracefully', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) => null); - - expect(await picker.getImage(source: ImageSource.gallery), isNull); - expect(await picker.getImage(source: ImageSource.camera), isNull); - }); - - test('camera position defaults to back', () async { - await picker.getImage(source: ImageSource.camera); - - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0, - }), - ], - ); - }); - - test('camera position can set to front', () async { - await picker.getImage( - source: ImageSource.camera, - preferredCameraDevice: CameraDevice.front); - - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 1, - }), - ], - ); - }); - }); - - group('#pickVideo', () { - test('passes the image source argument correctly', () async { - await picker.getVideo(source: ImageSource.camera); - await picker.getVideo(source: ImageSource.gallery); - - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'cameraDevice': 0, - 'maxDuration': null, - }), - isMethodCall('pickVideo', arguments: { - 'source': 1, - 'cameraDevice': 0, - 'maxDuration': null, - }), - ], - ); - }); - - test('passes the duration argument correctly', () async { - await picker.getVideo(source: ImageSource.camera); - await picker.getVideo( - source: ImageSource.camera, - maxDuration: const Duration(seconds: 10)); - await picker.getVideo( - source: ImageSource.camera, - maxDuration: const Duration(minutes: 1)); - await picker.getVideo( - source: ImageSource.camera, - maxDuration: const Duration(hours: 1)); - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': null, - 'cameraDevice': 0, - }), - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': 10, - 'cameraDevice': 0, - }), - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': 60, - 'cameraDevice': 0, - }), - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': 3600, - 'cameraDevice': 0, - }), - ], - ); - }); - - test('handles a null video path response gracefully', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) => null); - - expect(await picker.getVideo(source: ImageSource.gallery), isNull); - expect(await picker.getVideo(source: ImageSource.camera), isNull); - }); - - test('camera position defaults to back', () async { - await picker.getVideo(source: ImageSource.camera); - - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'cameraDevice': 0, - 'maxDuration': null, - }), - ], - ); - }); - - test('camera position can set to front', () async { - await picker.getVideo( - source: ImageSource.camera, - preferredCameraDevice: CameraDevice.front); - - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': null, - 'cameraDevice': 1, - }), - ], - ); - }); - }); - - group('#retrieveLostData', () { - test('retrieveLostData get success response', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return { - 'type': 'image', - 'path': '/example/path', - }; - }); - final LostData response = await picker.getLostData(); - expect(response.type, RetrieveType.image); - expect(response.file!.path, '/example/path'); - }); - - test('retrieveLostData get error response', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return { - 'type': 'video', - 'errorCode': 'test_error_code', - 'errorMessage': 'test_error_message', - }; - }); - final LostData response = await picker.getLostData(); - expect(response.type, RetrieveType.video); - expect(response.exception!.code, 'test_error_code'); - expect(response.exception!.message, 'test_error_message'); - }); - - test('retrieveLostData get null response', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return null; - }); - expect((await picker.getLostData()).isEmpty, true); - }); - - test('retrieveLostData get both path and error should throw', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return { - 'type': 'video', - 'errorCode': 'test_error_code', - 'errorMessage': 'test_error_message', - 'path': '/example/path', - }; - }); - expect(picker.getLostData(), throwsAssertionError); - }); - }); - }); - - group('#Multi images (Deprecated)', () { - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - return []; - }); - log.clear(); - }); - - group('#pickMultiImage', () { - test('passes the width and height arguments correctly', () async { - await picker.getMultiImage(); - await picker.getMultiImage( - maxWidth: 10.0, - ); - await picker.getMultiImage( - maxHeight: 10.0, - ); - await picker.getMultiImage( - maxWidth: 10.0, - maxHeight: 20.0, - ); - await picker.getMultiImage( - maxWidth: 10.0, - imageQuality: 70, - ); - await picker.getMultiImage( - maxHeight: 10.0, - imageQuality: 70, - ); - await picker.getMultiImage( - maxWidth: 10.0, maxHeight: 20.0, imageQuality: 70); - - expect( - log, - [ - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': 70, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': 70, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': 70, - }), - ], - ); - }); - - test('does not accept a negative width or height argument', () { - expect( - picker.getMultiImage(maxWidth: -1.0), - throwsArgumentError, - ); - - expect( - picker.getMultiImage(maxHeight: -1.0), - throwsArgumentError, - ); - }); - - test('handles a null image path response gracefully', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) => null); - - expect(await picker.getMultiImage(), isNull); - expect(await picker.getMultiImage(), isNull); - }); - }); - }); - group('#Single image/video', () { setUp(() { channel.setMockMethodCallHandler((MethodCall methodCall) async { @@ -646,10 +229,10 @@ void main() { await picker.pickVideo( source: ImageSource.camera, maxDuration: const Duration(seconds: 10)); - await picker.getVideo( + await picker.pickVideo( source: ImageSource.camera, maxDuration: const Duration(minutes: 1)); - await picker.getVideo( + await picker.pickVideo( source: ImageSource.camera, maxDuration: const Duration(hours: 1)); expect( From a9534fe5dd32860a4c760f9d3c903c5443942a91 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Tue, 13 Jul 2021 19:36:22 -0700 Subject: [PATCH 24/25] Update Changelog to mention the deprecation of the current methods, and what to use instead. --- packages/image_picker/image_picker/CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index c106d6355042..22933a74f1ac 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,6 +1,11 @@ ## 0.8.2 -* Added pickImage, pickVideo, pickMultiImage and retrieveLostData methods that return XFile instances. +* Added new methods that return `package:cross_file` `XFile` instances. [Docs](https://pub.dev/documentation/cross_file/latest/index.html). +* Deprecate methods that return `PickedFile` instances: + * `getImage`: use **`pickImage`** instead. + * `getVideo`: use **`pickVideo`** instead. + * `getMultiImage`: use **`pickMultiImage`** instead. + * `getLostData`: use **`retrieveLostData`** instead. ## 0.8.1+3 From e59bb030756b6d9bf018e2486f24a05905e0a20d Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Tue, 13 Jul 2021 19:38:44 -0700 Subject: [PATCH 25/25] Remove unnecessary ignore_for_file from plugin code. --- packages/image_picker/image_picker/lib/image_picker.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/image_picker/image_picker/lib/image_picker.dart b/packages/image_picker/image_picker/lib/image_picker.dart index 1769ab92f15c..5bc99d7f0bb2 100755 --- a/packages/image_picker/image_picker/lib/image_picker.dart +++ b/packages/image_picker/image_picker/lib/image_picker.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: deprecated_member_use, deprecated_member_use_from_same_package - import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';