Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
11699ba
check if data from activity result is empty when picking a single ima…
Sep 2, 2023
3662c52
update version and CHANGELOG
Sep 4, 2023
434b3bd
add documentation
Sep 4, 2023
17e094e
fix formatting
Sep 5, 2023
68183bb
add test to verify the code
Sep 6, 2023
97c9e60
fix formatting
Sep 6, 2023
bb9d0f1
updates according to feedback
Sep 8, 2023
c976373
Merge branch 'main' into main
aldee Sep 8, 2023
ec9f733
Merge branch 'main' into main
aldee Sep 11, 2023
4bfc605
Merge commit '06cd9e967b9f17da3eea812a6f85394f62278aec'
Sep 13, 2023
efa3083
add check for null uri from clipdata.item
Sep 13, 2023
c30cc59
update changelog and pubspec.yaml
Sep 13, 2023
dd71b39
update the changelog
Sep 13, 2023
bfd10ce
fix the video pick from gallery
Sep 13, 2023
f783a22
fix formatting
Sep 13, 2023
685b95c
check if data from activity result is empty when picking a single ima…
Sep 2, 2023
6fd33a9
add documentation
Sep 4, 2023
f0a2011
fix formatting
Sep 5, 2023
53c2eb9
add test to verify the code
Sep 6, 2023
ac828b3
fix formatting
Sep 6, 2023
8b0a9f1
updates according to feedback
Sep 8, 2023
2343e5b
add check for null uri from clipdata.item
Sep 13, 2023
36e5758
update changelog and pubspec.yaml
Sep 13, 2023
4077868
fix the video pick from gallery
Sep 13, 2023
73874c9
fix formatting
Sep 13, 2023
d4f63b5
Merge branch 'main' of github.com:aldee/packages
Sep 13, 2023
646d678
update version to 0.8.9
Sep 13, 2023
faa641b
Merge branch 'main' into main
aldee Sep 15, 2023
196da1b
fix null check logic to perfection
Sep 19, 2023
84e5221
change version to 0.8.8+1
Sep 19, 2023
15c4e5c
Merge branch 'main' of github.com:aldee/packages
Sep 19, 2023
bb68a08
Merge branch 'main' into main
aldee Sep 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.8+1

* Fixes NullPointerException on pre-Android 13 devices when using Android Photo Picker to pick image or video.

## 0.8.8

* Adds additional category II and III exif tags to be copied during photo resize.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import android.Manifest;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
Expand Down Expand Up @@ -630,7 +631,21 @@ public boolean onActivityResult(

private void handleChooseImageResult(int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && data != null) {
String path = fileUtils.getPathFromUri(activity, data.getData());
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();
}
}
// If there's no valid Uri, return an error
if (uri == null) {
finishWithError("no_valid_image_uri", "Cannot find the selected image.");
return;
}

String path = fileUtils.getPathFromUri(activity, uri);
handleImageResult(path, false);
return;
}
Expand Down Expand Up @@ -701,7 +716,21 @@ private void handleChooseMultiImageResult(int resultCode, Intent intent) {

private void handleChooseVideoResult(int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && data != null) {
String path = fileUtils.getPathFromUri(activity, data.getData());
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();
}
}
// If there's no valid Uri, return an error
if (uri == null) {
finishWithError("no_valid_video_uri", "Cannot find the selected video.");
return;
}

String path = fileUtils.getPathFromUri(activity, uri);
handleVideoResult(path);
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import android.Manifest;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
Expand Down Expand Up @@ -437,6 +438,114 @@ public void onActivityResult_whenPickFromGalleryCanceled_storesNothingInCache()
verifyNoMoreInteractions(mockResult);
}

@Test
public void
onActivityResult_whenImagePickedFromGallery_nullUriFromGetData_andNoResizeNeeded_finishesWithImagePath() {
setupMockClipData();

when(mockIntent.getData()).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_IMAGE_FROM_GALLERY, Activity.RESULT_OK, mockIntent);

@SuppressWarnings("unchecked")
ArgumentCaptor<List<String>> pathListCapture = ArgumentCaptor.forClass(List.class);
verify(mockResult).success(pathListCapture.capture());
assertEquals("originalPath", pathListCapture.getValue().get(0));
verifyNoMoreInteractions(mockResult);
}

@Test
public void
onActivityResult_whenVideoPickedFromGallery_nullUriFromGetData_finishesWithVideoPath() {
setupMockClipData();

when(mockIntent.getData()).thenReturn(null);

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

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

@SuppressWarnings("unchecked")
ArgumentCaptor<List<String>> pathListCapture = ArgumentCaptor.forClass(List.class);
verify(mockResult).success(pathListCapture.capture());
assertEquals("pathFromUri", pathListCapture.getValue().get(0));
verifyNoMoreInteractions(mockResult);
}

@Test
public void
onActivityResult_whenImagePickedFromGallery_nullUri_andNoResizeNeeded_finishesWithNoValidUriError() {
setupMockClipDataNullUri();

when(mockIntent.getData()).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_IMAGE_FROM_GALLERY, Activity.RESULT_OK, mockIntent);

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

@Test
public void onActivityResult_whenVideoPickedFromGallery_nullUri_finishesWithNoValidUriError() {
setupMockClipDataNullUri();

when(mockIntent.getData()).thenReturn(null);

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

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

@SuppressWarnings("unchecked")
ArgumentCaptor<FlutterError> errorCaptor = ArgumentCaptor.forClass(FlutterError.class);
verify(mockResult).error(errorCaptor.capture());
assertEquals("no_valid_video_uri", errorCaptor.getValue().code);
assertEquals("Cannot find the selected video.", errorCaptor.getValue().getMessage());
verifyNoMoreInteractions(mockResult);
}

@Test
public void onActivityResult_whenImagePickedFromGallery_andNoResizeNeeded_storesImageInCache() {
Mockito.doAnswer(
Expand Down Expand Up @@ -743,4 +852,23 @@ private void verifyFinishedWithAlreadyActiveError() {
assertEquals("already_active", errorCaptor.getValue().code);
assertEquals("Image picker is already active", errorCaptor.getValue().getMessage());
}

private void setupMockClipData() {
ClipData mockClipData = mock(ClipData.class);
ClipData.Item mockItem = mock(ClipData.Item.class);
Uri mockUri = mock(Uri.class);
when(mockItem.getUri()).thenReturn(mockUri);
when(mockClipData.getItemCount()).thenReturn(1);
when(mockClipData.getItemAt(0)).thenReturn(mockItem);
when(mockIntent.getClipData()).thenReturn(mockClipData);
}

private void setupMockClipDataNullUri() {
ClipData mockClipData = mock(ClipData.class);
ClipData.Item mockItem = mock(ClipData.Item.class);
when(mockItem.getUri()).thenReturn(null);
when(mockClipData.getItemCount()).thenReturn(1);
when(mockClipData.getItemAt(0)).thenReturn(mockItem);
when(mockIntent.getClipData()).thenReturn(mockClipData);
}
}
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 @@ -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.8
version: 0.8.8+1

environment:
sdk: ">=2.19.0 <4.0.0"
Expand Down