Compare commits

...

1 Commits

Author SHA1 Message Date
Alexander Ustinov
8270dbb227 Refactoring source code and adding old PRs 2017-04-13 15:08:31 +07:00
7 changed files with 451 additions and 47 deletions

View File

@ -32,6 +32,9 @@ android {
lintOptions {
abortOnError false
}
testOptions {
unitTests.returnDefaultValues = true
}
}
repositories {

View File

@ -31,6 +31,7 @@ import com.facebook.react.bridge.ReadableMap;
import com.imagepicker.media.ImageConfig;
import com.imagepicker.permissions.PermissionUtils;
import com.imagepicker.permissions.OnImagePickerPermissionsCallback;
import com.imagepicker.utils.MediaUtils;
import com.imagepicker.utils.MediaUtils.ReadExifResult;
import com.imagepicker.utils.RealPathUtil;
import com.imagepicker.utils.UI;
@ -247,7 +248,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
requestCode = REQUEST_LAUNCH_IMAGE_CAPTURE;
cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
final File original = createNewFile(reactContext, this.options, false);
final File original = createNewFile(reactContext);
imageConfig = imageConfig.withOriginalFile(original);
cameraCaptureURI = RealPathUtil.compatUriFromFile(reactContext, imageConfig.original);
@ -430,7 +431,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
}
else
{
imageConfig = getResizedImage(reactContext, this.options, imageConfig, initialWidth, initialHeight, requestCode);
imageConfig = getResizedImage(reactContext, imageConfig, initialWidth, initialHeight);
if (imageConfig.resized == null)
{
removeUselessFiles(requestCode, imageConfig);
@ -445,6 +446,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
updatedResultResponse(uri, imageConfig.resized.getAbsolutePath());
fileScan(reactContext, imageConfig.resized.getAbsolutePath());
MediaUtils.removeOriginIfNeeded(imageConfig, requestCode);
}
}

View File

@ -37,32 +37,15 @@ import static com.imagepicker.ImagePickerModule.REQUEST_LAUNCH_IMAGE_CAPTURE;
public class MediaUtils
{
public static @Nullable File createNewFile(@NonNull final Context reactContext,
@NonNull final ReadableMap options,
@NonNull final boolean forceLocal)
public static @NonNull File createNewFile(@NonNull final Context reactContext)
{
final String filename = new StringBuilder("image-")
.append(UUID.randomUUID().toString())
.append(".jpg")
.toString();
final File path = ReadableMapUtils.hasAndNotNullReadableMap(options, "storageOptions") && !forceLocal
? Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
: reactContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
final File path = reactContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File result = new File(path, filename);
try
{
path.mkdirs();
result.createNewFile();
}
catch (IOException e)
{
e.printStackTrace();
result = null;
}
path.mkdirs();
return result;
}
@ -70,18 +53,15 @@ public class MediaUtils
* Create a resized image to fulfill the maxWidth/maxHeight, quality and rotation values
*
* @param context
* @param options
* @param imageConfig
* @param initialWidth
* @param initialHeight
* @return updated ImageConfig
*/
public static @NonNull ImageConfig getResizedImage(@NonNull final Context context,
@NonNull final ReadableMap options,
@NonNull final ImageConfig imageConfig,
final int initialWidth,
final int initialHeight,
final int requestCode)
final int initialHeight)
{
BitmapFactory.Options imageOptions = new BitmapFactory.Options();
imageOptions.inScaled = false;
@ -145,8 +125,7 @@ public class MediaUtils
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
scaledPhoto.compress(Bitmap.CompressFormat.JPEG, result.quality, bytes);
final boolean forceLocal = requestCode == REQUEST_LAUNCH_IMAGE_CAPTURE;
final File resized = createNewFile(context, options, !forceLocal);
final File resized = createNewFile(context);
if (resized == null)
{
@ -237,14 +216,11 @@ public class MediaUtils
ExifInterface exif = new ExifInterface(imageConfig.original.getAbsolutePath());
// extract lat, long, and timestamp and add to the response
float[] latlng = new float[2];
exif.getLatLong(latlng);
float latitude = latlng[0];
float longitude = latlng[1];
if(latitude != 0f || longitude != 0f)
float[] latLng = new float[2];
if(exif.getLatLong(latLng))
{
responseHelper.putDouble("latitude", latitude);
responseHelper.putDouble("longitude", longitude);
responseHelper.putDouble("latitude", latLng[0]);
responseHelper.putDouble("longitude", latLng[1]);
}
final String timestamp = exif.getAttribute(ExifInterface.TAG_DATETIME);
@ -324,26 +300,35 @@ public class MediaUtils
* This is done via copy + deletion, because Android will throw an error
* if you try to move a file across mount points, e.g. to the SD card.
*/
private static void moveFile(@NonNull final File oldFile,
public static void moveFile(@NonNull final File oldFile,
@NonNull final File newFile) throws IOException
{
FileChannel oldChannel = null;
FileChannel newChannel = null;
FileInputStream in = new FileInputStream(oldFile);
FileOutputStream out = new FileOutputStream(newFile);
try
{
oldChannel = new FileInputStream(oldFile).getChannel();
newChannel = new FileOutputStream(newFile).getChannel();
oldChannel.transferTo(0, oldChannel.size(), newChannel);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
oldFile.delete();
}
finally
{
try
{
if (oldChannel != null) oldChannel.close();
if (newChannel != null) newChannel.close();
if (in != null)
{
in.close();
}
if (out != null)
{
out.close();
}
}
catch (IOException e)
{
@ -352,6 +337,16 @@ public class MediaUtils
}
}
public static void removeOriginIfNeeded(@NonNull final ImageConfig imageConfig,
final int requestCode)
{
if (requestCode != ImagePickerModule.REQUEST_LAUNCH_IMAGE_CAPTURE)
{
return;
}
imageConfig.original.delete();
}
public static class RolloutPhotoResult
{

View File

@ -15,9 +15,6 @@ public class SampleCallback implements Callback
@Override
public void invoke(Object... args)
{
System.out.println(args.length);
System.out.println(String.valueOf(args[0]));
System.out.println(args[0].getClass());
for (int i = 0; i < args.length; i++) {
if (lookingForError(args[i])) {
break;

View File

@ -0,0 +1,78 @@
package com.imagepicker.testing.mock;
import android.annotation.TargetApi;
import android.media.ExifInterface;
import android.os.Build;
import org.robolectric.annotation.Implements;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
/**
* Created by rusfearuth on 13.04.17.
*/
@Implements(ExifInterface.class)
public class MockExifInterface
extends ExifInterface
{
private HashMap<String, String> metadata = new HashMap<>();
public MockExifInterface(String filename) throws
IOException
{
super(filename);
}
@TargetApi(Build.VERSION_CODES.N)
public MockExifInterface(FileDescriptor fileDescriptor) throws
IOException
{
super(fileDescriptor);
}
@TargetApi(Build.VERSION_CODES.N)
public MockExifInterface(InputStream inputStream) throws
IOException
{
super(inputStream);
}
@Override
public void setAttribute(String attr, String value)
{
metadata.put(attr, value);
}
@Override
public String getAttribute(String attr)
{
return metadata.get(attr);
}
@Override
public int getAttributeInt(String attr, int defaultValue)
{
if (!metadata.containsKey(attr))
{
return defaultValue;
}
return Integer.valueOf(metadata.get(attr));
}
@Override
public boolean getLatLong(float[] latLong)
{
if (!metadata.containsKey(ExifInterface.TAG_GPS_LATITUDE) ||
!metadata.containsKey(ExifInterface.TAG_GPS_LONGITUDE))
{
return false;
}
latLong[0] = Float.valueOf(metadata.get(ExifInterface.TAG_GPS_LATITUDE));
latLong[1] = Float.valueOf(metadata.get(ExifInterface.TAG_GPS_LONGITUDE));
return true;
}
}

View File

@ -0,0 +1,296 @@
package com.imagepicker.testing.utils;
import android.app.Application;
import android.graphics.Bitmap;
import android.media.ExifInterface;
import android.os.Environment;
import android.support.annotation.NonNull;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.JavaOnlyArray;
import com.facebook.react.bridge.JavaOnlyMap;
import com.imagepicker.ImagePickerModule;
import com.imagepicker.ResponseHelper;
import com.imagepicker.media.ImageConfig;
import com.imagepicker.testing.mock.MockExifInterface;
import com.imagepicker.utils.MediaUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor;
import org.powermock.modules.junit4.rule.PowerMockRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowBitmap;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.powermock.api.mockito.PowerMockito.when;
import static org.powermock.api.mockito.PowerMockito.whenNew;
/**
* Created by rusfearuth on 12.04.17.
*/
@RunWith(RobolectricTestRunner.class)
@SuppressStaticInitializationFor("com.facebook.react.common.build.ReactBuildConfig")
@PrepareForTest({ Arguments.class, MediaUtils.class })
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
@Config(manifest = Config.NONE)
public class MediaUtilsTest
{
@Rule
public PowerMockRule rule = new PowerMockRule();
private ExifInterface exifMock;
@Before
public void setUp() throws Exception
{
PowerMockito.mockStatic(Arguments.class);
when(Arguments.createArray()).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return new JavaOnlyArray();
}
});
when(Arguments.createMap()).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return new JavaOnlyMap();
}
});
exifMock = new MockExifInterface("");
whenNew(ExifInterface.class).withAnyArguments().thenReturn(exifMock);
}
@Test
public void testCreatingFile() throws IOException
{
Application application = RuntimeEnvironment.application;
File newFile = MediaUtils.createNewFile(application);
assertNotNull("File was created", newFile);
newFile.createNewFile();
assertTrue("File exists", newFile.exists());
}
@Test
public void testGetResizedFile()
{
Application application = RuntimeEnvironment.application;
Bitmap original = ShadowBitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8);
File file = MediaUtils.createNewFile(application);
assertTrue("Image was created", saveToFile(file, original));
ImageConfig config = new ImageConfig(file, null, 50, 50, 100, 0, false);
ImageConfig resizedConfig = MediaUtils.getResizedImage(application, config, 10, 10);
assertNotNull("Image was resized", resizedConfig.resized);
assertNotSame(
"Original and resized files aren't the same",
config.original.getAbsolutePath(),
resizedConfig.resized.getAbsolutePath()
);
assertNotSame(
"Original and resized files have different size",
config.original.length(),
resizedConfig.resized.length()
);
}
@Test
public void testRemoveUselessFiles()
{
Application application = RuntimeEnvironment.application;
Bitmap original = ShadowBitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8);
File originalFile = MediaUtils.createNewFile(application);
assertTrue("Original file was created", saveToFile(originalFile, original));
Bitmap resized = ShadowBitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8);
File resizedFile = MediaUtils.createNewFile(application);
assertTrue("Resized file was created", saveToFile(resizedFile, resized));
assertTrue("Original file exists", originalFile.exists());
assertTrue("Resized file exists", resizedFile.exists());
ImageConfig config = new ImageConfig(originalFile, resizedFile, 100, 100, 100, 0, false);
MediaUtils.removeUselessFiles(ImagePickerModule.REQUEST_LAUNCH_IMAGE_LIBRARY, config);
assertTrue("Original file exists, because requestCode is invalid", originalFile.exists());
assertTrue("Resized file exists, because requestCode is invalid", resizedFile.exists());
MediaUtils.removeUselessFiles(ImagePickerModule.REQUEST_LAUNCH_IMAGE_CAPTURE, config);
assertFalse("Original file was removed", config.original.exists());
assertFalse("Resized file was removed", config.resized.exists());
}
@Test
public void testReadExifInterface() throws Exception
{
final SimpleDateFormat exifDatetimeFormat = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
final DateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
final long currentTimestamp = System.currentTimeMillis();
final String fileDateTime = exifDatetimeFormat.format(new Date(currentTimestamp));
final String dateTimeForCheckout = isoFormat.format(new Date(currentTimestamp));
final Application application = RuntimeEnvironment.application;
final Bitmap original = ShadowBitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8);
final File originalFile = MediaUtils.createNewFile(application);
final ImageConfig imageConfig = new ImageConfig(originalFile, null, 100, 100, 100, 0, false);
assertTrue("Original file was created", saveToFile(originalFile, original));
ExifInterface exif = new ExifInterface(originalFile.getAbsolutePath());
exif.setAttribute(ExifInterface.TAG_ORIENTATION, String.valueOf(ExifInterface.ORIENTATION_ROTATE_270));
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, String.valueOf(1.0f));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, String.valueOf(-1.0f));
exif.setAttribute(ExifInterface.TAG_DATETIME, fileDateTime);
exif.saveAttributes();
ResponseHelper helper = new ResponseHelper();
helper.cleanResponse();
MediaUtils.ReadExifResult result = MediaUtils.readExifInterface(helper, imageConfig);
assertNull("Exif interface was read", result.error);
assertNotNull("Orientation was read", helper.getResponse().getInt("originalRotation"));
assertEquals("Orientation value is", helper.getResponse().getInt("originalRotation"), 270);
assertNotNull("Latitude was read", helper.getResponse().getDouble("latitude"));
assertEquals("Latitude value is", Math.floor(helper.getResponse().getDouble("latitude")), Math.floor(1.0f));
assertNotNull("Longitude was read", helper.getResponse().getDouble("longitude"));
assertEquals("Longitude value is", Math.floor(helper.getResponse().getDouble("longitude")), Math.floor(-1.0f));
assertNotNull("DateTime was read", helper.getResponse().getString("timestamp"));
assertEquals("DateTIme value is", helper.getResponse().getString("timestamp"), dateTimeForCheckout);
assertNotNull("Vertical flag was generated", helper.getResponse().getBoolean("isVertical"));
assertFalse("Is image vertical", helper.getResponse().getBoolean("isVertical"));
}
@Test
public void testRolloutPhotoFromCamera()
{
Application application = RuntimeEnvironment.application;
Bitmap original = ShadowBitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8);
File originalFile = MediaUtils.createNewFile(application);
assertTrue("Original file was created", saveToFile(originalFile, original));
ImageConfig imageConfig = new ImageConfig(originalFile, null, 100, 100, 100, 0, true);
MediaUtils.RolloutPhotoResult result = MediaUtils.rolloutPhotoFromCamera(imageConfig);
assertNull("Rollout of original file has done", result.error);
assertNotSame("Original files are different", imageConfig.original.getAbsolutePath(), result.imageConfig.original.getAbsolutePath());
assertEquals("Original file names are the same", imageConfig.original.getName(), result.imageConfig.original.getName());
assertTrue("Original file was moved", result.imageConfig.original.getAbsolutePath().toLowerCase().contains("/dcim/"));
assertFalse("Original file was moved", imageConfig.original.exists());
Bitmap resized = ShadowBitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8);
File resizedFile = MediaUtils.createNewFile(application);
assertTrue("Resized file was created", saveToFile(resizedFile, resized));
imageConfig = imageConfig.withResizedFile(resizedFile);
result = MediaUtils.rolloutPhotoFromCamera(imageConfig);
assertNull("Rollout of resized file has done", result.error);
assertNotSame("Different resized files", imageConfig.resized.getAbsolutePath(), result.imageConfig.resized.getAbsolutePath());
assertEquals("Resized file names are the same", imageConfig.resized.getName(), result.imageConfig.resized.getName());
assertTrue("Resized file was moved", result.imageConfig.resized.getAbsolutePath().toLowerCase().contains("/dcim/"));
assertFalse("Resized file was moved", imageConfig.original.exists());
}
@Test
public void testMoveFile() throws IOException
{
Application application = RuntimeEnvironment.application;
File oldFile = new File(application.getCacheDir(), "original.txt");
oldFile.createNewFile();
File targetDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
File newFile = new File(targetDir, oldFile.getName());
MediaUtils.moveFile(oldFile, newFile);
assertFalse("Old file has left", oldFile.exists());
assertTrue("New file was copied", newFile.exists());
}
@Test
public void testRemoveOriginIfNeeded() throws IOException
{
Application application = RuntimeEnvironment.application;
Bitmap original = ShadowBitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8);
File originalFile = MediaUtils.createNewFile(application);
assertTrue("Original file was created", saveToFile(originalFile, original));
assertTrue("Original file exists", originalFile.exists());
ImageConfig imageConfig = new ImageConfig(originalFile, null, 100, 100, 100, 0, false);
MediaUtils.removeOriginIfNeeded(imageConfig, ImagePickerModule.REQUEST_LAUNCH_IMAGE_LIBRARY);
assertTrue("Original file wasn't removed on REQUEST_LAUNCH_IMAGE_LIBRARY", originalFile.exists());
MediaUtils.removeOriginIfNeeded(imageConfig, ImagePickerModule.REQUEST_LAUNCH_IMAGE_CAPTURE);
assertFalse("Original file was removed on REQUEST_LAUNCH_IMAGE_CAPTURE", originalFile.exists());
}
private boolean saveToFile(@NonNull final File imageFile,
@NonNull final Bitmap bitmap)
{
boolean result = false;
FileOutputStream fos = null;
try
{
fos = new FileOutputStream(imageFile);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
result = true;
}
catch (Exception e)
{
}
finally
{
if (fos != null)
{
try
{
fos.close();
}
catch (IOException e)
{
result = false;
e.printStackTrace();
}
}
}
return result;
}
}

View File

@ -0,0 +1,33 @@
package com.imagepicker.testing.utils;
import android.net.Uri;
import com.imagepicker.utils.MediaUtils;
import com.imagepicker.utils.RealPathUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.internal.SdkEnvironment;
import java.io.File;
import static junit.framework.Assert.assertNotNull;
/**
* Created by rusfearuth on 13.04.17.
*/
@RunWith(RobolectricTestRunner.class)
public class RealPathUtilTest
{
@Test
public void testCompatUriFromFile()
{
File newFile = MediaUtils.createNewFile(RuntimeEnvironment.application);
Uri uri = RealPathUtil.compatUriFromFile(RuntimeEnvironment.application, newFile);
assertNotNull("Uri was created", uri);
}
}