[image_picker] Switch Android to internal method channel (#5958)
diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md
index a5cc613..664be36 100644
--- a/packages/image_picker/image_picker_android/CHANGELOG.md
+++ b/packages/image_picker/image_picker_android/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.8.5+1
+
+* Switches to an internal method channel implementation.
+
## 0.8.5
* Updates gradle to 7.1.2.
diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java
index 311ef19..8336a14 100644
--- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java
+++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java
@@ -183,7 +183,7 @@
private static final String METHOD_CALL_RETRIEVE = "retrieve";
private static final int CAMERA_DEVICE_FRONT = 1;
private static final int CAMERA_DEVICE_REAR = 0;
- private static final String CHANNEL = "plugins.flutter.io/image_picker";
+ private static final String CHANNEL = "plugins.flutter.io/image_picker_android";
private static final int SOURCE_CAMERA = 0;
private static final int SOURCE_GALLERY = 1;
diff --git a/packages/image_picker/image_picker_android/lib/image_picker_android.dart b/packages/image_picker/image_picker_android/lib/image_picker_android.dart
new file mode 100644
index 0000000..b6073c7
--- /dev/null
+++ b/packages/image_picker/image_picker_android/lib/image_picker_android.dart
@@ -0,0 +1,282 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/services.dart';
+
+import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
+
+const MethodChannel _channel =
+ MethodChannel('plugins.flutter.io/image_picker_android');
+
+/// An Android implementation of [ImagePickerPlatform].
+class ImagePickerAndroid extends ImagePickerPlatform {
+ /// The MethodChannel that is being used by this implementation of the plugin.
+ @visibleForTesting
+ MethodChannel get channel => _channel;
+
+ /// Registers this class as the default platform implementation.
+ static void registerWith() {
+ ImagePickerPlatform.instance = ImagePickerAndroid();
+ }
+
+ @override
+ Future<PickedFile?> pickImage({
+ required ImageSource source,
+ double? maxWidth,
+ double? maxHeight,
+ int? imageQuality,
+ CameraDevice preferredCameraDevice = CameraDevice.rear,
+ }) async {
+ final String? path = await _getImagePath(
+ source: source,
+ maxWidth: maxWidth,
+ maxHeight: maxHeight,
+ imageQuality: imageQuality,
+ preferredCameraDevice: preferredCameraDevice,
+ );
+ return path != null ? PickedFile(path) : null;
+ }
+
+ @override
+ Future<List<PickedFile>?> pickMultiImage({
+ double? maxWidth,
+ double? maxHeight,
+ int? imageQuality,
+ }) async {
+ final List<dynamic>? paths = await _getMultiImagePath(
+ maxWidth: maxWidth,
+ maxHeight: maxHeight,
+ imageQuality: imageQuality,
+ );
+ if (paths == null) {
+ return null;
+ }
+
+ return paths.map((dynamic path) => PickedFile(path as String)).toList();
+ }
+
+ Future<List<dynamic>?> _getMultiImagePath({
+ double? maxWidth,
+ double? maxHeight,
+ int? imageQuality,
+ }) {
+ if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) {
+ throw ArgumentError.value(
+ imageQuality, 'imageQuality', 'must be between 0 and 100');
+ }
+
+ if (maxWidth != null && maxWidth < 0) {
+ throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative');
+ }
+
+ if (maxHeight != null && maxHeight < 0) {
+ throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative');
+ }
+
+ return _channel.invokeMethod<List<dynamic>?>(
+ 'pickMultiImage',
+ <String, dynamic>{
+ 'maxWidth': maxWidth,
+ 'maxHeight': maxHeight,
+ 'imageQuality': imageQuality,
+ },
+ );
+ }
+
+ Future<String?> _getImagePath({
+ required ImageSource source,
+ double? maxWidth,
+ double? maxHeight,
+ int? imageQuality,
+ CameraDevice preferredCameraDevice = CameraDevice.rear,
+ bool requestFullMetadata = true,
+ }) {
+ if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) {
+ throw ArgumentError.value(
+ imageQuality, 'imageQuality', 'must be between 0 and 100');
+ }
+
+ if (maxWidth != null && maxWidth < 0) {
+ throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative');
+ }
+
+ if (maxHeight != null && maxHeight < 0) {
+ throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative');
+ }
+
+ return _channel.invokeMethod<String>(
+ 'pickImage',
+ <String, dynamic>{
+ 'source': source.index,
+ 'maxWidth': maxWidth,
+ 'maxHeight': maxHeight,
+ 'imageQuality': imageQuality,
+ 'cameraDevice': preferredCameraDevice.index,
+ 'requestFullMetadata': requestFullMetadata,
+ },
+ );
+ }
+
+ @override
+ Future<PickedFile?> pickVideo({
+ required ImageSource source,
+ CameraDevice preferredCameraDevice = CameraDevice.rear,
+ Duration? maxDuration,
+ }) async {
+ final String? path = await _getVideoPath(
+ source: source,
+ maxDuration: maxDuration,
+ preferredCameraDevice: preferredCameraDevice,
+ );
+ return path != null ? PickedFile(path) : null;
+ }
+
+ Future<String?> _getVideoPath({
+ required ImageSource source,
+ CameraDevice preferredCameraDevice = CameraDevice.rear,
+ Duration? maxDuration,
+ }) {
+ return _channel.invokeMethod<String>(
+ 'pickVideo',
+ <String, dynamic>{
+ 'source': source.index,
+ 'maxDuration': maxDuration?.inSeconds,
+ 'cameraDevice': preferredCameraDevice.index
+ },
+ );
+ }
+
+ @override
+ Future<XFile?> getImage({
+ required ImageSource source,
+ double? maxWidth,
+ double? maxHeight,
+ int? imageQuality,
+ CameraDevice preferredCameraDevice = CameraDevice.rear,
+ }) async {
+ final String? path = await _getImagePath(
+ source: source,
+ maxWidth: maxWidth,
+ maxHeight: maxHeight,
+ imageQuality: imageQuality,
+ preferredCameraDevice: preferredCameraDevice,
+ );
+ return path != null ? XFile(path) : null;
+ }
+
+ @override
+ Future<XFile?> getImageFromSource({
+ required ImageSource source,
+ ImagePickerOptions options = const ImagePickerOptions(),
+ }) async {
+ final String? path = await _getImagePath(
+ source: source,
+ maxHeight: options.maxHeight,
+ maxWidth: options.maxWidth,
+ imageQuality: options.imageQuality,
+ preferredCameraDevice: options.preferredCameraDevice,
+ requestFullMetadata: options.requestFullMetadata,
+ );
+ return path != null ? XFile(path) : null;
+ }
+
+ @override
+ Future<List<XFile>?> getMultiImage({
+ double? maxWidth,
+ double? maxHeight,
+ int? imageQuality,
+ }) async {
+ final List<dynamic>? paths = await _getMultiImagePath(
+ maxWidth: maxWidth,
+ maxHeight: maxHeight,
+ imageQuality: imageQuality,
+ );
+ if (paths == null) {
+ return null;
+ }
+
+ return paths.map((dynamic path) => XFile(path as String)).toList();
+ }
+
+ @override
+ Future<XFile?> 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<LostData> retrieveLostData() async {
+ final LostDataResponse result = await getLostData();
+
+ if (result.isEmpty) {
+ return LostData.empty();
+ }
+
+ return LostData(
+ file: result.file != null ? PickedFile(result.file!.path) : null,
+ exception: result.exception,
+ type: result.type,
+ );
+ }
+
+ @override
+ Future<LostDataResponse> getLostData() async {
+ List<XFile>? pickedFileList;
+
+ final Map<String, dynamic>? result =
+ await _channel.invokeMapMethod<String, dynamic>('retrieve');
+
+ if (result == null) {
+ return LostDataResponse.empty();
+ }
+
+ assert(result.containsKey('path') != result.containsKey('errorCode'));
+
+ final String? type = result['type'] as String?;
+ 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']! as String,
+ message: result['errorMessage'] as String?);
+ }
+
+ final String? path = result['path'] as String?;
+
+ final List<String>? pathList =
+ (result['pathList'] as List<dynamic>?)?.cast<String>();
+ if (pathList != null) {
+ pickedFileList = <XFile>[];
+ for (final String path in pathList) {
+ pickedFileList.add(XFile(path));
+ }
+ }
+
+ return LostDataResponse(
+ file: path != null ? XFile(path) : null,
+ exception: exception,
+ type: retrieveType,
+ files: pickedFileList,
+ );
+ }
+}
diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml
index 3d17e8a..8cbaaac 100755
--- a/packages/image_picker/image_picker_android/pubspec.yaml
+++ b/packages/image_picker/image_picker_android/pubspec.yaml
@@ -2,7 +2,7 @@
description: Android implementation of the image_picker plugin.
repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_android
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22
-version: 0.8.5
+version: 0.8.5+1
environment:
sdk: ">=2.14.0 <3.0.0"
@@ -15,6 +15,7 @@
android:
package: io.flutter.plugins.imagepicker
pluginClass: ImagePickerPlugin
+ dartPluginClass: ImagePickerAndroid
dependencies:
flutter:
diff --git a/packages/image_picker/image_picker_android/test/image_picker_android_test.dart b/packages/image_picker/image_picker_android/test/image_picker_android_test.dart
new file mode 100644
index 0000000..ee1eb79
--- /dev/null
+++ b/packages/image_picker/image_picker_android/test/image_picker_android_test.dart
@@ -0,0 +1,1256 @@
+// 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:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'package:image_picker_android/image_picker_android.dart';
+import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
+
+void main() {
+ TestWidgetsFlutterBinding.ensureInitialized();
+
+ final ImagePickerAndroid picker = ImagePickerAndroid();
+
+ final List<MethodCall> log = <MethodCall>[];
+ dynamic returnValue = '';
+
+ setUp(() {
+ returnValue = '';
+ picker.channel.setMockMethodCallHandler((MethodCall methodCall) async {
+ log.add(methodCall);
+ return returnValue;
+ });
+
+ log.clear();
+ });
+
+ test('registers instance', () async {
+ ImagePickerAndroid.registerWith();
+ expect(ImagePickerPlatform.instance, isA<ImagePickerAndroid>());
+ });
+
+ group('#pickImage', () {
+ test('passes the image source argument correctly', () async {
+ await picker.pickImage(source: ImageSource.camera);
+ await picker.pickImage(source: ImageSource.gallery);
+
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 1,
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ ],
+ );
+ });
+
+ 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,
+ <Matcher>[
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': 10.0,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': 10.0,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': 10.0,
+ 'maxHeight': 20.0,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': 10.0,
+ 'maxHeight': null,
+ 'imageQuality': 70,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': 10.0,
+ 'imageQuality': 70,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': 10.0,
+ 'maxHeight': 20.0,
+ 'imageQuality': 70,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ ],
+ );
+ });
+
+ test('does not accept an invalid imageQuality argument', () {
+ expect(
+ () => picker.pickImage(imageQuality: -1, source: ImageSource.gallery),
+ throwsArgumentError,
+ );
+
+ expect(
+ () => picker.pickImage(imageQuality: 101, source: ImageSource.gallery),
+ throwsArgumentError,
+ );
+
+ expect(
+ () => picker.pickImage(imageQuality: -1, source: ImageSource.camera),
+ throwsArgumentError,
+ );
+
+ expect(
+ () => picker.pickImage(imageQuality: 101, source: ImageSource.camera),
+ throwsArgumentError,
+ );
+ });
+
+ 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 {
+ picker.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,
+ <Matcher>[
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ ],
+ );
+ });
+
+ test('camera position can set to front', () async {
+ await picker.pickImage(
+ source: ImageSource.camera,
+ preferredCameraDevice: CameraDevice.front);
+
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 1,
+ 'requestFullMetadata': true,
+ }),
+ ],
+ );
+ });
+ });
+
+ group('#pickMultiImage', () {
+ test('calls the method correctly', () async {
+ returnValue = <dynamic>['0', '1'];
+ await picker.pickMultiImage();
+
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('pickMultiImage', arguments: <String, dynamic>{
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ }),
+ ],
+ );
+ });
+
+ test('passes the width and height arguments correctly', () async {
+ returnValue = <dynamic>['0', '1'];
+ 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,
+ <Matcher>[
+ isMethodCall('pickMultiImage', arguments: <String, dynamic>{
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ }),
+ isMethodCall('pickMultiImage', arguments: <String, dynamic>{
+ 'maxWidth': 10.0,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ }),
+ isMethodCall('pickMultiImage', arguments: <String, dynamic>{
+ 'maxWidth': null,
+ 'maxHeight': 10.0,
+ 'imageQuality': null,
+ }),
+ isMethodCall('pickMultiImage', arguments: <String, dynamic>{
+ 'maxWidth': 10.0,
+ 'maxHeight': 20.0,
+ 'imageQuality': null,
+ }),
+ isMethodCall('pickMultiImage', arguments: <String, dynamic>{
+ 'maxWidth': 10.0,
+ 'maxHeight': null,
+ 'imageQuality': 70,
+ }),
+ isMethodCall('pickMultiImage', arguments: <String, dynamic>{
+ 'maxWidth': null,
+ 'maxHeight': 10.0,
+ 'imageQuality': 70,
+ }),
+ isMethodCall('pickMultiImage', arguments: <String, dynamic>{
+ 'maxWidth': 10.0,
+ 'maxHeight': 20.0,
+ 'imageQuality': 70,
+ }),
+ ],
+ );
+ });
+
+ test('does not accept a negative width or height argument', () {
+ returnValue = <dynamic>['0', '1'];
+ expect(
+ () => picker.pickMultiImage(maxWidth: -1.0),
+ throwsArgumentError,
+ );
+
+ expect(
+ () => picker.pickMultiImage(maxHeight: -1.0),
+ throwsArgumentError,
+ );
+ });
+
+ test('does not accept an invalid imageQuality argument', () {
+ returnValue = <dynamic>['0', '1'];
+ expect(
+ () => picker.pickMultiImage(imageQuality: -1),
+ throwsArgumentError,
+ );
+
+ expect(
+ () => picker.pickMultiImage(imageQuality: 101),
+ throwsArgumentError,
+ );
+ });
+
+ test('handles a null image path response gracefully', () async {
+ picker.channel.setMockMethodCallHandler((MethodCall methodCall) => null);
+
+ expect(await picker.pickMultiImage(), isNull);
+ expect(await picker.pickMultiImage(), isNull);
+ });
+ });
+
+ group('#pickVideo', () {
+ test('passes the image source argument correctly', () async {
+ await picker.pickVideo(source: ImageSource.camera);
+ await picker.pickVideo(source: ImageSource.gallery);
+
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('pickVideo', arguments: <String, dynamic>{
+ 'source': 0,
+ 'cameraDevice': 0,
+ 'maxDuration': null,
+ }),
+ isMethodCall('pickVideo', arguments: <String, dynamic>{
+ '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.pickVideo(
+ source: ImageSource.camera,
+ maxDuration: const Duration(minutes: 1),
+ );
+ await picker.pickVideo(
+ source: ImageSource.camera,
+ maxDuration: const Duration(hours: 1),
+ );
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('pickVideo', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxDuration': null,
+ 'cameraDevice': 0,
+ }),
+ isMethodCall('pickVideo', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxDuration': 10,
+ 'cameraDevice': 0,
+ }),
+ isMethodCall('pickVideo', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxDuration': 60,
+ 'cameraDevice': 0,
+ }),
+ isMethodCall('pickVideo', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxDuration': 3600,
+ 'cameraDevice': 0,
+ }),
+ ],
+ );
+ });
+
+ test('handles a null video path response gracefully', () async {
+ picker.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,
+ <Matcher>[
+ isMethodCall('pickVideo', arguments: <String, dynamic>{
+ '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,
+ <Matcher>[
+ isMethodCall('pickVideo', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxDuration': null,
+ 'cameraDevice': 1,
+ }),
+ ],
+ );
+ });
+ });
+
+ group('#retrieveLostData', () {
+ test('retrieveLostData get success response', () async {
+ picker.channel.setMockMethodCallHandler((MethodCall methodCall) async {
+ return <String, String>{
+ 'type': 'image',
+ 'path': '/example/path',
+ };
+ });
+ final LostData response = await picker.retrieveLostData();
+ expect(response.type, RetrieveType.image);
+ expect(response.file, isNotNull);
+ expect(response.file!.path, '/example/path');
+ });
+
+ test('retrieveLostData get error response', () async {
+ picker.channel.setMockMethodCallHandler((MethodCall methodCall) async {
+ return <String, String>{
+ 'type': 'video',
+ 'errorCode': 'test_error_code',
+ 'errorMessage': 'test_error_message',
+ };
+ });
+ final LostData response = await picker.retrieveLostData();
+ expect(response.type, RetrieveType.video);
+ expect(response.exception, isNotNull);
+ expect(response.exception!.code, 'test_error_code');
+ expect(response.exception!.message, 'test_error_message');
+ });
+
+ test('retrieveLostData get null response', () async {
+ picker.channel.setMockMethodCallHandler((MethodCall methodCall) async {
+ return null;
+ });
+ expect((await picker.retrieveLostData()).isEmpty, true);
+ });
+
+ test('retrieveLostData get both path and error should throw', () async {
+ picker.channel.setMockMethodCallHandler((MethodCall methodCall) async {
+ return <String, String>{
+ 'type': 'video',
+ 'errorCode': 'test_error_code',
+ 'errorMessage': 'test_error_message',
+ 'path': '/example/path',
+ };
+ });
+ 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,
+ <Matcher>[
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 1,
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ ],
+ );
+ });
+
+ 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,
+ <Matcher>[
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': 10.0,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': 10.0,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': 10.0,
+ 'maxHeight': 20.0,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': 10.0,
+ 'maxHeight': null,
+ 'imageQuality': 70,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': 10.0,
+ 'imageQuality': 70,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': 10.0,
+ 'maxHeight': 20.0,
+ 'imageQuality': 70,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ ],
+ );
+ });
+
+ test('does not accept an 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,
+ <Matcher>[
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ ],
+ );
+ });
+
+ test('camera position can set to front', () async {
+ await picker.getImage(
+ source: ImageSource.camera,
+ preferredCameraDevice: CameraDevice.front);
+
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 1,
+ 'requestFullMetadata': true,
+ }),
+ ],
+ );
+ });
+ });
+
+ group('#getMultiImage', () {
+ test('calls the method correctly', () async {
+ returnValue = <dynamic>['0', '1'];
+ await picker.getMultiImage();
+
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('pickMultiImage', arguments: <String, dynamic>{
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ }),
+ ],
+ );
+ });
+
+ test('passes the width and height arguments correctly', () async {
+ returnValue = <dynamic>['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,
+ <Matcher>[
+ isMethodCall('pickMultiImage', arguments: <String, dynamic>{
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ }),
+ isMethodCall('pickMultiImage', arguments: <String, dynamic>{
+ 'maxWidth': 10.0,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ }),
+ isMethodCall('pickMultiImage', arguments: <String, dynamic>{
+ 'maxWidth': null,
+ 'maxHeight': 10.0,
+ 'imageQuality': null,
+ }),
+ isMethodCall('pickMultiImage', arguments: <String, dynamic>{
+ 'maxWidth': 10.0,
+ 'maxHeight': 20.0,
+ 'imageQuality': null,
+ }),
+ isMethodCall('pickMultiImage', arguments: <String, dynamic>{
+ 'maxWidth': 10.0,
+ 'maxHeight': null,
+ 'imageQuality': 70,
+ }),
+ isMethodCall('pickMultiImage', arguments: <String, dynamic>{
+ 'maxWidth': null,
+ 'maxHeight': 10.0,
+ 'imageQuality': 70,
+ }),
+ isMethodCall('pickMultiImage', arguments: <String, dynamic>{
+ 'maxWidth': 10.0,
+ 'maxHeight': 20.0,
+ 'imageQuality': 70,
+ }),
+ ],
+ );
+ });
+
+ test('does not accept a negative width or height argument', () {
+ returnValue = <dynamic>['0', '1'];
+ expect(
+ () => picker.getMultiImage(maxWidth: -1.0),
+ throwsArgumentError,
+ );
+
+ expect(
+ () => picker.getMultiImage(maxHeight: -1.0),
+ throwsArgumentError,
+ );
+ });
+
+ test('does not accept an invalid imageQuality argument', () {
+ returnValue = <dynamic>['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,
+ <Matcher>[
+ isMethodCall('pickVideo', arguments: <String, dynamic>{
+ 'source': 0,
+ 'cameraDevice': 0,
+ 'maxDuration': null,
+ }),
+ isMethodCall('pickVideo', arguments: <String, dynamic>{
+ '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,
+ <Matcher>[
+ isMethodCall('pickVideo', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxDuration': null,
+ 'cameraDevice': 0,
+ }),
+ isMethodCall('pickVideo', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxDuration': 10,
+ 'cameraDevice': 0,
+ }),
+ isMethodCall('pickVideo', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxDuration': 60,
+ 'cameraDevice': 0,
+ }),
+ isMethodCall('pickVideo', arguments: <String, dynamic>{
+ '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,
+ <Matcher>[
+ isMethodCall('pickVideo', arguments: <String, dynamic>{
+ '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,
+ <Matcher>[
+ isMethodCall('pickVideo', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxDuration': null,
+ 'cameraDevice': 1,
+ }),
+ ],
+ );
+ });
+ });
+
+ group('#getLostData', () {
+ test('getLostData get success response', () async {
+ picker.channel.setMockMethodCallHandler((MethodCall methodCall) async {
+ return <String, String>{
+ 'type': 'image',
+ 'path': '/example/path',
+ };
+ });
+ final LostDataResponse response = await picker.getLostData();
+ expect(response.type, RetrieveType.image);
+ expect(response.file, isNotNull);
+ expect(response.file!.path, '/example/path');
+ });
+
+ test('getLostData should successfully retrieve multiple files', () async {
+ picker.channel.setMockMethodCallHandler((MethodCall methodCall) async {
+ return <String, dynamic>{
+ 'type': 'image',
+ 'path': '/example/path1',
+ 'pathList': <dynamic>['/example/path0', '/example/path1'],
+ };
+ });
+ final LostDataResponse response = await picker.getLostData();
+ expect(response.type, RetrieveType.image);
+ expect(response.file, isNotNull);
+ expect(response.file!.path, '/example/path1');
+ expect(response.files!.first.path, '/example/path0');
+ expect(response.files!.length, 2);
+ });
+
+ test('getLostData get error response', () async {
+ picker.channel.setMockMethodCallHandler((MethodCall methodCall) async {
+ return <String, String>{
+ 'type': 'video',
+ 'errorCode': 'test_error_code',
+ 'errorMessage': 'test_error_message',
+ };
+ });
+ 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 <String, String>{
+ 'type': 'video',
+ 'errorCode': 'test_error_code',
+ 'errorMessage': 'test_error_message',
+ 'path': '/example/path',
+ };
+ });
+ expect(picker.getLostData(), throwsAssertionError);
+ });
+ });
+
+ group('#getImageFromSource', () {
+ test('passes the image source argument correctly', () async {
+ await picker.getImageFromSource(source: ImageSource.camera);
+ await picker.getImageFromSource(source: ImageSource.gallery);
+
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 1,
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ ],
+ );
+ });
+
+ test('passes the width and height arguments correctly', () async {
+ await picker.getImageFromSource(source: ImageSource.camera);
+ await picker.getImageFromSource(
+ source: ImageSource.camera,
+ options: const ImagePickerOptions(maxWidth: 10.0),
+ );
+ await picker.getImageFromSource(
+ source: ImageSource.camera,
+ options: const ImagePickerOptions(maxHeight: 10.0),
+ );
+ await picker.getImageFromSource(
+ source: ImageSource.camera,
+ options: const ImagePickerOptions(
+ maxWidth: 10.0,
+ maxHeight: 20.0,
+ ),
+ );
+ await picker.getImageFromSource(
+ source: ImageSource.camera,
+ options: const ImagePickerOptions(
+ maxWidth: 10.0,
+ imageQuality: 70,
+ ),
+ );
+ await picker.getImageFromSource(
+ source: ImageSource.camera,
+ options: const ImagePickerOptions(
+ maxHeight: 10.0,
+ imageQuality: 70,
+ ),
+ );
+ await picker.getImageFromSource(
+ source: ImageSource.camera,
+ options: const ImagePickerOptions(
+ maxWidth: 10.0,
+ maxHeight: 20.0,
+ imageQuality: 70,
+ ),
+ );
+
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': 10.0,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': 10.0,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': 10.0,
+ 'maxHeight': 20.0,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': 10.0,
+ 'maxHeight': null,
+ 'imageQuality': 70,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': 10.0,
+ 'imageQuality': 70,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': 10.0,
+ 'maxHeight': 20.0,
+ 'imageQuality': 70,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ ],
+ );
+ });
+
+ test('does not accept an invalid imageQuality argument', () {
+ expect(
+ () => picker.getImageFromSource(
+ source: ImageSource.gallery,
+ options: const ImagePickerOptions(imageQuality: -1),
+ ),
+ throwsArgumentError,
+ );
+
+ expect(
+ () => picker.getImageFromSource(
+ source: ImageSource.gallery,
+ options: const ImagePickerOptions(imageQuality: 101),
+ ),
+ throwsArgumentError,
+ );
+
+ expect(
+ () => picker.getImageFromSource(
+ source: ImageSource.camera,
+ options: const ImagePickerOptions(imageQuality: -1),
+ ),
+ throwsArgumentError,
+ );
+
+ expect(
+ () => picker.getImageFromSource(
+ source: ImageSource.camera,
+ options: const ImagePickerOptions(imageQuality: 101),
+ ),
+ throwsArgumentError,
+ );
+ });
+
+ test('does not accept a negative width or height argument', () {
+ expect(
+ () => picker.getImageFromSource(
+ source: ImageSource.camera,
+ options: const ImagePickerOptions(maxWidth: -1.0),
+ ),
+ throwsArgumentError,
+ );
+
+ expect(
+ () => picker.getImageFromSource(
+ source: ImageSource.camera,
+ options: const ImagePickerOptions(maxHeight: -1.0),
+ ),
+ throwsArgumentError,
+ );
+ });
+
+ test('handles a null image path response gracefully', () async {
+ picker.channel.setMockMethodCallHandler((MethodCall methodCall) => null);
+
+ expect(
+ await picker.getImageFromSource(source: ImageSource.gallery), isNull);
+ expect(
+ await picker.getImageFromSource(source: ImageSource.camera), isNull);
+ });
+
+ test('camera position defaults to back', () async {
+ await picker.getImageFromSource(source: ImageSource.camera);
+
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': true,
+ }),
+ ],
+ );
+ });
+
+ test('camera position can set to front', () async {
+ await picker.getImageFromSource(
+ source: ImageSource.camera,
+ options: const ImagePickerOptions(
+ preferredCameraDevice: CameraDevice.front,
+ ),
+ );
+
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 1,
+ 'requestFullMetadata': true,
+ }),
+ ],
+ );
+ });
+
+ test('passes the full metadata argument correctly', () async {
+ await picker.getImageFromSource(
+ source: ImageSource.camera,
+ options: const ImagePickerOptions(requestFullMetadata: false),
+ );
+
+ expect(
+ log,
+ <Matcher>[
+ isMethodCall('pickImage', arguments: <String, dynamic>{
+ 'source': 0,
+ 'maxWidth': null,
+ 'maxHeight': null,
+ 'imageQuality': null,
+ 'cameraDevice': 0,
+ 'requestFullMetadata': false,
+ }),
+ ],
+ );
+ });
+ });
+}