Skip to content

Commit

Permalink
[image_picker_android] Refactor getting of paths from intent to singl…
Browse files Browse the repository at this point in the history
…e helper (#5009)

Fixes flutter/flutter#104168
  • Loading branch information
gmackall committed May 14, 2024
1 parent 2316df1 commit 0e75adf
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 58 deletions.
4 changes: 4 additions & 0 deletions packages/image_picker/image_picker_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.8.12+1

* Fixes another app crash case on Android 12+, and refactors getting of paths from intents.

## 0.8.12

* Fixes app crashes on Android 12+ caused by selecting images with size 0.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -638,24 +638,57 @@ public boolean onActivityResult(
return true;
}

private void handleChooseImageResult(int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && data != null) {
Uri uri = data.getData();
// On several pre-Android 13 devices using Android Photo Picker, the Uri from getData() could be null.
if (uri == null) {
ClipData clipData = data.getClipData();
if (clipData != null && clipData.getItemCount() == 1) {
uri = clipData.getItemAt(0).getUri();
@Nullable
private ArrayList<MediaPath> getPathsFromIntent(@NonNull Intent data, boolean includeMimeType) {
ArrayList<MediaPath> paths = new ArrayList<>();

Uri uri = data.getData();
// On several pre-Android 13 devices using Android Photo Picker, the Uri from getData() could
// be null.
if (uri == null) {
ClipData clipData = data.getClipData();

// If data.getData() and data.getClipData() are both null, we are in an error state. By
// convention we return null from here, and then finish with an error from the corresponding
// handler.
if (clipData == null) {
return null;
}

for (int i = 0; i < data.getClipData().getItemCount(); i++) {
uri = data.getClipData().getItemAt(i).getUri();
// Same error state as above.
if (uri == null) {
return null;
}
String path = fileUtils.getPathFromUri(activity, uri);
// Again, same error state as above.
if (path == null) {
return null;
}
String mimeType = includeMimeType ? activity.getContentResolver().getType(uri) : null;
paths.add(new MediaPath(path, mimeType));
}
} else {
String path = fileUtils.getPathFromUri(activity, uri);
if (path == null) {
return null;
}
paths.add(new MediaPath(path, null));
}
return paths;
}

private void handleChooseImageResult(int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && data != null) {
ArrayList<MediaPath> paths = getPathsFromIntent(data, false);
// If there's no valid Uri, return an error
if (uri == null) {
if (paths == null) {
finishWithError("no_valid_image_uri", "Cannot find the selected image.");
return;
}

String path = fileUtils.getPathFromUri(activity, uri);
handleImageResult(path, false);
handleMediaResult(paths);
return;
}

Expand Down Expand Up @@ -683,20 +716,13 @@ public MediaPath(@NonNull String path, @Nullable String mimeType) {

private void handleChooseMediaResult(int resultCode, Intent intent) {
if (resultCode == Activity.RESULT_OK && intent != null) {
ArrayList<MediaPath> paths = new ArrayList<>();
if (intent.getClipData() != null) {
for (int i = 0; i < intent.getClipData().getItemCount(); i++) {
Uri uri = intent.getClipData().getItemAt(i).getUri();
String path = fileUtils.getPathFromUri(activity, uri);
String mimeType = activity.getContentResolver().getType(uri);
paths.add(new MediaPath(path, mimeType));
}
} else {
Uri uri = intent.getData();
if (uri != null) {
paths.add(new MediaPath(fileUtils.getPathFromUri(activity, uri), null));
}
ArrayList<MediaPath> paths = getPathsFromIntent(intent, true);
// If there's no valid Uri, return an error
if (paths == null) {
finishWithError("no_valid_media_uri", "Cannot find the selected media.");
return;
}

handleMediaResult(paths);
return;
}
Expand All @@ -707,17 +733,14 @@ private void handleChooseMediaResult(int resultCode, Intent intent) {

private void handleChooseMultiImageResult(int resultCode, Intent intent) {
if (resultCode == Activity.RESULT_OK && intent != null) {
ArrayList<MediaPath> paths = new ArrayList<>();
if (intent.getClipData() != null) {
for (int i = 0; i < intent.getClipData().getItemCount(); i++) {
paths.add(
new MediaPath(
fileUtils.getPathFromUri(activity, intent.getClipData().getItemAt(i).getUri()),
null));
}
} else {
paths.add(new MediaPath(fileUtils.getPathFromUri(activity, intent.getData()), null));
ArrayList<MediaPath> paths = getPathsFromIntent(intent, false);
// If there's no valid Uri, return an error
if (paths == null) {
finishWithError(
"missing_valid_image_uri", "Cannot find at least one of the selected images.");
return;
}

handleMediaResult(paths);
return;
}
Expand All @@ -728,22 +751,14 @@ private void handleChooseMultiImageResult(int resultCode, Intent intent) {

private void handleChooseVideoResult(int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && data != null) {
Uri uri = data.getData();
// On several pre-Android 13 devices using Android Photo Picker, the Uri from getData() could be null.
if (uri == null) {
ClipData clipData = data.getClipData();
if (clipData != null && clipData.getItemCount() == 1) {
uri = clipData.getItemAt(0).getUri();
}
}
ArrayList<MediaPath> paths = getPathsFromIntent(data, false);
// If there's no valid Uri, return an error
if (uri == null) {
if (paths == null || paths.size() < 1) {
finishWithError("no_valid_video_uri", "Cannot find the selected video.");
return;
}

String path = fileUtils.getPathFromUri(activity, uri);
handleVideoResult(path);
finishWithSuccess(paths.get(0).path);
return;
}

Expand Down Expand Up @@ -774,7 +789,7 @@ private void handleCaptureVideoResult(int resultCode) {
localPendingCameraMediaUrl != null
? localPendingCameraMediaUrl
: Uri.parse(cache.retrievePendingCameraMediaUriPath()),
this::handleVideoResult);
this::finishWithSuccess);
return;
}

Expand Down Expand Up @@ -837,10 +852,6 @@ private void handleMediaResult(@NonNull ArrayList<MediaPath> paths) {
}
}

private void handleVideoResult(String path) {
finishWithSuccess(path);
}

private boolean setPendingOptionsAndResult(
@Nullable ImageSelectionOptions imageOptions,
@Nullable VideoSelectionOptions videoOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -815,8 +815,31 @@ public void onActivityResult_withUnknownRequest_returnsFalse() {
}

@Test
public void
onActivityResult_whenImagePickedFromGallery_finishesWithEmptyListIfIntentDataIsNull() {
public void onActivityResult_whenImagePickedFromGallery_finishesWithErrorIfClipDataIsNull() {
when(mockIntent.getData()).thenReturn(null);
when(mockIntent.getClipData()).thenReturn(null);

Mockito.doAnswer(
invocation -> {
((Runnable) invocation.getArgument(0)).run();
return null;
})
.when(mockExecutor)
.execute(any(Runnable.class));
ImagePickerDelegate delegate =
createDelegateWithPendingResultAndOptions(DEFAULT_IMAGE_OPTIONS, null);

delegate.onActivityResult(
ImagePickerDelegate.REQUEST_CODE_CHOOSE_MEDIA_FROM_GALLERY, Activity.RESULT_OK, mockIntent);

ArgumentCaptor<FlutterError> errorCaptor = ArgumentCaptor.forClass(FlutterError.class);
verify(mockResult).error(errorCaptor.capture());
assertEquals("no_valid_media_uri", errorCaptor.getValue().code);
assertEquals("Cannot find the selected media.", errorCaptor.getValue().getMessage());
}

@Test
public void onActivityResult_whenImagePickedFromGallery_finishesWithErrorIfClipDataUriIsNull() {
setupMockClipDataNullUri();
when(mockIntent.getData()).thenReturn(null);
when(mockIntent.getClipData()).thenReturn(null);
Expand All @@ -834,11 +857,10 @@ public void onActivityResult_withUnknownRequest_returnsFalse() {
delegate.onActivityResult(
ImagePickerDelegate.REQUEST_CODE_CHOOSE_MEDIA_FROM_GALLERY, Activity.RESULT_OK, mockIntent);

@SuppressWarnings("unchecked")
ArgumentCaptor<List<String>> pathListCapture = ArgumentCaptor.forClass(List.class);
verify(mockResult).success(pathListCapture.capture());
assertEquals(0, pathListCapture.getValue().size());
verifyNoMoreInteractions(mockResult);
ArgumentCaptor<FlutterError> errorCaptor = ArgumentCaptor.forClass(FlutterError.class);
verify(mockResult).error(errorCaptor.capture());
assertEquals("no_valid_media_uri", errorCaptor.getValue().code);
assertEquals("Cannot find the selected media.", errorCaptor.getValue().getMessage());
}

private ImagePickerDelegate createDelegate() {
Expand Down
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.12
version: 0.8.12+1

environment:
sdk: ^3.3.0
Expand Down

0 comments on commit 0e75adf

Please sign in to comment.