From b3ec19fe17557a20dec7ab2f45ab74b8776fafec Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Fri, 5 Oct 2018 17:46:47 -0300 Subject: [PATCH] Fix Option to capture and share video (#2225) --- Makefile | 3 - app/components/attachment_button.js | 31 +- assets/base/i18n/en.json | 3 +- ios/Mattermost.xcodeproj/project.pbxproj | 10 +- ios/Mattermost/Info.plist | 6 +- native_modules/ImagePickerModule.java | 729 ----------------------- package-lock.json | 6 +- package.json | 2 +- 8 files changed, 37 insertions(+), 753 deletions(-) delete mode 100644 native_modules/ImagePickerModule.java diff --git a/Makefile b/Makefile index 1c2d013db..a4acdf9a8 100644 --- a/Makefile +++ b/Makefile @@ -61,9 +61,6 @@ clean: ## Cleans dependencies, previous builds and temp files @echo Cleanup finished post-install: - @# Need to copy custom ImagePickerModule.java that implements correct permission checks for android - @rm node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModule.java - @cp ./native_modules/ImagePickerModule.java node_modules/react-native-image-picker/android/src/main/java/com/imagepicker @# Need to copy custom RNDocumentPicker.m that implements direct access to the document picker in iOS @cp ./native_modules/RNDocumentPicker.m node_modules/react-native-document-picker/ios/RNDocumentPicker/RNDocumentPicker.m diff --git a/app/components/attachment_button.js b/app/components/attachment_button.js index 49eb0ff37..44024a573 100644 --- a/app/components/attachment_button.js +++ b/app/components/attachment_button.js @@ -42,11 +42,17 @@ export default class AttachmentButton extends PureComponent { intl: intlShape.isRequired, }; - attachFileFromCamera = async () => { + attachPhotoFromCamera = () => { + return this.attachFileFromCamera('photo'); + }; + + attachFileFromCamera = async (mediaType) => { const {formatMessage} = this.context.intl; const options = { - quality: 1.0, + quality: 1, + videoQuality: 'high', noData: true, + mediaType, storageOptions: { cameraRoll: true, waitUntilSaved: true, @@ -84,7 +90,7 @@ export default class AttachmentButton extends PureComponent { attachFileFromLibrary = () => { const {formatMessage} = this.context.intl; const options = { - quality: 1.0, + quality: 1, noData: true, permissionDenied: { title: formatMessage({ @@ -116,10 +122,14 @@ export default class AttachmentButton extends PureComponent { }); }; + attachVideoFromCamera = () => { + return this.attachFileFromCamera('video'); + }; + attachVideoFromLibraryAndroid = () => { const {formatMessage} = this.context.intl; const options = { - quality: 1.0, + videoQuality: 'high', mediaType: 'video', noData: true, permissionDenied: { @@ -313,12 +323,19 @@ export default class AttachmentButton extends PureComponent { this.props.blurTextBox(); const options = { items: [{ - action: () => this.handleFileAttachmentOption(this.attachFileFromCamera), + action: () => this.handleFileAttachmentOption(this.attachPhotoFromCamera), text: { - id: t('mobile.file_upload.camera'), - defaultMessage: 'Take Photo or Video', + id: t('mobile.file_upload.camera_photo'), + defaultMessage: 'Take Photo', }, icon: 'camera', + }, { + action: () => this.handleFileAttachmentOption(this.attachVideoFromCamera), + text: { + id: t('mobile.file_upload.camera_video'), + defaultMessage: 'Take Video', + }, + icon: 'video-camera', }, { action: () => this.handleFileAttachmentOption(this.attachFileFromLibrary), text: { diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index ad5afc2bb..af4c34c3b 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -235,7 +235,8 @@ "mobile.failed_network_action.shortDescription": "Make sure you have an active connection and try again.", "mobile.failed_network_action.title": "No internet connection", "mobile.file_upload.browse": "Browse Files", - "mobile.file_upload.camera": "Take Photo or Video", + "mobile.file_upload.camera_photo": "Take Photo", + "mobile.file_upload.camera_video": "Take Video", "mobile.file_upload.library": "Photo Library", "mobile.file_upload.max_warning": "Uploads limited to 5 files maximum.", "mobile.file_upload.video": "Video Library", diff --git a/ios/Mattermost.xcodeproj/project.pbxproj b/ios/Mattermost.xcodeproj/project.pbxproj index 83cb0e285..807cf586d 100644 --- a/ios/Mattermost.xcodeproj/project.pbxproj +++ b/ios/Mattermost.xcodeproj/project.pbxproj @@ -5,6 +5,7 @@ }; objectVersion = 46; objects = { + /* Begin PBXBuildFile section */ 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; @@ -1043,7 +1044,6 @@ EE3EE4548D3F4A49B1274722 /* libRNLocalAuth.a */, 17A75F5A3D0D4A4A995DCD76 /* libRNPasscodeStatus.a */, 77810F0A063349439B0D8B6B /* libJailMonkey.a */, - 0C0C75A424714EAC8E876A8F /* libFastImage.a */, 4B19E38FBCB94C57BB03B8E6 /* libRNFetchBlob.a */, 4A22BC320BA04E18A7F2A4E6 /* libReactNativeExceptionHandler.a */, 2AE23B96BF7545B9A77C0C87 /* libRNSentry.a */, @@ -2376,9 +2376,7 @@ INFOPLIST_FILE = MattermostTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -2405,9 +2403,7 @@ INFOPLIST_FILE = MattermostTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/ios/Mattermost/Info.plist b/ios/Mattermost/Info.plist index 191789729..e16a7afdc 100644 --- a/ios/Mattermost/Info.plist +++ b/ios/Mattermost/Info.plist @@ -62,10 +62,12 @@ Share contacts in your Mattermost instance NSCameraUsageDescription Take a Photo or Video and upload it to your mattermost instance + NSLocationAlwaysUsageDescription + Send your current location to your Mattermost instance NSLocationWhenInUseUsageDescription Send your current location to your Mattermost instance - NSLocationAlwaysUsageDescription - Send your current location to your Mattermost instance + NSMicrophoneUsageDescription + Capture audio when recording a video to share to your Mattermost instance NSMotionUsageDescription Share your route in your Mattermost instance NSPhotoLibraryUsageDescription diff --git a/native_modules/ImagePickerModule.java b/native_modules/ImagePickerModule.java deleted file mode 100644 index b3b3ee9c6..000000000 --- a/native_modules/ImagePickerModule.java +++ /dev/null @@ -1,729 +0,0 @@ -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.content.pm.ResolveInfo; -import android.graphics.BitmapFactory; -import android.net.Uri; -import android.os.Build; -import android.provider.MediaStore; -import android.provider.Settings; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.StyleRes; -import android.support.v4.app.ActivityCompat; -import android.support.v7.app.AlertDialog; -import android.text.TextUtils; -import android.util.Base64; -import android.util.Patterns; -import android.webkit.MimeTypeMap; -import android.content.pm.PackageManager; - -import com.facebook.react.ReactActivity; -import com.facebook.react.bridge.ActivityEventListener; -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.imagepicker.media.ImageConfig; -import com.imagepicker.permissions.PermissionUtils; -import com.imagepicker.permissions.OnImagePickerPermissionsCallback; -import com.imagepicker.utils.MediaUtils.ReadExifResult; -import com.imagepicker.utils.RealPathUtil; -import com.imagepicker.utils.UI; - -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.lang.ref.WeakReference; -import java.util.List; - -import com.facebook.react.modules.core.PermissionListener; -import com.facebook.react.modules.core.PermissionAwareActivity; - -import static com.imagepicker.utils.MediaUtils.*; -import static com.imagepicker.utils.MediaUtils.createNewFile; -import static com.imagepicker.utils.MediaUtils.getResizedImage; - -public class ImagePickerModule extends ReactContextBaseJavaModule - implements ActivityEventListener -{ - - public static final int REQUEST_LAUNCH_IMAGE_CAPTURE = 13001; - 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_PERMISSIONS_FOR_CAMERA = 14001; - public static final int REQUEST_PERMISSIONS_FOR_LIBRARY = 14002; - - private final ReactApplicationContext reactContext; - private final int dialogThemeId; - - protected Callback callback; - private ReadableMap options; - protected Uri cameraCaptureURI; - private Boolean noData = false; - private Boolean pickVideo = false; - private ImageConfig imageConfig = new ImageConfig(null, null, 0, 0, 100, 0, false); - - @Deprecated - private int videoQuality = 1; - - @Deprecated - private int videoDurationLimit = 0; - - private ResponseHelper responseHelper = new ResponseHelper(); - private PermissionListener listener = new PermissionListener() - { - public boolean onRequestPermissionsResult(final int requestCode, - @NonNull final String[] permissions, - @NonNull final int[] grantResults) - { - boolean permissionsGranted = true; - for (int i = 0; i < permissions.length; i++) - { - final boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED; - permissionsGranted = permissionsGranted && granted; - } - - if (callback == null || options == null) - { - return false; - } - - if (!permissionsGranted) - { - responseHelper.invokeError(callback, "Permissions weren't granted"); - return false; - } - - switch (requestCode) - { - case REQUEST_PERMISSIONS_FOR_CAMERA: - launchCamera(options, callback); - break; - - case REQUEST_PERMISSIONS_FOR_LIBRARY: - launchImageLibrary(options, callback); - break; - - } - return true; - } - }; - - public ImagePickerModule(ReactApplicationContext reactContext, - @StyleRes final int dialogThemeId) - { - super(reactContext); - - this.dialogThemeId = dialogThemeId; - this.reactContext = reactContext; - this.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) - { - responseHelper.invokeError(callback, "can't find current Activity"); - return; - } - - this.callback = callback; - this.options = options; - imageConfig = new ImageConfig(null, null, 0, 0, 100, 0, false); - - final AlertDialog dialog = UI.chooseDialog(this, options, new UI.OnAction() - { - @Override - public void onTakePhoto(@NonNull final ImagePickerModule module) - { - if (module == null) - { - return; - } - module.launchCamera(); - } - - @Override - public void onUseLibrary(@NonNull final ImagePickerModule module) - { - if (module == null) - { - return; - } - module.launchImageLibrary(); - } - - @Override - public void onCancel(@NonNull final ImagePickerModule module) - { - if (module == null) - { - return; - } - module.doOnCancel(); - } - - @Override - public void onCustomButton(@NonNull final ImagePickerModule module, - @NonNull final String action) - { - if (module == null) - { - return; - } - module.invokeCustomButton(action); - } - }); - dialog.show(); - } - - public void doOnCancel() - { - if (this.callback != null) { - responseHelper.invokeCancel(this.callback); - } - } - - public void launchCamera() - { - this.launchCamera(this.options, this.callback); - } - - // NOTE: Currently not reentrant / doesn't support concurrent requests - @ReactMethod - public void launchCamera(final ReadableMap options, final Callback callback) - { - if (!isCameraAvailable()) - { - responseHelper.invokeError(callback, "Camera not available"); - return; - } - - final Activity currentActivity = getCurrentActivity(); - if (currentActivity == null) - { - responseHelper.invokeError(callback, "can't find current Activity"); - return; - } - - this.callback = callback; - this.options = options; - - if (!permissionsCheck(currentActivity, callback, REQUEST_PERMISSIONS_FOR_CAMERA)) - { - return; - } - - parseOptions(this.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); - - 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); - } - - if (cameraIntent.resolveActivity(reactContext.getPackageManager()) == null) - { - responseHelper.invokeError(callback, "Cannot launch camera"); - return; - } - - // Workaround for Android bug. - // grantUriPermission also needed for KITKAT, - // see https://code.google.com/p/android/issues/detail?id=76683 - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { - List resInfoList = reactContext.getPackageManager().queryIntentActivities(cameraIntent, PackageManager.MATCH_DEFAULT_ONLY); - for (ResolveInfo resolveInfo : resInfoList) { - String packageName = resolveInfo.activityInfo.packageName; - reactContext.grantUriPermission(packageName, cameraCaptureURI, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); - } - } - - try - { - currentActivity.startActivityForResult(cameraIntent, requestCode); - } - catch (ActivityNotFoundException e) - { - e.printStackTrace(); - responseHelper.invokeError(callback, "Cannot launch camera"); - } - } - - public void launchImageLibrary() - { - this.launchImageLibrary(this.options, this.callback); - } - // NOTE: Currently not reentrant / doesn't support concurrent requests - @ReactMethod - public void launchImageLibrary(final ReadableMap options, final Callback callback) - { - final Activity currentActivity = getCurrentActivity(); - if (currentActivity == null) { - responseHelper.invokeError(callback, "can't find current Activity"); - return; - } - - this.options = options; - this.callback = callback; - - if (!permissionsCheck(currentActivity, callback, REQUEST_PERMISSIONS_FOR_LIBRARY)) - { - return; - } - - parseOptions(this.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(reactContext.getPackageManager()) == null) - { - responseHelper.invokeError(callback, "Cannot launch photo library"); - return; - } - - try - { - currentActivity.startActivityForResult(libraryIntent, requestCode); - } - catch (ActivityNotFoundException e) - { - e.printStackTrace(); - responseHelper.invokeError(callback, "Cannot launch photo library"); - } - } - - @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; - } - - Uri uri = null; - switch (requestCode) - { - 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); - responseHelper.invokeResponse(callback); - callback = null; - return; - } - - final ReadExifResult result = readExifInterface(responseHelper, imageConfig); - - if (result.error != null) - { - removeUselessFiles(requestCode, imageConfig); - responseHelper.invokeError(callback, result.error.getMessage()); - callback = null; - return; - } - - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeFile(imageConfig.original.getAbsolutePath(), options); - int initialWidth = options.outWidth; - int initialHeight = options.outHeight; - updatedResultResponse(uri, imageConfig.original.getAbsolutePath()); - - // don't create a new file if contraint are respected - if (imageConfig.useOriginal(initialWidth, initialHeight, result.currentRotation)) - { - responseHelper.putInt("width", initialWidth); - responseHelper.putInt("height", initialHeight); - fileScan(reactContext, imageConfig.original.getAbsolutePath()); - } - else - { - imageConfig = getResizedImage(reactContext, this.options, imageConfig, initialWidth, initialHeight, requestCode); - if (imageConfig.resized == null) - { - removeUselessFiles(requestCode, imageConfig); - responseHelper.putString("error", "Can't resize the image"); - } - else - { - uri = Uri.fromFile(imageConfig.resized); - BitmapFactory.decodeFile(imageConfig.resized.getAbsolutePath(), options); - responseHelper.putInt("width", options.outWidth); - responseHelper.putInt("height", options.outHeight); - - updatedResultResponse(uri, imageConfig.resized.getAbsolutePath()); - fileScan(reactContext, imageConfig.resized.getAbsolutePath()); - } - } - - if (imageConfig.saveToCameraRoll && requestCode == REQUEST_LAUNCH_IMAGE_CAPTURE) - { - final RolloutPhotoResult rolloutResult = rolloutPhotoFromCamera(imageConfig); - - if (rolloutResult.error == null) - { - imageConfig = rolloutResult.imageConfig; - uri = Uri.fromFile(imageConfig.getActualFile()); - updatedResultResponse(uri, imageConfig.getActualFile().getAbsolutePath()); - } - else - { - removeUselessFiles(requestCode, imageConfig); - final String errorMessage = new StringBuilder("Error moving image to camera roll: ") - .append(rolloutResult.error.getMessage()).toString(); - responseHelper.putString("error", errorMessage); - return; - } - } - - responseHelper.invokeResponse(callback); - callback = null; - this.options = null; - } - - public void invokeCustomButton(@NonNull final String action) - { - responseHelper.invokeCustomButton(this.callback, action); - } - - @Override - public void onNewIntent(Intent intent) { } - - public Context getContext() - { - return getReactApplicationContext(); - } - - public @StyleRes int getDialogThemeId() - { - return this.dialogThemeId; - } - - public @NonNull Activity getActivity() - { - return getCurrentActivity(); - } - - - private boolean passResult(int requestCode) - { - 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); - } - - private void updatedResultResponse(@Nullable final Uri uri, - @NonNull final String path) - { - responseHelper.putString("uri", uri.toString()); - responseHelper.putString("path", path); - - if (!noData) { - responseHelper.putString("data", getBase64StringFromFile(path)); - } - - putExtraFileInfo(path, responseHelper); - } - - private boolean permissionsCheck(@NonNull final Activity activity, - @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); - - final boolean permissionsGrated = writePermission == PackageManager.PERMISSION_GRANTED && - cameraPermission == PackageManager.PERMISSION_GRANTED; - - if (!permissionsGrated) - { - final Boolean dontAskAgain = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) && ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA); - - if (dontAskAgain) - { - final AlertDialog dialog = PermissionUtils - .explainingDialog(this, options, new PermissionUtils.OnExplainingPermissionCallback() - { - @Override - public void onCancel(WeakReference moduleInstance, - DialogInterface dialogInterface) - { - final ImagePickerModule module = moduleInstance.get(); - if (module == null) - { - return; - } - module.doOnCancel(); - } - - @Override - public void onReTry(WeakReference moduleInstance, - DialogInterface dialogInterface) - { - final ImagePickerModule module = moduleInstance.get(); - if (module == null) - { - return; - } - Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - Uri uri = Uri.fromParts("package", module.getContext().getPackageName(), null); - intent.setData(uri); - final Activity innerActivity = module.getActivity(); - if (innerActivity == null) - { - return; - } - innerActivity.startActivityForResult(intent, 1); - } - }); - if (dialog != null) { - dialog.show(); - } - return false; - } - else - { - String[] PERMISSIONS = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA}; - if (activity instanceof ReactActivity) - { - ((ReactActivity) activity).requestPermissions(PERMISSIONS, requestCode, listener); - } - else if (activity instanceof OnImagePickerPermissionsCallback) - { - ((OnImagePickerPermissionsCallback) activity).setPermissionListener(listener); - ActivityCompat.requestPermissions(activity, PERMISSIONS, requestCode); - } - else if (activity instanceof PermissionAwareActivity) { - ((PermissionAwareActivity) activity).requestPermissions(PERMISSIONS, requestCode, listener); - } - else - { - final String errorDescription = new StringBuilder(activity.getClass().getSimpleName()) - .append(" must implement ") - .append(OnImagePickerPermissionsCallback.class.getSimpleName()) - .append(PermissionAwareActivity.class.getSimpleName()) - .toString(); - throw new UnsupportedOperationException(errorDescription); - } - return false; - } - } - return true; - } - - private boolean isCameraAvailable() { - return reactContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA) - || reactContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY); - } - - private @NonNull String getRealPathFromURI(@NonNull final Uri uri) { - return RealPathUtil.getRealPathFromURI(reactContext, 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(reactContext.getExternalCacheDir(), "photo-" + uri.getLastPathSegment()); - InputStream input = reactContext.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); - } - - private void putExtraFileInfo(@NonNull final String path, - @NonNull final ResponseHelper responseHelper) - { - // size && filename - try { - File f = new File(path); - responseHelper.putDouble("fileSize", f.length()); - responseHelper.putString("fileName", f.getName()); - } catch (Exception e) { - e.printStackTrace(); - } - - // type - String extension = MimeTypeMap.getFileExtensionFromUrl(path); - if (extension != null) { - responseHelper.putString("type", MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)); - } - } - - private void parseOptions(final ReadableMap options) { - noData = false; - if (options.hasKey("noData")) { - noData = options.getBoolean("noData"); - } - imageConfig = imageConfig.updateFromOptions(options); - 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"); - } - } -} diff --git a/package-lock.json b/package-lock.json index e9f941cc1..f0724e141 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12754,9 +12754,9 @@ } }, "react-native-image-picker": { - "version": "0.26.10", - "resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-0.26.10.tgz", - "integrity": "sha512-z6gAbru2E6SyGWm4ZTbiM9hPHZ5Tsl9kXGfRxW6YQXf9us7zybKoS7dKE1fQPsssv/OSvpPDKannJNncE+ATRA==" + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-0.27.0.tgz", + "integrity": "sha512-x4UDEs1XvfO+wXx41CZcZ4eVB+zj0vZuIl14ExO00WhyRf2NUXd4BG1uJzajOiyYQ+eRJQgv2PaNUamN6JqXZg==" }, "react-native-iphone-x-helper": { "version": "1.1.0", diff --git a/package.json b/package.json index c1441a9eb..8f1609490 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "react-native-drawer-layout": "2.0.0", "react-native-exception-handler": "2.9.0", "react-native-image-gallery": "github:enahum/react-native-image-gallery#a98b1051e94f5a394541ca1ff9b15e2c9ffed84f", - "react-native-image-picker": "0.26.10", + "react-native-image-picker": "0.27.0", "react-native-keyboard-aware-scroll-view": "0.7.2", "react-native-keychain": "3.0.0", "react-native-linear-gradient": "2.4.2",