diff --git a/android-template/app/src/main/res/xml/file_paths.xml b/android-template/app/src/main/res/xml/file_paths.xml index fdc6271192..bd0c4d80d0 100644 --- a/android-template/app/src/main/res/xml/file_paths.xml +++ b/android-template/app/src/main/res/xml/file_paths.xml @@ -1,4 +1,5 @@ + \ No newline at end of file diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java index fcf4648aa2..4c46d73601 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java @@ -68,6 +68,7 @@ public class Camera extends Plugin { private static final String IMAGE_GALLERY_SAVE_ERROR = "Unable to save the image in the gallery"; private String imageFileSavePath; + private String imageEditedFileSavePath; private Uri imageFileUri; private boolean isEdited = false; @@ -223,18 +224,10 @@ public void openPhotos(final PluginCall call) { } public void processCameraImage(PluginCall call) { - boolean saveToGallery = call.getBoolean("saveToGallery", CameraSettings.DEFAULT_SAVE_IMAGE_TO_GALLERY); if(imageFileSavePath == null) { call.error(IMAGE_PROCESS_NO_FILE_ERROR); return; } - if (saveToGallery) { - try { - MediaStore.Images.Media.insertImage(getActivity().getContentResolver(), imageFileSavePath, "", ""); - } catch (FileNotFoundException e) { - Log.e(getLogTag(), IMAGE_GALLERY_SAVE_ERROR, e); - } - } // Load the image as a Bitmap File f = new File(imageFileSavePath); BitmapFactory.Options bmOptions = new BitmapFactory.Options(); @@ -331,10 +324,20 @@ private void returnResult(PluginCall call, Bitmap bitmap, Uri u) { bitmap.compress(Bitmap.CompressFormat.JPEG, settings.getQuality(), bitmapOutputStream); if (settings.isAllowEditing() && !isEdited) { - editImage(call, u); + editImage(call, bitmap, u, bitmapOutputStream); return; } + boolean saveToGallery = call.getBoolean("saveToGallery", CameraSettings.DEFAULT_SAVE_IMAGE_TO_GALLERY); + if (saveToGallery && (imageEditedFileSavePath != null || imageFileSavePath != null)) { + try { + String fileToSave = imageEditedFileSavePath != null ? imageEditedFileSavePath : imageFileSavePath; + MediaStore.Images.Media.insertImage(getActivity().getContentResolver(), fileToSave, "", ""); + } catch (FileNotFoundException e) { + Log.e(getLogTag(), IMAGE_GALLERY_SAVE_ERROR, e); + } + } + if (settings.getResultType() == CameraResultType.BASE64) { returnBase64(call, exif, bitmapOutputStream); } else if (settings.getResultType() == CameraResultType.URI) { @@ -348,22 +351,30 @@ private void returnResult(PluginCall call, Bitmap bitmap, Uri u) { // Result returned, clear stored paths imageFileSavePath = null; imageFileUri = null; + imageEditedFileSavePath = null; } private void returnFileURI(PluginCall call, ExifWrapper exif, Bitmap bitmap, Uri u, ByteArrayOutputStream bitmapOutputStream) { - ByteArrayInputStream bis = null; - - try { - bis = new ByteArrayInputStream(bitmapOutputStream.toByteArray()); - Uri newUri = saveTemporaryImage(bitmap, u, bis); + Uri newUri = getTempImage(bitmap, u, bitmapOutputStream); + if (newUri != null) { JSObject ret = new JSObject(); ret.put("format", "jpeg"); ret.put("exif", exif.toJson()); ret.put("path", newUri.toString()); ret.put("webPath", FileUtils.getPortablePath(getContext(), bridge.getLocalUrl(), newUri)); call.resolve(ret); + } else { + call.reject(UNABLE_TO_PROCESS_IMAGE); + } + } + + private Uri getTempImage(Bitmap bitmap, Uri u, ByteArrayOutputStream bitmapOutputStream) { + ByteArrayInputStream bis = null; + Uri newUri = null; + try { + bis = new ByteArrayInputStream(bitmapOutputStream.toByteArray()); + newUri = saveTemporaryImage(bitmap, u, bis); } catch (IOException ex) { - call.reject(UNABLE_TO_PROCESS_IMAGE, ex); } finally { if (bis != null) { try { @@ -373,6 +384,7 @@ private void returnFileURI(PluginCall call, ExifWrapper exif, Bitmap bitmap, Uri } } } + return newUri; } /** @@ -478,22 +490,44 @@ protected void handleOnActivityResult(int requestCode, int resultCode, Intent da } } - private void editImage(PluginCall call, Uri uri) { + private void editImage(PluginCall call, Bitmap bitmap, Uri uri, ByteArrayOutputStream bitmapOutputStream) { + Uri origPhotoUri = uri; + if (imageFileUri != null) { + origPhotoUri = imageFileUri; + } + try { + Intent editIntent = createEditIntent(origPhotoUri, false); + startActivityForResult(call, editIntent, REQUEST_IMAGE_EDIT); + } catch (SecurityException ex) { + Uri tempImage = getTempImage(bitmap, uri, bitmapOutputStream); + Intent editIntent = createEditIntent(tempImage, true); + if (editIntent != null) { + startActivityForResult(call, editIntent, REQUEST_IMAGE_EDIT); + } else { + call.error(IMAGE_EDIT_ERROR); + } + } catch (Exception ex) { + call.error(IMAGE_EDIT_ERROR, ex); + } + } + + private Intent createEditIntent(Uri origPhotoUri, boolean expose) { + Uri editUri = origPhotoUri; try { - Uri origPhotoUri = uri; - if (imageFileUri != null) { - origPhotoUri = imageFileUri; + if (expose) { + editUri = FileProvider.getUriForFile(getActivity(), getContext().getPackageName() + ".fileprovider", new File(origPhotoUri.getPath())); } Intent editIntent = new Intent(Intent.ACTION_EDIT); - editIntent.setDataAndType(origPhotoUri, "image/*"); + editIntent.setDataAndType(editUri, "image/*"); File editedFile = CameraUtils.createImageFile(getActivity()); + imageEditedFileSavePath = editedFile.getAbsolutePath(); Uri editedUri = Uri.fromFile(editedFile); editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); editIntent.putExtra(MediaStore.EXTRA_OUTPUT, editedUri); - startActivityForResult(call, editIntent, REQUEST_IMAGE_EDIT); + return editIntent; } catch (Exception ex) { - call.error(IMAGE_EDIT_ERROR, ex); + return null; } } diff --git a/core/src/core-plugin-definitions.ts b/core/src/core-plugin-definitions.ts index 5ad25babcf..427a8bb539 100644 --- a/core/src/core-plugin-definitions.ts +++ b/core/src/core-plugin-definitions.ts @@ -308,7 +308,8 @@ export interface CameraOptions { */ resultType: CameraResultType; /** - * Whether to save the photo to the gallery/photo stream. + * Whether to save the photo to the gallery. + * If the photo was picked from the gallery, it will only be saved if edited. * Default: false */ saveToGallery?: boolean; diff --git a/example/android/app/src/main/res/xml/file_paths.xml b/example/android/app/src/main/res/xml/file_paths.xml index 7930202088..3f2acc76cc 100644 --- a/example/android/app/src/main/res/xml/file_paths.xml +++ b/example/android/app/src/main/res/xml/file_paths.xml @@ -1,4 +1,5 @@ + diff --git a/ios/Capacitor/Capacitor/Plugins/Camera.swift b/ios/Capacitor/Capacitor/Plugins/Camera.swift index f086940d7d..8ce6041e83 100644 --- a/ios/Capacitor/Capacitor/Plugins/Camera.swift +++ b/ios/Capacitor/Capacitor/Plugins/Camera.swift @@ -198,9 +198,12 @@ public class CAPCameraPlugin : CAPPlugin, UIImagePickerControllerDelegate, UINav public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { var image: UIImage? + var isEdited = false + var isGallery = true if let editedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage { // Use editedImage Here + isEdited = true image = editedImage } else if let originalImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage { // Use originalImage Here @@ -210,6 +213,7 @@ public class CAPCameraPlugin : CAPPlugin, UIImagePickerControllerDelegate, UINav var imageMetadata: [AnyHashable: Any] = [:] if let photoMetadata = info[UIImagePickerController.InfoKey.mediaMetadata] as? [AnyHashable: Any] { imageMetadata = photoMetadata + isGallery = false } if let asset = info[UIImagePickerController.InfoKey.phAsset] as? PHAsset { imageMetadata = getImageMeta(asset: asset)! @@ -232,7 +236,9 @@ public class CAPCameraPlugin : CAPPlugin, UIImagePickerControllerDelegate, UINav } if settings.saveToGallery { + if !isGallery || isEdited { UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil); + } } guard let jpeg = image!.jpegData(compressionQuality: CGFloat(settings.quality/100)) else {