Skip to content
6 changes: 6 additions & 0 deletions packages/image_picker/image_picker_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand All @@ -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(
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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}.
*
* <p>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.
*
* <p>See <a href="https://github.com/flutter/flutter/issues/182071">flutter/flutter#182071</a>.
*/
static boolean effectiveUsePhotoPicker(boolean usePhotoPickerFromDart) {
return Build.VERSION.SDK_INT >= API_LEVEL_36 || usePhotoPickerFromDart;
}
}
Original file line number Diff line number Diff line change
@@ -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));
}
}
2 changes: 1 addition & 1 deletion packages/image_picker/image_picker_android/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down