diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md
index 793b6f0f1b62..d545d0a6d637 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.6+3
+
+* Switches to Pigeon for internal implementation.
+
## 0.8.6+2
* Fixes null pointer exception in `saveResult`.
diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImageOutputOptions.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImageOutputOptions.java
deleted file mode 100644
index 89648dcd24b3..000000000000
--- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImageOutputOptions.java
+++ /dev/null
@@ -1,29 +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.
-
-package io.flutter.plugins.imagepicker;
-
-import androidx.annotation.Nullable;
-
-/** Stores settings for image output options. */
-public class ImageOutputOptions {
- /** The maximum width of the image, if the width should be constrained. */
- @Nullable public final Double maxWidth;
- /** The maximum height of the image, if the width should be constrained. */
- @Nullable public final Double maxHeight;
- /**
- * The output quality of the image, as a number from 0 to 100.
- *
- *
Defaults to 100.
- */
- final int quality;
-
- public ImageOutputOptions(
- @Nullable Double maxWidth, @Nullable Double maxHeight, @Nullable Integer quality) {
- this.maxWidth = maxWidth;
- this.maxHeight = maxHeight;
- // Treat any invalid value as full quality.
- this.quality = quality == null || quality < 0 || quality > 100 ? 100 : quality;
- }
-}
diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerCache.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerCache.java
index 1593f87f8c9a..182b8a174342 100644
--- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerCache.java
+++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerCache.java
@@ -21,14 +21,15 @@ public enum CacheType {
VIDEO
}
- static final String MAP_KEY_PATH = "path";
static final String MAP_KEY_PATH_LIST = "pathList";
static final String MAP_KEY_MAX_WIDTH = "maxWidth";
static final String MAP_KEY_MAX_HEIGHT = "maxHeight";
static final String MAP_KEY_IMAGE_QUALITY = "imageQuality";
- private static final String MAP_KEY_TYPE = "type";
- private static final String MAP_KEY_ERROR_CODE = "errorCode";
- private static final String MAP_KEY_ERROR_MESSAGE = "errorMessage";
+ static final String MAP_KEY_TYPE = "type";
+ static final String MAP_KEY_ERROR = "error";
+
+ private static final String MAP_TYPE_VALUE_IMAGE = "image";
+ private static final String MAP_TYPE_VALUE_VIDEO = "video";
private static final String FLUTTER_IMAGE_PICKER_IMAGE_PATH_KEY =
"flutter_image_picker_image_path";
@@ -50,7 +51,7 @@ public enum CacheType {
@VisibleForTesting
static final String SHARED_PREFERENCES_NAME = "flutter_image_picker_shared_preference";
- private SharedPreferences prefs;
+ private final SharedPreferences prefs;
ImagePickerCache(Context context) {
prefs = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
@@ -59,10 +60,10 @@ public enum CacheType {
void saveType(CacheType type) {
switch (type) {
case IMAGE:
- setType("image");
+ setType(MAP_TYPE_VALUE_IMAGE);
break;
case VIDEO:
- setType("video");
+ setType(MAP_TYPE_VALUE_VIDEO);
break;
}
}
@@ -71,16 +72,17 @@ private void setType(String type) {
prefs.edit().putString(SHARED_PREFERENCE_TYPE_KEY, type).apply();
}
- void saveDimensionWithOutputOptions(ImageOutputOptions options) {
+ void saveDimensionWithOutputOptions(Messages.ImageSelectionOptions options) {
SharedPreferences.Editor editor = prefs.edit();
- if (options.maxWidth != null) {
- editor.putLong(SHARED_PREFERENCE_MAX_WIDTH_KEY, Double.doubleToRawLongBits(options.maxWidth));
+ if (options.getMaxWidth() != null) {
+ editor.putLong(
+ SHARED_PREFERENCE_MAX_WIDTH_KEY, Double.doubleToRawLongBits(options.getMaxWidth()));
}
- if (options.maxHeight != null) {
+ if (options.getMaxHeight() != null) {
editor.putLong(
- SHARED_PREFERENCE_MAX_HEIGHT_KEY, Double.doubleToRawLongBits(options.maxHeight));
+ SHARED_PREFERENCE_MAX_HEIGHT_KEY, Double.doubleToRawLongBits(options.getMaxHeight()));
}
- editor.putInt(SHARED_PREFERENCE_IMAGE_QUALITY_KEY, options.quality);
+ editor.putInt(SHARED_PREFERENCE_IMAGE_QUALITY_KEY, options.getQuality().intValue());
editor.apply();
}
@@ -115,35 +117,37 @@ void clear() {
}
Map getCacheMap() {
-
Map resultMap = new HashMap<>();
- ArrayList pathList = new ArrayList<>();
boolean hasData = false;
if (prefs.contains(FLUTTER_IMAGE_PICKER_IMAGE_PATH_KEY)) {
final Set imagePathList =
prefs.getStringSet(FLUTTER_IMAGE_PICKER_IMAGE_PATH_KEY, null);
if (imagePathList != null) {
- pathList.addAll(imagePathList);
+ ArrayList pathList = new ArrayList<>(imagePathList);
resultMap.put(MAP_KEY_PATH_LIST, pathList);
hasData = true;
}
}
if (prefs.contains(SHARED_PREFERENCE_ERROR_CODE_KEY)) {
- final String errorCodeValue = prefs.getString(SHARED_PREFERENCE_ERROR_CODE_KEY, "");
- resultMap.put(MAP_KEY_ERROR_CODE, errorCodeValue);
+ final Messages.CacheRetrievalError.Builder error = new Messages.CacheRetrievalError.Builder();
+ error.setCode(prefs.getString(SHARED_PREFERENCE_ERROR_CODE_KEY, ""));
hasData = true;
if (prefs.contains(SHARED_PREFERENCE_ERROR_MESSAGE_KEY)) {
- final String errorMessageValue = prefs.getString(SHARED_PREFERENCE_ERROR_MESSAGE_KEY, "");
- resultMap.put(MAP_KEY_ERROR_MESSAGE, errorMessageValue);
+ error.setMessage(prefs.getString(SHARED_PREFERENCE_ERROR_MESSAGE_KEY, ""));
}
+ resultMap.put(MAP_KEY_ERROR, error.build());
}
if (hasData) {
if (prefs.contains(SHARED_PREFERENCE_TYPE_KEY)) {
final String typeValue = prefs.getString(SHARED_PREFERENCE_TYPE_KEY, "");
- resultMap.put(MAP_KEY_TYPE, typeValue);
+ resultMap.put(
+ MAP_KEY_TYPE,
+ typeValue.equals(MAP_TYPE_VALUE_VIDEO)
+ ? Messages.CacheRetrievalType.VIDEO
+ : Messages.CacheRetrievalType.IMAGE);
}
if (prefs.contains(SHARED_PREFERENCE_MAX_WIDTH_KEY)) {
final long maxWidthValue = prefs.getLong(SHARED_PREFERENCE_MAX_WIDTH_KEY, 0);
@@ -153,12 +157,8 @@ Map getCacheMap() {
final long maxHeightValue = prefs.getLong(SHARED_PREFERENCE_MAX_HEIGHT_KEY, 0);
resultMap.put(MAP_KEY_MAX_HEIGHT, Double.longBitsToDouble(maxHeightValue));
}
- if (prefs.contains(SHARED_PREFERENCE_IMAGE_QUALITY_KEY)) {
- final int imageQuality = prefs.getInt(SHARED_PREFERENCE_IMAGE_QUALITY_KEY, 100);
- resultMap.put(MAP_KEY_IMAGE_QUALITY, imageQuality);
- } else {
- resultMap.put(MAP_KEY_IMAGE_QUALITY, 100);
- }
+ final int imageQuality = prefs.getInt(SHARED_PREFERENCE_IMAGE_QUALITY_KEY, 100);
+ resultMap.put(MAP_KEY_IMAGE_QUALITY, imageQuality);
}
return resultMap;
}
diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java
index a28611e5b5d7..9b355880b8d1 100644
--- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java
+++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java
@@ -22,8 +22,10 @@
import androidx.annotation.VisibleForTesting;
import androidx.core.app.ActivityCompat;
import androidx.core.content.FileProvider;
-import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry;
+import io.flutter.plugins.imagepicker.Messages.FlutterError;
+import io.flutter.plugins.imagepicker.Messages.ImageSelectionOptions;
+import io.flutter.plugins.imagepicker.Messages.VideoSelectionOptions;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -87,14 +89,14 @@ public enum CameraDevice {
/** Holds call state during intent handling. */
private static class PendingCallState {
- public final @Nullable ImageOutputOptions imageOptions;
- public final @Nullable VideoOptions videoOptions;
- public final @NonNull MethodChannel.Result result;
+ public final @Nullable ImageSelectionOptions imageOptions;
+ public final @Nullable VideoSelectionOptions videoOptions;
+ public final @NonNull Messages.Result> result;
private PendingCallState(
- @Nullable ImageOutputOptions imageOptions,
- @Nullable VideoOptions videoOptions,
- @NonNull MethodChannel.Result result) {
+ @Nullable ImageSelectionOptions imageOptions,
+ @Nullable VideoSelectionOptions videoOptions,
+ @NonNull Messages.Result> result) {
this.imageOptions = imageOptions;
this.videoOptions = videoOptions;
this.result = result;
@@ -195,9 +197,9 @@ public void onScanCompleted(String path, Uri uri) {
final Activity activity,
final File externalFilesDirectory,
final ImageResizer imageResizer,
- final @Nullable ImageOutputOptions pendingImageOptions,
- final @Nullable VideoOptions pendingVideoOptions,
- final @Nullable MethodChannel.Result result,
+ final @Nullable ImageSelectionOptions pendingImageOptions,
+ final @Nullable VideoSelectionOptions pendingVideoOptions,
+ final @Nullable Messages.Result> result,
final ImagePickerCache cache,
final PermissionManager permissionManager,
final FileUriResolver fileUriResolver,
@@ -220,10 +222,6 @@ void setCameraDevice(CameraDevice device) {
cameraDevice = device;
}
- CameraDevice getCameraDevice() {
- return cameraDevice;
- }
-
// Save the state of the image picker so it can be retrieved with `retrieveLostImage`.
void saveStateBeforeResult() {
if (pendingCallState == null) {
@@ -242,34 +240,44 @@ void saveStateBeforeResult() {
}
}
- void retrieveLostImage(MethodChannel.Result result) {
- Map resultMap = cache.getCacheMap();
+ @Nullable
+ Messages.CacheRetrievalResult retrieveLostImage() {
+ Map cacheMap = cache.getCacheMap();
+ if (cacheMap.isEmpty()) {
+ return null;
+ }
+
+ Messages.CacheRetrievalResult.Builder result = new Messages.CacheRetrievalResult.Builder();
+
+ Messages.CacheRetrievalType type =
+ (Messages.CacheRetrievalType) cacheMap.get(ImagePickerCache.MAP_KEY_TYPE);
+ if (type != null) {
+ result.setType(type);
+ }
+ result.setError((Messages.CacheRetrievalError) cacheMap.get(ImagePickerCache.MAP_KEY_ERROR));
@SuppressWarnings("unchecked")
ArrayList pathList =
- (ArrayList) resultMap.get(ImagePickerCache.MAP_KEY_PATH_LIST);
- ArrayList newPathList = new ArrayList<>();
+ (ArrayList) cacheMap.get(ImagePickerCache.MAP_KEY_PATH_LIST);
if (pathList != null) {
+ ArrayList newPathList = new ArrayList<>();
for (String path : pathList) {
- Double maxWidth = (Double) resultMap.get(ImagePickerCache.MAP_KEY_MAX_WIDTH);
- Double maxHeight = (Double) resultMap.get(ImagePickerCache.MAP_KEY_MAX_HEIGHT);
- Integer boxedImageQuality = (Integer) resultMap.get(ImagePickerCache.MAP_KEY_IMAGE_QUALITY);
+ Double maxWidth = (Double) cacheMap.get(ImagePickerCache.MAP_KEY_MAX_WIDTH);
+ Double maxHeight = (Double) cacheMap.get(ImagePickerCache.MAP_KEY_MAX_HEIGHT);
+ Integer boxedImageQuality = (Integer) cacheMap.get(ImagePickerCache.MAP_KEY_IMAGE_QUALITY);
int imageQuality = boxedImageQuality == null ? 100 : boxedImageQuality;
newPathList.add(imageResizer.resizeImageIfNeeded(path, maxWidth, maxHeight, imageQuality));
}
- resultMap.put(ImagePickerCache.MAP_KEY_PATH_LIST, newPathList);
- resultMap.put(ImagePickerCache.MAP_KEY_PATH, newPathList.get(newPathList.size() - 1));
- }
- if (resultMap.isEmpty()) {
- result.success(null);
- } else {
- result.success(resultMap);
+ result.setPaths(newPathList);
}
+
cache.clear();
+
+ return result.build();
}
public void chooseVideoFromGallery(
- VideoOptions options, boolean usePhotoPicker, MethodChannel.Result result) {
+ VideoSelectionOptions options, boolean usePhotoPicker, Messages.Result> result) {
if (!setPendingOptionsAndResult(null, options, result)) {
finishWithAlreadyActiveError(result);
return;
@@ -296,7 +304,8 @@ private void launchPickVideoFromGalleryIntent(Boolean useAndroidPhotoPicker) {
activity.startActivityForResult(pickVideoIntent, REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY);
}
- public void takeVideoWithCamera(VideoOptions options, MethodChannel.Result result) {
+ public void takeVideoWithCamera(
+ VideoSelectionOptions options, Messages.Result> result) {
if (!setPendingOptionsAndResult(null, options, result)) {
finishWithAlreadyActiveError(result);
return;
@@ -316,8 +325,8 @@ private void launchTakeVideoWithCameraIntent() {
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
if (pendingCallState != null
&& pendingCallState.videoOptions != null
- && pendingCallState.videoOptions.maxDuration != null) {
- int maxSeconds = pendingCallState.videoOptions.maxDuration;
+ && pendingCallState.videoOptions.getMaxDurationSeconds() != null) {
+ int maxSeconds = pendingCallState.videoOptions.getMaxDurationSeconds().intValue();
intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, maxSeconds);
}
if (cameraDevice == CameraDevice.FRONT) {
@@ -346,7 +355,9 @@ private void launchTakeVideoWithCameraIntent() {
}
public void chooseImageFromGallery(
- @NonNull ImageOutputOptions options, boolean usePhotoPicker, MethodChannel.Result result) {
+ @NonNull ImageSelectionOptions options,
+ boolean usePhotoPicker,
+ Messages.Result> result) {
if (!setPendingOptionsAndResult(options, null, result)) {
finishWithAlreadyActiveError(result);
return;
@@ -356,7 +367,9 @@ public void chooseImageFromGallery(
}
public void chooseMultiImageFromGallery(
- @NonNull ImageOutputOptions options, boolean usePhotoPicker, MethodChannel.Result result) {
+ @NonNull ImageSelectionOptions options,
+ boolean usePhotoPicker,
+ Messages.Result> result) {
if (!setPendingOptionsAndResult(options, null, result)) {
finishWithAlreadyActiveError(result);
return;
@@ -405,7 +418,7 @@ private void launchMultiPickImageFromGalleryIntent(Boolean useAndroidPhotoPicker
}
public void takeImageWithCamera(
- @NonNull ImageOutputOptions options, MethodChannel.Result result) {
+ @NonNull ImageSelectionOptions options, Messages.Result> result) {
if (!setPendingOptionsAndResult(options, null, result)) {
finishWithAlreadyActiveError(result);
return;
@@ -493,7 +506,7 @@ private void grantUriPermissions(Intent intent, Uri imageUri) {
@Override
public boolean onRequestPermissionsResult(
- int requestCode, String[] permissions, int[] grantResults) {
+ int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
boolean permissionGranted =
grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
@@ -661,9 +674,12 @@ private void handleImageResult(String path, boolean shouldDeleteOriginalIfScaled
}
}
- private String getResizedImagePath(String path, @NonNull ImageOutputOptions outputOptions) {
+ private String getResizedImagePath(String path, @NonNull ImageSelectionOptions outputOptions) {
return imageResizer.resizeImageIfNeeded(
- path, outputOptions.maxWidth, outputOptions.maxHeight, outputOptions.quality);
+ path,
+ outputOptions.getMaxWidth(),
+ outputOptions.getMaxHeight(),
+ outputOptions.getQuality().intValue());
}
private void handleVideoResult(String path) {
@@ -671,9 +687,9 @@ private void handleVideoResult(String path) {
}
private boolean setPendingOptionsAndResult(
- @Nullable ImageOutputOptions imageOptions,
- @Nullable VideoOptions videoOptions,
- @NonNull MethodChannel.Result result) {
+ @Nullable ImageSelectionOptions imageOptions,
+ @Nullable VideoSelectionOptions videoOptions,
+ @NonNull Messages.Result> result) {
if (pendingCallState != null) {
return false;
}
@@ -691,16 +707,18 @@ private boolean setPendingOptionsAndResult(
// A null imagePath indicates that the image picker was cancelled without
// selection.
private void finishWithSuccess(@Nullable String imagePath) {
+ ArrayList pathList = new ArrayList<>();
+ if (imagePath != null) {
+ pathList.add(imagePath);
+ }
if (pendingCallState == null) {
// Only save data for later retrieval if something was actually selected.
- if (imagePath != null) {
- ArrayList pathList = new ArrayList<>();
- pathList.add(imagePath);
+ if (!pathList.isEmpty()) {
cache.saveResult(pathList, null, null);
}
return;
}
- pendingCallState.result.success(imagePath);
+ pendingCallState.result.success(pathList);
pendingCallState = null;
}
@@ -713,8 +731,8 @@ private void finishWithListSuccess(ArrayList imagePaths) {
pendingCallState = null;
}
- private void finishWithAlreadyActiveError(MethodChannel.Result result) {
- result.error("already_active", "Image picker is already active", null);
+ private void finishWithAlreadyActiveError(Messages.Result> result) {
+ result.error(new FlutterError("already_active", "Image picker is already active", null));
}
private void finishWithError(String errorCode, String errorMessage) {
@@ -722,7 +740,7 @@ private void finishWithError(String errorCode, String errorMessage) {
cache.saveResult(null, errorCode, errorMessage);
return;
}
- pendingCallState.result.error(errorCode, errorMessage, null);
+ pendingCallState.result.error(new FlutterError(errorCode, errorMessage, null));
pendingCallState = null;
}
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 4a4b3114c5bf..cbf52bd07782 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
@@ -7,9 +7,8 @@
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.Lifecycle;
@@ -19,14 +18,16 @@
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.embedding.engine.plugins.lifecycle.FlutterLifecycleAdapter;
import io.flutter.plugin.common.BinaryMessenger;
-import io.flutter.plugin.common.MethodCall;
-import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry;
+import io.flutter.plugins.imagepicker.Messages.FlutterError;
+import io.flutter.plugins.imagepicker.Messages.ImagePickerApi;
+import io.flutter.plugins.imagepicker.Messages.Result;
+import io.flutter.plugins.imagepicker.Messages.SourceSpecification;
import java.io.File;
+import java.util.List;
@SuppressWarnings("deprecation")
-public class ImagePickerPlugin
- implements MethodChannel.MethodCallHandler, FlutterPlugin, ActivityAware {
+public class ImagePickerPlugin implements FlutterPlugin, ActivityAware, ImagePickerApi {
private class LifeCycleObserver
implements Application.ActivityLifecycleCallbacks, DefaultLifecycleObserver {
@@ -98,9 +99,9 @@ private class ActivityState {
private Application application;
private Activity activity;
private ImagePickerDelegate delegate;
- private MethodChannel channel;
private LifeCycleObserver observer;
private ActivityPluginBinding activityBinding;
+ private BinaryMessenger messenger;
// This is null when not using v2 embedding;
private Lifecycle lifecycle;
@@ -110,16 +111,16 @@ private class ActivityState {
final Application application,
final Activity activity,
final BinaryMessenger messenger,
- final MethodChannel.MethodCallHandler handler,
+ final ImagePickerApi handler,
final PluginRegistry.Registrar registrar,
final ActivityPluginBinding activityBinding) {
this.application = application;
this.activity = activity;
this.activityBinding = activityBinding;
+ this.messenger = messenger;
delegate = constructDelegate(activity);
- channel = new MethodChannel(messenger, CHANNEL);
- channel.setMethodCallHandler(handler);
+ ImagePickerApi.setup(messenger, handler);
observer = new LifeCycleObserver(activity);
if (registrar != null) {
// V1 embedding setup for activity listeners.
@@ -153,10 +154,7 @@ void release() {
lifecycle = null;
}
- if (channel != null) {
- channel.setMethodCallHandler(null);
- channel = null;
- }
+ ImagePickerApi.setup(messenger, null);
if (application != null) {
application.unregisterActivityLifecycleCallbacks(observer);
@@ -177,17 +175,6 @@ ImagePickerDelegate getDelegate() {
}
}
- static final String METHOD_CALL_IMAGE = "pickImage";
- static final String METHOD_CALL_MULTI_IMAGE = "pickMultiImage";
- static final String METHOD_CALL_VIDEO = "pickVideo";
- 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_android";
-
- private static final int SOURCE_CAMERA = 0;
- private static final int SOURCE_GALLERY = 1;
-
private FlutterPluginBinding pluginBinding;
private ActivityState activityState;
@@ -226,17 +213,17 @@ final ActivityState getActivityState() {
}
@Override
- public void onAttachedToEngine(FlutterPluginBinding binding) {
+ public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
pluginBinding = binding;
}
@Override
- public void onDetachedFromEngine(FlutterPluginBinding binding) {
+ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
pluginBinding = null;
}
@Override
- public void onAttachedToActivity(ActivityPluginBinding binding) {
+ public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
setup(
pluginBinding.getBinaryMessenger(),
(Application) pluginBinding.getApplicationContext(),
@@ -256,7 +243,7 @@ public void onDetachedFromActivityForConfigChanges() {
}
@Override
- public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {
+ public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
onAttachedToActivity(binding);
}
@@ -287,123 +274,99 @@ final ImagePickerDelegate constructDelegate(final Activity setupActivity) {
return new ImagePickerDelegate(setupActivity, externalFilesDirectory, imageResizer, cache);
}
- // MethodChannel.Result wrapper that responds on the platform thread.
- private static class MethodResultWrapper implements MethodChannel.Result {
- private MethodChannel.Result methodResult;
- private Handler handler;
-
- MethodResultWrapper(MethodChannel.Result result) {
- methodResult = result;
- handler = new Handler(Looper.getMainLooper());
+ private @Nullable ImagePickerDelegate getImagePickerDelegate() {
+ if (activityState == null || activityState.getActivity() == null) {
+ return null;
}
+ return activityState.getDelegate();
+ }
- @Override
- public void success(final Object result) {
- handler.post(
- new Runnable() {
- @Override
- public void run() {
- methodResult.success(result);
- }
- });
+ private void setCameraDevice(
+ @NonNull ImagePickerDelegate delegate, @NonNull SourceSpecification source) {
+ Messages.SourceCamera camera = source.getCamera();
+ if (camera != null) {
+ ImagePickerDelegate.CameraDevice device;
+ switch (camera) {
+ case FRONT:
+ device = ImagePickerDelegate.CameraDevice.FRONT;
+ break;
+ case REAR:
+ default:
+ device = ImagePickerDelegate.CameraDevice.REAR;
+ break;
+ }
+ delegate.setCameraDevice(device);
}
+ }
- @Override
- public void error(
- final String errorCode, final String errorMessage, final Object errorDetails) {
- handler.post(
- new Runnable() {
- @Override
- public void run() {
- methodResult.error(errorCode, errorMessage, errorDetails);
- }
- });
+ @Override
+ public void pickImages(
+ @NonNull SourceSpecification source,
+ @NonNull Messages.ImageSelectionOptions options,
+ @NonNull Boolean allowMultiple,
+ @NonNull Boolean usePhotoPicker,
+ Result> result) {
+ ImagePickerDelegate delegate = getImagePickerDelegate();
+ if (delegate == null) {
+ result.error(
+ new FlutterError(
+ "no_activity", "image_picker plugin requires a foreground activity.", null));
+ return;
}
- @Override
- public void notImplemented() {
- handler.post(
- new Runnable() {
- @Override
- public void run() {
- methodResult.notImplemented();
- }
- });
+ setCameraDevice(delegate, source);
+ if (allowMultiple) {
+ delegate.chooseMultiImageFromGallery(options, usePhotoPicker, result);
+ } else {
+ switch (source.getType()) {
+ case GALLERY:
+ delegate.chooseImageFromGallery(options, usePhotoPicker, result);
+ break;
+ case CAMERA:
+ delegate.takeImageWithCamera(options, result);
+ break;
+ }
}
}
@Override
- public void onMethodCall(MethodCall call, MethodChannel.Result rawResult) {
- if (activityState == null || activityState.getActivity() == null) {
- rawResult.error("no_activity", "image_picker plugin requires a foreground activity.", null);
+ public void pickVideos(
+ @NonNull SourceSpecification source,
+ @NonNull Messages.VideoSelectionOptions options,
+ @NonNull Boolean allowMultiple,
+ @NonNull Boolean usePhotoPicker,
+ Result> result) {
+ ImagePickerDelegate delegate = getImagePickerDelegate();
+ if (delegate == null) {
+ result.error(
+ new FlutterError(
+ "no_activity", "image_picker plugin requires a foreground activity.", null));
return;
}
- MethodChannel.Result result = new MethodResultWrapper(rawResult);
- int imageSource;
- ImagePickerDelegate delegate = activityState.getDelegate();
- if (call.argument("cameraDevice") != null) {
- ImagePickerDelegate.CameraDevice device;
- int deviceIntValue = call.argument("cameraDevice");
- if (deviceIntValue == CAMERA_DEVICE_FRONT) {
- device = ImagePickerDelegate.CameraDevice.FRONT;
- } else {
- device = ImagePickerDelegate.CameraDevice.REAR;
- }
- delegate.setCameraDevice(device);
- }
- Boolean usePhotoPicker = call.argument("useAndroidPhotoPicker");
- if (usePhotoPicker == null) {
- usePhotoPicker = false;
+ setCameraDevice(delegate, source);
+ if (allowMultiple) {
+ result.error(new RuntimeException("Multi-video selection is not implemented"));
+ } else {
+ switch (source.getType()) {
+ case GALLERY:
+ delegate.chooseVideoFromGallery(options, usePhotoPicker, result);
+ break;
+ case CAMERA:
+ delegate.takeVideoWithCamera(options, result);
+ break;
+ }
}
+ }
- switch (call.method) {
- case METHOD_CALL_IMAGE:
- imageSource = call.argument("source");
- ImageOutputOptions imageOptions =
- new ImageOutputOptions(
- call.argument("maxWidth"),
- call.argument("maxHeight"),
- call.argument("imageQuality"));
- switch (imageSource) {
- case SOURCE_GALLERY:
- delegate.chooseImageFromGallery(imageOptions, usePhotoPicker, result);
- break;
- case SOURCE_CAMERA:
- delegate.takeImageWithCamera(imageOptions, result);
- break;
- default:
- throw new IllegalArgumentException("Invalid image source: " + imageSource);
- }
- break;
- case METHOD_CALL_MULTI_IMAGE:
- delegate.chooseMultiImageFromGallery(
- new ImageOutputOptions(
- call.argument("maxWidth"),
- call.argument("maxHeight"),
- call.argument("imageQuality")),
- usePhotoPicker,
- result);
- break;
- case METHOD_CALL_VIDEO:
- imageSource = call.argument("source");
- VideoOptions videoOptions = new VideoOptions(call.argument("maxDuration"));
- switch (imageSource) {
- case SOURCE_GALLERY:
- delegate.chooseVideoFromGallery(videoOptions, usePhotoPicker, result);
- break;
- case SOURCE_CAMERA:
- delegate.takeVideoWithCamera(videoOptions, result);
- break;
- default:
- throw new IllegalArgumentException("Invalid video source: " + imageSource);
- }
- break;
- case METHOD_CALL_RETRIEVE:
- delegate.retrieveLostImage(result);
- break;
- default:
- throw new IllegalArgumentException("Unknown method " + call.method);
+ @Nullable
+ @Override
+ public Messages.CacheRetrievalResult retrieveLostResults() {
+ ImagePickerDelegate delegate = getImagePickerDelegate();
+ if (delegate == null) {
+ throw new FlutterError(
+ "no_activity", "image_picker plugin requires a foreground activity.", null);
}
+ return delegate.retrieveLostImage();
}
}
diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java
new file mode 100644
index 000000000000..af98078c673c
--- /dev/null
+++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java
@@ -0,0 +1,685 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Autogenerated from Pigeon (v9.1.0), do not edit directly.
+// See also: https://pub.dev/packages/pigeon
+
+package io.flutter.plugins.imagepicker;
+
+import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import io.flutter.plugin.common.BasicMessageChannel;
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugin.common.MessageCodec;
+import io.flutter.plugin.common.StandardMessageCodec;
+import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Generated class from Pigeon. */
+@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"})
+public class Messages {
+
+ /** Error class for passing custom error details to Flutter via a thrown PlatformException. */
+ public static class FlutterError extends RuntimeException {
+
+ /** The error code. */
+ public final String code;
+
+ /** The error details. Must be a datatype supported by the api codec. */
+ public final Object details;
+
+ public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) {
+ super(message);
+ this.code = code;
+ this.details = details;
+ }
+ }
+
+ @NonNull
+ private static ArrayList wrapError(@NonNull Throwable exception) {
+ ArrayList errorList = new ArrayList(3);
+ if (exception instanceof FlutterError) {
+ FlutterError error = (FlutterError) exception;
+ errorList.add(error.code);
+ errorList.add(error.getMessage());
+ errorList.add(error.details);
+ } else {
+ errorList.add(exception.toString());
+ errorList.add(exception.getClass().getSimpleName());
+ errorList.add(
+ "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception));
+ }
+ return errorList;
+ }
+
+ public enum SourceCamera {
+ REAR(0),
+ FRONT(1);
+
+ private final int index;
+
+ private SourceCamera(final int index) {
+ this.index = index;
+ }
+ }
+
+ public enum SourceType {
+ CAMERA(0),
+ GALLERY(1);
+
+ private final int index;
+
+ private SourceType(final int index) {
+ this.index = index;
+ }
+ }
+
+ public enum CacheRetrievalType {
+ IMAGE(0),
+ VIDEO(1);
+
+ private final int index;
+
+ private CacheRetrievalType(final int index) {
+ this.index = index;
+ }
+ }
+
+ /**
+ * Options for image selection and output.
+ *
+ * Generated class from Pigeon that represents data sent in messages.
+ */
+ public static final class ImageSelectionOptions {
+ /** If set, the max width that the image should be resized to fit in. */
+ private @Nullable Double maxWidth;
+
+ public @Nullable Double getMaxWidth() {
+ return maxWidth;
+ }
+
+ public void setMaxWidth(@Nullable Double setterArg) {
+ this.maxWidth = setterArg;
+ }
+
+ /** If set, the max height that the image should be resized to fit in. */
+ private @Nullable Double maxHeight;
+
+ public @Nullable Double getMaxHeight() {
+ return maxHeight;
+ }
+
+ public void setMaxHeight(@Nullable Double setterArg) {
+ this.maxHeight = setterArg;
+ }
+
+ /**
+ * The quality of the output image, from 0-100.
+ *
+ *
100 indicates original quality.
+ */
+ private @NonNull Long quality;
+
+ public @NonNull Long getQuality() {
+ return quality;
+ }
+
+ public void setQuality(@NonNull Long setterArg) {
+ if (setterArg == null) {
+ throw new IllegalStateException("Nonnull field \"quality\" is null.");
+ }
+ this.quality = setterArg;
+ }
+
+ /** Constructor is private to enforce null safety; use Builder. */
+ private ImageSelectionOptions() {}
+
+ public static final class Builder {
+
+ private @Nullable Double maxWidth;
+
+ public @NonNull Builder setMaxWidth(@Nullable Double setterArg) {
+ this.maxWidth = setterArg;
+ return this;
+ }
+
+ private @Nullable Double maxHeight;
+
+ public @NonNull Builder setMaxHeight(@Nullable Double setterArg) {
+ this.maxHeight = setterArg;
+ return this;
+ }
+
+ private @Nullable Long quality;
+
+ public @NonNull Builder setQuality(@NonNull Long setterArg) {
+ this.quality = setterArg;
+ return this;
+ }
+
+ public @NonNull ImageSelectionOptions build() {
+ ImageSelectionOptions pigeonReturn = new ImageSelectionOptions();
+ pigeonReturn.setMaxWidth(maxWidth);
+ pigeonReturn.setMaxHeight(maxHeight);
+ pigeonReturn.setQuality(quality);
+ return pigeonReturn;
+ }
+ }
+
+ @NonNull
+ ArrayList toList() {
+ ArrayList toListResult = new ArrayList(3);
+ toListResult.add(maxWidth);
+ toListResult.add(maxHeight);
+ toListResult.add(quality);
+ return toListResult;
+ }
+
+ static @NonNull ImageSelectionOptions fromList(@NonNull ArrayList list) {
+ ImageSelectionOptions pigeonResult = new ImageSelectionOptions();
+ Object maxWidth = list.get(0);
+ pigeonResult.setMaxWidth((Double) maxWidth);
+ Object maxHeight = list.get(1);
+ pigeonResult.setMaxHeight((Double) maxHeight);
+ Object quality = list.get(2);
+ pigeonResult.setQuality(
+ (quality == null)
+ ? null
+ : ((quality instanceof Integer) ? (Integer) quality : (Long) quality));
+ return pigeonResult;
+ }
+ }
+
+ /**
+ * Options for image selection and output.
+ *
+ * Generated class from Pigeon that represents data sent in messages.
+ */
+ public static final class VideoSelectionOptions {
+ /** The maximum desired length for the video, in seconds. */
+ private @Nullable Long maxDurationSeconds;
+
+ public @Nullable Long getMaxDurationSeconds() {
+ return maxDurationSeconds;
+ }
+
+ public void setMaxDurationSeconds(@Nullable Long setterArg) {
+ this.maxDurationSeconds = setterArg;
+ }
+
+ public static final class Builder {
+
+ private @Nullable Long maxDurationSeconds;
+
+ public @NonNull Builder setMaxDurationSeconds(@Nullable Long setterArg) {
+ this.maxDurationSeconds = setterArg;
+ return this;
+ }
+
+ public @NonNull VideoSelectionOptions build() {
+ VideoSelectionOptions pigeonReturn = new VideoSelectionOptions();
+ pigeonReturn.setMaxDurationSeconds(maxDurationSeconds);
+ return pigeonReturn;
+ }
+ }
+
+ @NonNull
+ ArrayList toList() {
+ ArrayList toListResult = new ArrayList(1);
+ toListResult.add(maxDurationSeconds);
+ return toListResult;
+ }
+
+ static @NonNull VideoSelectionOptions fromList(@NonNull ArrayList list) {
+ VideoSelectionOptions pigeonResult = new VideoSelectionOptions();
+ Object maxDurationSeconds = list.get(0);
+ pigeonResult.setMaxDurationSeconds(
+ (maxDurationSeconds == null)
+ ? null
+ : ((maxDurationSeconds instanceof Integer)
+ ? (Integer) maxDurationSeconds
+ : (Long) maxDurationSeconds));
+ return pigeonResult;
+ }
+ }
+
+ /**
+ * Specification for the source of an image or video selection.
+ *
+ * Generated class from Pigeon that represents data sent in messages.
+ */
+ public static final class SourceSpecification {
+ private @NonNull SourceType type;
+
+ public @NonNull SourceType getType() {
+ return type;
+ }
+
+ public void setType(@NonNull SourceType setterArg) {
+ if (setterArg == null) {
+ throw new IllegalStateException("Nonnull field \"type\" is null.");
+ }
+ this.type = setterArg;
+ }
+
+ private @Nullable SourceCamera camera;
+
+ public @Nullable SourceCamera getCamera() {
+ return camera;
+ }
+
+ public void setCamera(@Nullable SourceCamera setterArg) {
+ this.camera = setterArg;
+ }
+
+ /** Constructor is private to enforce null safety; use Builder. */
+ private SourceSpecification() {}
+
+ public static final class Builder {
+
+ private @Nullable SourceType type;
+
+ public @NonNull Builder setType(@NonNull SourceType setterArg) {
+ this.type = setterArg;
+ return this;
+ }
+
+ private @Nullable SourceCamera camera;
+
+ public @NonNull Builder setCamera(@Nullable SourceCamera setterArg) {
+ this.camera = setterArg;
+ return this;
+ }
+
+ public @NonNull SourceSpecification build() {
+ SourceSpecification pigeonReturn = new SourceSpecification();
+ pigeonReturn.setType(type);
+ pigeonReturn.setCamera(camera);
+ return pigeonReturn;
+ }
+ }
+
+ @NonNull
+ ArrayList toList() {
+ ArrayList toListResult = new ArrayList(2);
+ toListResult.add(type == null ? null : type.index);
+ toListResult.add(camera == null ? null : camera.index);
+ return toListResult;
+ }
+
+ static @NonNull SourceSpecification fromList(@NonNull ArrayList list) {
+ SourceSpecification pigeonResult = new SourceSpecification();
+ Object type = list.get(0);
+ pigeonResult.setType(type == null ? null : SourceType.values()[(int) type]);
+ Object camera = list.get(1);
+ pigeonResult.setCamera(camera == null ? null : SourceCamera.values()[(int) camera]);
+ return pigeonResult;
+ }
+ }
+
+ /**
+ * An error that occurred during lost result retrieval.
+ *
+ * The data here maps to the `PlatformException` that will be created from it.
+ *
+ *
Generated class from Pigeon that represents data sent in messages.
+ */
+ public static final class CacheRetrievalError {
+ private @NonNull String code;
+
+ public @NonNull String getCode() {
+ return code;
+ }
+
+ public void setCode(@NonNull String setterArg) {
+ if (setterArg == null) {
+ throw new IllegalStateException("Nonnull field \"code\" is null.");
+ }
+ this.code = setterArg;
+ }
+
+ private @Nullable String message;
+
+ public @Nullable String getMessage() {
+ return message;
+ }
+
+ public void setMessage(@Nullable String setterArg) {
+ this.message = setterArg;
+ }
+
+ /** Constructor is private to enforce null safety; use Builder. */
+ private CacheRetrievalError() {}
+
+ public static final class Builder {
+
+ private @Nullable String code;
+
+ public @NonNull Builder setCode(@NonNull String setterArg) {
+ this.code = setterArg;
+ return this;
+ }
+
+ private @Nullable String message;
+
+ public @NonNull Builder setMessage(@Nullable String setterArg) {
+ this.message = setterArg;
+ return this;
+ }
+
+ public @NonNull CacheRetrievalError build() {
+ CacheRetrievalError pigeonReturn = new CacheRetrievalError();
+ pigeonReturn.setCode(code);
+ pigeonReturn.setMessage(message);
+ return pigeonReturn;
+ }
+ }
+
+ @NonNull
+ ArrayList toList() {
+ ArrayList toListResult = new ArrayList(2);
+ toListResult.add(code);
+ toListResult.add(message);
+ return toListResult;
+ }
+
+ static @NonNull CacheRetrievalError fromList(@NonNull ArrayList list) {
+ CacheRetrievalError pigeonResult = new CacheRetrievalError();
+ Object code = list.get(0);
+ pigeonResult.setCode((String) code);
+ Object message = list.get(1);
+ pigeonResult.setMessage((String) message);
+ return pigeonResult;
+ }
+ }
+
+ /**
+ * The result of retrieving cached results from a previous run.
+ *
+ * Generated class from Pigeon that represents data sent in messages.
+ */
+ public static final class CacheRetrievalResult {
+ /** The type of the retrieved data. */
+ private @NonNull CacheRetrievalType type;
+
+ public @NonNull CacheRetrievalType getType() {
+ return type;
+ }
+
+ public void setType(@NonNull CacheRetrievalType setterArg) {
+ if (setterArg == null) {
+ throw new IllegalStateException("Nonnull field \"type\" is null.");
+ }
+ this.type = setterArg;
+ }
+
+ /** The error from the last selection, if any. */
+ private @Nullable CacheRetrievalError error;
+
+ public @Nullable CacheRetrievalError getError() {
+ return error;
+ }
+
+ public void setError(@Nullable CacheRetrievalError setterArg) {
+ this.error = setterArg;
+ }
+
+ /**
+ * The results from the last selection, if any.
+ *
+ *
Elements must not be null, by convention. See
+ * https://github.com/flutter/flutter/issues/97848
+ */
+ private @NonNull List paths;
+
+ public @NonNull List getPaths() {
+ return paths;
+ }
+
+ public void setPaths(@NonNull List setterArg) {
+ if (setterArg == null) {
+ throw new IllegalStateException("Nonnull field \"paths\" is null.");
+ }
+ this.paths = setterArg;
+ }
+
+ /** Constructor is private to enforce null safety; use Builder. */
+ private CacheRetrievalResult() {}
+
+ public static final class Builder {
+
+ private @Nullable CacheRetrievalType type;
+
+ public @NonNull Builder setType(@NonNull CacheRetrievalType setterArg) {
+ this.type = setterArg;
+ return this;
+ }
+
+ private @Nullable CacheRetrievalError error;
+
+ public @NonNull Builder setError(@Nullable CacheRetrievalError setterArg) {
+ this.error = setterArg;
+ return this;
+ }
+
+ private @Nullable List paths;
+
+ public @NonNull Builder setPaths(@NonNull List setterArg) {
+ this.paths = setterArg;
+ return this;
+ }
+
+ public @NonNull CacheRetrievalResult build() {
+ CacheRetrievalResult pigeonReturn = new CacheRetrievalResult();
+ pigeonReturn.setType(type);
+ pigeonReturn.setError(error);
+ pigeonReturn.setPaths(paths);
+ return pigeonReturn;
+ }
+ }
+
+ @NonNull
+ ArrayList toList() {
+ ArrayList toListResult = new ArrayList(3);
+ toListResult.add(type == null ? null : type.index);
+ toListResult.add((error == null) ? null : error.toList());
+ toListResult.add(paths);
+ return toListResult;
+ }
+
+ static @NonNull CacheRetrievalResult fromList(@NonNull ArrayList list) {
+ CacheRetrievalResult pigeonResult = new CacheRetrievalResult();
+ Object type = list.get(0);
+ pigeonResult.setType(type == null ? null : CacheRetrievalType.values()[(int) type]);
+ Object error = list.get(1);
+ pigeonResult.setError(
+ (error == null) ? null : CacheRetrievalError.fromList((ArrayList) error));
+ Object paths = list.get(2);
+ pigeonResult.setPaths((List) paths);
+ return pigeonResult;
+ }
+ }
+
+ public interface Result {
+ void success(T result);
+
+ void error(Throwable error);
+ }
+
+ private static class ImagePickerApiCodec extends StandardMessageCodec {
+ public static final ImagePickerApiCodec INSTANCE = new ImagePickerApiCodec();
+
+ private ImagePickerApiCodec() {}
+
+ @Override
+ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
+ switch (type) {
+ case (byte) 128:
+ return CacheRetrievalError.fromList((ArrayList) readValue(buffer));
+ case (byte) 129:
+ return CacheRetrievalResult.fromList((ArrayList) readValue(buffer));
+ case (byte) 130:
+ return ImageSelectionOptions.fromList((ArrayList) readValue(buffer));
+ case (byte) 131:
+ return SourceSpecification.fromList((ArrayList) readValue(buffer));
+ case (byte) 132:
+ return VideoSelectionOptions.fromList((ArrayList) readValue(buffer));
+ default:
+ return super.readValueOfType(type, buffer);
+ }
+ }
+
+ @Override
+ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
+ if (value instanceof CacheRetrievalError) {
+ stream.write(128);
+ writeValue(stream, ((CacheRetrievalError) value).toList());
+ } else if (value instanceof CacheRetrievalResult) {
+ stream.write(129);
+ writeValue(stream, ((CacheRetrievalResult) value).toList());
+ } else if (value instanceof ImageSelectionOptions) {
+ stream.write(130);
+ writeValue(stream, ((ImageSelectionOptions) value).toList());
+ } else if (value instanceof SourceSpecification) {
+ stream.write(131);
+ writeValue(stream, ((SourceSpecification) value).toList());
+ } else if (value instanceof VideoSelectionOptions) {
+ stream.write(132);
+ writeValue(stream, ((VideoSelectionOptions) value).toList());
+ } else {
+ super.writeValue(stream, value);
+ }
+ }
+ }
+
+ /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
+ public interface ImagePickerApi {
+ /**
+ * Selects images and returns their paths.
+ *
+ * Elements must not be null, by convention. See
+ * https://github.com/flutter/flutter/issues/97848
+ */
+ void pickImages(
+ @NonNull SourceSpecification source,
+ @NonNull ImageSelectionOptions options,
+ @NonNull Boolean allowMultiple,
+ @NonNull Boolean usePhotoPicker,
+ Result> result);
+ /**
+ * Selects video and returns their paths.
+ *
+ * Elements must not be null, by convention. See
+ * https://github.com/flutter/flutter/issues/97848
+ */
+ void pickVideos(
+ @NonNull SourceSpecification source,
+ @NonNull VideoSelectionOptions options,
+ @NonNull Boolean allowMultiple,
+ @NonNull Boolean usePhotoPicker,
+ Result> result);
+ /** Returns results from a previous app session, if any. */
+ @Nullable
+ CacheRetrievalResult retrieveLostResults();
+
+ /** The codec used by ImagePickerApi. */
+ static MessageCodec getCodec() {
+ return ImagePickerApiCodec.INSTANCE;
+ }
+ /** Sets up an instance of `ImagePickerApi` to handle messages through the `binaryMessenger`. */
+ static void setup(BinaryMessenger binaryMessenger, ImagePickerApi api) {
+ {
+ BasicMessageChannel channel =
+ new BasicMessageChannel<>(
+ binaryMessenger, "dev.flutter.pigeon.ImagePickerApi.pickImages", getCodec());
+ if (api != null) {
+ channel.setMessageHandler(
+ (message, reply) -> {
+ ArrayList wrapped = new ArrayList();
+ ArrayList args = (ArrayList) message;
+ SourceSpecification sourceArg = (SourceSpecification) args.get(0);
+ ImageSelectionOptions optionsArg = (ImageSelectionOptions) args.get(1);
+ Boolean allowMultipleArg = (Boolean) args.get(2);
+ Boolean usePhotoPickerArg = (Boolean) args.get(3);
+ Result> resultCallback =
+ new Result>() {
+ public void success(List result) {
+ wrapped.add(0, result);
+ reply.reply(wrapped);
+ }
+
+ public void error(Throwable error) {
+ ArrayList wrappedError = wrapError(error);
+ reply.reply(wrappedError);
+ }
+ };
+
+ api.pickImages(
+ sourceArg, optionsArg, allowMultipleArg, usePhotoPickerArg, resultCallback);
+ });
+ } else {
+ channel.setMessageHandler(null);
+ }
+ }
+ {
+ BasicMessageChannel channel =
+ new BasicMessageChannel<>(
+ binaryMessenger, "dev.flutter.pigeon.ImagePickerApi.pickVideos", getCodec());
+ if (api != null) {
+ channel.setMessageHandler(
+ (message, reply) -> {
+ ArrayList wrapped = new ArrayList();
+ ArrayList args = (ArrayList) message;
+ SourceSpecification sourceArg = (SourceSpecification) args.get(0);
+ VideoSelectionOptions optionsArg = (VideoSelectionOptions) args.get(1);
+ Boolean allowMultipleArg = (Boolean) args.get(2);
+ Boolean usePhotoPickerArg = (Boolean) args.get(3);
+ Result> resultCallback =
+ new Result>() {
+ public void success(List result) {
+ wrapped.add(0, result);
+ reply.reply(wrapped);
+ }
+
+ public void error(Throwable error) {
+ ArrayList wrappedError = wrapError(error);
+ reply.reply(wrappedError);
+ }
+ };
+
+ api.pickVideos(
+ sourceArg, optionsArg, allowMultipleArg, usePhotoPickerArg, resultCallback);
+ });
+ } else {
+ channel.setMessageHandler(null);
+ }
+ }
+ {
+ BasicMessageChannel channel =
+ new BasicMessageChannel<>(
+ binaryMessenger,
+ "dev.flutter.pigeon.ImagePickerApi.retrieveLostResults",
+ getCodec());
+ if (api != null) {
+ channel.setMessageHandler(
+ (message, reply) -> {
+ ArrayList wrapped = new ArrayList();
+ try {
+ CacheRetrievalResult output = api.retrieveLostResults();
+ wrapped.add(0, output);
+ } catch (Throwable exception) {
+ ArrayList wrappedError = wrapError(exception);
+ wrapped = wrappedError;
+ }
+ reply.reply(wrapped);
+ });
+ } else {
+ channel.setMessageHandler(null);
+ }
+ }
+ }
+ }
+}
diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/VideoOptions.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/VideoOptions.java
deleted file mode 100644
index e3d73204d451..000000000000
--- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/VideoOptions.java
+++ /dev/null
@@ -1,16 +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.
-
-package io.flutter.plugins.imagepicker;
-
-import androidx.annotation.Nullable;
-
-/** Stores settings for video selection and output options. */
-public class VideoOptions {
- @Nullable public final Integer maxDuration;
-
- public VideoOptions(@Nullable Integer maxDuration) {
- this.maxDuration = maxDuration;
- }
-}
diff --git a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerCacheTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerCacheTest.java
index aa6fbc75ee81..2b8cca5b314a 100644
--- a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerCacheTest.java
+++ b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerCacheTest.java
@@ -105,12 +105,14 @@ public void tearDown() throws Exception {
public void imageCache_shouldBeAbleToSetAndGetQuality() {
final int quality = 90;
ImagePickerCache cache = new ImagePickerCache(mockActivity);
- cache.saveDimensionWithOutputOptions(new ImageOutputOptions(null, null, quality));
+ cache.saveDimensionWithOutputOptions(
+ new Messages.ImageSelectionOptions.Builder().setQuality((long) quality).build());
Map resultMap = cache.getCacheMap();
int imageQuality = (int) resultMap.get(ImagePickerCache.MAP_KEY_IMAGE_QUALITY);
assertThat(imageQuality, equalTo(quality));
- cache.saveDimensionWithOutputOptions(new ImageOutputOptions(null, null, null));
+ cache.saveDimensionWithOutputOptions(
+ new Messages.ImageSelectionOptions.Builder().setQuality((long) 100).build());
Map resultMapWithDefaultQuality = cache.getCacheMap();
int defaultImageQuality =
(int) resultMapWithDefaultQuality.get(ImagePickerCache.MAP_KEY_IMAGE_QUALITY);
diff --git a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java
index 0c9fdb94fb2c..77a34b452b0e 100644
--- a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java
+++ b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java
@@ -10,7 +10,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -27,11 +26,12 @@
import android.content.pm.PackageManager;
import android.net.Uri;
import androidx.annotation.Nullable;
-import io.flutter.plugin.common.MethodChannel;
+import io.flutter.plugins.imagepicker.Messages.FlutterError;
+import io.flutter.plugins.imagepicker.Messages.ImageSelectionOptions;
+import io.flutter.plugins.imagepicker.Messages.VideoSelectionOptions;
import java.io.File;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -48,15 +48,18 @@
public class ImagePickerDelegateTest {
private static final Double WIDTH = 10.0;
private static final Double HEIGHT = 10.0;
- private static final int MAX_DURATION = 10;
+ private static final Long MAX_DURATION = 10L;
private static final Integer IMAGE_QUALITY = 90;
- private static final ImageOutputOptions DEFAULT_IMAGE_OPTIONS =
- new ImageOutputOptions(null, null, null);
- private static final VideoOptions DEFAULT_VIDEO_OPTIONS = new VideoOptions(null);
+ private static final ImageSelectionOptions DEFAULT_IMAGE_OPTIONS =
+ new ImageSelectionOptions.Builder().setQuality((long) 100).build();
+ private static final ImageSelectionOptions RESIZE_TRIGGERING_IMAGE_OPTIONS =
+ new ImageSelectionOptions.Builder().setQuality((long) 100).setMaxWidth(WIDTH).build();
+ private static final VideoSelectionOptions DEFAULT_VIDEO_OPTIONS =
+ new VideoSelectionOptions.Builder().build();
@Mock Activity mockActivity;
@Mock ImageResizer mockImageResizer;
- @Mock MethodChannel.Result mockResult;
+ @Mock Messages.Result> mockResult;
@Mock ImagePickerDelegate.PermissionManager mockPermissionManager;
@Mock FileUtils mockFileUtils;
@Mock Intent mockIntent;
@@ -128,7 +131,7 @@ public void chooseImageFromGallery_whenPendingResultExists_finishesWithAlreadyAc
ImagePickerDelegate delegate =
createDelegateWithPendingResultAndOptions(DEFAULT_IMAGE_OPTIONS, null);
- delegate.chooseImageFromGallery(new ImageOutputOptions(null, null, null), false, mockResult);
+ delegate.chooseImageFromGallery(DEFAULT_IMAGE_OPTIONS, false, mockResult);
verifyFinishedWithAlreadyActiveError();
verifyNoMoreInteractions(mockResult);
@@ -139,8 +142,7 @@ public void chooseMultiImageFromGallery_whenPendingResultExists_finishesWithAlre
ImagePickerDelegate delegate =
createDelegateWithPendingResultAndOptions(DEFAULT_IMAGE_OPTIONS, null);
- delegate.chooseMultiImageFromGallery(
- new ImageOutputOptions(null, null, null), false, mockResult);
+ delegate.chooseMultiImageFromGallery(DEFAULT_IMAGE_OPTIONS, false, mockResult);
verifyFinishedWithAlreadyActiveError();
verifyNoMoreInteractions(mockResult);
@@ -151,7 +153,7 @@ public void chooseMultiImageFromGallery_whenPendingResultExists_finishesWithAlre
public void chooseImageFromGallery_launchesChooseFromGalleryIntent() {
ImagePickerDelegate delegate = createDelegate();
- delegate.chooseImageFromGallery(new ImageOutputOptions(null, null, null), false, mockResult);
+ delegate.chooseImageFromGallery(DEFAULT_IMAGE_OPTIONS, false, mockResult);
verify(mockActivity)
.startActivityForResult(
@@ -163,7 +165,7 @@ public void chooseImageFromGallery_launchesChooseFromGalleryIntent() {
public void chooseImageFromGallery_withPhotoPicker_launchesChooseFromGalleryIntent() {
ImagePickerDelegate delegate = createDelegate();
- delegate.chooseImageFromGallery(new ImageOutputOptions(null, null, null), true, mockResult);
+ delegate.chooseImageFromGallery(DEFAULT_IMAGE_OPTIONS, true, mockResult);
verify(mockActivity)
.startActivityForResult(
@@ -175,8 +177,7 @@ public void chooseImageFromGallery_withPhotoPicker_launchesChooseFromGalleryInte
public void chooseMultiImageFromGallery_launchesChooseFromGalleryIntent() {
ImagePickerDelegate delegate = createDelegate();
- delegate.chooseMultiImageFromGallery(
- new ImageOutputOptions(null, null, null), true, mockResult);
+ delegate.chooseMultiImageFromGallery(DEFAULT_IMAGE_OPTIONS, true, mockResult);
verify(mockActivity)
.startActivityForResult(
@@ -189,8 +190,7 @@ public void chooseMultiImageFromGallery_launchesChooseFromGalleryIntent() {
public void chooseMultiImageFromGallery_withPhotoPicker_launchesChooseFromGalleryIntent() {
ImagePickerDelegate delegate = createDelegate();
- delegate.chooseMultiImageFromGallery(
- new ImageOutputOptions(null, null, null), false, mockResult);
+ delegate.chooseMultiImageFromGallery(DEFAULT_IMAGE_OPTIONS, false, mockResult);
verify(mockActivity)
.startActivityForResult(
@@ -203,7 +203,7 @@ public void chooseMultiImageFromGallery_withPhotoPicker_launchesChooseFromGaller
public void chooseVideoFromGallery_launchesChooseFromGalleryIntent() {
ImagePickerDelegate delegate = createDelegate();
- delegate.chooseVideoFromGallery(new VideoOptions(null), true, mockResult);
+ delegate.chooseVideoFromGallery(DEFAULT_VIDEO_OPTIONS, true, mockResult);
verify(mockActivity)
.startActivityForResult(
@@ -215,7 +215,7 @@ public void chooseVideoFromGallery_launchesChooseFromGalleryIntent() {
public void chooseVideoFromGallery_withPhotoPicker_launchesChooseFromGalleryIntent() {
ImagePickerDelegate delegate = createDelegate();
- delegate.chooseVideoFromGallery(new VideoOptions(null), true, mockResult);
+ delegate.chooseVideoFromGallery(DEFAULT_VIDEO_OPTIONS, true, mockResult);
verify(mockActivity)
.startActivityForResult(
@@ -227,7 +227,7 @@ public void takeImageWithCamera_whenPendingResultExists_finishesWithAlreadyActiv
ImagePickerDelegate delegate =
createDelegateWithPendingResultAndOptions(DEFAULT_IMAGE_OPTIONS, null);
- delegate.takeImageWithCamera(new ImageOutputOptions(null, null, null), mockResult);
+ delegate.takeImageWithCamera(DEFAULT_IMAGE_OPTIONS, mockResult);
verifyFinishedWithAlreadyActiveError();
verifyNoMoreInteractions(mockResult);
@@ -239,7 +239,7 @@ public void takeImageWithCamera_whenHasNoCameraPermission_requestsForPermission(
when(mockPermissionManager.needRequestCameraPermission()).thenReturn(true);
ImagePickerDelegate delegate = createDelegate();
- delegate.takeImageWithCamera(new ImageOutputOptions(null, null, null), mockResult);
+ delegate.takeImageWithCamera(DEFAULT_IMAGE_OPTIONS, mockResult);
verify(mockPermissionManager)
.askForPermission(
@@ -251,7 +251,7 @@ public void takeImageWithCamera_whenCameraPermissionNotPresent_requestsForPermis
when(mockPermissionManager.needRequestCameraPermission()).thenReturn(false);
ImagePickerDelegate delegate = createDelegate();
- delegate.takeImageWithCamera(new ImageOutputOptions(null, null, null), mockResult);
+ delegate.takeImageWithCamera(DEFAULT_IMAGE_OPTIONS, mockResult);
verify(mockActivity)
.startActivityForResult(
@@ -264,7 +264,7 @@ public void takeImageWithCamera_whenCameraPermissionNotPresent_requestsForPermis
when(mockPermissionManager.isPermissionGranted(Manifest.permission.CAMERA)).thenReturn(true);
ImagePickerDelegate delegate = createDelegate();
- delegate.takeImageWithCamera(new ImageOutputOptions(null, null, null), mockResult);
+ delegate.takeImageWithCamera(DEFAULT_IMAGE_OPTIONS, mockResult);
verify(mockActivity)
.startActivityForResult(
@@ -279,10 +279,12 @@ public void takeImageWithCamera_whenCameraPermissionNotPresent_requestsForPermis
.when(mockActivity)
.startActivityForResult(any(Intent.class), anyInt());
ImagePickerDelegate delegate = createDelegate();
- delegate.takeImageWithCamera(new ImageOutputOptions(null, null, null), mockResult);
+ delegate.takeImageWithCamera(DEFAULT_IMAGE_OPTIONS, mockResult);
- verify(mockResult)
- .error("no_available_camera", "No cameras available for taking pictures.", null);
+ ArgumentCaptor errorCaptor = ArgumentCaptor.forClass(FlutterError.class);
+ verify(mockResult).error(errorCaptor.capture());
+ assertEquals("no_available_camera", errorCaptor.getValue().code);
+ assertEquals("No cameras available for taking pictures.", errorCaptor.getValue().getMessage());
verifyNoMoreInteractions(mockResult);
}
@@ -291,7 +293,7 @@ public void takeImageWithCamera_writesImageToCacheDirectory() {
when(mockPermissionManager.isPermissionGranted(Manifest.permission.CAMERA)).thenReturn(true);
ImagePickerDelegate delegate = createDelegate();
- delegate.takeImageWithCamera(new ImageOutputOptions(null, null, null), mockResult);
+ delegate.takeImageWithCamera(DEFAULT_IMAGE_OPTIONS, mockResult);
mockStaticFile.verify(
() -> File.createTempFile(any(), eq(".jpg"), eq(new File("/image_picker_cache"))),
@@ -308,7 +310,10 @@ public void onRequestPermissionsResult_whenCameraPermissionDenied_finishesWithEr
new String[] {Manifest.permission.CAMERA},
new int[] {PackageManager.PERMISSION_DENIED});
- verify(mockResult).error("camera_access_denied", "The user did not allow camera access.", null);
+ ArgumentCaptor errorCaptor = ArgumentCaptor.forClass(FlutterError.class);
+ verify(mockResult).error(errorCaptor.capture());
+ assertEquals("camera_access_denied", errorCaptor.getValue().code);
+ assertEquals("The user did not allow camera access.", errorCaptor.getValue().getMessage());
verifyNoMoreInteractions(mockResult);
}
@@ -343,14 +348,17 @@ public void onRequestPermissionsResult_whenCameraPermissionDenied_finishesWithEr
}
@Test
- public void onActivityResult_whenPickFromGalleryCanceled_finishesWithNull() {
+ public void onActivityResult_whenPickFromGalleryCanceled_finishesWithEmptyList() {
ImagePickerDelegate delegate =
createDelegateWithPendingResultAndOptions(DEFAULT_IMAGE_OPTIONS, null);
delegate.onActivityResult(
ImagePickerDelegate.REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY, Activity.RESULT_CANCELED, null);
- verify(mockResult).success(null);
+ @SuppressWarnings("unchecked")
+ ArgumentCaptor> pathListCapture = ArgumentCaptor.forClass(List.class);
+ verify(mockResult).success(pathListCapture.capture());
+ assertEquals(0, pathListCapture.getValue().size());
verifyNoMoreInteractions(mockResult);
}
@@ -373,7 +381,10 @@ public void onActivityResult_whenPickFromGalleryCanceled_storesNothingInCache()
delegate.onActivityResult(
ImagePickerDelegate.REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY, Activity.RESULT_OK, mockIntent);
- verify(mockResult).success("originalPath");
+ @SuppressWarnings("unchecked")
+ ArgumentCaptor> pathListCapture = ArgumentCaptor.forClass(List.class);
+ verify(mockResult).success(pathListCapture.capture());
+ assertEquals("originalPath", pathListCapture.getValue().get(0));
verifyNoMoreInteractions(mockResult);
}
@@ -394,11 +405,14 @@ public void onActivityResult_whenImagePickedFromGallery_andNoResizeNeeded_stores
public void
onActivityResult_whenImagePickedFromGallery_andResizeNeeded_finishesWithScaledImagePath() {
ImagePickerDelegate delegate =
- createDelegateWithPendingResultAndOptions(new ImageOutputOptions(WIDTH, null, null), null);
+ createDelegateWithPendingResultAndOptions(RESIZE_TRIGGERING_IMAGE_OPTIONS, null);
delegate.onActivityResult(
ImagePickerDelegate.REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY, Activity.RESULT_OK, mockIntent);
- verify(mockResult).success("scaledPath");
+ @SuppressWarnings("unchecked")
+ ArgumentCaptor> pathListCapture = ArgumentCaptor.forClass(List.class);
+ verify(mockResult).success(pathListCapture.capture());
+ assertEquals("scaledPath", pathListCapture.getValue().get(0));
verifyNoMoreInteractions(mockResult);
}
@@ -406,23 +420,29 @@ public void onActivityResult_whenImagePickedFromGallery_andNoResizeNeeded_stores
public void
onActivityResult_whenVideoPickedFromGallery_andResizeParametersSupplied_finishesWithFilePath() {
ImagePickerDelegate delegate =
- createDelegateWithPendingResultAndOptions(new ImageOutputOptions(WIDTH, null, null), null);
+ createDelegateWithPendingResultAndOptions(RESIZE_TRIGGERING_IMAGE_OPTIONS, null);
delegate.onActivityResult(
ImagePickerDelegate.REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY, Activity.RESULT_OK, mockIntent);
- verify(mockResult).success("pathFromUri");
+ @SuppressWarnings("unchecked")
+ ArgumentCaptor> pathListCapture = ArgumentCaptor.forClass(List.class);
+ verify(mockResult).success(pathListCapture.capture());
+ assertEquals("pathFromUri", pathListCapture.getValue().get(0));
verifyNoMoreInteractions(mockResult);
}
@Test
- public void onActivityResult_whenTakeImageWithCameraCanceled_finishesWithNull() {
+ public void onActivityResult_whenTakeImageWithCameraCanceled_finishesWithEmptyList() {
ImagePickerDelegate delegate =
createDelegateWithPendingResultAndOptions(DEFAULT_IMAGE_OPTIONS, null);
delegate.onActivityResult(
ImagePickerDelegate.REQUEST_CODE_TAKE_IMAGE_WITH_CAMERA, Activity.RESULT_CANCELED, null);
- verify(mockResult).success(null);
+ @SuppressWarnings("unchecked")
+ ArgumentCaptor> pathListCapture = ArgumentCaptor.forClass(List.class);
+ verify(mockResult).success(pathListCapture.capture());
+ assertEquals(0, pathListCapture.getValue().size());
verifyNoMoreInteractions(mockResult);
}
@@ -435,7 +455,10 @@ public void onActivityResult_whenImageTakenWithCamera_andNoResizeNeeded_finishes
delegate.onActivityResult(
ImagePickerDelegate.REQUEST_CODE_TAKE_IMAGE_WITH_CAMERA, Activity.RESULT_OK, mockIntent);
- verify(mockResult).success("originalPath");
+ @SuppressWarnings("unchecked")
+ ArgumentCaptor> pathListCapture = ArgumentCaptor.forClass(List.class);
+ verify(mockResult).success(pathListCapture.capture());
+ assertEquals("originalPath", pathListCapture.getValue().get(0));
verifyNoMoreInteractions(mockResult);
}
@@ -445,11 +468,14 @@ public void onActivityResult_whenImageTakenWithCamera_andNoResizeNeeded_finishes
when(cache.retrievePendingCameraMediaUriPath()).thenReturn("testString");
ImagePickerDelegate delegate =
- createDelegateWithPendingResultAndOptions(new ImageOutputOptions(WIDTH, null, null), null);
+ createDelegateWithPendingResultAndOptions(RESIZE_TRIGGERING_IMAGE_OPTIONS, null);
delegate.onActivityResult(
ImagePickerDelegate.REQUEST_CODE_TAKE_IMAGE_WITH_CAMERA, Activity.RESULT_OK, mockIntent);
- verify(mockResult).success("scaledPath");
+ @SuppressWarnings("unchecked")
+ ArgumentCaptor> pathListCapture = ArgumentCaptor.forClass(List.class);
+ verify(mockResult).success(pathListCapture.capture());
+ assertEquals("scaledPath", pathListCapture.getValue().get(0));
verifyNoMoreInteractions(mockResult);
}
@@ -459,11 +485,14 @@ public void onActivityResult_whenImageTakenWithCamera_andNoResizeNeeded_finishes
when(cache.retrievePendingCameraMediaUriPath()).thenReturn("testString");
ImagePickerDelegate delegate =
- createDelegateWithPendingResultAndOptions(new ImageOutputOptions(WIDTH, null, null), null);
+ createDelegateWithPendingResultAndOptions(RESIZE_TRIGGERING_IMAGE_OPTIONS, null);
delegate.onActivityResult(
ImagePickerDelegate.REQUEST_CODE_TAKE_VIDEO_WITH_CAMERA, Activity.RESULT_OK, mockIntent);
- verify(mockResult).success("pathFromUri");
+ @SuppressWarnings("unchecked")
+ ArgumentCaptor> pathListCapture = ArgumentCaptor.forClass(List.class);
+ verify(mockResult).success(pathListCapture.capture());
+ assertEquals("pathFromUri", pathListCapture.getValue().get(0));
verifyNoMoreInteractions(mockResult);
}
@@ -473,41 +502,16 @@ public void onActivityResult_whenImageTakenWithCamera_andNoResizeNeeded_finishes
when(cache.retrievePendingCameraMediaUriPath()).thenReturn("testString");
ImagePickerDelegate delegate =
- createDelegateWithPendingResultAndOptions(null, new VideoOptions(MAX_DURATION));
+ createDelegateWithPendingResultAndOptions(
+ null, new VideoSelectionOptions.Builder().setMaxDurationSeconds(MAX_DURATION).build());
delegate.onActivityResult(
ImagePickerDelegate.REQUEST_CODE_TAKE_VIDEO_WITH_CAMERA, Activity.RESULT_OK, mockIntent);
- verify(mockResult).success("pathFromUri");
- verifyNoMoreInteractions(mockResult);
- }
-
- @Test
- public void
- retrieveLostImage_shouldBeAbleToReturnLastItemFromResultMapWhenSingleFileIsRecovered() {
- Map resultMap = new HashMap<>();
- ArrayList pathList = new ArrayList<>();
- pathList.add("/example/first_item");
- pathList.add("/example/last_item");
- resultMap.put("pathList", pathList);
-
- when(mockImageResizer.resizeImageIfNeeded(pathList.get(0), null, null, 100))
- .thenReturn(pathList.get(0));
- when(mockImageResizer.resizeImageIfNeeded(pathList.get(1), null, null, 100))
- .thenReturn(pathList.get(1));
- when(cache.getCacheMap()).thenReturn(resultMap);
-
- MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
-
- ImagePickerDelegate mockDelegate = createDelegate();
-
@SuppressWarnings("unchecked")
- ArgumentCaptor> valueCapture = ArgumentCaptor.forClass(Map.class);
-
- doNothing().when(mockResult).success(valueCapture.capture());
-
- mockDelegate.retrieveLostImage(mockResult);
-
- assertEquals("/example/last_item", valueCapture.getValue().get("path"));
+ ArgumentCaptor> pathListCapture = ArgumentCaptor.forClass(List.class);
+ verify(mockResult).success(pathListCapture.capture());
+ assertEquals("pathFromUri", pathListCapture.getValue().get(0));
+ verifyNoMoreInteractions(mockResult);
}
private ImagePickerDelegate createDelegate() {
@@ -525,7 +529,7 @@ private ImagePickerDelegate createDelegate() {
}
private ImagePickerDelegate createDelegateWithPendingResultAndOptions(
- @Nullable ImageOutputOptions imageOptions, @Nullable VideoOptions videoOptions) {
+ @Nullable ImageSelectionOptions imageOptions, @Nullable VideoSelectionOptions videoOptions) {
return new ImagePickerDelegate(
mockActivity,
new File("/image_picker_cache"),
@@ -540,6 +544,9 @@ private ImagePickerDelegate createDelegateWithPendingResultAndOptions(
}
private void verifyFinishedWithAlreadyActiveError() {
- verify(mockResult).error("already_active", "Image picker is already active", null);
+ ArgumentCaptor errorCaptor = ArgumentCaptor.forClass(FlutterError.class);
+ verify(mockResult).error(errorCaptor.capture());
+ assertEquals("already_active", errorCaptor.getValue().code);
+ assertEquals("Image picker is already active", errorCaptor.getValue().getMessage());
}
}
diff --git a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java
index 8b9135128e75..5cc480a8c39d 100644
--- a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java
+++ b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java
@@ -21,29 +21,41 @@
import android.app.Activity;
import android.app.Application;
-import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.embedding.engine.plugins.lifecycle.HiddenLifecycleReference;
import io.flutter.plugin.common.BinaryMessenger;
-import io.flutter.plugin.common.MethodCall;
-import io.flutter.plugin.common.MethodChannel;
+import io.flutter.plugins.imagepicker.Messages.FlutterError;
+import io.flutter.plugins.imagepicker.Messages.ImageSelectionOptions;
+import io.flutter.plugins.imagepicker.Messages.SourceSpecification;
+import io.flutter.plugins.imagepicker.Messages.VideoSelectionOptions;
import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
public class ImagePickerPluginTest {
- private static final int SOURCE_CAMERA = 0;
- private static final int SOURCE_GALLERY = 1;
- private static final String PICK_IMAGE = "pickImage";
- private static final String PICK_MULTI_IMAGE = "pickMultiImage";
- private static final String PICK_VIDEO = "pickVideo";
+ private static final ImageSelectionOptions DEFAULT_IMAGE_OPTIONS =
+ new ImageSelectionOptions.Builder().setQuality((long) 100).build();
+ private static final VideoSelectionOptions DEFAULT_VIDEO_OPTIONS =
+ new VideoSelectionOptions.Builder().build();
+ private static final SourceSpecification SOURCE_GALLERY =
+ new SourceSpecification.Builder().setType(Messages.SourceType.GALLERY).build();
+ private static final SourceSpecification SOURCE_CAMERA_FRONT =
+ new SourceSpecification.Builder()
+ .setType(Messages.SourceType.CAMERA)
+ .setCamera(Messages.SourceCamera.FRONT)
+ .build();
+ private static final SourceSpecification SOURCE_CAMERA_REAR =
+ new SourceSpecification.Builder()
+ .setType(Messages.SourceType.CAMERA)
+ .setCamera(Messages.SourceCamera.REAR)
+ .build();
@SuppressWarnings("deprecation")
@Mock
@@ -55,7 +67,7 @@ public class ImagePickerPluginTest {
@Mock Activity mockActivity;
@Mock Application mockApplication;
@Mock ImagePickerDelegate mockImagePickerDelegate;
- @Mock MethodChannel.Result mockResult;
+ @Mock Messages.Result> mockResult;
ImagePickerPlugin plugin;
@@ -76,113 +88,102 @@ public void tearDown() throws Exception {
}
@Test
- public void onMethodCall_whenActivityIsNull_finishesWithForegroundActivityRequiredError() {
- MethodCall call = buildMethodCall(PICK_IMAGE, SOURCE_GALLERY, false);
+ public void pickImages_whenActivityIsNull_finishesWithForegroundActivityRequiredError() {
ImagePickerPlugin imagePickerPluginWithNullActivity =
new ImagePickerPlugin(mockImagePickerDelegate, null);
- imagePickerPluginWithNullActivity.onMethodCall(call, mockResult);
- verify(mockResult)
- .error("no_activity", "image_picker plugin requires a foreground activity.", null);
+ imagePickerPluginWithNullActivity.pickImages(
+ SOURCE_GALLERY, DEFAULT_IMAGE_OPTIONS, false, false, mockResult);
+
+ ArgumentCaptor errorCaptor = ArgumentCaptor.forClass(FlutterError.class);
+ verify(mockResult).error(errorCaptor.capture());
+ assertEquals("no_activity", errorCaptor.getValue().code);
+ assertEquals(
+ "image_picker plugin requires a foreground activity.", errorCaptor.getValue().getMessage());
verifyNoInteractions(mockImagePickerDelegate);
}
@Test
- public void onMethodCall_whenCalledWithUnknownMethod_throwsException() {
- IllegalArgumentException e =
- assertThrows(
- IllegalArgumentException.class,
- () -> plugin.onMethodCall(new MethodCall("test", null), mockResult));
- assertEquals(e.getMessage(), "Unknown method test");
+ public void pickVideos_whenActivityIsNull_finishesWithForegroundActivityRequiredError() {
+ ImagePickerPlugin imagePickerPluginWithNullActivity =
+ new ImagePickerPlugin(mockImagePickerDelegate, null);
+ imagePickerPluginWithNullActivity.pickVideos(
+ SOURCE_CAMERA_REAR, DEFAULT_VIDEO_OPTIONS, false, false, mockResult);
+
+ ArgumentCaptor errorCaptor = ArgumentCaptor.forClass(FlutterError.class);
+ verify(mockResult).error(errorCaptor.capture());
+ assertEquals("no_activity", errorCaptor.getValue().code);
+ assertEquals(
+ "image_picker plugin requires a foreground activity.", errorCaptor.getValue().getMessage());
verifyNoInteractions(mockImagePickerDelegate);
- verifyNoInteractions(mockResult);
}
@Test
- public void onMethodCall_whenCalledWithUnknownImageSource_throwsException() {
- IllegalArgumentException e =
- assertThrows(
- IllegalArgumentException.class,
- () -> plugin.onMethodCall(buildMethodCall(PICK_IMAGE, -1, false), mockResult));
- assertEquals(e.getMessage(), "Invalid image source: -1");
+ public void retrieveLostResults_whenActivityIsNull_finishesWithForegroundActivityRequiredError() {
+ ImagePickerPlugin imagePickerPluginWithNullActivity =
+ new ImagePickerPlugin(mockImagePickerDelegate, null);
+ FlutterError error =
+ assertThrows(FlutterError.class, imagePickerPluginWithNullActivity::retrieveLostResults);
+ assertEquals("image_picker plugin requires a foreground activity.", error.getMessage());
+ assertEquals("no_activity", error.code);
verifyNoInteractions(mockImagePickerDelegate);
- verifyNoInteractions(mockResult);
}
@Test
- public void onMethodCall_whenSourceIsGallery_invokesChooseImageFromGallery() {
- MethodCall call = buildMethodCall(PICK_IMAGE, SOURCE_GALLERY, false);
- plugin.onMethodCall(call, mockResult);
+ public void pickImages_whenSourceIsGallery_invokesChooseImageFromGallery() {
+ plugin.pickImages(SOURCE_GALLERY, DEFAULT_IMAGE_OPTIONS, false, false, mockResult);
verify(mockImagePickerDelegate).chooseImageFromGallery(any(), eq(false), any());
verifyNoInteractions(mockResult);
}
@Test
- public void onMethodCall_whenSourceIsGalleryUsingPhotoPicker_invokesChooseImageFromGallery() {
- MethodCall call = buildMethodCall(PICK_IMAGE, SOURCE_GALLERY, true);
- plugin.onMethodCall(call, mockResult);
+ public void pickImages_whenSourceIsGalleryUsingPhotoPicker_invokesChooseImageFromGallery() {
+ plugin.pickImages(SOURCE_GALLERY, DEFAULT_IMAGE_OPTIONS, false, true, mockResult);
verify(mockImagePickerDelegate).chooseImageFromGallery(any(), eq(true), any());
verifyNoInteractions(mockResult);
}
@Test
- public void onMethodCall_invokesChooseMultiImageFromGallery() {
- MethodCall call = buildMethodCall(PICK_MULTI_IMAGE);
- plugin.onMethodCall(call, mockResult);
+ public void pickImages_invokesChooseMultiImageFromGallery() {
+ plugin.pickImages(SOURCE_GALLERY, DEFAULT_IMAGE_OPTIONS, true, false, mockResult);
verify(mockImagePickerDelegate).chooseMultiImageFromGallery(any(), eq(false), any());
verifyNoInteractions(mockResult);
}
@Test
- public void onMethodCall_usingPhotoPicker_invokesChooseMultiImageFromGallery() {
- MethodCall call = buildMethodCall(PICK_MULTI_IMAGE, SOURCE_GALLERY, true);
- plugin.onMethodCall(call, mockResult);
+ public void pickImages_usingPhotoPicker_invokesChooseMultiImageFromGallery() {
+ plugin.pickImages(SOURCE_GALLERY, DEFAULT_IMAGE_OPTIONS, true, true, mockResult);
verify(mockImagePickerDelegate).chooseMultiImageFromGallery(any(), eq(true), any());
verifyNoInteractions(mockResult);
}
@Test
- public void onMethodCall_whenSourceIsCamera_invokesTakeImageWithCamera() {
- MethodCall call = buildMethodCall(PICK_IMAGE, SOURCE_CAMERA, null);
- plugin.onMethodCall(call, mockResult);
+ public void pickImages_whenSourceIsCamera_invokesTakeImageWithCamera() {
+ plugin.pickImages(SOURCE_CAMERA_REAR, DEFAULT_IMAGE_OPTIONS, false, false, mockResult);
verify(mockImagePickerDelegate).takeImageWithCamera(any(), any());
verifyNoInteractions(mockResult);
}
@Test
- public void onMethodCall_PickingImage_whenSourceIsCamera_invokesTakeImageWithCamera_RearCamera() {
- MethodCall call = buildMethodCall(PICK_IMAGE, SOURCE_CAMERA, null);
- HashMap arguments = getArgumentMap(call);
- arguments.put("cameraDevice", 0);
- plugin.onMethodCall(call, mockResult);
+ public void pickImages_whenSourceIsCamera_invokesTakeImageWithCamera_RearCamera() {
+ plugin.pickImages(SOURCE_CAMERA_REAR, DEFAULT_IMAGE_OPTIONS, false, false, mockResult);
verify(mockImagePickerDelegate).setCameraDevice(eq(ImagePickerDelegate.CameraDevice.REAR));
}
@Test
- public void
- onMethodCall_PickingImage_whenSourceIsCamera_invokesTakeImageWithCamera_FrontCamera() {
- MethodCall call = buildMethodCall(PICK_IMAGE, SOURCE_CAMERA, null);
- HashMap arguments = getArgumentMap(call);
- arguments.put("cameraDevice", 1);
- plugin.onMethodCall(call, mockResult);
+ public void pickImages_whenSourceIsCamera_invokesTakeImageWithCamera_FrontCamera() {
+ plugin.pickImages(SOURCE_CAMERA_FRONT, DEFAULT_IMAGE_OPTIONS, false, false, mockResult);
verify(mockImagePickerDelegate).setCameraDevice(eq(ImagePickerDelegate.CameraDevice.FRONT));
}
@Test
- public void onMethodCall_PickingVideo_whenSourceIsCamera_invokesTakeImageWithCamera_RearCamera() {
- MethodCall call = buildMethodCall(PICK_IMAGE, SOURCE_CAMERA, null);
- HashMap arguments = getArgumentMap(call);
- arguments.put("cameraDevice", 0);
- plugin.onMethodCall(call, mockResult);
+ public void pickVideos_whenSourceIsCamera_invokesTakeImageWithCamera_RearCamera() {
+ plugin.pickVideos(SOURCE_CAMERA_REAR, DEFAULT_VIDEO_OPTIONS, false, false, mockResult);
verify(mockImagePickerDelegate).setCameraDevice(eq(ImagePickerDelegate.CameraDevice.REAR));
}
@Test
- public void
- onMethodCall_PickingVideo_whenSourceIsCamera_invokesTakeImageWithCamera_FrontCamera() {
- MethodCall call = buildMethodCall(PICK_IMAGE, SOURCE_CAMERA, null);
- HashMap arguments = getArgumentMap(call);
- arguments.put("cameraDevice", 1);
- plugin.onMethodCall(call, mockResult);
+ public void pickVideos_whenSourceIsCamera_invokesTakeImageWithCamera_FrontCamera() {
+ plugin.pickVideos(SOURCE_CAMERA_FRONT, DEFAULT_VIDEO_OPTIONS, false, false, mockResult);
verify(mockImagePickerDelegate).setCameraDevice(eq(ImagePickerDelegate.CameraDevice.FRONT));
}
@@ -233,24 +234,4 @@ public void onDetachedFromActivity_shouldReleaseActivityState() {
plugin.onDetachedFromActivity();
assertNull(plugin.getActivityState());
}
-
- private MethodCall buildMethodCall(
- String method, final int source, @Nullable Boolean usePhotoPicker) {
- final Map arguments = new HashMap<>();
- arguments.put("source", source);
- if (usePhotoPicker != null) {
- arguments.put("useAndroidPhotoPicker", usePhotoPicker);
- }
-
- return new MethodCall(method, arguments);
- }
-
- private MethodCall buildMethodCall(String method) {
- return new MethodCall(method, null);
- }
-
- @SuppressWarnings("unchecked")
- private HashMap getArgumentMap(MethodCall call) {
- return (HashMap) call.arguments;
- }
}
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
index 97c981eb3178..fbc7fa7c2ad2 100644
--- a/packages/image_picker/image_picker_android/lib/image_picker_android.dart
+++ b/packages/image_picker/image_picker_android/lib/image_picker_android.dart
@@ -2,21 +2,20 @@
// 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');
+import 'src/messages.g.dart';
/// 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;
+ /// Creates a new plugin implemenation instance.
+ ImagePickerAndroid({@visibleForTesting ImagePickerApi? api})
+ : _hostApi = api ?? ImagePickerApi();
+
+ final ImagePickerApi _hostApi;
/// Registers this class as the default platform implementation.
static void registerWith() {
@@ -47,19 +46,19 @@ class ImagePickerAndroid extends ImagePickerPlatform {
double? maxHeight,
int? imageQuality,
}) async {
- final List? paths = await _getMultiImagePath(
+ final List paths = await _getMultiImagePath(
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: imageQuality,
);
- if (paths == null) {
+ if (paths.isEmpty) {
return null;
}
return paths.map((dynamic path) => PickedFile(path as String)).toList();
}
- Future?> _getMultiImagePath({
+ Future> _getMultiImagePath({
double? maxWidth,
double? maxHeight,
int? imageQuality,
@@ -77,15 +76,14 @@ class ImagePickerAndroid extends ImagePickerPlatform {
throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative');
}
- return _channel.invokeMethod?>(
- 'pickMultiImage',
- {
- 'maxWidth': maxWidth,
- 'maxHeight': maxHeight,
- 'imageQuality': imageQuality,
- 'useAndroidPhotoPicker': useAndroidPhotoPicker,
- },
- );
+ return _hostApi.pickImages(
+ SourceSpecification(type: SourceType.gallery),
+ ImageSelectionOptions(
+ maxWidth: maxWidth,
+ maxHeight: maxHeight,
+ quality: imageQuality ?? 100),
+ /* allowMultiple */ true,
+ useAndroidPhotoPicker);
}
Future _getImagePath({
@@ -95,7 +93,7 @@ class ImagePickerAndroid extends ImagePickerPlatform {
int? imageQuality,
CameraDevice preferredCameraDevice = CameraDevice.rear,
bool requestFullMetadata = true,
- }) {
+ }) async {
if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) {
throw ArgumentError.value(
imageQuality, 'imageQuality', 'must be between 0 and 100');
@@ -109,18 +107,15 @@ class ImagePickerAndroid extends ImagePickerPlatform {
throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative');
}
- return _channel.invokeMethod(
- 'pickImage',
- {
- 'source': source.index,
- 'maxWidth': maxWidth,
- 'maxHeight': maxHeight,
- 'imageQuality': imageQuality,
- 'cameraDevice': preferredCameraDevice.index,
- 'requestFullMetadata': requestFullMetadata,
- 'useAndroidPhotoPicker': useAndroidPhotoPicker,
- },
- );
+ final List paths = await _hostApi.pickImages(
+ _buildSourceSpec(source, preferredCameraDevice),
+ ImageSelectionOptions(
+ maxWidth: maxWidth,
+ maxHeight: maxHeight,
+ quality: imageQuality ?? 100),
+ /* allowMultiple */ false,
+ useAndroidPhotoPicker);
+ return paths.isEmpty ? null : paths.first;
}
@override
@@ -141,16 +136,13 @@ class ImagePickerAndroid extends ImagePickerPlatform {
required ImageSource source,
CameraDevice preferredCameraDevice = CameraDevice.rear,
Duration? maxDuration,
- }) {
- return _channel.invokeMethod(
- 'pickVideo',
- {
- 'source': source.index,
- 'maxDuration': maxDuration?.inSeconds,
- 'cameraDevice': preferredCameraDevice.index,
- 'useAndroidPhotoPicker': useAndroidPhotoPicker,
- },
- );
+ }) async {
+ final List paths = await _hostApi.pickVideos(
+ _buildSourceSpec(source, preferredCameraDevice),
+ VideoSelectionOptions(maxDurationSeconds: maxDuration?.inSeconds),
+ /* allowMultiple */ false,
+ useAndroidPhotoPicker);
+ return paths.isEmpty ? null : paths.first;
}
@override
@@ -193,12 +185,12 @@ class ImagePickerAndroid extends ImagePickerPlatform {
double? maxHeight,
int? imageQuality,
}) async {
- final List? paths = await _getMultiImagePath(
+ final List paths = await _getMultiImagePath(
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: imageQuality,
);
- if (paths == null) {
+ if (paths.isEmpty) {
return null;
}
@@ -236,54 +228,89 @@ class ImagePickerAndroid extends ImagePickerPlatform {
@override
Future getLostData() async {
- List? pickedFileList;
-
- final Map? result =
- await _channel.invokeMapMethod('retrieve');
+ final CacheRetrievalResult? result = await _hostApi.retrieveLostResults();
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);
+ // There must either be data or an error if the response wasn't null.
+ assert(result.paths.isEmpty != (result.error == null));
- 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 CacheRetrievalError? error = result.error;
+ final PlatformException? exception = error == null
+ ? null
+ : PlatformException(code: error.code, message: error.message);
- final String? path = result['path'] as String?;
-
- final List? pathList =
- (result['pathList'] as List?)?.cast();
- if (pathList != null) {
- pickedFileList = [];
- for (final String path in pathList) {
- pickedFileList.add(XFile(path));
- }
- }
+ // Entries are guaranteed not to be null, even though that's not currently
+ // expressable in Pigeon.
+ final List pickedFileList =
+ result.paths.map((String? path) => XFile(path!)).toList();
return LostDataResponse(
- file: path != null ? XFile(path) : null,
+ file: pickedFileList.isEmpty ? null : pickedFileList.last,
exception: exception,
- type: retrieveType,
+ type: _retrieveTypeForCacheType(result.type),
files: pickedFileList,
);
}
- /// Set [ImagePickerAndroid] to use Android 13 Photo Picker.
+ SourceSpecification _buildSourceSpec(
+ ImageSource source, CameraDevice device) {
+ return SourceSpecification(
+ type: _sourceSpecTypeForSource(source),
+ camera: _sourceSpecCameraForDevice(device));
+ }
+
+ SourceType _sourceSpecTypeForSource(ImageSource source) {
+ switch (source) {
+ case ImageSource.camera:
+ return SourceType.camera;
+ case ImageSource.gallery:
+ return SourceType.gallery;
+ }
+ // The enum comes from a different package, which could get a new value at
+ // any time, so provide a fallback that ensures this won't break when used
+ // with a version that contains new values. This is deliberately outside
+ // the switch rather than a `default` so that the linter will flag the
+ // switch as needing an update.
+ // ignore: dead_code
+ return SourceType.gallery;
+ }
+
+ SourceCamera _sourceSpecCameraForDevice(CameraDevice device) {
+ switch (device) {
+ case CameraDevice.front:
+ return SourceCamera.front;
+ case CameraDevice.rear:
+ return SourceCamera.rear;
+ }
+ // The enum comes from a different package, which could get a new value at
+ // any time, so provide a fallback that ensures this won't break when used
+ // with a version that contains new values. This is deliberately outside
+ // the switch rather than a `default` so that the linter will flag the
+ // switch as needing an update.
+ // ignore: dead_code
+ return SourceCamera.rear;
+ }
+
+ RetrieveType _retrieveTypeForCacheType(CacheRetrievalType type) {
+ switch (type) {
+ case CacheRetrievalType.image:
+ return RetrieveType.image;
+ case CacheRetrievalType.video:
+ return RetrieveType.video;
+ }
+ // The enum comes from a different package, which could get a new value at
+ // any time, so provide a fallback that ensures this won't break when used
+ // with a version that contains new values. This is deliberately outside
+ // the switch rather than a `default` so that the linter will flag the
+ // switch as needing an update.
+ // ignore: dead_code
+ return RetrieveType.image;
+ }
+
+ /// Sets [ImagePickerAndroid] to use Android 13 Photo Picker.
///
/// Currently defaults to false, but the default is subject to change.
bool useAndroidPhotoPicker = false;
diff --git a/packages/image_picker/image_picker_android/lib/src/messages.g.dart b/packages/image_picker/image_picker_android/lib/src/messages.g.dart
new file mode 100644
index 000000000000..c0506ad4daec
--- /dev/null
+++ b/packages/image_picker/image_picker_android/lib/src/messages.g.dart
@@ -0,0 +1,337 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Autogenerated from Pigeon (v9.1.0), do not edit directly.
+// See also: https://pub.dev/packages/pigeon
+// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import
+
+import 'dart:async';
+import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
+
+import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
+import 'package:flutter/services.dart';
+
+enum SourceCamera {
+ rear,
+ front,
+}
+
+enum SourceType {
+ camera,
+ gallery,
+}
+
+enum CacheRetrievalType {
+ image,
+ video,
+}
+
+/// Options for image selection and output.
+class ImageSelectionOptions {
+ ImageSelectionOptions({
+ this.maxWidth,
+ this.maxHeight,
+ required this.quality,
+ });
+
+ /// If set, the max width that the image should be resized to fit in.
+ double? maxWidth;
+
+ /// If set, the max height that the image should be resized to fit in.
+ double? maxHeight;
+
+ /// The quality of the output image, from 0-100.
+ ///
+ /// 100 indicates original quality.
+ int quality;
+
+ Object encode() {
+ return [
+ maxWidth,
+ maxHeight,
+ quality,
+ ];
+ }
+
+ static ImageSelectionOptions decode(Object result) {
+ result as List;
+ return ImageSelectionOptions(
+ maxWidth: result[0] as double?,
+ maxHeight: result[1] as double?,
+ quality: result[2]! as int,
+ );
+ }
+}
+
+/// Options for image selection and output.
+class VideoSelectionOptions {
+ VideoSelectionOptions({
+ this.maxDurationSeconds,
+ });
+
+ /// The maximum desired length for the video, in seconds.
+ int? maxDurationSeconds;
+
+ Object encode() {
+ return [
+ maxDurationSeconds,
+ ];
+ }
+
+ static VideoSelectionOptions decode(Object result) {
+ result as List;
+ return VideoSelectionOptions(
+ maxDurationSeconds: result[0] as int?,
+ );
+ }
+}
+
+/// Specification for the source of an image or video selection.
+class SourceSpecification {
+ SourceSpecification({
+ required this.type,
+ this.camera,
+ });
+
+ SourceType type;
+
+ SourceCamera? camera;
+
+ Object encode() {
+ return [
+ type.index,
+ camera?.index,
+ ];
+ }
+
+ static SourceSpecification decode(Object result) {
+ result as List;
+ return SourceSpecification(
+ type: SourceType.values[result[0]! as int],
+ camera: result[1] != null ? SourceCamera.values[result[1]! as int] : null,
+ );
+ }
+}
+
+/// An error that occurred during lost result retrieval.
+///
+/// The data here maps to the `PlatformException` that will be created from it.
+class CacheRetrievalError {
+ CacheRetrievalError({
+ required this.code,
+ this.message,
+ });
+
+ String code;
+
+ String? message;
+
+ Object encode() {
+ return [
+ code,
+ message,
+ ];
+ }
+
+ static CacheRetrievalError decode(Object result) {
+ result as List;
+ return CacheRetrievalError(
+ code: result[0]! as String,
+ message: result[1] as String?,
+ );
+ }
+}
+
+/// The result of retrieving cached results from a previous run.
+class CacheRetrievalResult {
+ CacheRetrievalResult({
+ required this.type,
+ this.error,
+ required this.paths,
+ });
+
+ /// The type of the retrieved data.
+ CacheRetrievalType type;
+
+ /// The error from the last selection, if any.
+ CacheRetrievalError? error;
+
+ /// The results from the last selection, if any.
+ ///
+ /// Elements must not be null, by convention. See
+ /// https://github.com/flutter/flutter/issues/97848
+ List paths;
+
+ Object encode() {
+ return [
+ type.index,
+ error?.encode(),
+ paths,
+ ];
+ }
+
+ static CacheRetrievalResult decode(Object result) {
+ result as List;
+ return CacheRetrievalResult(
+ type: CacheRetrievalType.values[result[0]! as int],
+ error: result[1] != null
+ ? CacheRetrievalError.decode(result[1]! as List)
+ : null,
+ paths: (result[2] as List?)!.cast(),
+ );
+ }
+}
+
+class _ImagePickerApiCodec extends StandardMessageCodec {
+ const _ImagePickerApiCodec();
+ @override
+ void writeValue(WriteBuffer buffer, Object? value) {
+ if (value is CacheRetrievalError) {
+ buffer.putUint8(128);
+ writeValue(buffer, value.encode());
+ } else if (value is CacheRetrievalResult) {
+ buffer.putUint8(129);
+ writeValue(buffer, value.encode());
+ } else if (value is ImageSelectionOptions) {
+ buffer.putUint8(130);
+ writeValue(buffer, value.encode());
+ } else if (value is SourceSpecification) {
+ buffer.putUint8(131);
+ writeValue(buffer, value.encode());
+ } else if (value is VideoSelectionOptions) {
+ buffer.putUint8(132);
+ writeValue(buffer, value.encode());
+ } else {
+ super.writeValue(buffer, value);
+ }
+ }
+
+ @override
+ Object? readValueOfType(int type, ReadBuffer buffer) {
+ switch (type) {
+ case 128:
+ return CacheRetrievalError.decode(readValue(buffer)!);
+ case 129:
+ return CacheRetrievalResult.decode(readValue(buffer)!);
+ case 130:
+ return ImageSelectionOptions.decode(readValue(buffer)!);
+ case 131:
+ return SourceSpecification.decode(readValue(buffer)!);
+ case 132:
+ return VideoSelectionOptions.decode(readValue(buffer)!);
+ default:
+ return super.readValueOfType(type, buffer);
+ }
+ }
+}
+
+class ImagePickerApi {
+ /// Constructor for [ImagePickerApi]. The [binaryMessenger] named argument is
+ /// available for dependency injection. If it is left null, the default
+ /// BinaryMessenger will be used which routes to the host platform.
+ ImagePickerApi({BinaryMessenger? binaryMessenger})
+ : _binaryMessenger = binaryMessenger;
+ final BinaryMessenger? _binaryMessenger;
+
+ static const MessageCodec codec = _ImagePickerApiCodec();
+
+ /// Selects images and returns their paths.
+ ///
+ /// Elements must not be null, by convention. See
+ /// https://github.com/flutter/flutter/issues/97848
+ Future> pickImages(
+ SourceSpecification arg_source,
+ ImageSelectionOptions arg_options,
+ bool arg_allowMultiple,
+ bool arg_usePhotoPicker) async {
+ final BasicMessageChannel channel = BasicMessageChannel(
+ 'dev.flutter.pigeon.ImagePickerApi.pickImages', codec,
+ binaryMessenger: _binaryMessenger);
+ final List? replyList = await channel.send([
+ arg_source,
+ arg_options,
+ arg_allowMultiple,
+ arg_usePhotoPicker
+ ]) as List?;
+ if (replyList == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ );
+ } else if (replyList.length > 1) {
+ throw PlatformException(
+ code: replyList[0]! as String,
+ message: replyList[1] as String?,
+ details: replyList[2],
+ );
+ } else if (replyList[0] == null) {
+ throw PlatformException(
+ code: 'null-error',
+ message: 'Host platform returned null value for non-null return value.',
+ );
+ } else {
+ return (replyList[0] as List?)!.cast();
+ }
+ }
+
+ /// Selects video and returns their paths.
+ ///
+ /// Elements must not be null, by convention. See
+ /// https://github.com/flutter/flutter/issues/97848
+ Future> pickVideos(
+ SourceSpecification arg_source,
+ VideoSelectionOptions arg_options,
+ bool arg_allowMultiple,
+ bool arg_usePhotoPicker) async {
+ final BasicMessageChannel channel = BasicMessageChannel(
+ 'dev.flutter.pigeon.ImagePickerApi.pickVideos', codec,
+ binaryMessenger: _binaryMessenger);
+ final List? replyList = await channel.send([
+ arg_source,
+ arg_options,
+ arg_allowMultiple,
+ arg_usePhotoPicker
+ ]) as List?;
+ if (replyList == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ );
+ } else if (replyList.length > 1) {
+ throw PlatformException(
+ code: replyList[0]! as String,
+ message: replyList[1] as String?,
+ details: replyList[2],
+ );
+ } else if (replyList[0] == null) {
+ throw PlatformException(
+ code: 'null-error',
+ message: 'Host platform returned null value for non-null return value.',
+ );
+ } else {
+ return (replyList[0] as List?)!.cast();
+ }
+ }
+
+ /// Returns results from a previous app session, if any.
+ Future retrieveLostResults() async {
+ final BasicMessageChannel channel = BasicMessageChannel(
+ 'dev.flutter.pigeon.ImagePickerApi.retrieveLostResults', codec,
+ binaryMessenger: _binaryMessenger);
+ final List? replyList = await channel.send(null) as List?;
+ if (replyList == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ );
+ } else if (replyList.length > 1) {
+ throw PlatformException(
+ code: replyList[0]! as String,
+ message: replyList[1] as String?,
+ details: replyList[2],
+ );
+ } else {
+ return (replyList[0] as CacheRetrievalResult?);
+ }
+ }
+}
diff --git a/packages/image_picker/image_picker_android/pigeons/copyright.txt b/packages/image_picker/image_picker_android/pigeons/copyright.txt
new file mode 100644
index 000000000000..1236b63caf3a
--- /dev/null
+++ b/packages/image_picker/image_picker_android/pigeons/copyright.txt
@@ -0,0 +1,3 @@
+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.
diff --git a/packages/image_picker/image_picker_android/pigeons/messages.dart b/packages/image_picker/image_picker_android/pigeons/messages.dart
new file mode 100644
index 000000000000..120799d0a1a1
--- /dev/null
+++ b/packages/image_picker/image_picker_android/pigeons/messages.dart
@@ -0,0 +1,104 @@
+// 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:pigeon/pigeon.dart';
+
+@ConfigurePigeon(PigeonOptions(
+ dartOut: 'lib/src/messages.g.dart',
+ dartTestOut: 'test/test_api.g.dart',
+ javaOut: 'android/src/main/java/io/flutter/plugins/imagepicker/Messages.java',
+ javaOptions: JavaOptions(
+ package: 'io.flutter.plugins.imagepicker',
+ ),
+ copyrightHeader: 'pigeons/copyright.txt',
+))
+
+/// Options for image selection and output.
+class ImageSelectionOptions {
+ ImageSelectionOptions({this.maxWidth, this.maxHeight, required this.quality});
+
+ /// If set, the max width that the image should be resized to fit in.
+ double? maxWidth;
+
+ /// If set, the max height that the image should be resized to fit in.
+ double? maxHeight;
+
+ /// The quality of the output image, from 0-100.
+ ///
+ /// 100 indicates original quality.
+ int quality;
+}
+
+/// Options for image selection and output.
+class VideoSelectionOptions {
+ VideoSelectionOptions({this.maxDurationSeconds});
+
+ /// The maximum desired length for the video, in seconds.
+ int? maxDurationSeconds;
+}
+
+// Corresponds to `CameraDevice` from the platform interface package.
+enum SourceCamera { rear, front }
+
+// Corresponds to `ImageSource` from the platform interface package.
+enum SourceType { camera, gallery }
+
+/// Specification for the source of an image or video selection.
+class SourceSpecification {
+ SourceSpecification(this.type, this.camera);
+ SourceType type;
+ SourceCamera? camera;
+}
+
+/// An error that occurred during lost result retrieval.
+///
+/// The data here maps to the `PlatformException` that will be created from it.
+class CacheRetrievalError {
+ CacheRetrievalError({required this.code, this.message});
+ final String code;
+ final String? message;
+}
+
+// Corresponds to `RetrieveType` from the platform interface package.
+enum CacheRetrievalType { image, video }
+
+/// The result of retrieving cached results from a previous run.
+class CacheRetrievalResult {
+ CacheRetrievalResult(
+ {required this.type, this.error, this.paths = const []});
+
+ /// The type of the retrieved data.
+ final CacheRetrievalType type;
+
+ /// The error from the last selection, if any.
+ final CacheRetrievalError? error;
+
+ /// The results from the last selection, if any.
+ ///
+ /// Elements must not be null, by convention. See
+ /// https://github.com/flutter/flutter/issues/97848
+ final List paths;
+}
+
+@HostApi(dartHostTestHandler: 'TestHostImagePickerApi')
+abstract class ImagePickerApi {
+ /// Selects images and returns their paths.
+ ///
+ /// Elements must not be null, by convention. See
+ /// https://github.com/flutter/flutter/issues/97848
+ @async
+ List pickImages(SourceSpecification source,
+ ImageSelectionOptions options, bool allowMultiple, bool usePhotoPicker);
+
+ /// Selects video and returns their paths.
+ ///
+ /// Elements must not be null, by convention. See
+ /// https://github.com/flutter/flutter/issues/97848
+ @async
+ List pickVideos(SourceSpecification source,
+ VideoSelectionOptions options, bool allowMultiple, bool usePhotoPicker);
+
+ /// Returns results from a previous app session, if any.
+ CacheRetrievalResult? retrieveLostResults();
+}
diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml
index b03d84bba6ed..3d79bbc539dc 100755
--- a/packages/image_picker/image_picker_android/pubspec.yaml
+++ b/packages/image_picker/image_picker_android/pubspec.yaml
@@ -3,7 +3,7 @@ description: Android implementation of the image_picker plugin.
repository: https://github.com/flutter/packages/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.6+2
+version: 0.8.6+3
environment:
sdk: ">=2.17.0 <3.0.0"
@@ -28,3 +28,4 @@ dev_dependencies:
flutter_test:
sdk: flutter
mockito: ^5.0.0
+ pigeon: ^9.1.0
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
index 4c9d31ae2b9a..f17d078a9031 100644
--- 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
@@ -2,31 +2,19 @@
// 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_android/src/messages.g.dart';
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
void main() {
- TestWidgetsFlutterBinding.ensureInitialized();
-
- final ImagePickerAndroid picker = ImagePickerAndroid();
-
- final List log = [];
- dynamic returnValue = '';
+ late ImagePickerAndroid picker;
+ late _FakeImagePickerApi api;
setUp(() {
- returnValue = '';
- _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
- .defaultBinaryMessenger
- .setMockMethodCallHandler(picker.channel,
- (MethodCall methodCall) async {
- log.add(methodCall);
- return returnValue;
- });
-
- log.clear();
+ api = _FakeImagePickerApi();
+ picker = ImagePickerAndroid(api: api);
});
test('registers instance', () async {
@@ -35,60 +23,38 @@ void main() {
});
group('#pickImage', () {
- test('passes the image source argument correctly', () async {
+ test('calls the method correctly', () async {
+ const String fakePath = '/foo.jpg';
+ api.returnValue = [fakePath];
+ final PickedFile? result =
+ await picker.pickImage(source: ImageSource.camera);
+
+ expect(result?.path, fakePath);
+ expect(api.lastCall, _LastPickType.image);
+ expect(api.passedAllowMultiple, false);
+ });
+
+ test('passes the gallery image source argument correctly', () async {
await picker.pickImage(source: ImageSource.camera);
+
+ expect(api.passedSource?.type, SourceType.camera);
+ });
+
+ test('passes the camera image source argument correctly', () async {
await picker.pickImage(source: ImageSource.gallery);
- expect(
- log,
- [
- isMethodCall('pickImage', arguments: {
- 'source': 0,
- 'maxWidth': null,
- 'maxHeight': null,
- 'imageQuality': null,
- 'cameraDevice': 0,
- 'requestFullMetadata': true,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickImage', arguments: {
- 'source': 1,
- 'maxWidth': null,
- 'maxHeight': null,
- 'imageQuality': null,
- 'cameraDevice': 0,
- 'requestFullMetadata': true,
- 'useAndroidPhotoPicker': false,
- }),
- ],
- );
+ expect(api.passedSource?.type, SourceType.gallery);
});
- 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,
- );
+ test('passes default image options', () async {
+ await picker.pickImage(source: ImageSource.gallery);
+
+ expect(api.passedImageOptions?.maxWidth, null);
+ expect(api.passedImageOptions?.maxHeight, null);
+ expect(api.passedImageOptions?.quality, 100);
+ });
+
+ test('passes image option arguments correctly', () async {
await picker.pickImage(
source: ImageSource.camera,
maxWidth: 10.0,
@@ -96,74 +62,9 @@ void main() {
imageQuality: 70,
);
- expect(
- log,
- [
- isMethodCall('pickImage', arguments: {
- 'source': 0,
- 'maxWidth': null,
- 'maxHeight': null,
- 'imageQuality': null,
- 'cameraDevice': 0,
- 'requestFullMetadata': true,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickImage', arguments: {
- 'source': 0,
- 'maxWidth': 10.0,
- 'maxHeight': null,
- 'imageQuality': null,
- 'cameraDevice': 0,
- 'requestFullMetadata': true,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickImage', arguments: {
- 'source': 0,
- 'maxWidth': null,
- 'maxHeight': 10.0,
- 'imageQuality': null,
- 'cameraDevice': 0,
- 'requestFullMetadata': true,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickImage', arguments: {
- 'source': 0,
- 'maxWidth': 10.0,
- 'maxHeight': 20.0,
- 'imageQuality': null,
- 'cameraDevice': 0,
- 'requestFullMetadata': true,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickImage', arguments: {
- 'source': 0,
- 'maxWidth': 10.0,
- 'maxHeight': null,
- 'imageQuality': 70,
- 'cameraDevice': 0,
- 'requestFullMetadata': true,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickImage', arguments: {
- 'source': 0,
- 'maxWidth': null,
- 'maxHeight': 10.0,
- 'imageQuality': 70,
- 'cameraDevice': 0,
- 'requestFullMetadata': true,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickImage', arguments: {
- 'source': 0,
- 'maxWidth': 10.0,
- 'maxHeight': 20.0,
- 'imageQuality': 70,
- 'cameraDevice': 0,
- 'requestFullMetadata': true,
- 'useAndroidPhotoPicker': false,
- }),
- ],
- );
+ expect(api.passedImageOptions?.maxWidth, 10.0);
+ expect(api.passedImageOptions?.maxHeight, 20.0);
+ expect(api.passedImageOptions?.quality, 70);
});
test('does not accept an invalid imageQuality argument', () {
@@ -201,10 +102,7 @@ void main() {
});
test('handles a null image path response gracefully', () async {
- _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
- .defaultBinaryMessenger
- .setMockMethodCallHandler(
- picker.channel, (MethodCall methodCall) => null);
+ api.returnValue = null;
expect(await picker.pickImage(source: ImageSource.gallery), isNull);
expect(await picker.pickImage(source: ImageSource.camera), isNull);
@@ -213,140 +111,66 @@ void main() {
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,
- 'requestFullMetadata': true,
- 'useAndroidPhotoPicker': false,
- }),
- ],
- );
+ expect(api.passedSource?.camera, SourceCamera.rear);
});
- test('camera position can set to front', () async {
+ test('camera position can be 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,
- 'requestFullMetadata': true,
- 'useAndroidPhotoPicker': false,
- }),
- ],
- );
+ expect(api.passedSource?.camera, SourceCamera.front);
+ });
+
+ test('defaults to not using Android Photo Picker', () async {
+ await picker.pickImage(source: ImageSource.gallery);
+
+ expect(api.passedPhotoPickerFlag, false);
+ });
+
+ test('allows using Android Photo Picker', () async {
+ picker.useAndroidPhotoPicker = true;
+ await picker.pickImage(source: ImageSource.gallery);
+
+ expect(api.passedPhotoPickerFlag, true);
});
});
group('#pickMultiImage', () {
test('calls the method correctly', () async {
- returnValue = ['0', '1'];
- await picker.pickMultiImage();
+ const List fakePaths = ['/foo.jgp', 'bar.jpg'];
+ api.returnValue = fakePaths;
- expect(
- log,
- [
- isMethodCall('pickMultiImage', arguments: {
- 'maxWidth': null,
- 'maxHeight': null,
- 'imageQuality': null,
- 'useAndroidPhotoPicker': false,
- }),
- ],
- );
+ final List? files = await picker.pickMultiImage();
+
+ expect(api.lastCall, _LastPickType.image);
+ expect(api.passedAllowMultiple, true);
+ expect(files?.length, 2);
+ expect(files?[0].path, fakePaths[0]);
+ expect(files?[1].path, fakePaths[1]);
});
- test('passes the width and height arguments correctly', () async {
- returnValue = ['0', '1'];
+ test('passes default image options', () 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,
- );
+
+ expect(api.passedImageOptions?.maxWidth, null);
+ expect(api.passedImageOptions?.maxHeight, null);
+ expect(api.passedImageOptions?.quality, 100);
+ });
+
+ test('passes image option arguments correctly', () async {
await picker.pickMultiImage(
maxWidth: 10.0,
maxHeight: 20.0,
imageQuality: 70,
);
- expect(
- log,
- [
- isMethodCall('pickMultiImage', arguments: {
- 'maxWidth': null,
- 'maxHeight': null,
- 'imageQuality': null,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickMultiImage', arguments: {
- 'maxWidth': 10.0,
- 'maxHeight': null,
- 'imageQuality': null,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickMultiImage', arguments: {
- 'maxWidth': null,
- 'maxHeight': 10.0,
- 'imageQuality': null,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickMultiImage', arguments: {
- 'maxWidth': 10.0,
- 'maxHeight': 20.0,
- 'imageQuality': null,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickMultiImage', arguments: {
- 'maxWidth': 10.0,
- 'maxHeight': null,
- 'imageQuality': 70,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickMultiImage', arguments: {
- 'maxWidth': null,
- 'maxHeight': 10.0,
- 'imageQuality': 70,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickMultiImage', arguments: {
- 'maxWidth': 10.0,
- 'maxHeight': 20.0,
- 'imageQuality': 70,
- 'useAndroidPhotoPicker': false,
- }),
- ],
- );
+ expect(api.passedImageOptions?.maxWidth, 10.0);
+ expect(api.passedImageOptions?.maxHeight, 20.0);
+ expect(api.passedImageOptions?.quality, 70);
});
test('does not accept a negative width or height argument', () {
- returnValue = ['0', '1'];
expect(
() => picker.pickMultiImage(maxWidth: -1.0),
throwsArgumentError,
@@ -359,7 +183,6 @@ void main() {
});
test('does not accept an invalid imageQuality argument', () {
- returnValue = ['0', '1'];
expect(
() => picker.pickMultiImage(imageQuality: -1),
throwsArgumentError,
@@ -371,91 +194,68 @@ void main() {
);
});
- test('handles a null image path response gracefully', () async {
- _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
- .defaultBinaryMessenger
- .setMockMethodCallHandler(
- picker.channel, (MethodCall methodCall) => null);
+ test('handles an empty path response gracefully', () async {
+ api.returnValue = [];
expect(await picker.pickMultiImage(), isNull);
- expect(await picker.pickMultiImage(), isNull);
+ });
+
+ test('defaults to not using Android Photo Picker', () async {
+ await picker.pickMultiImage();
+
+ expect(api.passedPhotoPickerFlag, false);
+ });
+
+ test('allows using Android Photo Picker', () async {
+ picker.useAndroidPhotoPicker = true;
+ await picker.pickMultiImage();
+
+ expect(api.passedPhotoPickerFlag, true);
});
});
group('#pickVideo', () {
- test('passes the image source argument correctly', () async {
+ test('calls the method correctly', () async {
+ const String fakePath = '/foo.jpg';
+ api.returnValue = [fakePath];
+ final PickedFile? result =
+ await picker.pickVideo(source: ImageSource.camera);
+
+ expect(result?.path, fakePath);
+ expect(api.lastCall, _LastPickType.video);
+ expect(api.passedAllowMultiple, false);
+ });
+
+ test('passes the gallery image source argument correctly', () async {
await picker.pickVideo(source: ImageSource.camera);
+
+ expect(api.passedSource?.type, SourceType.camera);
+ });
+
+ test('passes the camera image source argument correctly', () async {
await picker.pickVideo(source: ImageSource.gallery);
- expect(
- log,
- [
- isMethodCall('pickVideo', arguments: {
- 'source': 0,
- 'cameraDevice': 0,
- 'maxDuration': null,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickVideo', arguments: {
- 'source': 1,
- 'cameraDevice': 0,
- 'maxDuration': null,
- 'useAndroidPhotoPicker': false,
- }),
- ],
- );
+ expect(api.passedSource?.type, SourceType.gallery);
+ });
+
+ test('passes null as the default duration', () async {
+ await picker.pickVideo(source: ImageSource.gallery);
+
+ expect(api.passedVideoOptions, isNotNull);
+ expect(api.passedVideoOptions?.maxDurationSeconds, 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,
- [
- isMethodCall('pickVideo', arguments: {
- 'source': 0,
- 'maxDuration': null,
- 'cameraDevice': 0,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickVideo', arguments: {
- 'source': 0,
- 'maxDuration': 10,
- 'cameraDevice': 0,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickVideo', arguments: {
- 'source': 0,
- 'maxDuration': 60,
- 'cameraDevice': 0,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickVideo', arguments: {
- 'source': 0,
- 'maxDuration': 3600,
- 'cameraDevice': 0,
- 'useAndroidPhotoPicker': false,
- }),
- ],
- );
+
+ expect(api.passedVideoOptions?.maxDurationSeconds, 60);
});
test('handles a null video path response gracefully', () async {
- _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
- .defaultBinaryMessenger
- .setMockMethodCallHandler(
- picker.channel, (MethodCall methodCall) => null);
+ api.returnValue = null;
expect(await picker.pickVideo(source: ImageSource.gallery), isNull);
expect(await picker.pickVideo(source: ImageSource.camera), isNull);
@@ -464,17 +264,7 @@ void main() {
test('camera position defaults to back', () async {
await picker.pickVideo(source: ImageSource.camera);
- expect(
- log,
- [
- isMethodCall('pickVideo', arguments: {
- 'source': 0,
- 'cameraDevice': 0,
- 'maxDuration': null,
- 'useAndroidPhotoPicker': false,
- }),
- ],
- );
+ expect(api.passedSource?.camera, SourceCamera.rear);
});
test('camera position can set to front', () async {
@@ -483,31 +273,28 @@ void main() {
preferredCameraDevice: CameraDevice.front,
);
- expect(
- log,
- [
- isMethodCall('pickVideo', arguments: {
- 'source': 0,
- 'maxDuration': null,
- 'cameraDevice': 1,
- 'useAndroidPhotoPicker': false,
- }),
- ],
- );
+ expect(api.passedSource?.camera, SourceCamera.front);
+ });
+
+ test('defaults to not using Android Photo Picker', () async {
+ await picker.pickVideo(source: ImageSource.gallery);
+
+ expect(api.passedPhotoPickerFlag, false);
+ });
+
+ test('allows using Android Photo Picker', () async {
+ picker.useAndroidPhotoPicker = true;
+ await picker.pickVideo(source: ImageSource.gallery);
+
+ expect(api.passedPhotoPickerFlag, true);
});
});
group('#retrieveLostData', () {
test('retrieveLostData get success response', () async {
- _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
- .defaultBinaryMessenger
- .setMockMethodCallHandler(picker.channel,
- (MethodCall methodCall) async {
- return {
- 'type': 'image',
- 'path': '/example/path',
- };
- });
+ api.returnValue = CacheRetrievalResult(
+ type: CacheRetrievalType.image, paths: ['/example/path']);
+
final LostData response = await picker.retrieveLostData();
expect(response.type, RetrieveType.image);
expect(response.file, isNotNull);
@@ -515,16 +302,12 @@ void main() {
});
test('retrieveLostData get error response', () async {
- _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
- .defaultBinaryMessenger
- .setMockMethodCallHandler(picker.channel,
- (MethodCall methodCall) async {
- return {
- 'type': 'video',
- 'errorCode': 'test_error_code',
- 'errorMessage': 'test_error_message',
- };
- });
+ api.returnValue = CacheRetrievalResult(
+ type: CacheRetrievalType.video,
+ paths: [],
+ error: CacheRetrievalError(
+ code: 'test_error_code', message: 'test_error_message'));
+
final LostData response = await picker.retrieveLostData();
expect(response.type, RetrieveType.video);
expect(response.exception, isNotNull);
@@ -533,86 +316,54 @@ void main() {
});
test('retrieveLostData get null response', () async {
- _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
- .defaultBinaryMessenger
- .setMockMethodCallHandler(picker.channel,
- (MethodCall methodCall) async {
- return null;
- });
+ api.returnValue = null;
+
expect((await picker.retrieveLostData()).isEmpty, true);
});
test('retrieveLostData get both path and error should throw', () async {
- _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
- .defaultBinaryMessenger
- .setMockMethodCallHandler(picker.channel,
- (MethodCall methodCall) async {
- return {
- 'type': 'video',
- 'errorCode': 'test_error_code',
- 'errorMessage': 'test_error_message',
- 'path': '/example/path',
- };
- });
+ api.returnValue = CacheRetrievalResult(
+ type: CacheRetrievalType.video,
+ paths: ['/example/path'],
+ error: CacheRetrievalError(
+ code: 'test_error_code', message: 'test_error_message'));
+
expect(picker.retrieveLostData(), throwsAssertionError);
});
});
group('#getImage', () {
- test('passes the image source argument correctly', () async {
+ test('calls the method correctly', () async {
+ const String fakePath = '/foo.jpg';
+ api.returnValue = [fakePath];
+ final XFile? result = await picker.getImage(source: ImageSource.camera);
+
+ expect(result?.path, fakePath);
+ expect(api.lastCall, _LastPickType.image);
+ expect(api.passedAllowMultiple, false);
+ });
+
+ test('passes the gallery image source argument correctly', () async {
await picker.getImage(source: ImageSource.camera);
+
+ expect(api.passedSource?.type, SourceType.camera);
+ });
+
+ test('passes the camera image source argument correctly', () async {
await picker.getImage(source: ImageSource.gallery);
- expect(
- log,
- [
- isMethodCall('pickImage', arguments: {
- 'source': 0,
- 'maxWidth': null,
- 'maxHeight': null,
- 'imageQuality': null,
- 'cameraDevice': 0,
- 'requestFullMetadata': true,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickImage', arguments: {
- 'source': 1,
- 'maxWidth': null,
- 'maxHeight': null,
- 'imageQuality': null,
- 'cameraDevice': 0,
- 'requestFullMetadata': true,
- 'useAndroidPhotoPicker': false,
- }),
- ],
- );
+ expect(api.passedSource?.type, SourceType.gallery);
});
- 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,
- );
+ test('passes default image options', () async {
+ await picker.getImage(source: ImageSource.gallery);
+
+ expect(api.passedImageOptions?.maxWidth, null);
+ expect(api.passedImageOptions?.maxHeight, null);
+ expect(api.passedImageOptions?.quality, 100);
+ });
+
+ test('passes image option arguments correctly', () async {
await picker.getImage(
source: ImageSource.camera,
maxWidth: 10.0,
@@ -620,74 +371,9 @@ void main() {
imageQuality: 70,
);
- expect(
- log,
- [
- isMethodCall('pickImage', arguments: {
- 'source': 0,
- 'maxWidth': null,
- 'maxHeight': null,
- 'imageQuality': null,
- 'cameraDevice': 0,
- 'requestFullMetadata': true,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickImage', arguments: {
- 'source': 0,
- 'maxWidth': 10.0,
- 'maxHeight': null,
- 'imageQuality': null,
- 'cameraDevice': 0,
- 'requestFullMetadata': true,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickImage', arguments: {
- 'source': 0,
- 'maxWidth': null,
- 'maxHeight': 10.0,
- 'imageQuality': null,
- 'cameraDevice': 0,
- 'requestFullMetadata': true,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickImage', arguments: {
- 'source': 0,
- 'maxWidth': 10.0,
- 'maxHeight': 20.0,
- 'imageQuality': null,
- 'cameraDevice': 0,
- 'requestFullMetadata': true,
- 'useAndroidPhotoPicker': false,
- }),
- isMethodCall('pickImage', arguments: