755 lines
24 KiB
Java
755 lines
24 KiB
Java
package com.imagepicker;
|
|
|
|
import android.Manifest;
|
|
import android.app.Activity;
|
|
import android.content.ActivityNotFoundException;
|
|
import android.content.Context;
|
|
import android.content.DialogInterface;
|
|
import android.content.Intent;
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.BitmapFactory;
|
|
import android.graphics.BitmapFactory.Options;
|
|
import android.graphics.Matrix;
|
|
import android.graphics.drawable.ColorDrawable;
|
|
import android.media.ExifInterface;
|
|
import android.net.Uri;
|
|
import android.os.Build;
|
|
import android.os.Environment;
|
|
import android.provider.MediaStore;
|
|
import android.support.annotation.NonNull;
|
|
import android.support.v4.app.ActivityCompat;
|
|
import android.support.v4.content.FileProvider;
|
|
import android.app.AlertDialog;
|
|
import android.util.Base64;
|
|
import android.util.Log;
|
|
import android.widget.ArrayAdapter;
|
|
import android.webkit.MimeTypeMap;
|
|
import android.content.pm.PackageManager;
|
|
import android.media.MediaScannerConnection;
|
|
|
|
import com.facebook.react.bridge.ActivityEventListener;
|
|
import com.facebook.react.bridge.Arguments;
|
|
import com.facebook.react.bridge.Callback;
|
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
import com.facebook.react.bridge.ReactMethod;
|
|
import com.facebook.react.bridge.ReadableMap;
|
|
import com.facebook.react.bridge.ReadableArray;
|
|
import com.facebook.react.bridge.WritableMap;
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.net.MalformedURLException;
|
|
import java.net.URL;
|
|
import java.text.DateFormat;
|
|
import java.text.ParsePosition;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.ArrayList;
|
|
import java.util.Date;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.TimeZone;
|
|
import java.util.UUID;
|
|
|
|
public class ImagePickerModule extends ReactContextBaseJavaModule implements ActivityEventListener {
|
|
|
|
static final int REQUEST_LAUNCH_IMAGE_CAPTURE = 13001;
|
|
static final int REQUEST_LAUNCH_IMAGE_LIBRARY = 13002;
|
|
static final int REQUEST_LAUNCH_VIDEO_LIBRARY = 13003;
|
|
static final int REQUEST_LAUNCH_VIDEO_CAPTURE = 13004;
|
|
|
|
private final ReactApplicationContext mReactContext;
|
|
|
|
private Uri mCameraCaptureURI;
|
|
private Callback mCallback;
|
|
private Boolean noData = false;
|
|
private Boolean pickVideo = false;
|
|
private int maxWidth = 0;
|
|
private int maxHeight = 0;
|
|
private int quality = 100;
|
|
private int rotation = 0;
|
|
private int videoQuality = 1;
|
|
private int videoDurationLimit = 0;
|
|
WritableMap response;
|
|
|
|
public ImagePickerModule(ReactApplicationContext reactContext) {
|
|
super(reactContext);
|
|
|
|
mReactContext = reactContext;
|
|
|
|
reactContext.addActivityEventListener(this);
|
|
}
|
|
|
|
@Override
|
|
public String getName() {
|
|
return "ImagePickerManager";
|
|
}
|
|
|
|
@ReactMethod
|
|
public void showImagePicker(final ReadableMap options, final Callback callback) {
|
|
Activity currentActivity = getCurrentActivity();
|
|
|
|
if (currentActivity == null) {
|
|
response = Arguments.createMap();
|
|
response.putString("error", "can't find current Activity");
|
|
callback.invoke(response);
|
|
return;
|
|
}
|
|
|
|
final List<String> titles = new ArrayList<String>();
|
|
final List<String> actions = new ArrayList<String>();
|
|
|
|
if (options.hasKey("takePhotoButtonTitle")
|
|
&& options.getString("takePhotoButtonTitle") != null
|
|
&& !options.getString("takePhotoButtonTitle").isEmpty()
|
|
&& isCameraAvailable()) {
|
|
titles.add(options.getString("takePhotoButtonTitle"));
|
|
actions.add("photo");
|
|
}
|
|
if (options.hasKey("chooseFromLibraryButtonTitle")
|
|
&& options.getString("chooseFromLibraryButtonTitle") != null
|
|
&& !options.getString("chooseFromLibraryButtonTitle").isEmpty()) {
|
|
titles.add(options.getString("chooseFromLibraryButtonTitle"));
|
|
actions.add("library");
|
|
}
|
|
if (options.hasKey("customButtons")) {
|
|
ReadableArray customButtons = options.getArray("customButtons");
|
|
for (int i = 0; i < customButtons.size(); i++) {
|
|
ReadableMap button = customButtons.getMap(i);
|
|
int currentIndex = titles.size();
|
|
titles.add(currentIndex, button.getString("title"));
|
|
actions.add(currentIndex, button.getString("name"));
|
|
}
|
|
}
|
|
if (options.hasKey("cancelButtonTitle")
|
|
&& options.getString("cancelButtonTitle") != null
|
|
&& !options.getString("cancelButtonTitle").isEmpty()) {
|
|
titles.add(options.getString("cancelButtonTitle"));
|
|
actions.add("cancel");
|
|
}
|
|
|
|
ArrayAdapter<String> adapter = new ArrayAdapter<String>(currentActivity, android.R.layout.select_dialog_item, titles);
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(currentActivity, android.R.style.Theme_Holo_Light_Dialog);
|
|
if (options.hasKey("title") && options.getString("title") != null && !options.getString("title").isEmpty()) {
|
|
builder.setTitle(options.getString("title"));
|
|
}
|
|
|
|
builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
|
|
public void onClick(DialogInterface dialog, int index) {
|
|
String action = actions.get(index);
|
|
response = Arguments.createMap();
|
|
|
|
switch (action) {
|
|
case "photo":
|
|
launchCamera(options, callback);
|
|
break;
|
|
case "library":
|
|
launchImageLibrary(options, callback);
|
|
break;
|
|
case "cancel":
|
|
response.putBoolean("didCancel", true);
|
|
callback.invoke(response);
|
|
break;
|
|
default: // custom button
|
|
response.putString("customButton", action);
|
|
callback.invoke(response);
|
|
}
|
|
}
|
|
});
|
|
|
|
final AlertDialog dialog = builder.create();
|
|
/**
|
|
* override onCancel method to callback cancel in case of a touch outside of
|
|
* the dialog or the BACK key pressed
|
|
*/
|
|
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
|
@Override
|
|
public void onCancel(DialogInterface dialog) {
|
|
response = Arguments.createMap();
|
|
dialog.dismiss();
|
|
response.putBoolean("didCancel", true);
|
|
callback.invoke(response);
|
|
}
|
|
});
|
|
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
|
|
dialog.show();
|
|
}
|
|
|
|
// NOTE: Currently not reentrant / doesn't support concurrent requests
|
|
@ReactMethod
|
|
public void launchCamera(final ReadableMap options, final Callback callback) {
|
|
response = Arguments.createMap();
|
|
|
|
if (!isCameraAvailable()) {
|
|
response.putString("error", "Camera not available");
|
|
callback.invoke(response);
|
|
return;
|
|
}
|
|
|
|
Activity currentActivity = getCurrentActivity();
|
|
if (currentActivity == null) {
|
|
response.putString("error", "can't find current Activity");
|
|
callback.invoke(response);
|
|
return;
|
|
}
|
|
|
|
if (!permissionsCheck(currentActivity)) {
|
|
return;
|
|
}
|
|
|
|
parseOptions(options);
|
|
|
|
int requestCode;
|
|
Intent cameraIntent;
|
|
if (pickVideo) {
|
|
requestCode = REQUEST_LAUNCH_VIDEO_CAPTURE;
|
|
cameraIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
|
|
cameraIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, videoQuality);
|
|
if (videoDurationLimit > 0) {
|
|
cameraIntent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, videoDurationLimit);
|
|
}
|
|
} else {
|
|
requestCode = REQUEST_LAUNCH_IMAGE_CAPTURE;
|
|
cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
|
|
|
// we create a tmp file to save the result
|
|
File imageFile = createNewFile();
|
|
mCameraCaptureURI = compatUriFromFile(mReactContext, imageFile);
|
|
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCameraCaptureURI);
|
|
}
|
|
|
|
if (cameraIntent.resolveActivity(mReactContext.getPackageManager()) == null) {
|
|
response.putString("error", "Cannot launch camera");
|
|
callback.invoke(response);
|
|
return;
|
|
}
|
|
|
|
mCallback = callback;
|
|
|
|
try {
|
|
currentActivity.startActivityForResult(cameraIntent, requestCode);
|
|
} catch (ActivityNotFoundException e) {
|
|
e.printStackTrace();
|
|
response = Arguments.createMap();
|
|
response.putString("error", "Cannot launch camera");
|
|
callback.invoke(response);
|
|
}
|
|
}
|
|
|
|
// NOTE: Currently not reentrant / doesn't support concurrent requests
|
|
@ReactMethod
|
|
public void launchImageLibrary(final ReadableMap options, final Callback callback) {
|
|
response = Arguments.createMap();
|
|
|
|
Activity currentActivity = getCurrentActivity();
|
|
if (currentActivity == null) {
|
|
response.putString("error", "can't find current Activity");
|
|
callback.invoke(response);
|
|
return;
|
|
}
|
|
|
|
if (!permissionsCheck(currentActivity)) {
|
|
return;
|
|
}
|
|
|
|
parseOptions(options);
|
|
|
|
int requestCode;
|
|
Intent libraryIntent;
|
|
if (pickVideo) {
|
|
requestCode = REQUEST_LAUNCH_VIDEO_LIBRARY;
|
|
libraryIntent = new Intent(Intent.ACTION_PICK);
|
|
libraryIntent.setType("video/*");
|
|
} else {
|
|
requestCode = REQUEST_LAUNCH_IMAGE_LIBRARY;
|
|
libraryIntent = new Intent(Intent.ACTION_PICK,
|
|
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
|
}
|
|
|
|
if (libraryIntent.resolveActivity(mReactContext.getPackageManager()) == null) {
|
|
response.putString("error", "Cannot launch photo library");
|
|
callback.invoke(response);
|
|
return;
|
|
}
|
|
|
|
mCallback = callback;
|
|
|
|
try {
|
|
currentActivity.startActivityForResult(libraryIntent, requestCode);
|
|
} catch (ActivityNotFoundException e) {
|
|
e.printStackTrace();
|
|
response.putString("error", "Cannot launch photo library");
|
|
callback.invoke(response);
|
|
}
|
|
}
|
|
|
|
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
|
//robustness code
|
|
if (mCallback == null || (mCameraCaptureURI == null && requestCode == REQUEST_LAUNCH_IMAGE_CAPTURE)
|
|
|| (requestCode != REQUEST_LAUNCH_IMAGE_CAPTURE && requestCode != REQUEST_LAUNCH_IMAGE_LIBRARY
|
|
&& requestCode != REQUEST_LAUNCH_VIDEO_LIBRARY && requestCode != REQUEST_LAUNCH_VIDEO_CAPTURE)) {
|
|
return;
|
|
}
|
|
|
|
response = Arguments.createMap();
|
|
|
|
// user cancel
|
|
if (resultCode != Activity.RESULT_OK) {
|
|
response.putBoolean("didCancel", true);
|
|
mCallback.invoke(response);
|
|
mCallback = null;
|
|
return;
|
|
}
|
|
|
|
Uri uri;
|
|
switch (requestCode) {
|
|
case REQUEST_LAUNCH_IMAGE_CAPTURE:
|
|
uri = mCameraCaptureURI;
|
|
this.fileScan(uri.getPath());
|
|
break;
|
|
case REQUEST_LAUNCH_IMAGE_LIBRARY:
|
|
uri = data.getData();
|
|
break;
|
|
case REQUEST_LAUNCH_VIDEO_LIBRARY:
|
|
response.putString("uri", data.getData().toString());
|
|
response.putString("path", getRealPathFromURI(data.getData()));
|
|
mCallback.invoke(response);
|
|
mCallback = null;
|
|
return;
|
|
case REQUEST_LAUNCH_VIDEO_CAPTURE:
|
|
response.putString("uri", data.getData().toString());
|
|
response.putString("path", getRealPathFromURI(data.getData()));
|
|
this.fileScan(response.getString("path"));
|
|
mCallback.invoke(response);
|
|
mCallback = null;
|
|
return;
|
|
default:
|
|
uri = null;
|
|
}
|
|
|
|
String realPath = getRealPathFromURI(uri);
|
|
boolean isUrl = false;
|
|
|
|
if (realPath != null) {
|
|
try {
|
|
URL url = new URL(realPath);
|
|
isUrl = true;
|
|
} catch (MalformedURLException e) {
|
|
// not a url
|
|
}
|
|
}
|
|
|
|
// image isn't in memory cache
|
|
if (realPath == null || isUrl) {
|
|
try {
|
|
File file = createFileFromURI(uri);
|
|
realPath = file.getAbsolutePath();
|
|
uri = Uri.fromFile(file);
|
|
} catch (Exception e) {
|
|
// image not in cache
|
|
response.putString("error", "Could not read photo");
|
|
response.putString("uri", uri.toString());
|
|
mCallback.invoke(response);
|
|
mCallback = null;
|
|
return;
|
|
}
|
|
}
|
|
|
|
int currentRotation = 0;
|
|
try {
|
|
ExifInterface exif = new ExifInterface(realPath);
|
|
|
|
// 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) {
|
|
response.putDouble("latitude", latitude);
|
|
response.putDouble("longitude", longitude);
|
|
}
|
|
|
|
String timestamp = exif.getAttribute(ExifInterface.TAG_DATETIME);
|
|
SimpleDateFormat exifDatetimeFormat = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
|
|
|
|
DateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
|
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
|
|
|
try {
|
|
String isoFormatString = isoFormat.format(exifDatetimeFormat.parse(timestamp)) + "Z";
|
|
response.putString("timestamp", isoFormatString);
|
|
} catch (Exception e) {}
|
|
|
|
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
|
|
boolean isVertical = true;
|
|
switch (orientation) {
|
|
case ExifInterface.ORIENTATION_ROTATE_270:
|
|
isVertical = false;
|
|
currentRotation = 270;
|
|
break;
|
|
case ExifInterface.ORIENTATION_ROTATE_90:
|
|
isVertical = false;
|
|
currentRotation = 90;
|
|
break;
|
|
case ExifInterface.ORIENTATION_ROTATE_180:
|
|
currentRotation = 180;
|
|
break;
|
|
}
|
|
response.putInt("originalRotation", currentRotation);
|
|
response.putBoolean("isVertical", isVertical);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
response.putString("error", e.getMessage());
|
|
mCallback.invoke(response);
|
|
mCallback = null;
|
|
return;
|
|
}
|
|
|
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
|
options.inJustDecodeBounds = true;
|
|
BitmapFactory.decodeFile(realPath, options);
|
|
int initialWidth = options.outWidth;
|
|
int initialHeight = options.outHeight;
|
|
|
|
// don't create a new file if contraint are respected
|
|
if (((initialWidth < maxWidth && maxWidth > 0) || maxWidth == 0) && ((initialHeight < maxHeight && maxHeight > 0) || maxHeight == 0) && quality == 100 && (rotation == 0 || currentRotation == rotation)) {
|
|
response.putInt("width", initialWidth);
|
|
response.putInt("height", initialHeight);
|
|
} else {
|
|
File resized = getResizedImage(realPath, initialWidth, initialHeight);
|
|
if (resized == null) {
|
|
response.putString("error", "Can't resize the image");
|
|
} else {
|
|
realPath = resized.getAbsolutePath();
|
|
uri = Uri.fromFile(resized);
|
|
BitmapFactory.decodeFile(realPath, options);
|
|
response.putInt("width", options.outWidth);
|
|
response.putInt("height", options.outHeight);
|
|
}
|
|
}
|
|
|
|
response.putString("uri", uri.toString());
|
|
response.putString("path", realPath);
|
|
|
|
if (!noData) {
|
|
response.putString("data", getBase64StringFromFile(realPath));
|
|
}
|
|
|
|
putExtraFileInfo(realPath, response);
|
|
|
|
mCallback.invoke(response);
|
|
mCallback = null;
|
|
}
|
|
|
|
/**
|
|
* Returns number of milliseconds since Jan. 1, 1970, midnight local time.
|
|
* Returns -1 if the date time information if not available.
|
|
* copied from ExifInterface.java
|
|
* @hide
|
|
*/
|
|
private static long parseTimestamp(String dateTimeString, String subSecs) {
|
|
if (dateTimeString == null) return -1;
|
|
|
|
SimpleDateFormat sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", Locale.getDefault());
|
|
sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
|
|
ParsePosition pos = new ParsePosition(0);
|
|
try {
|
|
// The exif field is in local time. Parsing it as if it is UTC will yield time
|
|
// since 1/1/1970 local time
|
|
Date datetime = sFormatter.parse(dateTimeString, pos);
|
|
if (datetime == null) return -1;
|
|
long msecs = datetime.getTime();
|
|
|
|
if (subSecs != null) {
|
|
try {
|
|
long sub = Long.valueOf(subSecs);
|
|
while (sub > 1000) {
|
|
sub /= 10;
|
|
}
|
|
msecs += sub;
|
|
} catch (NumberFormatException e) {
|
|
//expected
|
|
}
|
|
}
|
|
return msecs;
|
|
} catch (IllegalArgumentException ex) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
private boolean permissionsCheck(Activity activity) {
|
|
int writePermission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
|
int cameraPermission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.CAMERA);
|
|
if (writePermission != PackageManager.PERMISSION_GRANTED || cameraPermission != PackageManager.PERMISSION_GRANTED) {
|
|
String[] PERMISSIONS = {
|
|
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
|
Manifest.permission.CAMERA
|
|
};
|
|
ActivityCompat.requestPermissions(activity, PERMISSIONS, 1);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private boolean isCameraAvailable() {
|
|
return mReactContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)
|
|
|| mReactContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
|
|
}
|
|
|
|
private @NonNull String getRealPathFromURI(@NonNull final Uri uri) {
|
|
return RealPathUtil.getRealPathFromURI(mReactContext, uri);
|
|
}
|
|
|
|
/**
|
|
* Create a file from uri to allow image picking of image in disk cache
|
|
* (Exemple: facebook image, google image etc..)
|
|
*
|
|
* @doc =>
|
|
* https://github.com/nostra13/Android-Universal-Image-Loader#load--display-task-flow
|
|
*
|
|
* @param uri
|
|
* @return File
|
|
* @throws Exception
|
|
*/
|
|
private File createFileFromURI(Uri uri) throws Exception {
|
|
File file = new File(mReactContext.getExternalCacheDir(), "photo-" + uri.getLastPathSegment());
|
|
InputStream input = mReactContext.getContentResolver().openInputStream(uri);
|
|
OutputStream output = new FileOutputStream(file);
|
|
|
|
try {
|
|
byte[] buffer = new byte[4 * 1024];
|
|
int read;
|
|
while ((read = input.read(buffer)) != -1) {
|
|
output.write(buffer, 0, read);
|
|
}
|
|
output.flush();
|
|
} finally {
|
|
output.close();
|
|
input.close();
|
|
}
|
|
|
|
return file;
|
|
}
|
|
|
|
private String getBase64StringFromFile(String absoluteFilePath) {
|
|
InputStream inputStream = null;
|
|
try {
|
|
inputStream = new FileInputStream(new File(absoluteFilePath));
|
|
} catch (FileNotFoundException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
byte[] bytes;
|
|
byte[] buffer = new byte[8192];
|
|
int bytesRead;
|
|
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
|
try {
|
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
|
output.write(buffer, 0, bytesRead);
|
|
}
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
bytes = output.toByteArray();
|
|
return Base64.encodeToString(bytes, Base64.NO_WRAP);
|
|
}
|
|
|
|
/**
|
|
* Create a resized image to fulfill the maxWidth/maxHeight, quality and rotation values
|
|
*
|
|
* @param realPath
|
|
* @param initialWidth
|
|
* @param initialHeight
|
|
* @return resized file
|
|
*/
|
|
private File getResizedImage(final String realPath, final int initialWidth, final int initialHeight) {
|
|
Options options = new BitmapFactory.Options();
|
|
options.inScaled = false;
|
|
Bitmap photo = BitmapFactory.decodeFile(realPath, options);
|
|
|
|
if (photo == null) {
|
|
return null;
|
|
}
|
|
|
|
Bitmap scaledphoto = null;
|
|
if (maxWidth == 0 || maxWidth > initialWidth) {
|
|
maxWidth = initialWidth;
|
|
}
|
|
if (maxHeight == 0 || maxWidth > initialHeight) {
|
|
maxHeight = initialHeight;
|
|
}
|
|
double widthRatio = (double) maxWidth / initialWidth;
|
|
double heightRatio = (double) maxHeight / initialHeight;
|
|
|
|
double ratio = (widthRatio < heightRatio)
|
|
? widthRatio
|
|
: heightRatio;
|
|
|
|
Matrix matrix = new Matrix();
|
|
matrix.postRotate(rotation);
|
|
matrix.postScale((float) ratio, (float) ratio);
|
|
|
|
ExifInterface exif;
|
|
try {
|
|
exif = new ExifInterface(realPath);
|
|
|
|
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
|
|
|
|
if (orientation == 6) {
|
|
matrix.postRotate(90);
|
|
} else if (orientation == 3) {
|
|
matrix.postRotate(180);
|
|
} else if (orientation == 8) {
|
|
matrix.postRotate(270);
|
|
}
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
scaledphoto = Bitmap.createBitmap(photo, 0, 0, photo.getWidth(), photo.getHeight(), matrix, true);
|
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
|
scaledphoto.compress(Bitmap.CompressFormat.JPEG, quality, bytes);
|
|
|
|
File f = createNewFile();
|
|
FileOutputStream fo;
|
|
try {
|
|
fo = new FileOutputStream(f);
|
|
try {
|
|
fo.write(bytes.toByteArray());
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
} catch (FileNotFoundException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
// recycle to avoid java.lang.OutOfMemoryError
|
|
if (photo != null) {
|
|
scaledphoto.recycle();
|
|
photo.recycle();
|
|
scaledphoto = null;
|
|
photo = null;
|
|
}
|
|
return f;
|
|
}
|
|
|
|
/**
|
|
* Create a new file
|
|
*
|
|
* @return an empty file
|
|
*/
|
|
private File createNewFile() {
|
|
String filename = new StringBuilder("image-")
|
|
.append(UUID.randomUUID().toString())
|
|
.append(".jpg")
|
|
.toString();
|
|
File path = mReactContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
|
|
|
|
File f = new File(path, filename);
|
|
try {
|
|
path.mkdirs();
|
|
f.createNewFile();
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
return f;
|
|
}
|
|
|
|
private void putExtraFileInfo(final String path, WritableMap response) {
|
|
// size && filename
|
|
try {
|
|
File f = new File(path);
|
|
response.putDouble("fileSize", f.length());
|
|
response.putString("fileName", f.getName());
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
// type
|
|
String extension = MimeTypeMap.getFileExtensionFromUrl(path);
|
|
if (extension != null) {
|
|
response.putString("type", MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension));
|
|
}
|
|
}
|
|
|
|
private void parseOptions(final ReadableMap options) {
|
|
noData = false;
|
|
if (options.hasKey("noData")) {
|
|
noData = options.getBoolean("noData");
|
|
}
|
|
maxWidth = 0;
|
|
if (options.hasKey("maxWidth")) {
|
|
maxWidth = options.getInt("maxWidth");
|
|
}
|
|
maxHeight = 0;
|
|
if (options.hasKey("maxHeight")) {
|
|
maxHeight = options.getInt("maxHeight");
|
|
}
|
|
quality = 100;
|
|
if (options.hasKey("quality")) {
|
|
quality = (int) (options.getDouble("quality") * 100);
|
|
}
|
|
rotation = 0;
|
|
if (options.hasKey("rotation")) {
|
|
rotation = options.getInt("rotation");
|
|
}
|
|
pickVideo = false;
|
|
if (options.hasKey("mediaType") && options.getString("mediaType").equals("video")) {
|
|
pickVideo = true;
|
|
}
|
|
videoQuality = 1;
|
|
if (options.hasKey("videoQuality") && options.getString("videoQuality").equals("low")) {
|
|
videoQuality = 0;
|
|
}
|
|
videoDurationLimit = 0;
|
|
if (options.hasKey("durationLimit")) {
|
|
videoDurationLimit = options.getInt("durationLimit");
|
|
}
|
|
}
|
|
|
|
public void fileScan(String path){
|
|
MediaScannerConnection.scanFile(mReactContext,
|
|
new String[] { path }, null,
|
|
new MediaScannerConnection.OnScanCompletedListener() {
|
|
|
|
public void onScanCompleted(String path, Uri uri) {
|
|
Log.i("TAG", "Finished scanning " + path);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Required for ActivityEventListener
|
|
public void onNewIntent(Intent intent) { }
|
|
|
|
// public void onActivityResult(int requestCode,
|
|
// int resultCode,
|
|
// Intent data)
|
|
// {
|
|
//
|
|
// }
|
|
|
|
private static Uri compatUriFromFile(@NonNull final Context context,
|
|
@NonNull final File file)
|
|
{
|
|
Uri result = null;
|
|
if (Build.VERSION.SDK_INT < 19)
|
|
{
|
|
result = Uri.fromFile(file);
|
|
}
|
|
else
|
|
{
|
|
final String packageName = context.getApplicationContext().getPackageName();
|
|
final String authority = new StringBuilder(packageName).append(".provider").toString();
|
|
result = FileProvider.getUriForFile(context, authority, file);
|
|
}
|
|
return result;
|
|
}
|
|
}
|