Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit db123e8

Browse files
author
Chris Yang
authored
[image_picker]fix a crash when a non-image file is picked. (#2293)
1 parent 95873dc commit db123e8

9 files changed

Lines changed: 140 additions & 37 deletions

File tree

packages/image_picker/CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
## 0.6.2+1
2+
3+
* Android: Fix a crash when a non-image file is picked.
4+
* Android: Fix unwanted bitmap scaling.
5+
16
## 0.6.2
27

3-
* iOS: Fixes an issue where picking conent from Gallery would result in a crash on iOS 13.
8+
* iOS: Fixes an issue where picking content from Gallery would result in a crash on iOS 13.
49

510
## 0.6.1+11
611

packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -521,10 +521,7 @@ private void handleImageResult(String path, boolean shouldDeleteOriginalIfScaled
521521
if (methodCall != null) {
522522
Double maxWidth = methodCall.argument("maxWidth");
523523
Double maxHeight = methodCall.argument("maxHeight");
524-
int imageQuality =
525-
methodCall.argument("imageQuality") == null
526-
? 100
527-
: (int) methodCall.argument("imageQuality");
524+
Integer imageQuality = methodCall.argument("imageQuality");
528525

529526
String finalImagePath =
530527
imageResizer.resizeImageIfNeeded(path, maxWidth, maxHeight, imageQuality);

packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImageResizer.java

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import android.graphics.Bitmap;
88
import android.graphics.BitmapFactory;
99
import android.util.Log;
10+
import androidx.annotation.Nullable;
1011
import java.io.ByteArrayOutputStream;
1112
import java.io.File;
1213
import java.io.FileOutputStream;
@@ -28,31 +29,39 @@ class ImageResizer {
2829
* <p>If no resizing is needed, returns the path for the original image.
2930
*/
3031
String resizeImageIfNeeded(
31-
String imagePath, Double maxWidth, Double maxHeight, int imageQuality) {
32+
String imagePath,
33+
@Nullable Double maxWidth,
34+
@Nullable Double maxHeight,
35+
@Nullable Integer imageQuality) {
3236
boolean shouldScale =
33-
maxWidth != null || maxHeight != null || (imageQuality > -1 && imageQuality < 101);
34-
35-
if (!shouldScale) {
36-
return imagePath;
37+
maxWidth != null || maxHeight != null || isImageQualityValid(imageQuality);
38+
String[] pathParts = imagePath.split("/");
39+
String imageName = pathParts[pathParts.length - 1];
40+
File file;
41+
Bitmap bmp = decodeFile(imagePath);
42+
if (bmp == null) {
43+
return null;
3744
}
38-
3945
try {
40-
File scaledImage = resizedImage(imagePath, maxWidth, maxHeight, imageQuality);
41-
exifDataCopier.copyExif(imagePath, scaledImage.getPath());
42-
43-
return scaledImage.getPath();
46+
if (!shouldScale) {
47+
file = createImageOnExternalDirectory(imageName, bmp, 100);
48+
} else {
49+
file = resizedImage(bmp, maxWidth, maxHeight, imageQuality, imageName);
50+
}
51+
copyExif(imagePath, file.getPath());
52+
return file.getPath();
4453
} catch (IOException e) {
4554
throw new RuntimeException(e);
4655
}
4756
}
4857

49-
private File resizedImage(String path, Double maxWidth, Double maxHeight, int imageQuality)
58+
private File resizedImage(
59+
Bitmap bmp, Double maxWidth, Double maxHeight, Integer imageQuality, String outputImageName)
5060
throws IOException {
51-
Bitmap bmp = BitmapFactory.decodeFile(path);
5261
double originalWidth = bmp.getWidth() * 1.0;
5362
double originalHeight = bmp.getHeight() * 1.0;
5463

55-
if (imageQuality < 0 || imageQuality > 100) {
64+
if (!isImageQualityValid(imageQuality)) {
5665
imageQuality = 100;
5766
}
5867

@@ -91,24 +100,51 @@ private File resizedImage(String path, Double maxWidth, Double maxHeight, int im
91100
}
92101
}
93102

94-
Bitmap scaledBmp = Bitmap.createScaledBitmap(bmp, width.intValue(), height.intValue(), false);
103+
Bitmap scaledBmp = createScaledBitmap(bmp, width.intValue(), height.intValue(), false);
104+
File file =
105+
createImageOnExternalDirectory("/scaled_" + outputImageName, scaledBmp, imageQuality);
106+
return file;
107+
}
108+
109+
private File createFile(File externalFilesDirectory, String child) {
110+
return new File(externalFilesDirectory, child);
111+
}
112+
113+
private FileOutputStream createOutputStream(File imageFile) throws IOException {
114+
return new FileOutputStream(imageFile);
115+
}
116+
117+
private void copyExif(String filePathOri, String filePathDest) {
118+
exifDataCopier.copyExif(filePathOri, filePathDest);
119+
}
120+
121+
private Bitmap decodeFile(String path) {
122+
return BitmapFactory.decodeFile(path);
123+
}
124+
125+
private Bitmap createScaledBitmap(Bitmap bmp, int width, int height, boolean filter) {
126+
return Bitmap.createScaledBitmap(bmp, width, height, filter);
127+
}
128+
129+
private boolean isImageQualityValid(Integer imageQuality) {
130+
return imageQuality != null && imageQuality > 0 && imageQuality < 100;
131+
}
132+
133+
private File createImageOnExternalDirectory(String name, Bitmap bitmap, int imageQuality)
134+
throws IOException {
95135
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
96-
boolean saveAsPNG = bmp.hasAlpha();
136+
boolean saveAsPNG = bitmap.hasAlpha();
97137
if (saveAsPNG) {
98138
Log.d(
99139
"ImageResizer",
100140
"image_picker: compressing is not supported for type PNG. Returning the image with original quality");
101141
}
102-
scaledBmp.compress(
142+
bitmap.compress(
103143
saveAsPNG ? Bitmap.CompressFormat.PNG : Bitmap.CompressFormat.JPEG,
104144
imageQuality,
105145
outputStream);
106-
107-
String[] pathParts = path.split("/");
108-
String imageName = pathParts[pathParts.length - 1];
109-
110-
File imageFile = new File(externalFilesDirectory, "/scaled_" + imageName);
111-
FileOutputStream fileOutput = new FileOutputStream(imageFile);
146+
File imageFile = createFile(externalFilesDirectory, name);
147+
FileOutputStream fileOutput = createOutputStream(imageFile);
112148
fileOutput.write(outputStream.toByteArray());
113149
fileOutput.close();
114150
return imageFile;

packages/image_picker/example/android/app/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,5 @@ dependencies {
6262
testImplementation 'org.mockito:mockito-core:2.17.0'
6363
androidTestImplementation 'androidx.test:runner:1.1.1'
6464
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
65+
testImplementation "org.robolectric:robolectric:3.3.2"
6566
}

packages/image_picker/example/android/app/src/test/java/io/flutter/plugins/imagepicker/ImagePickerCacheTest.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@
2525
import org.mockito.MockitoAnnotations;
2626

2727
public class ImagePickerCacheTest {
28-
private static final double WIDTH = 10.0;
29-
private static final double HEIGHT = 10.0;
3028
private static final int IMAGE_QUALITY = 90;
31-
private static final String PATH = "a_mock_path";
3229

3330
@Mock Activity mockActivity;
3431
@Mock SharedPreferences mockPreference;

packages/image_picker/example/android/app/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
import org.mockito.MockitoAnnotations;
2525

2626
public class ImagePickerDelegateTest {
27-
private static final double WIDTH = 10.0;
28-
private static final double HEIGHT = 10.0;
29-
private static final int IMAGE_QUALITY = 100;
27+
private static final Double WIDTH = 10.0;
28+
private static final Double HEIGHT = 10.0;
29+
private static final Integer IMAGE_QUALITY = 90;
3030

3131
@Mock Activity mockActivity;
3232
@Mock ImageResizer mockImageResizer;
@@ -62,13 +62,15 @@ public void setUp() {
6262
when(mockFileUtils.getPathFromUri(any(Context.class), any(Uri.class)))
6363
.thenReturn("pathFromUri");
6464

65+
when(mockImageResizer.resizeImageIfNeeded("pathFromUri", null, null, null))
66+
.thenReturn("originalPath");
6567
when(mockImageResizer.resizeImageIfNeeded("pathFromUri", null, null, IMAGE_QUALITY))
6668
.thenReturn("originalPath");
67-
when(mockImageResizer.resizeImageIfNeeded("pathFromUri", WIDTH, HEIGHT, IMAGE_QUALITY))
69+
when(mockImageResizer.resizeImageIfNeeded("pathFromUri", WIDTH, HEIGHT, null))
6870
.thenReturn("scaledPath");
69-
when(mockImageResizer.resizeImageIfNeeded("pathFromUri", WIDTH, null, IMAGE_QUALITY))
71+
when(mockImageResizer.resizeImageIfNeeded("pathFromUri", WIDTH, null, null))
7072
.thenReturn("scaledPath");
71-
when(mockImageResizer.resizeImageIfNeeded("pathFromUri", null, HEIGHT, IMAGE_QUALITY))
73+
when(mockImageResizer.resizeImageIfNeeded("pathFromUri", null, HEIGHT, null))
7274
.thenReturn("scaledPath");
7375

7476
mockFileUriResolver = new MockFileUriResolver();
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright 2019 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.plugins.imagepicker;
6+
7+
import static org.hamcrest.core.IsEqual.equalTo;
8+
import static org.junit.Assert.assertThat;
9+
10+
import android.graphics.Bitmap;
11+
import android.graphics.BitmapFactory;
12+
import java.io.File;
13+
import java.io.IOException;
14+
import org.junit.Before;
15+
import org.junit.Test;
16+
import org.junit.rules.TemporaryFolder;
17+
import org.junit.runner.RunWith;
18+
import org.mockito.MockitoAnnotations;
19+
import org.robolectric.RobolectricTestRunner;
20+
21+
// RobolectricTestRunner always creates a default mock bitmap when reading from file. So we cannot actually test the scaling.
22+
// But we can still test whether the original or scaled file is created.
23+
@RunWith(RobolectricTestRunner.class)
24+
public class ImageResizerTest {
25+
26+
ImageResizer resizer;
27+
File imageFile;
28+
File externalDirectory;
29+
Bitmap originalImageBitmap;
30+
31+
@Before
32+
public void setUp() throws IOException {
33+
MockitoAnnotations.initMocks(this);
34+
imageFile = new File(getClass().getClassLoader().getResource("pngImage.png").getFile());
35+
originalImageBitmap = BitmapFactory.decodeFile(imageFile.getPath());
36+
TemporaryFolder temporaryFolder = new TemporaryFolder();
37+
temporaryFolder.create();
38+
externalDirectory = temporaryFolder.newFolder("image_picker_testing_path");
39+
resizer = new ImageResizer(externalDirectory, new ExifDataCopier());
40+
}
41+
42+
@Test
43+
public void onResizeImageIfNeeded_WhenQualityIsNull_ShoultNotResize_ReturnTheUnscaledFile() {
44+
String outoutFile = resizer.resizeImageIfNeeded(imageFile.getPath(), null, null, null);
45+
assertThat(outoutFile, equalTo(externalDirectory.getPath() + "/pngImage.png"));
46+
}
47+
48+
@Test
49+
public void onResizeImageIfNeeded_WhenQualityIsNotNull_ShoulResize_ReturnResizedFile() {
50+
String outoutFile = resizer.resizeImageIfNeeded(imageFile.getPath(), null, null, 50);
51+
assertThat(outoutFile, equalTo(externalDirectory.getPath() + "/scaled_pngImage.png"));
52+
}
53+
54+
@Test
55+
public void onResizeImageIfNeeded_WhenWidthIsNotNull_ShoulResize_ReturnResizedFile() {
56+
String outoutFile = resizer.resizeImageIfNeeded(imageFile.getPath(), 50.0, null, null);
57+
assertThat(outoutFile, equalTo(externalDirectory.getPath() + "/scaled_pngImage.png"));
58+
}
59+
60+
@Test
61+
public void onResizeImageIfNeeded_WhenHeightIsNotNull_ShoulResize_ReturnResizedFile() {
62+
String outoutFile = resizer.resizeImageIfNeeded(imageFile.getPath(), null, 50.0, null);
63+
assertThat(outoutFile, equalTo(externalDirectory.getPath() + "/scaled_pngImage.png"));
64+
}
65+
}
629 Bytes
Loading

packages/image_picker/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ authors:
55
- Flutter Team <[email protected]>
66
- Rhodes Davis Jr. <[email protected]>
77
homepage: https://github.com/flutter/plugins/tree/master/packages/image_picker
8-
version: 0.6.2
8+
version: 0.6.2+1
99

1010
flutter:
1111
plugin:

0 commit comments

Comments
 (0)