diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 80d91b1deb87..e7f89d9b8a67 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.8.13+16 + +* Fixes gallery image/video selection on Android API 36+ returning no paths when + `useAndroidPhotoPicker` was false. The plugin now uses the Android Photo Picker on API 36 and + above regardless of that flag, avoiding a broken `ACTION_GET_CONTENT` result from the system UI. + ## 0.8.13+15 * Updates build files from Groovy to Kotlin. 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 043546de2b3e..b5ff2e53fbb1 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 @@ -296,7 +296,7 @@ public void chooseMediaFromGallery( private void launchPickMediaFromGalleryIntent(Messages.GeneralOptions generalOptions) { Intent pickMediaIntent; - if (generalOptions.getUsePhotoPicker()) { + if (ImagePickerUtils.effectiveUsePhotoPicker(generalOptions.getUsePhotoPicker())) { if (generalOptions.getAllowMultiple()) { int limit = ImagePickerUtils.getLimitFromOption(generalOptions); @@ -342,7 +342,7 @@ public void chooseVideoFromGallery( private void launchPickVideoFromGalleryIntent(Boolean usePhotoPicker) { Intent pickVideoIntent; - if (usePhotoPicker) { + if (ImagePickerUtils.effectiveUsePhotoPicker(usePhotoPicker)) { pickVideoIntent = new ActivityResultContracts.PickVisualMedia() .createIntent( @@ -441,7 +441,7 @@ public void chooseMultiImageFromGallery( private void launchPickImageFromGalleryIntent(Boolean usePhotoPicker) { Intent pickImageIntent; - if (usePhotoPicker) { + if (ImagePickerUtils.effectiveUsePhotoPicker(usePhotoPicker)) { pickImageIntent = new ActivityResultContracts.PickVisualMedia() .createIntent( @@ -458,7 +458,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( @@ -490,7 +490,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 1499a860d7d2..97b221ca810f 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(Messages.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/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index aba9ecbef800..a2ed7681559b 100755 --- a/packages/image_picker/image_picker_android/pubspec.yaml +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_android 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.13+15 +version: 0.8.13+16 environment: sdk: ^3.9.0