Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix issue 711 heic format #731

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 56 additions & 49 deletions src/android/CameraLauncher.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
private static final String PNG_EXTENSION = "." + PNG_TYPE;
private static final String PNG_MIME_TYPE = "image/png";
private static final String JPEG_MIME_TYPE = "image/jpeg";
private static final String HEIC_MIME_TYPE = "image/heic";
private static final String GET_PICTURE = "Get Picture";
private static final String GET_VIDEO = "Get Video";
private static final String GET_All = "Get All";
Expand Down Expand Up @@ -197,7 +198,7 @@ else if ((this.srcType == PHOTOLIBRARY) || (this.srcType == SAVEDPHOTOALBUM)) {
if(!PermissionHelper.hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
PermissionHelper.requestPermission(this, SAVE_TO_ALBUM_SEC, Manifest.permission.READ_EXTERNAL_STORAGE);
} else {
this.getImage(this.srcType, destType, encodingType);
this.getImage(this.srcType, destType);
}
}
}
Expand Down Expand Up @@ -273,9 +274,9 @@ public void callTakePicture(int returnType, int encodingType) {

if (takePicturePermission && saveAlbumPermission) {
takePicture(returnType, encodingType);
} else if (saveAlbumPermission && !takePicturePermission) {
} else if (saveAlbumPermission) {
PermissionHelper.requestPermission(this, TAKE_PIC_SEC, Manifest.permission.CAMERA);
} else if (!saveAlbumPermission && takePicturePermission) {
} else if (takePicturePermission) {
PermissionHelper.requestPermissions(this, TAKE_PIC_SEC,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE});
} else {
Expand Down Expand Up @@ -356,11 +357,10 @@ private File createCaptureFile(int encodingType, String fileName) {
*
* @param srcType The album to get image from.
* @param returnType Set the type of image to return.
* @param encodingType
*/
// TODO: Images selected from SDCARD don't display correctly, but from CAMERA ALBUM do!
// TODO: Images from kitkat filechooser not going into crop function
public void getImage(int srcType, int returnType, int encodingType) {
public void getImage(int srcType, int returnType) {
Intent intent = new Intent();
String title = GET_PICTURE;
croppedUri = null;
Expand Down Expand Up @@ -420,7 +420,6 @@ private void performCrop(Uri picUri, int destType, Intent cameraIntent) {
// set crop properties
cropIntent.putExtra("crop", "true");


// indicate output X and Y
if (targetWidth > 0) {
cropIntent.putExtra("outputX", targetWidth);
Expand All @@ -439,7 +438,6 @@ private void performCrop(Uri picUri, int destType, Intent cameraIntent) {
cropIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
cropIntent.putExtra("output", croppedUri);


// start the activity - we handle returning in onActivityResult

if (this.cordova != null) {
Expand All @@ -450,9 +448,7 @@ private void performCrop(Uri picUri, int destType, Intent cameraIntent) {
LOG.e(LOG_TAG, "Crop operation not supported on this device");
try {
processResultFromCamera(destType, cameraIntent);
}
catch (IOException e)
{
} catch (IOException e) {
e.printStackTrace();
LOG.e(LOG_TAG, "Unable to write to file");
}
Expand All @@ -475,7 +471,6 @@ private void processResultFromCamera(int destType, Intent intent) throws IOExcep
this.croppedFilePath :
this.imageFilePath;


if (this.encodingType == JPEG) {
try {
//We don't support PNG, so let's not pretend we do
Expand Down Expand Up @@ -610,7 +605,7 @@ private void writeTakenPictureToGalleryStartingFromAndroidQ(GalleryPathVO galler
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));
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, getMimetypeForEncodingType());
Uri galleryOutputUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);

InputStream fileStream = org.apache.cordova.camera.FileHelper.getInputStreamFromUriString(imageUri.toString(), cordova);
Expand All @@ -623,7 +618,7 @@ private CompressFormat getCompressFormatForEncodingType(int encodingType) {

private GalleryPathVO getPicturesPath() {
String timeStamp = new SimpleDateFormat(TIME_FORMAT).format(new Date());
String imageFileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? JPEG_EXTENSION : PNG_EXTENSION);
String imageFileName = "IMG_" + timeStamp + getExtensionForEncodingType();
File storageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
storageDir.mkdirs();
Expand All @@ -639,28 +634,20 @@ private void refreshGallery(Uri contentUri) {

/**
* Converts output image format int value to string value of mime type.
* @param outputFormat int Output format of camera API.
* Must be value of either JPEG or PNG constant
* @return String String value of mime type or empty string if mime type is not supported
*/
private String getMimetypeForFormat(int outputFormat) {
if (outputFormat == PNG) return PNG_MIME_TYPE;
if (outputFormat == JPEG) return JPEG_MIME_TYPE;
private String getMimetypeForEncodingType() {
if (encodingType == PNG) return PNG_MIME_TYPE;
if (encodingType == JPEG) return JPEG_MIME_TYPE;
return "";
}


private String outputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
private String outputModifiedBitmap(Bitmap bitmap, Uri uri, String mimeTypeOfOriginalFile) throws IOException {
// Some content: URIs do not map to file paths (e.g. picasa).
String realPath = FileHelper.getRealPath(uri, this.cordova);
String fileName = calculateModifiedBitmapOutputFileName(mimeTypeOfOriginalFile, realPath);

// Get filename from uri
String fileName = realPath != null ?
realPath.substring(realPath.lastIndexOf('/') + 1) :
"modified." + (this.encodingType == JPEG ? JPEG_TYPE : PNG_TYPE);

String timeStamp = new SimpleDateFormat(TIME_FORMAT).format(new Date());
//String fileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? ".jpg" : ".png");
String modifiedPath = getTempDirectoryPath() + "/" + fileName;

OutputStream os = new FileOutputStream(modifiedPath);
Expand All @@ -684,6 +671,23 @@ private String outputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
return modifiedPath;
}

private String calculateModifiedBitmapOutputFileName(String mimeTypeOfOriginalFile, String realPath) {
if (realPath == null) {
return "modified" + getExtensionForEncodingType();
}
String fileName = realPath.substring(realPath.lastIndexOf('/') + 1);
if (getMimetypeForEncodingType().equals(mimeTypeOfOriginalFile)) {
return fileName;
}
// if the picture is not a jpeg or png, (a .heic for example) when processed to a bitmap
// the file extension is changed to the output format, f.e. an input file my_photo.heic could become my_photo.jpg
return fileName.substring(fileName.lastIndexOf(".") + 1) + getExtensionForEncodingType();
}

private String getExtensionForEncodingType() {
return this.encodingType == JPEG ? JPEG_EXTENSION : PNG_EXTENSION;
}


/**
* Applies all needed transformation to the image received from the gallery.
Expand All @@ -707,24 +711,22 @@ private void processResultFromGallery(int destType, Intent intent) {

String uriString = uri.toString();
String finalLocation = fileLocation != null ? fileLocation : uriString;
String mimeType = FileHelper.getMimeType(uriString, this.cordova);
String mimeTypeOfGalleryFile = FileHelper.getMimeType(uriString, this.cordova);

if (finalLocation == null) {
this.failPicture("Error retrieving result.");
} else {

// If you ask for video or the selected file doesn't have JPEG or PNG mime type
// there will be no attempt to resize any returned data
if (this.mediaType == VIDEO || !(JPEG_MIME_TYPE.equalsIgnoreCase(mimeType) || PNG_MIME_TYPE.equalsIgnoreCase(mimeType))) {
// If you ask for video or the selected file cannot be processed
// there will be no attempt to resize any returned data.
if (this.mediaType == VIDEO || !isImageMimeTypeProcessable(mimeTypeOfGalleryFile)) {
this.callbackContext.success(finalLocation);
}
else {
} else {

// This is a special case to just return the path as no scaling,
// rotating, nor compressing needs to be done
if (this.targetHeight == -1 && this.targetWidth == -1 &&
destType == FILE_URI && !this.correctOrientation &&
mimeType != null && mimeType.equalsIgnoreCase(getMimetypeForFormat(encodingType)))
getMimetypeForEncodingType().equalsIgnoreCase(mimeTypeOfGalleryFile))
{
this.callbackContext.success(finalLocation);
} else {
Expand All @@ -750,10 +752,10 @@ else if (destType == FILE_URI) {
// Did we modify the image?
if ( (this.targetHeight > 0 && this.targetWidth > 0) ||
(this.correctOrientation && this.orientationCorrected) ||
!mimeType.equalsIgnoreCase(getMimetypeForFormat(encodingType)))
!mimeTypeOfGalleryFile.equalsIgnoreCase(getMimetypeForEncodingType()))
{
try {
String modifiedPath = this.outputModifiedBitmap(bitmap, uri);
String modifiedPath = this.outputModifiedBitmap(bitmap, uri, mimeTypeOfGalleryFile);
// The modified image is cached by the app in order to get around this and not have to delete you
// application cache I'm adding the current system time to the end of the file url.
this.callbackContext.success("file://" + modifiedPath + "?" + System.currentTimeMillis());
Expand All @@ -777,6 +779,18 @@ else if (destType == FILE_URI) {

}

/**
* JPEG, PNG and HEIC mime types (images) can be scaled, decreased in quantity, corrected by orientation.
* But f.e. an image/gif cannot be scaled, but is can be selected through the PHOTOLIBRARY.
*
* @param mimeType The mimeType to check
* @return if the mimeType is a processable image mime type
*/
private boolean isImageMimeTypeProcessable(String mimeType) {
return JPEG_MIME_TYPE.equalsIgnoreCase(mimeType) || PNG_MIME_TYPE.equalsIgnoreCase(mimeType)
|| HEIC_MIME_TYPE.equalsIgnoreCase(mimeType);
}

/**
* Called when the camera view exits.
*
Expand Down Expand Up @@ -974,7 +988,7 @@ private Bitmap getScaledAndRotatedBitmap(String imageUrl) throws IOException {
if (fileStream != null) {
// Generate a temporary file
String timeStamp = new SimpleDateFormat(TIME_FORMAT).format(new Date());
String fileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? JPEG_EXTENSION : PNG_EXTENSION);
String fileName = "IMG_" + timeStamp + (getExtensionForEncodingType());
localFile = new File(getTempDirectoryPath() + fileName);
galleryUri = Uri.fromFile(localFile);
writeUncompressedImage(fileStream, galleryUri);
Expand All @@ -998,15 +1012,11 @@ private Bitmap getScaledAndRotatedBitmap(String imageUrl) throws IOException {
rotate = 0;
}
}
}
catch (Exception e)
{
} catch (Exception e) {
LOG.e(LOG_TAG,"Exception while getting input stream: "+ e.toString());
return null;
}



try {
// figure out the original width and height of the image
BitmapFactory.Options options = new BitmapFactory.Options();
Expand Down Expand Up @@ -1052,7 +1062,6 @@ private Bitmap getScaledAndRotatedBitmap(String imageUrl) throws IOException {
// determine the correct aspect ratio
int[] widthHeight = calculateAspectRatio(rotatedWidth, rotatedHeight);


// Load in the smallest bitmap possible that is closest to the size we want
options.inJustDecodeBounds = false;
options.inSampleSize = calculateSampleSize(rotatedWidth, rotatedHeight, widthHeight[0], widthHeight[1]);
Expand Down Expand Up @@ -1092,8 +1101,7 @@ private Bitmap getScaledAndRotatedBitmap(String imageUrl) throws IOException {
}
}
return scaledBitmap;
}
finally {
} finally {
// delete the temporary copy
if (localFile != null) {
localFile.delete();
Expand Down Expand Up @@ -1305,9 +1313,8 @@ public void onScanCompleted(String path, Uri uri) {
this.conn.disconnect();
}


public void onRequestPermissionResult(int requestCode, String[] permissions,
int[] grantResults) throws JSONException {
int[] grantResults) {
for (int r : grantResults) {
if (r == PackageManager.PERMISSION_DENIED) {
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, PERMISSION_DENIED_ERROR));
Expand All @@ -1319,7 +1326,7 @@ public void onRequestPermissionResult(int requestCode, String[] permissions,
takePicture(this.destType, this.encodingType);
break;
case SAVE_TO_ALBUM_SEC:
this.getImage(this.srcType, this.destType, this.encodingType);
this.getImage(this.srcType, this.destType);
break;
}
}
Expand Down Expand Up @@ -1381,7 +1388,7 @@ public void onRestoreStateForActivityResult(Bundle state, CallbackContext callba
}

if (state.containsKey(IMAGE_FILE_PATH_KEY)) {
this.imageFilePath = state.getString(IMAGE_FILE_PATH_KEY);
this.imageFilePath = state.getString(IMAGE_FILE_PATH_KEY);
}

this.callbackContext = callbackContext;
Expand Down
20 changes: 2 additions & 18 deletions src/android/FileHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,22 +142,6 @@ else if ("file".equalsIgnoreCase(uri.getScheme())) {
return null;
}

public static String getRealPathFromURI_BelowAPI11(Context context, Uri contentUri) {
String[] proj = { MediaStore.Images.Media.DATA };
String result = null;

try {
Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
result = cursor.getString(column_index);

} catch (Exception e) {
result = null;
}
return result;
}

/**
* Returns an input stream based on given URI string.
*
Expand Down Expand Up @@ -225,15 +209,15 @@ public static String getMimeTypeForExtension(String path) {
}
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}

/**
* Returns the mime type of the data specified by the given URI string.
*
* @param uriString the URI string of the data
* @return the mime type of the specified data
*/
public static String getMimeType(String uriString, CordovaInterface cordova) {
String mimeType = null;
String mimeType;

Uri uri = Uri.parse(uriString);
if (uriString.startsWith("content://")) {
Expand Down