diff --git a/src/android/CameraLauncher.java b/src/android/CameraLauncher.java index 070cef8..8172e73 100644 --- a/src/android/CameraLauncher.java +++ b/src/android/CameraLauncher.java @@ -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"; @@ -197,7 +198,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect 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); } } } @@ -273,9 +274,9 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect 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 { @@ -356,11 +357,10 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect * * @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; @@ -420,7 +420,6 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect // set crop properties cropIntent.putExtra("crop", "true"); - // indicate output X and Y if (targetWidth > 0) { cropIntent.putExtra("outputX", targetWidth); @@ -439,7 +438,6 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect cropIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); cropIntent.putExtra("output", croppedUri); - // start the activity - we handle returning in onActivityResult if (this.cordova != null) { @@ -450,9 +448,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect 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"); } @@ -475,7 +471,6 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect this.croppedFilePath : this.imageFilePath; - if (this.encodingType == JPEG) { try { //We don't support PNG, so let's not pretend we do @@ -610,7 +605,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect 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); @@ -623,7 +618,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect 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(); @@ -639,28 +634,20 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect /** * 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); @@ -684,6 +671,23 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect 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. @@ -707,24 +711,22 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect 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 { @@ -750,10 +752,10 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect // 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()); @@ -777,6 +779,18 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect } + /** + * 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. * @@ -974,7 +988,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect 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); @@ -998,15 +1012,11 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect 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(); @@ -1052,7 +1062,6 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect // 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]); @@ -1092,8 +1101,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect } } return scaledBitmap; - } - finally { + } finally { // delete the temporary copy if (localFile != null) { localFile.delete(); @@ -1305,9 +1313,8 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect 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)); @@ -1319,7 +1326,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect 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; } } @@ -1381,7 +1388,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect } 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; diff --git a/src/android/FileHelper.java b/src/android/FileHelper.java index 7a5524b..03c91c3 100644 --- a/src/android/FileHelper.java +++ b/src/android/FileHelper.java @@ -142,22 +142,6 @@ public class FileHelper { 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. * @@ -225,7 +209,7 @@ public class FileHelper { } return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); } - + /** * Returns the mime type of the data specified by the given URI string. * @@ -233,7 +217,7 @@ public class FileHelper { * @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://")) {