-
Notifications
You must be signed in to change notification settings - Fork 9.7k
[image_picker]fix a crash when a non-image file is picked. #2293
Conversation
| */ | ||
| String resizeImageIfNeeded( | ||
| String imagePath, Double maxWidth, Double maxHeight, int imageQuality) { | ||
| String imagePath, Double maxWidth, Double maxHeight, Integer imageQuality) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: If we're including androidx.annotation already, it would be nice if imageQuality and the other potentially null params here were tagged with @Nullable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| File scaledImage = resizedImage(imagePath, maxWidth, maxHeight, imageQuality); | ||
| exifDataCopier.copyExif(imagePath, scaledImage.getPath()); | ||
| Bitmap bmp = decodeFile(imagePath); | ||
| if (bmp == null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Do all of these lines need to be guarded by an IOException, or can they be moved outside of the try/catch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| double originalHeight = bmp.getHeight() * 1.0; | ||
|
|
||
| if (imageQuality < 0 || imageQuality > 100) { | ||
| if (imageQuality == null || imageQuality < 0 || imageQuality > 100) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: This condition is repeated in a lot of places, and it uses two magic numbers (0 and 100). I think it would help readability a little to extract it out into a helper function called isImageQualityValid that was called in multiple places instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| import org.mockito.Spy; | ||
| import org.mockito.invocation.InvocationOnMock; | ||
|
|
||
| public class ImageResizerTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we have some time to improve this, it would be better to change the style of testing here so that it's really testing the behavior of ImageResizer's public API instead of the interactions of a bunch of its private methods. The way this test is written right now it's really just verifying that when certain methods are called, other methods also get triggered. This isn't really testing the logic of the class, and it could be broken by trivial method renames. It also exposes a bunch of methods with @VisibleForTesting that would be better kept private.
Here's the approach I would probably take:
- Use TemporaryFolder to create a file directory for the unit tests, and pass it into
ImageResizerasexternalFilesDirectoryas its constructed in the tests. - Write a sample bitmap to
TemporaryFolder. - Call
ImageResizer.resizeImageIfNeededwith a combination of settings. Verify that the bitmap at the returned path exists and has been resized correctly for each one.
What do you think, does that sound workable to you? The catch here is I'm not sure how creating a Bitmap in a test goes, and I'm not sure if it's easy to check a Bitmap's image quality or not. I don't remember that API off the top of my head.
This is still an improvement over what we have so I am happy to merge this if needed, thank you for writing tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will take a look
| return null; | ||
| } | ||
| String[] pathParts = imagePath.split("/"); | ||
| String imageName = pathParts[pathParts.length - 1]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are all images always guaranteed to have an extension?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the imageName. For example if the imagePath is foo/bar/image.png, the imageName would be image.png.
Or you are asking what we should do if the imageName doesn't contain an extension?
cyanglaz
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done some changes based on the comments. Will look into improving the tests.
| */ | ||
| String resizeImageIfNeeded( | ||
| String imagePath, Double maxWidth, Double maxHeight, int imageQuality) { | ||
| String imagePath, Double maxWidth, Double maxHeight, Integer imageQuality) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| File scaledImage = resizedImage(imagePath, maxWidth, maxHeight, imageQuality); | ||
| exifDataCopier.copyExif(imagePath, scaledImage.getPath()); | ||
| Bitmap bmp = decodeFile(imagePath); | ||
| if (bmp == null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| return null; | ||
| } | ||
| String[] pathParts = imagePath.split("/"); | ||
| String imageName = pathParts[pathParts.length - 1]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the imageName. For example if the imagePath is foo/bar/image.png, the imageName would be image.png.
Or you are asking what we should do if the imageName doesn't contain an extension?
| double originalHeight = bmp.getHeight() * 1.0; | ||
|
|
||
| if (imageQuality < 0 || imageQuality > 100) { | ||
| if (imageQuality == null || imageQuality < 0 || imageQuality > 100) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| import org.mockito.Spy; | ||
| import org.mockito.invocation.InvocationOnMock; | ||
|
|
||
| public class ImageResizerTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will take a look
mklim
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Better testing for this isn't feasible right now, Robolectric fakes out too much of Bitmap and decoding it from files/byte streams.
Description
This PR fixes 2 things:
The scaling is performed even when the quality/maxWidth/maxHeight are null.
When a non-image file is picked, scaling process is performed on the file.
Related Issues
flutter/flutter#38775
flutter/flutter#40233
b/142680906
b/143851555
Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]). This will ensure a smooth and quick review process.///).flutter analyze) does not report any problems on my PR.Breaking Change
Does your PR require plugin users to manually update their apps to accommodate your change?