diff --git a/plugin.xml b/plugin.xml
index 5b7ab1d94..7cbd19e85 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -73,6 +73,7 @@
+
diff --git a/src/android/CameraLauncher.java b/src/android/CameraLauncher.java
index cc8a0e3c8..32a27d4e7 100644
--- a/src/android/CameraLauncher.java
+++ b/src/android/CameraLauncher.java
@@ -21,6 +21,7 @@ Licensed to the Apache Software Foundation (ASF) under one
import android.Manifest;
import android.app.Activity;
import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -34,6 +35,7 @@ Licensed to the Apache Software Foundation (ASF) under one
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
@@ -51,7 +53,6 @@ Licensed to the Apache Software Foundation (ASF) under one
import java.io.ByteArrayOutputStream;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -405,58 +406,57 @@ public void getImage(int srcType, int returnType, int encodingType) {
}
}
-
- /**
- * Brings up the UI to perform crop on passed image URI
- *
- * @param picUri
- */
- private void performCrop(Uri picUri, int destType, Intent cameraIntent) {
- try {
- Intent cropIntent = new Intent("com.android.camera.action.CROP");
- // indicate image type and Uri
- cropIntent.setDataAndType(picUri, "image/*");
- // set crop properties
- cropIntent.putExtra("crop", "true");
+ /**
+ * Brings up the UI to perform crop on passed image URI
+ *
+ * @param picUri
+ */
+ private void performCrop(Uri picUri, int destType, Intent cameraIntent) {
+ try {
+ Intent cropIntent = new Intent("com.android.camera.action.CROP");
+ // indicate image type and Uri
+ cropIntent.setDataAndType(picUri, "image/*");
+ // set crop properties
+ cropIntent.putExtra("crop", "true");
- // indicate output X and Y
- if (targetWidth > 0) {
- cropIntent.putExtra("outputX", targetWidth);
- }
- if (targetHeight > 0) {
- cropIntent.putExtra("outputY", targetHeight);
- }
- if (targetHeight > 0 && targetWidth > 0 && targetWidth == targetHeight) {
- cropIntent.putExtra("aspectX", 1);
- cropIntent.putExtra("aspectY", 1);
- }
- // create new file handle to get full resolution crop
- croppedFilePath = createCaptureFile(this.encodingType, System.currentTimeMillis() + "").getAbsolutePath();
- croppedUri = Uri.parse(croppedFilePath);
- cropIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- cropIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- cropIntent.putExtra("output", croppedUri);
+ // indicate output X and Y
+ if (targetWidth > 0) {
+ cropIntent.putExtra("outputX", targetWidth);
+ }
+ if (targetHeight > 0) {
+ cropIntent.putExtra("outputY", targetHeight);
+ }
+ if (targetHeight > 0 && targetWidth > 0 && targetWidth == targetHeight) {
+ cropIntent.putExtra("aspectX", 1);
+ cropIntent.putExtra("aspectY", 1);
+ }
+ // create new file handle to get full resolution crop
+ croppedFilePath = createCaptureFile(this.encodingType, System.currentTimeMillis() + "").getAbsolutePath();
+ croppedUri = Uri.parse(croppedFilePath);
+ cropIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ cropIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ cropIntent.putExtra("output", croppedUri);
- // start the activity - we handle returning in onActivityResult
+ // start the activity - we handle returning in onActivityResult
- if (this.cordova != null) {
- this.cordova.startActivityForResult((CordovaPlugin) this,
- cropIntent, CROP_CAMERA + destType);
+ if (this.cordova != null) {
+ this.cordova.startActivityForResult((CordovaPlugin) this,
+ cropIntent, CROP_CAMERA + destType);
+ }
+ } catch (ActivityNotFoundException anfe) {
+ LOG.e(LOG_TAG, "Crop operation not supported on this device");
+ try {
+ processResultFromCamera(destType, cameraIntent);
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ LOG.e(LOG_TAG, "Unable to write to file");
+ }
}
- } catch (ActivityNotFoundException anfe) {
- LOG.e(LOG_TAG, "Crop operation not supported on this device");
- try {
- processResultFromCamera(destType, cameraIntent);
- }
- catch (IOException e)
- {
- e.printStackTrace();
- LOG.e(LOG_TAG, "Unable to write to file");
- }
}
- }
/**
* Applies all needed transformation to the image received from the camera.
@@ -494,16 +494,18 @@ private void processResultFromCamera(int destType, Intent intent) throws IOExcep
// in the gallery and the modified image is saved in the temporary
// directory
if (this.saveToPhotoAlbum) {
- galleryUri = Uri.fromFile(new File(getPicturesPath()));
+ GalleryPathVO galleryPathVO = getPicturesPath();
+ galleryUri = Uri.fromFile(new File(galleryPathVO.getGalleryPath()));
if (this.allowEdit && this.croppedUri != null) {
writeUncompressedImage(croppedUri, galleryUri);
} else {
- Uri imageUri = this.imageUri;
- writeUncompressedImage(imageUri, galleryUri);
+ if (Build.VERSION.SDK_INT <= 28) { // Between LOLLIPOP_MR1 and P, can be changed later to the constant Build.VERSION_CODES.P
+ writeTakenPictureToGalleryLowerThanAndroidQ(galleryUri);
+ } else { // Android Q or higher
+ writeTakenPictureToGalleryStartingFromAndroidQ(galleryPathVO);
+ }
}
-
- refreshGallery(galleryUri);
}
// If sending base64 image back
@@ -567,9 +569,7 @@ else if (destType == FILE_URI) {
// Add compressed version of captured image to returned media store Uri
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
- CompressFormat compressFormat = encodingType == JPEG ?
- CompressFormat.JPEG :
- CompressFormat.PNG;
+ CompressFormat compressFormat = getCompressFormatForEncodingType(encodingType);
bitmap.compress(compressFormat, this.mQuality, os);
os.close();
@@ -597,18 +597,41 @@ else if (destType == FILE_URI) {
bitmap = null;
}
- private String getPicturesPath() {
+ private void writeTakenPictureToGalleryLowerThanAndroidQ(Uri galleryUri) throws IOException {
+ writeUncompressedImage(imageUri, galleryUri);
+ refreshGallery(galleryUri);
+ }
+
+ private void writeTakenPictureToGalleryStartingFromAndroidQ(GalleryPathVO galleryPathVO) throws IOException {
+ // Starting from Android Q, working with the ACTION_MEDIA_SCANNER_SCAN_FILE intent is deprecated
+ // https://developer.android.com/reference/android/content/Intent#ACTION_MEDIA_SCANNER_SCAN_FILE
+ // we must start working with the MediaStore from Android Q on.
+ ContentResolver resolver = this.cordova.getActivity().getContentResolver();
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, galleryPathVO.getGalleryFileName());
+ contentValues.put(MediaStore.MediaColumns.MIME_TYPE, getMimetypeForFormat(encodingType));
+ Uri galleryOutputUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
+
+ InputStream fileStream = org.apache.cordova.camera.FileHelper.getInputStreamFromUriString(imageUri.toString(), cordova);
+ writeUncompressedImage(fileStream, galleryOutputUri);
+ }
+
+ private CompressFormat getCompressFormatForEncodingType(int encodingType) {
+ return encodingType == JPEG ? CompressFormat.JPEG : CompressFormat.PNG;
+ }
+
+ private GalleryPathVO getPicturesPath() {
String timeStamp = new SimpleDateFormat(TIME_FORMAT).format(new Date());
String imageFileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? JPEG_EXTENSION : PNG_EXTENSION);
File storageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
storageDir.mkdirs();
- String galleryPath = storageDir.getAbsolutePath() + "/" + imageFileName;
- return galleryPath;
+ return new GalleryPathVO(storageDir.getAbsolutePath(), imageFileName);
}
private void refreshGallery(Uri contentUri) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ // Starting from Android Q, working with the ACTION_MEDIA_SCANNER_SCAN_FILE intent is deprecated
mediaScanIntent.setData(contentUri);
this.cordova.getActivity().sendBroadcast(mediaScanIntent);
}
@@ -640,9 +663,7 @@ private String outputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
String modifiedPath = getTempDirectoryPath() + "/" + fileName;
OutputStream os = new FileOutputStream(modifiedPath);
- CompressFormat compressFormat = this.encodingType == JPEG ?
- CompressFormat.JPEG :
- CompressFormat.PNG;
+ CompressFormat compressFormat = getCompressFormatForEncodingType(this.encodingType);
bitmap.compress(compressFormat, this.mQuality, os);
os.close();
@@ -679,7 +700,6 @@ private void processResultFromGallery(int destType, Intent intent) {
return;
}
}
- int rotate = 0;
String fileLocation = FileHelper.getRealPath(uri, this.cordova);
LOG.d(LOG_TAG, "File location is: " + fileLocation);
@@ -900,34 +920,11 @@ private void writeUncompressedImage(InputStream fis, Uri dest) throws FileNotFou
private void writeUncompressedImage(Uri src, Uri dest) throws FileNotFoundException,
IOException {
- FileInputStream fis = new FileInputStream(FileHelper.stripFileProtocol(src.toString()));
+ InputStream fis = FileHelper.getInputStreamFromUriString(src.toString(), cordova);
writeUncompressedImage(fis, dest);
}
- /**
- * Create entry in media store for image
- *
- * @return uri
- */
- private Uri getUriFromMediaStore() {
- ContentValues values = new ContentValues();
- values.put(MediaStore.Images.Media.MIME_TYPE, JPEG_MIME_TYPE);
- Uri uri;
- try {
- uri = this.cordova.getActivity().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
- } catch (RuntimeException e) {
- LOG.d(LOG_TAG, "Can't write to external media storage.");
- try {
- uri = this.cordova.getActivity().getContentResolver().insert(MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
- } catch (RuntimeException ex) {
- LOG.d(LOG_TAG, "Can't write to internal media storage.");
- return null;
- }
- }
- return uri;
- }
-
/**
* Return a scaled and rotated bitmap based on the target width and height
*
@@ -1258,9 +1255,7 @@ private Uri whichContentStore() {
*/
public void processPicture(Bitmap bitmap, int encodingType) {
ByteArrayOutputStream jpeg_data = new ByteArrayOutputStream();
- CompressFormat compressFormat = encodingType == JPEG ?
- CompressFormat.JPEG :
- CompressFormat.PNG;
+ CompressFormat compressFormat = getCompressFormatForEncodingType(encodingType);
try {
if (bitmap.compress(compressFormat, mQuality, jpeg_data)) {
diff --git a/src/android/GalleryPathVO.java b/src/android/GalleryPathVO.java
new file mode 100644
index 000000000..4f2e7af33
--- /dev/null
+++ b/src/android/GalleryPathVO.java
@@ -0,0 +1,25 @@
+package org.apache.cordova.camera;
+
+public class GalleryPathVO {
+ private final String galleryPath;
+ private String picturesDirectory;
+ private String galleryFileName;
+
+ public GalleryPathVO(String picturesDirectory, String galleryFileName) {
+ this.picturesDirectory = picturesDirectory;
+ this.galleryFileName = galleryFileName;
+ this.galleryPath = this.picturesDirectory + "/" + this.galleryFileName;
+ }
+
+ public String getGalleryPath() {
+ return galleryPath;
+ }
+
+ public String getPicturesDirectory() {
+ return picturesDirectory;
+ }
+
+ public String getGalleryFileName() {
+ return galleryFileName;
+ }
+}