diff --git a/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModule.java b/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModule.java index 48fb5c1..3872d91 100644 --- a/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModule.java +++ b/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModule.java @@ -3,6 +3,7 @@ package com.imagepicker; import android.Manifest; import android.app.Activity; import android.content.ActivityNotFoundException; +import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -49,6 +50,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.lang.ref.WeakReference; import java.util.List; +import java.util.ArrayList; import com.facebook.react.modules.core.PermissionListener; import com.facebook.react.modules.core.PermissionAwareActivity; @@ -69,6 +71,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule public static final int REQUEST_LAUNCH_IMAGE_LIBRARY = 13002; public static final int REQUEST_LAUNCH_VIDEO_LIBRARY = 13003; public static final int REQUEST_LAUNCH_VIDEO_CAPTURE = 13004; + public static final int REQUEST_LAUNCH_MIXED_CAPTURE = 13005; public static final int REQUEST_PERMISSIONS_FOR_CAMERA = 14001; public static final int REQUEST_PERMISSIONS_FOR_LIBRARY = 14002; @@ -266,26 +269,24 @@ public class ImagePickerModule extends ReactContextBaseJavaModule cameraIntent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, videoDurationLimit); } } + else if (pickBoth) { + Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + this.setImageCaptureUri(takePictureIntent); + Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); + cameraIntent = new Intent(Intent.ACTION_CHOOSER); + Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT); + Intent[] intentArray = new Intent[]{takePictureIntent,takeVideoIntent}; + cameraIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent); + cameraIntent.putExtra(Intent.EXTRA_TITLE, "Choose an action"); + cameraIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray); + requestCode = REQUEST_LAUNCH_MIXED_CAPTURE; + } else { requestCode = REQUEST_LAUNCH_IMAGE_CAPTURE; cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - final File original = createNewFile(reactContext, this.options, false); - imageConfig = imageConfig.withOriginalFile(original); - - if (imageConfig.original != null) { - cameraCaptureURI = RealPathUtil.compatUriFromFile(reactContext, imageConfig.original); - }else { - responseHelper.invokeError(callback, "Couldn't get file path for photo"); - return; - } - if (cameraCaptureURI == null) - { - responseHelper.invokeError(callback, "Couldn't get file path for photo"); - return; - } - cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraCaptureURI); + this.setImageCaptureUri(cameraIntent); } if (cameraIntent.resolveActivity(reactContext.getPackageManager()) == null) @@ -350,16 +351,19 @@ public class ImagePickerModule extends ReactContextBaseJavaModule libraryIntent = new Intent(Intent.ACTION_PICK); libraryIntent.setType("video/*"); } + else if (pickBoth) { + libraryIntent = new Intent(Intent.ACTION_GET_CONTENT); + libraryIntent.addCategory(Intent.CATEGORY_OPENABLE); + libraryIntent.setType("image/*"); + libraryIntent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {"image/*", "video/*"}); + requestCode = REQUEST_LAUNCH_MIXED_CAPTURE; + } else { requestCode = REQUEST_LAUNCH_IMAGE_LIBRARY; libraryIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); - - if (pickBoth) - { - libraryIntent.setType("image/* video/*"); - } + libraryIntent.setType("image/*"); } if (libraryIntent.resolveActivity(reactContext.getPackageManager()) == null) @@ -385,75 +389,47 @@ public class ImagePickerModule extends ReactContextBaseJavaModule } } - @Override - public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { - //robustness code - if (passResult(requestCode)) - { - return; - } - - responseHelper.cleanResponse(); - - // user cancel - if (resultCode != Activity.RESULT_OK) - { - removeUselessFiles(requestCode, imageConfig); - responseHelper.invokeCancel(callback); - callback = null; - return; - } + protected String getMimeType(Activity activity, Uri uri) { + String mimeType = null; + if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) { + ContentResolver cr = activity.getApplicationContext().getContentResolver(); + mimeType = cr.getType(uri); + } else { + String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri + .toString()); + mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension( + fileExtension.toLowerCase()); + } + return mimeType; + } - Uri uri = null; - switch (requestCode) + protected void extractImageFromResult(Activity activity, Uri uri, int requestCode) { + String realPath = getRealPathFromURI(uri); + String mime = getMimeType(activity, uri); + final boolean isUrl = !TextUtils.isEmpty(realPath) && + Patterns.WEB_URL.matcher(realPath).matches(); + if (realPath == null || isUrl) { - case REQUEST_LAUNCH_IMAGE_CAPTURE: - uri = cameraCaptureURI; - break; - - case REQUEST_LAUNCH_IMAGE_LIBRARY: - uri = data.getData(); - String realPath = getRealPathFromURI(uri); - final boolean isUrl = !TextUtils.isEmpty(realPath) && - Patterns.WEB_URL.matcher(realPath).matches(); - if (realPath == null || isUrl) - { - try - { - File file = createFileFromURI(uri); - realPath = file.getAbsolutePath(); - uri = Uri.fromFile(file); - } - catch (Exception e) - { - // image not in cache - responseHelper.putString("error", "Could not read photo"); - responseHelper.putString("uri", uri.toString()); - responseHelper.invokeResponse(callback); - callback = null; - return; - } - } - imageConfig = imageConfig.withOriginalFile(new File(realPath)); - break; - - case REQUEST_LAUNCH_VIDEO_LIBRARY: - responseHelper.putString("uri", data.getData().toString()); - responseHelper.putString("path", getRealPathFromURI(data.getData())); - responseHelper.invokeResponse(callback); - callback = null; - return; - - case REQUEST_LAUNCH_VIDEO_CAPTURE: - final String path = getRealPathFromURI(data.getData()); - responseHelper.putString("uri", data.getData().toString()); - responseHelper.putString("path", path); - fileScan(reactContext, path); + try + { + File file = createFileFromURI(uri); + realPath = file.getAbsolutePath(); + uri = Uri.fromFile(file); + } + catch (Exception e) + { + // image not in cache + responseHelper.putString("error", "Could not read photo"); + responseHelper.putString("uri", uri.toString()); responseHelper.invokeResponse(callback); callback = null; + this.options = null; return; + } } + imageConfig = imageConfig.withOriginalFile(new File(realPath)); + final ReadExifResult result = readExifInterface(responseHelper, imageConfig); if (result.error != null) @@ -461,6 +437,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule removeUselessFiles(requestCode, imageConfig); responseHelper.invokeError(callback, result.error.getMessage()); callback = null; + this.options = null; return; } @@ -472,7 +449,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule updatedResultResponse(uri, imageConfig.original.getAbsolutePath()); // don't create a new file if contraint are respected - if (imageConfig.useOriginal(initialWidth, initialHeight, result.currentRotation)) + if (imageConfig.useOriginal(initialWidth, initialHeight, result.currentRotation) || mime.equals("image/gif")) { responseHelper.putInt("width", initialWidth); responseHelper.putInt("height", initialHeight); @@ -481,6 +458,14 @@ public class ImagePickerModule extends ReactContextBaseJavaModule else { imageConfig = getResizedImage(reactContext, this.options, imageConfig, initialWidth, initialHeight, requestCode); + if (imageConfig == null) + { + responseHelper.invokeError(callback, "Could not read image"); + callback = null; + this.options = null; + return; + } + if (imageConfig.resized == null) { removeUselessFiles(requestCode, imageConfig); @@ -523,6 +508,64 @@ public class ImagePickerModule extends ReactContextBaseJavaModule this.options = null; } + protected void extractVideoFromResult(Uri uri) { + responseHelper.putString("uri", uri.toString()); + responseHelper.putString("path", getRealPathFromURI(uri)); + responseHelper.invokeResponse(callback); + callback = null; + this.options = null; + } + + @Override + public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { + //robustness code + if (passResult(requestCode)) + { + return; + } + + responseHelper.cleanResponse(); + + // user cancel + if (resultCode != Activity.RESULT_OK) + { + removeUselessFiles(requestCode, imageConfig); + responseHelper.invokeCancel(callback); + callback = null; + return; + } + + switch (requestCode) + { + case REQUEST_LAUNCH_IMAGE_CAPTURE: + extractImageFromResult(activity, cameraCaptureURI, requestCode); + break; + + case REQUEST_LAUNCH_IMAGE_LIBRARY: + extractImageFromResult(activity, data.getData(), requestCode); + break; + + case REQUEST_LAUNCH_VIDEO_LIBRARY: + extractVideoFromResult(data.getData()); + break; + + case REQUEST_LAUNCH_MIXED_CAPTURE: + case REQUEST_LAUNCH_VIDEO_CAPTURE: + if (data == null || data.getData() == null) { + extractImageFromResult(activity, cameraCaptureURI, requestCode); + } else { + Uri selectedMediaUri = data.getData(); + String mime = getMimeType(activity, selectedMediaUri); + if (mime.contains("image")) { + extractImageFromResult(activity, selectedMediaUri, requestCode); + } else { + extractVideoFromResult(selectedMediaUri); + } + } + break; + } + } + public void invokeCustomButton(@NonNull final String action) { responseHelper.invokeCustomButton(this.callback, action); @@ -551,7 +594,8 @@ public class ImagePickerModule extends ReactContextBaseJavaModule { return callback == null || (cameraCaptureURI == 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); + && requestCode != REQUEST_LAUNCH_VIDEO_LIBRARY && requestCode != REQUEST_LAUNCH_VIDEO_CAPTURE + && requestCode != REQUEST_LAUNCH_MIXED_CAPTURE); } private void updatedResultResponse(@Nullable final Uri uri, @@ -571,22 +615,23 @@ public class ImagePickerModule extends ReactContextBaseJavaModule @NonNull final Callback callback, @NonNull final int requestCode) { - final int writePermission = ActivityCompat - .checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE); - final int cameraPermission = ActivityCompat - .checkSelfPermission(activity, Manifest.permission.CAMERA); - - boolean permissionsGranted = false; - + int selfCheckResult = 0; switch (requestCode) { case REQUEST_PERMISSIONS_FOR_LIBRARY: - permissionsGranted = writePermission == PackageManager.PERMISSION_GRANTED; + selfCheckResult = ActivityCompat + .checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE); break; case REQUEST_PERMISSIONS_FOR_CAMERA: - permissionsGranted = cameraPermission == PackageManager.PERMISSION_GRANTED && writePermission == PackageManager.PERMISSION_GRANTED; + selfCheckResult = ActivityCompat + .checkSelfPermission(activity, Manifest.permission.CAMERA); + if (selfCheckResult == PackageManager.PERMISSION_GRANTED) { + selfCheckResult = ActivityCompat + .checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE); + } break; } + final boolean permissionsGranted = selfCheckResult == PackageManager.PERMISSION_GRANTED; if (!permissionsGranted) { final Boolean dontAskAgain = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) && ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA); @@ -787,4 +832,22 @@ public class ImagePickerModule extends ReactContextBaseJavaModule videoDurationLimit = options.getInt("durationLimit"); } } + + private void setImageCaptureUri(Intent cameraIntent) { + final File original = createNewFile(reactContext, this.options, false); + imageConfig = imageConfig.withOriginalFile(original); + + if (imageConfig.original != null) { + cameraCaptureURI = RealPathUtil.compatUriFromFile(reactContext, imageConfig.original); + }else { + responseHelper.invokeError(callback, "Couldn't get file path for photo"); + return; + } + if (cameraCaptureURI == null) + { + responseHelper.invokeError(callback, "Couldn't get file path for photo"); + return; + } + cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraCaptureURI); + } } diff --git a/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/utils/RealPathUtil.java b/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/utils/RealPathUtil.java index cc90dce..c1cc74d 100644 --- a/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/utils/RealPathUtil.java +++ b/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/utils/RealPathUtil.java @@ -7,16 +7,22 @@ import android.net.Uri; import android.os.Build; import android.provider.DocumentsContract; import android.provider.MediaStore; +import android.provider.OpenableColumns; import android.content.ContentUris; import android.os.Environment; +import android.os.ParcelFileDescriptor; +import android.webkit.MimeTypeMap; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.FileProvider; -import java.io.File; +import java.io.*; +import java.nio.channels.FileChannel; public class RealPathUtil { + public static final String CACHE_DIR_NAME = "mmShare"; + public static @Nullable Uri compatUriFromFile(@NonNull final Context context, @NonNull final File file) { Uri result = null; @@ -58,12 +64,7 @@ public class RealPathUtil { } // DownloadsProvider else if (isDownloadsDocument(uri)) { - - final String id = DocumentsContract.getDocumentId(uri); - final Uri contentUri = ContentUris.withAppendedId( - Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); - - return getDataColumn(context, contentUri, null, null); + return getPathFromSavingTempFile(context, uri); } // MediaProvider else if (isMediaDocument(uri)) { @@ -89,7 +90,7 @@ public class RealPathUtil { } } // MediaStore (and general) - else if ("content".equalsIgnoreCase(uri.getScheme())) { + if ("content".equalsIgnoreCase(uri.getScheme())) { // Return the remote address if (isGooglePhotosUri(uri)) @@ -98,7 +99,7 @@ public class RealPathUtil { if (isFileProviderUri(context, uri)) return getFileProviderPath(context, uri); - return getDataColumn(context, uri, null, null); + return getPathFromSavingTempFile(context, uri); } // File else if ("file".equalsIgnoreCase(uri.getScheme())) { @@ -108,6 +109,49 @@ public class RealPathUtil { return null; } + + public static String getPathFromSavingTempFile(Context context, final Uri uri) { + File tmpFile; + String fileName = null; + + // Try and get the filename from the Uri + try { + Cursor returnCursor = + context.getContentResolver().query(uri, null, null, null, null); + int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); + returnCursor.moveToFirst(); + fileName = returnCursor.getString(nameIndex); + } catch (Exception e) { + // just continue to get the filename with the last segment of the path + } + + try { + if (fileName == null) { + fileName = uri.getLastPathSegment().toString().trim(); + } + + + File cacheDir = new File(context.getCacheDir(), CACHE_DIR_NAME); + if (!cacheDir.exists()) { + cacheDir.mkdirs(); + } + + tmpFile = new File(cacheDir, fileName); + tmpFile.createNewFile(); + + ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r"); + + FileChannel src = new FileInputStream(pfd.getFileDescriptor()).getChannel(); + FileChannel dst = new FileOutputStream(tmpFile).getChannel(); + dst.transferFrom(src, 0, src.size()); + src.close(); + dst.close(); + } catch (IOException ex) { + return null; + } + return tmpFile.getAbsolutePath(); + } + /** * Get the value of the data column for this Uri. This is useful for * MediaStore Uris, and other file-based ContentProviders.