diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index a5ea80375249..b9b67b3cf867 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates plugin to use Android Photo Picker on API 36 and above. * Updates minimum supported SDK version to Flutter 3.38/Dart 3.10. ## 0.8.13+17 diff --git a/packages/image_picker/image_picker_android/README.md b/packages/image_picker/image_picker_android/README.md index a0bcc70294d6..883efa518fef 100755 --- a/packages/image_picker/image_picker_android/README.md +++ b/packages/image_picker/image_picker_android/README.md @@ -15,9 +15,12 @@ should add it to your `pubspec.yaml` as usual. ## Photo Picker -On Android 13 and above this packages uses the Android Photo Picker. +On Android 16 (API level 36) and above, gallery image, video, and mixed-media +picks always use the Android Photo Picker. +cannot be set to false to use the legacy ACTION_GET_CONTENT flow on those +versions. See [flutter/flutter#182071][5]. -On Android 12 and below this package has optional Android Photo Picker functionality. +On Android 15 and below this package has optional Android Photo Picker functionality. To use this feature, add the following code to your app before calling any `image_picker` APIs: @@ -38,3 +41,5 @@ In addition, `ImagePickerAndroid.useAndroidPhotoPicker` must be set to `true` to [1]: https://pub.dev/packages/image_picker [2]: https://flutter.dev/to/endorsed-federated-plugin [3]: https://developer.android.google.cn/reference/kotlin/androidx/activity/result/contract/ActivityResultContracts.PickMultipleVisualMedia +[4]: https://pub.dev/documentation/image_picker_android/latest/image_picker_android/ImagePickerAndroid/useAndroidPhotoPicker.htmlAdd a comment on line R52Add diff commentMarkdown input: edit mode selected.WritePreviewHeadingBoldItalicQuoteCodeLinkUnordered listNumbered listTask listMentionReferenceMore Formatting tools items 0Saved repliesAdd FilesPaste, drop, or click to add filesCancelCommentStart a review +[5]: https://github.com/flutter/flutter/issues/182071 \ No newline at end of file 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 e763571b67dc..b1d101f6cd95 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 @@ -304,7 +304,7 @@ public void chooseMediaFromGallery( private void launchPickMediaFromGalleryIntent(GeneralOptions generalOptions) { Intent pickMediaIntent; - if (generalOptions.getUsePhotoPicker()) { + if (ImagePickerUtils.effectiveUsePhotoPicker(generalOptions.getUsePhotoPicker())) { if (generalOptions.getAllowMultiple()) { int limit = ImagePickerUtils.getLimitFromOption(generalOptions); @@ -353,7 +353,7 @@ public void chooseVideoFromGallery( private void launchPickVideoFromGalleryIntent(Boolean usePhotoPicker) { Intent pickVideoIntent; - if (usePhotoPicker) { + if (ImagePickerUtils.effectiveUsePhotoPicker(usePhotoPicker)) { pickVideoIntent = new ActivityResultContracts.PickVisualMedia() .createIntent( @@ -462,7 +462,7 @@ public void chooseMultiImageFromGallery( private void launchPickImageFromGalleryIntent(Boolean usePhotoPicker) { Intent pickImageIntent; - if (usePhotoPicker) { + if (ImagePickerUtils.effectiveUsePhotoPicker(usePhotoPicker)) { pickImageIntent = new ActivityResultContracts.PickVisualMedia() .createIntent( @@ -479,7 +479,7 @@ private void launchPickImageFromGalleryIntent(Boolean usePhotoPicker) { private void launchMultiPickImageFromGalleryIntent(Boolean usePhotoPicker, int limit) { Intent pickMultiImageIntent; - if (usePhotoPicker) { + if (ImagePickerUtils.effectiveUsePhotoPicker(usePhotoPicker)) { pickMultiImageIntent = new ActivityResultContracts.PickMultipleVisualMedia(limit) .createIntent( @@ -514,7 +514,7 @@ public void chooseMultiVideoFromGallery( private void launchMultiPickVideoFromGalleryIntent(Boolean usePhotoPicker, int limit) { Intent pickMultiVideoIntent; - if (usePhotoPicker) { + if (ImagePickerUtils.effectiveUsePhotoPicker(usePhotoPicker)) { pickMultiVideoIntent = new ActivityResultContracts.PickMultipleVisualMedia(limit) .createIntent( diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java index 8fbf44fa6741..438f1860da0b 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java @@ -15,6 +15,8 @@ import java.util.Arrays; final class ImagePickerUtils { + private static final int API_LEVEL_36 = 36; + /** returns true, if permission present in manifest, otherwise false */ private static boolean isPermissionPresentInManifest(Context context, String permissionName) { try { @@ -86,4 +88,22 @@ static int getLimitFromOption(GeneralOptions generalOptions) { return effectiveLimit; } + + /** + * Returns whether gallery/media selection should use {@link + * androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia} (Android Photo + * Picker) instead of {@link android.content.Intent#ACTION_GET_CONTENT}. + * + *

On Android API 36+, {@code ACTION_GET_CONTENT} for images may be handled by the system + * photo picker's {@code PhotopickerGetContentActivity}. That path combined with {@code + * startActivityForResult} can return {@link android.app.Activity#RESULT_OK} without {@link + * android.content.Intent#getData()} or usable {@link android.content.ClipData}, so the plugin + * would complete with no paths. The {@code PickVisualMedia} contract uses the Activity Result API + * and receives URIs reliably. + * + *

See flutter/flutter#182071. + */ + static boolean effectiveUsePhotoPicker(boolean usePhotoPickerFromDart) { + return Build.VERSION.SDK_INT >= API_LEVEL_36 || usePhotoPickerFromDart; + } } diff --git a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerUtilsTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerUtilsTest.java new file mode 100644 index 000000000000..396c84410545 --- /dev/null +++ b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerUtilsTest.java @@ -0,0 +1,31 @@ +// Copyright 2013 The Flutter Authors +// 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 static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +@RunWith(RobolectricTestRunner.class) +public class ImagePickerUtilsTest { + + @Test + @Config(sdk = 35) + public void effectiveUsePhotoPicker_belowApi36_usesDartPreference() { + assertFalse(ImagePickerUtils.effectiveUsePhotoPicker(false)); + assertTrue(ImagePickerUtils.effectiveUsePhotoPicker(true)); + } + + @Test + @Config(sdk = 36) + public void effectiveUsePhotoPicker_onApi36_alwaysUsesPhotoPicker() { + assertTrue(ImagePickerUtils.effectiveUsePhotoPicker(false)); + assertTrue(ImagePickerUtils.effectiveUsePhotoPicker(true)); + } +} 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 fd7080f536b5..91342b9ac811 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 @@ -17,9 +17,14 @@ class ImagePickerAndroid extends ImagePickerPlatform { final ImagePickerApi _hostApi; - /// Sets [ImagePickerAndroid] to use Android 13 Photo Picker. + /// Whether to use the Android Photo Picker on Android versions less than 16. /// - /// Currently defaults to false, but the default is subject to change. + /// On Android 16+ the Photo Picker is always used regardless of this setting. + /// + /// On earlier Android versions, setting this to false uses the legacy + /// `ACTION_GET_CONTENT` picker instead of the Photo Picker. + /// + /// Defaults to false. bool useAndroidPhotoPicker = false; /// Registers this class as the default platform implementation.