From 2df903ae611e1961968e85f75d0fe56c9ee11371 Mon Sep 17 00:00:00 2001 From: Serge Huijben Date: Fri, 20 Feb 2015 00:32:31 +0100 Subject: [PATCH 1/7] more distinguishable error messages enable dropbox images (won't crash the app anymore) option to choose only from gallery apps, to stop people from introducing new problems --- src/android/CameraLauncher.java | 123 ++++++++++-------- src/android/FileHelper.java | 212 +++++++++++++++++++++++++------- www/Camera.js | 3 +- 3 files changed, 244 insertions(+), 94 deletions(-) diff --git a/src/android/CameraLauncher.java b/src/android/CameraLauncher.java index b5574ab48..3cd10eeef 100644 --- a/src/android/CameraLauncher.java +++ b/src/android/CameraLauncher.java @@ -32,6 +32,7 @@ Licensed to the Apache Software Foundation (ASF) under one import org.apache.cordova.PluginResult; import org.json.JSONArray; import org.json.JSONException; +import org.json.JSONObject; import android.app.Activity; import android.content.ActivityNotFoundException; @@ -75,10 +76,23 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect private static final String GET_PICTURE = "Get Picture"; private static final String GET_VIDEO = "Get Video"; private static final String GET_All = "Get All"; - + private static final String LOG_TAG = "CameraLauncher"; private static final int CROP_CAMERA = 100; + //error messages + private static final String E001 = "E001: Unable to create bitmap!"; + private static final String E002 = "E002: Error capturing image - no media storage found."; + private static final String E003 = "E003: null data from photo library"; + private static final String E004 = "E004: Unable to retrieve path to picture!"; + private static final String E005 = "E005: Error retrieving image."; + private static final String E006 = "E006: Error capturing image."; + private static final String E007 = "E007: Camera cancelled."; + private static final String E008 = "E008: Did not complete!"; + private static final String E009 = "E009: Selection cancelled."; + private static final String E010 = "E010: Selection did not complete!"; + private static final String E011 = "E011: Error compressing image."; + private int mQuality; // Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality) private int targetWidth; // desired width of the image private int targetHeight; // desired height of the image @@ -89,6 +103,9 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect private boolean correctOrientation; // Should the pictures orientation be corrected private boolean orientationCorrected; // Has the picture's orientation been corrected private boolean allowEdit; // Should we allow the user to crop the image. + private JSONObject popoverOptions; // not used on android + private int cameraDirection; // not used on android + private boolean onlyGalleries; // Should we force the user to select images from only the galleries on the device. public CallbackContext callbackContext; private int numPics; @@ -118,16 +135,22 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo this.mediaType = PICTURE; this.mQuality = 80; - this.mQuality = args.getInt(0); - destType = args.getInt(1); - srcType = args.getInt(2); - this.targetWidth = args.getInt(3); - this.targetHeight = args.getInt(4); - this.encodingType = args.getInt(5); - this.mediaType = args.getInt(6); - this.allowEdit = args.getBoolean(7); - this.correctOrientation = args.getBoolean(8); - this.saveToPhotoAlbum = args.getBoolean(9); + this.mQuality = args.optInt(0); + destType = args.optInt(1); + srcType = args.optInt(2); + this.targetWidth = args.optInt(3); + this.targetHeight = args.optInt(4); + this.encodingType = args.optInt(5); + this.mediaType = args.optInt(6); + this.allowEdit = args.optBoolean(7); + this.correctOrientation = args.optBoolean(8); + this.saveToPhotoAlbum = args.optBoolean(9); + this.popoverOptions = args.optJSONObject(10); + this.cameraDirection = args.optInt(11); + this.onlyGalleries = args.optBoolean(12); + if (this.allowEdit){ + this.onlyGalleries = true; + } // If the user specifies a 0 or smaller width/height // make it -1 so later comparisons succeed @@ -153,11 +176,11 @@ else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) { callbackContext.sendPluginResult(r); return true; } - + PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT); r.setKeepCallback(true); callbackContext.sendPluginResult(r); - + return true; } return false; @@ -242,7 +265,7 @@ private File createCaptureFile(int encodingType) { * @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality) * @param srcType The album to get image from. * @param returnType Set the type of image to return. - * @param encodingType + * @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 @@ -252,18 +275,20 @@ public void getImage(int srcType, int returnType, int encodingType) { croppedUri = null; if (this.mediaType == PICTURE) { intent.setType("image/*"); - if (this.allowEdit) { + if (this.onlyGalleries) { intent.setAction(Intent.ACTION_PICK); - intent.putExtra("crop", "true"); - if (targetWidth > 0) { - intent.putExtra("outputX", targetWidth); - } - if (targetHeight > 0) { - intent.putExtra("outputY", targetHeight); - } - if (targetHeight > 0 && targetWidth > 0 && targetWidth == targetHeight) { - intent.putExtra("aspectX", 1); - intent.putExtra("aspectY", 1); + if (this.allowEdit) { + intent.putExtra("crop", "true"); + if (targetWidth > 0) { + intent.putExtra("outputX", targetWidth); + } + if (targetHeight > 0) { + intent.putExtra("outputY", targetHeight); + } + if (targetHeight > 0 && targetWidth > 0 && targetWidth == targetHeight) { + intent.putExtra("aspectX", 1); + intent.putExtra("aspectY", 1); + } } File photo = createCaptureFile(encodingType); croppedUri = Uri.fromFile(photo); @@ -293,7 +318,7 @@ 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) { @@ -366,11 +391,11 @@ private void processResultFromCamera(int destType, Intent intent) throws IOExcep // Try to get the bitmap from intent. bitmap = (Bitmap)intent.getExtras().get("data"); } - + // Double-check the bitmap. if (bitmap == null) { Log.d(LOG_TAG, "I either have a null image path or bitmap"); - this.failPicture("Unable to create bitmap!"); + this.failPicture(E001); return; } @@ -397,12 +422,12 @@ else if (destType == FILE_URI || destType == NATIVE_URI) { } if (uri == null) { - this.failPicture("Error capturing image - no media storage found."); + this.failPicture(E002); return; } // If all this is true we shouldn't compress the image. - if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100 && + if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100 && !this.correctOrientation) { writeUncompressedImage(uri); @@ -445,7 +470,7 @@ else if (destType == FILE_URI || destType == NATIVE_URI) { bitmap = null; } -private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException { + private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException { // Create an ExifHelper to save the exif data that is lost during compression String modifiedPath = getTempDirectoryPath() + "/modified.jpg"; @@ -472,7 +497,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException { return modifiedPath; } -/** + /** * Applies all needed transformation to the image received from the gallery. * * @param destType In which form should we return the image @@ -484,7 +509,7 @@ private void processResultFromGallery(int destType, Intent intent) { if (croppedUri != null) { uri = croppedUri; } else { - this.failPicture("null data from photo library"); + this.failPicture(E003); return; } } @@ -508,7 +533,7 @@ private void processResultFromGallery(int destType, Intent intent) { // If we don't have a valid image so quit. if (!("image/jpeg".equalsIgnoreCase(mimeType) || "image/png".equalsIgnoreCase(mimeType))) { Log.d(LOG_TAG, "I either have a null image path or bitmap"); - this.failPicture("Unable to retrieve path to picture!"); + this.failPicture(E004); return; } Bitmap bitmap = null; @@ -519,7 +544,7 @@ private void processResultFromGallery(int destType, Intent intent) { } if (bitmap == null) { Log.d(LOG_TAG, "I either have a null image path or bitmap"); - this.failPicture("Unable to create bitmap!"); + this.failPicture(E001); return; } @@ -554,7 +579,7 @@ else if (destType == FILE_URI || destType == NATIVE_URI) { this.callbackContext.success("file://" + modifiedPath + "?" + System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); - this.failPicture("Error retrieving image."); + this.failPicture(E005); } } else { @@ -569,7 +594,7 @@ else if (destType == FILE_URI || destType == NATIVE_URI) { } } } - + /** * Called when the camera view exits. * @@ -590,15 +615,15 @@ public void onActivityResult(int requestCode, int resultCode, Intent intent) { this.callbackContext .success(croppedUri.toString()); croppedUri = null; - + }// If cancelled else if (resultCode == Activity.RESULT_CANCELED) { - this.failPicture("Camera cancelled."); + this.failPicture(E007); } // If something else else { - this.failPicture("Did not complete!"); + this.failPicture(E008); } } @@ -610,18 +635,18 @@ else if (resultCode == Activity.RESULT_CANCELED) { this.processResultFromCamera(destType, intent); } catch (IOException e) { e.printStackTrace(); - this.failPicture("Error capturing image."); + this.failPicture(E006); } } // If cancelled else if (resultCode == Activity.RESULT_CANCELED) { - this.failPicture("Camera cancelled."); + this.failPicture(E007); } // If something else else { - this.failPicture("Did not complete!"); + this.failPicture(E008); } } @@ -631,10 +656,10 @@ else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) { this.processResultFromGallery(destType, intent); } else if (resultCode == Activity.RESULT_CANCELED) { - this.failPicture("Selection cancelled."); + this.failPicture(E009); } else { - this.failPicture("Selection did not complete!"); + this.failPicture(E010); } } } @@ -738,7 +763,7 @@ private Uri getUriFromMediaStore() { * * @param imagePath * @return - * @throws IOException + * @throws IOException */ private Bitmap getScaledBitmap(String imageUrl) throws IOException { // If no new width or height were specified return the original bitmap @@ -750,13 +775,13 @@ private Bitmap getScaledBitmap(String imageUrl) throws IOException { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova), null, options); - + //CB-2292: WTF? Why is the width null? if(options.outWidth == 0 || options.outHeight == 0) { return null; } - + // determine the correct aspect ratio int[] widthHeight = calculateAspectRatio(options.outWidth, options.outHeight); @@ -934,7 +959,7 @@ public void processPicture(Bitmap bitmap) { code = null; } } catch (Exception e) { - this.failPicture("Error compressing image."); + this.failPicture(E011); } jpeg_data = null; } diff --git a/src/android/FileHelper.java b/src/android/FileHelper.java index 24ced59e7..3365f3b31 100644 --- a/src/android/FileHelper.java +++ b/src/android/FileHelper.java @@ -18,8 +18,14 @@ Licensed to the Apache Software Foundation (ASF) under one */ package org.apache.cordova.camera; +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.CursorLoader; import android.database.Cursor; import android.net.Uri; +import android.os.Build; +import android.provider.DocumentsContract; +import android.provider.MediaStore; import android.webkit.MimeTypeMap; import org.apache.cordova.CordovaInterface; @@ -35,85 +41,201 @@ public class FileHelper { private static final String _DATA = "_data"; /** - * Returns the real path of the given URI string. - * If the given URI string represents a content:// URI, the real path is retrieved from the media store. + * Returns the real path of the given URI string. If the given URI string + * represents a content:// URI, the real path is retrieved from the media + * store. * - * @param uriString the URI string of the audio/image/video - * @param cordova the current application context + * @param uriString + * the URI string of the audio/image/video + * @param cordova + * the current application context * @return the full path to the file */ @SuppressWarnings("deprecation") - public static String getRealPath(String uriString, CordovaInterface cordova) { + public static String getRealPath(Uri uri, CordovaInterface cordova) { String realPath = null; - if (uriString.startsWith("content://")) { - String[] proj = { _DATA }; - Cursor cursor = cordova.getActivity().managedQuery(Uri.parse(uriString), proj, null, null, null); - int column_index = cursor.getColumnIndexOrThrow(_DATA); - cursor.moveToFirst(); - realPath = cursor.getString(column_index); - if (realPath == null) { - LOG.e(LOG_TAG, "Could get real path for URI string %s", uriString); - } - } else if (uriString.startsWith("file://")) { - realPath = uriString.substring(7); - if (realPath.startsWith("/android_asset/")) { - LOG.e(LOG_TAG, "Cannot get real path for URI string %s because it is a file:///android_asset/ URI.", uriString); - realPath = null; - } - } else { - realPath = uriString; - } + if (Build.VERSION.SDK_INT < 11) + realPath = FileHelper.getRealPathFromURI_BelowAPI11( + cordova.getActivity(), uri); + + // SDK >= 11 && SDK < 19 + else if (Build.VERSION.SDK_INT < 19) + realPath = FileHelper.getRealPathFromURI_API11to18( + cordova.getActivity(), uri); + + // SDK > 19 (Android 4.4) + else + realPath = FileHelper.getRealPathFromURI_API19( + cordova.getActivity(), uri); + + // if (uriString.startsWith("content://")) { + // String[] proj = { _DATA }; + // Cursor cursor = + // cordova.getActivity().managedQuery(Uri.parse(uriString), proj, null, + // null, null); + // int column_index = cursor.getColumnIndexOrThrow(_DATA); + // cursor.moveToFirst(); + // realPath = cursor.getString(column_index); + // if (realPath == null) { + // LOG.e(LOG_TAG, "Could get real path for URI string %s", uriString); + // } + // } else if (uriString.startsWith("file://")) { + // realPath = uriString.substring(7); + // if (realPath.startsWith("/android_asset/")) { + // LOG.e(LOG_TAG, + // "Cannot get real path for URI string %s because it is a file:///android_asset/ URI.", + // uriString); + // realPath = null; + // } + // } else { + // realPath = uriString; + // } return realPath; } /** - * Returns the real path of the given URI. - * If the given URI is a content:// URI, the real path is retrieved from the media store. + * Returns the real path of the given URI. If the given URI is a content:// + * URI, the real path is retrieved from the media store. * - * @param uri the URI of the audio/image/video - * @param cordova the current application context + * @param uri + * the URI of the audio/image/video + * @param cordova + * the current application context * @return the full path to the file */ - public static String getRealPath(Uri uri, CordovaInterface cordova) { - return FileHelper.getRealPath(uri.toString(), cordova); + public static String getRealPath(String uriString, CordovaInterface cordova) { + return FileHelper.getRealPath(Uri.parse(uriString), cordova); + } + + @SuppressLint("NewApi") + public static String getRealPathFromURI_API19(Context context, Uri uri) { + String filePath = ""; + try { + String wholeID = DocumentsContract.getDocumentId(uri); + + // Split at colon, use second item in the array + String id = wholeID.indexOf(":") > -1 ? wholeID.split(":")[1] + : wholeID.indexOf(";") > -1 ? wholeID.split(";")[1] + : wholeID; + + String[] column = { MediaStore.Images.Media.DATA }; + + // where id is equal to + String sel = MediaStore.Images.Media._ID + "=?"; + + Cursor cursor = context.getContentResolver().query( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, column, sel, + new String[] { id }, null); + + int columnIndex = cursor.getColumnIndex(column[0]); + + if (cursor.moveToFirst()) { + filePath = cursor.getString(columnIndex); + } + cursor.close(); + } catch (Exception e) { + filePath = ""; + } + return filePath; + } + + @SuppressLint("NewApi") + public static String getRealPathFromURI_API11to18(Context context, + Uri contentUri) { + String[] proj = { MediaStore.Images.Media.DATA }; + String result = null; + + try { + CursorLoader cursorLoader = new CursorLoader(context, contentUri, + proj, null, null, null); + Cursor cursor = cursorLoader.loadInBackground(); + + if (cursor != null) { + int column_index = cursor + .getColumnIndexOrThrow(MediaStore.Images.Media.DATA); + cursor.moveToFirst(); + result = cursor.getString(column_index); + } + } catch (Exception e) { + result = null; + } + return result; } + 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. * - * @param uriString the URI string from which to obtain the input stream - * @param cordova the current application context - * @return an input stream into the data at the given URI or null if given an invalid URI string + * @param uriString + * the URI string from which to obtain the input stream + * @param cordova + * the current application context + * @return an input stream into the data at the given URI or null if given + * an invalid URI string * @throws IOException */ - public static InputStream getInputStreamFromUriString(String uriString, CordovaInterface cordova) throws IOException { + public static InputStream getInputStreamFromUriString(String uriString, + CordovaInterface cordova) throws IOException { + InputStream returnValue = null; if (uriString.startsWith("content")) { Uri uri = Uri.parse(uriString); - return cordova.getActivity().getContentResolver().openInputStream(uri); + returnValue = cordova.getActivity().getContentResolver() + .openInputStream(uri); } else if (uriString.startsWith("file://")) { int question = uriString.indexOf("?"); if (question > -1) { - uriString = uriString.substring(0,question); + uriString = uriString.substring(0, question); } if (uriString.startsWith("file:///android_asset/")) { Uri uri = Uri.parse(uriString); String relativePath = uri.getPath().substring(15); - return cordova.getActivity().getAssets().open(relativePath); + returnValue = cordova.getActivity().getAssets() + .open(relativePath); } else { - return new FileInputStream(getRealPath(uriString, cordova)); + // might still be content so try that first + try { + returnValue = cordova.getActivity().getContentResolver() + .openInputStream(Uri.parse(uriString)); + } catch (Exception e) { + returnValue = null; + } + if (returnValue == null) { + returnValue = new FileInputStream(getRealPath(uriString, + cordova)); + } } } else { - return new FileInputStream(getRealPath(uriString, cordova)); + returnValue = new FileInputStream(uriString); } + return returnValue; } /** - * Removes the "file://" prefix from the given URI string, if applicable. - * If the given URI string doesn't have a "file://" prefix, it is returned unchanged. + * Removes the "file://" prefix from the given URI string, if applicable. If + * the given URI string doesn't have a "file://" prefix, it is returned + * unchanged. * - * @param uriString the URI string to operate on + * @param uriString + * the URI string to operate on * @return a path without the "file://" prefix */ public static String stripFileProtocol(String uriString) { @@ -129,18 +251,20 @@ public static String getMimeTypeForExtension(String path) { if (lastDot != -1) { extension = extension.substring(lastDot + 1); } - // Convert the URI string to lower case to ensure compatibility with MimeTypeMap (see CB-2185). + // Convert the URI string to lower case to ensure compatibility with + // MimeTypeMap (see CB-2185). extension = extension.toLowerCase(Locale.getDefault()); if (extension.equals("3ga")) { return "audio/3gpp"; } 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 + * @param uriString + * the URI string of the data * @return the mime type of the specified data */ public static String getMimeType(String uriString, CordovaInterface cordova) { diff --git a/www/Camera.js b/www/Camera.js index 555bb5efa..6ec82ee2d 100644 --- a/www/Camera.js +++ b/www/Camera.js @@ -59,9 +59,10 @@ cameraExport.getPicture = function(successCallback, errorCallback, options) { var saveToPhotoAlbum = !!options.saveToPhotoAlbum; var popoverOptions = getValue(options.popoverOptions, null); var cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK); + var onlyGalleries = !!options.onlyGalleries; var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType, - mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection]; + mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection, onlyGalleries]; exec(successCallback, errorCallback, "Camera", "takePicture", args); // XXX: commented out From 4636cbe2565406a36e4165662ba445c72bec1940 Mon Sep 17 00:00:00 2001 From: Serge Huijben Date: Fri, 20 Feb 2015 10:07:21 +0100 Subject: [PATCH 2/7] add doc for added property onlyGalleries --- doc/index.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/index.md b/doc/index.md index 0aee1e6f5..98683f5e3 100644 --- a/doc/index.md +++ b/doc/index.md @@ -118,7 +118,7 @@ Can only return photos as base64-encoded image. ### Firefox OS Quirks -Camera plugin is currently implemented using [Web Activities](https://hacks.mozilla.org/2013/01/introducing-web-activities/). +Camera plugin is currently implemented using [Web Activities](https://hacks.mozilla.org/2013/01/introducing-web-activities/). ### iOS Quirks @@ -241,6 +241,8 @@ Optional parameters to customize the camera settings. FRONT : 1 // Use the front-facing camera }; +- __onlyGalleries__: Android-only Allow getImage to only get from gallery apps, excluding dropbox,drive,onedrive and filesystem explorers. _(Boolean)_ + ### Amazon Fire OS Quirks - Any `cameraDirection` value results in a back-facing photo. From 8176ba3160ee2d5860c9adcace2dcfc4eb4c0806 Mon Sep 17 00:00:00 2001 From: Serge Huijben Date: Fri, 20 Feb 2015 16:58:50 +0100 Subject: [PATCH 3/7] edit index.md --- doc/index.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/index.md b/doc/index.md index 98683f5e3..d643826e6 100644 --- a/doc/index.md +++ b/doc/index.md @@ -255,10 +255,12 @@ Optional parameters to customize the camera settings. - Any `cameraDirection` value results in a back-facing photo. -- Ignores the `allowEdit` parameter. +- `allowEdit` does not always shop crop function. - `Camera.PictureSourceType.PHOTOLIBRARY` and `Camera.PictureSourceType.SAVEDPHOTOALBUM` both display the same photo album. +- `onlyGalleries` is an Android only option for when picking images (if true, use only gallery apps to get a photo from) + ### BlackBerry 10 Quirks - Ignores the `quality` parameter. From 829d5c0f96bd7975b302276b10088da3d64d3197 Mon Sep 17 00:00:00 2001 From: Serge Huijben Date: Wed, 4 Mar 2015 23:57:56 +0100 Subject: [PATCH 4/7] CB-8235 cordova plugin fails to get dropbox image with spaces in name or path --- src/android/FileHelper.java | 129 ++++++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 27 deletions(-) diff --git a/src/android/FileHelper.java b/src/android/FileHelper.java index 24ced59e7..d3d7867ca 100644 --- a/src/android/FileHelper.java +++ b/src/android/FileHelper.java @@ -18,8 +18,14 @@ Licensed to the Apache Software Foundation (ASF) under one */ package org.apache.cordova.camera; +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.CursorLoader; import android.database.Cursor; import android.net.Uri; +import android.os.Build; +import android.provider.DocumentsContract; +import android.provider.MediaStore; import android.webkit.MimeTypeMap; import org.apache.cordova.CordovaInterface; @@ -43,27 +49,19 @@ public class FileHelper { * @return the full path to the file */ @SuppressWarnings("deprecation") - public static String getRealPath(String uriString, CordovaInterface cordova) { + public static String getRealPath(Uri uri, CordovaInterface cordova) { String realPath = null; - if (uriString.startsWith("content://")) { - String[] proj = { _DATA }; - Cursor cursor = cordova.getActivity().managedQuery(Uri.parse(uriString), proj, null, null, null); - int column_index = cursor.getColumnIndexOrThrow(_DATA); - cursor.moveToFirst(); - realPath = cursor.getString(column_index); - if (realPath == null) { - LOG.e(LOG_TAG, "Could get real path for URI string %s", uriString); - } - } else if (uriString.startsWith("file://")) { - realPath = uriString.substring(7); - if (realPath.startsWith("/android_asset/")) { - LOG.e(LOG_TAG, "Cannot get real path for URI string %s because it is a file:///android_asset/ URI.", uriString); - realPath = null; - } - } else { - realPath = uriString; - } + if (Build.VERSION.SDK_INT < 11) + realPath = FileHelper.getRealPathFromURI_BelowAPI11(cordova.getActivity(), uri); + + // SDK >= 11 && SDK < 19 + else if (Build.VERSION.SDK_INT < 19) + realPath = FileHelper.getRealPathFromURI_API11to18(cordova.getActivity(), uri); + + // SDK > 19 (Android 4.4) + else + realPath = FileHelper.getRealPathFromURI_API19(cordova.getActivity(), uri); return realPath; } @@ -76,8 +74,74 @@ public static String getRealPath(String uriString, CordovaInterface cordova) { * @param cordova the current application context * @return the full path to the file */ - public static String getRealPath(Uri uri, CordovaInterface cordova) { - return FileHelper.getRealPath(uri.toString(), cordova); + public static String getRealPath(String uriString, CordovaInterface cordova) { + return FileHelper.getRealPath(Uri.parse(uriString), cordova); + } + + @SuppressLint("NewApi") + public static String getRealPathFromURI_API19(Context context, Uri uri) { + String filePath = ""; + try { + String wholeID = DocumentsContract.getDocumentId(uri); + + // Split at colon, use second item in the array + String id = wholeID.indexOf(":") > -1 ? wholeID.split(":")[1] : wholeID.indexOf(";") > -1 ? wholeID + .split(";")[1] : wholeID; + + String[] column = { MediaStore.Images.Media.DATA }; + + // where id is equal to + String sel = MediaStore.Images.Media._ID + "=?"; + + Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, column, + sel, new String[] { id }, null); + + int columnIndex = cursor.getColumnIndex(column[0]); + + if (cursor.moveToFirst()) { + filePath = cursor.getString(columnIndex); + } + cursor.close(); + } catch (Exception e) { + filePath = ""; + } + return filePath; + } + + @SuppressLint("NewApi") + public static String getRealPathFromURI_API11to18(Context context, Uri contentUri) { + String[] proj = { MediaStore.Images.Media.DATA }; + String result = null; + + try { + CursorLoader cursorLoader = new CursorLoader(context, contentUri, proj, null, null, null); + Cursor cursor = cursorLoader.loadInBackground(); + + if (cursor != null) { + int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); + cursor.moveToFirst(); + result = cursor.getString(column_index); + } + } catch (Exception e) { + result = null; + } + return result; + } + + 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; } /** @@ -88,25 +152,36 @@ public static String getRealPath(Uri uri, CordovaInterface cordova) { * @return an input stream into the data at the given URI or null if given an invalid URI string * @throws IOException */ - public static InputStream getInputStreamFromUriString(String uriString, CordovaInterface cordova) throws IOException { + public static InputStream getInputStreamFromUriString(String uriString, CordovaInterface cordova) + throws IOException { + InputStream returnValue = null; if (uriString.startsWith("content")) { Uri uri = Uri.parse(uriString); - return cordova.getActivity().getContentResolver().openInputStream(uri); + returnValue = cordova.getActivity().getContentResolver().openInputStream(uri); } else if (uriString.startsWith("file://")) { int question = uriString.indexOf("?"); if (question > -1) { - uriString = uriString.substring(0,question); + uriString = uriString.substring(0, question); } if (uriString.startsWith("file:///android_asset/")) { Uri uri = Uri.parse(uriString); String relativePath = uri.getPath().substring(15); - return cordova.getActivity().getAssets().open(relativePath); + returnValue = cordova.getActivity().getAssets().open(relativePath); } else { - return new FileInputStream(getRealPath(uriString, cordova)); + // might still be content so try that first + try { + returnValue = cordova.getActivity().getContentResolver().openInputStream(Uri.parse(uriString)); + } catch (Exception e) { + returnValue = null; + } + if (returnValue == null) { + returnValue = new FileInputStream(getRealPath(uriString, cordova)); + } } } else { - return new FileInputStream(getRealPath(uriString, cordova)); + returnValue = new FileInputStream(uriString); } + return returnValue; } /** From e0fe3eefd52676ffbccc6d70e1756a1b3747cc7b Mon Sep 17 00:00:00 2001 From: Serge Huijben Date: Thu, 5 Mar 2015 07:55:46 +0100 Subject: [PATCH 5/7] add option - onlyGalleries: Android-only Allow getImage to only get from gallery apps, excluding dropbox,drive,onedrive and filesystem explorers. --- doc/index.md | 2 ++ src/android/CameraLauncher.java | 53 ++++++++++++++++++++------------- www/Camera.js | 3 +- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/doc/index.md b/doc/index.md index 0aee1e6f5..09bdfc7a4 100644 --- a/doc/index.md +++ b/doc/index.md @@ -241,6 +241,8 @@ Optional parameters to customize the camera settings. FRONT : 1 // Use the front-facing camera }; +- __onlyGalleries__: Android-only Allow getImage to only get from gallery apps, excluding dropbox,drive,onedrive and filesystem explorers. _(Boolean)_ + ### Amazon Fire OS Quirks - Any `cameraDirection` value results in a back-facing photo. diff --git a/src/android/CameraLauncher.java b/src/android/CameraLauncher.java index b5574ab48..b409d379e 100644 --- a/src/android/CameraLauncher.java +++ b/src/android/CameraLauncher.java @@ -89,6 +89,9 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect private boolean correctOrientation; // Should the pictures orientation be corrected private boolean orientationCorrected; // Has the picture's orientation been corrected private boolean allowEdit; // Should we allow the user to crop the image. + private JSONObject popoverOptions; // not used on android + private int cameraDirection; // not used on android + private boolean onlyGalleries; // Should we force the user to only be able to select images from the gallery apps on the device. public CallbackContext callbackContext; private int numPics; @@ -118,16 +121,22 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo this.mediaType = PICTURE; this.mQuality = 80; - this.mQuality = args.getInt(0); - destType = args.getInt(1); - srcType = args.getInt(2); - this.targetWidth = args.getInt(3); - this.targetHeight = args.getInt(4); - this.encodingType = args.getInt(5); - this.mediaType = args.getInt(6); - this.allowEdit = args.getBoolean(7); - this.correctOrientation = args.getBoolean(8); - this.saveToPhotoAlbum = args.getBoolean(9); + this.mQuality = args.optInt(0); + destType = args.optInt(1); + srcType = args.optInt(2); + this.targetWidth = args.optInt(3); + this.targetHeight = args.optInt(4); + this.encodingType = args.optInt(5); + this.mediaType = args.optInt(6); + this.allowEdit = args.optBoolean(7); + this.correctOrientation = args.optBoolean(8); + this.saveToPhotoAlbum = args.optBoolean(9); + this.popoverOptions = args.optJSONObject(10); + this.cameraDirection = args.optInt(11); + this.onlyGalleries = args.optBoolean(12); + if (this.allowEdit){ + this.onlyGalleries = true; + } // If the user specifies a 0 or smaller width/height // make it -1 so later comparisons succeed @@ -252,18 +261,20 @@ public void getImage(int srcType, int returnType, int encodingType) { croppedUri = null; if (this.mediaType == PICTURE) { intent.setType("image/*"); - if (this.allowEdit) { + if (this.onlyGalleries) { intent.setAction(Intent.ACTION_PICK); - intent.putExtra("crop", "true"); - if (targetWidth > 0) { - intent.putExtra("outputX", targetWidth); - } - if (targetHeight > 0) { - intent.putExtra("outputY", targetHeight); - } - if (targetHeight > 0 && targetWidth > 0 && targetWidth == targetHeight) { - intent.putExtra("aspectX", 1); - intent.putExtra("aspectY", 1); + if (this.allowEdit) { + intent.putExtra("crop", "true"); + if (targetWidth > 0) { + intent.putExtra("outputX", targetWidth); + } + if (targetHeight > 0) { + intent.putExtra("outputY", targetHeight); + } + if (targetHeight > 0 && targetWidth > 0 && targetWidth == targetHeight) { + intent.putExtra("aspectX", 1); + intent.putExtra("aspectY", 1); + } } File photo = createCaptureFile(encodingType); croppedUri = Uri.fromFile(photo); diff --git a/www/Camera.js b/www/Camera.js index 555bb5efa..6ec82ee2d 100644 --- a/www/Camera.js +++ b/www/Camera.js @@ -59,9 +59,10 @@ cameraExport.getPicture = function(successCallback, errorCallback, options) { var saveToPhotoAlbum = !!options.saveToPhotoAlbum; var popoverOptions = getValue(options.popoverOptions, null); var cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK); + var onlyGalleries = !!options.onlyGalleries; var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType, - mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection]; + mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection, onlyGalleries]; exec(successCallback, errorCallback, "Camera", "takePicture", args); // XXX: commented out From e65d9050de06012a83514660ed558f445f9f2bf8 Mon Sep 17 00:00:00 2001 From: Serge Huijben Date: Thu, 5 Mar 2015 07:55:46 +0100 Subject: [PATCH 6/7] add option - onlyGalleries: Android-only Allow getImage to only get from gallery apps, excluding dropbox,drive,onedrive and filesystem explorers. --- doc/index.md | 2 ++ src/android/CameraLauncher.java | 54 ++++++++++++++++++++------------- www/Camera.js | 3 +- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/doc/index.md b/doc/index.md index 0aee1e6f5..09bdfc7a4 100644 --- a/doc/index.md +++ b/doc/index.md @@ -241,6 +241,8 @@ Optional parameters to customize the camera settings. FRONT : 1 // Use the front-facing camera }; +- __onlyGalleries__: Android-only Allow getImage to only get from gallery apps, excluding dropbox,drive,onedrive and filesystem explorers. _(Boolean)_ + ### Amazon Fire OS Quirks - Any `cameraDirection` value results in a back-facing photo. diff --git a/src/android/CameraLauncher.java b/src/android/CameraLauncher.java index b5574ab48..dec21a7c8 100644 --- a/src/android/CameraLauncher.java +++ b/src/android/CameraLauncher.java @@ -32,6 +32,7 @@ Licensed to the Apache Software Foundation (ASF) under one import org.apache.cordova.PluginResult; import org.json.JSONArray; import org.json.JSONException; +import org.json.JSONObject; import android.app.Activity; import android.content.ActivityNotFoundException; @@ -89,6 +90,9 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect private boolean correctOrientation; // Should the pictures orientation be corrected private boolean orientationCorrected; // Has the picture's orientation been corrected private boolean allowEdit; // Should we allow the user to crop the image. + private JSONObject popoverOptions; // not used on android + private int cameraDirection; // not used on android + private boolean onlyGalleries; // Should we force the user to only be able to select images from the gallery apps on the device. public CallbackContext callbackContext; private int numPics; @@ -118,16 +122,22 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo this.mediaType = PICTURE; this.mQuality = 80; - this.mQuality = args.getInt(0); - destType = args.getInt(1); - srcType = args.getInt(2); - this.targetWidth = args.getInt(3); - this.targetHeight = args.getInt(4); - this.encodingType = args.getInt(5); - this.mediaType = args.getInt(6); - this.allowEdit = args.getBoolean(7); - this.correctOrientation = args.getBoolean(8); - this.saveToPhotoAlbum = args.getBoolean(9); + this.mQuality = args.optInt(0); + destType = args.optInt(1); + srcType = args.optInt(2); + this.targetWidth = args.optInt(3); + this.targetHeight = args.optInt(4); + this.encodingType = args.optInt(5); + this.mediaType = args.optInt(6); + this.allowEdit = args.optBoolean(7); + this.correctOrientation = args.optBoolean(8); + this.saveToPhotoAlbum = args.optBoolean(9); + this.popoverOptions = args.optJSONObject(10); + this.cameraDirection = args.optInt(11); + this.onlyGalleries = args.optBoolean(12); + if (this.allowEdit){ + this.onlyGalleries = true; + } // If the user specifies a 0 or smaller width/height // make it -1 so later comparisons succeed @@ -252,18 +262,20 @@ public void getImage(int srcType, int returnType, int encodingType) { croppedUri = null; if (this.mediaType == PICTURE) { intent.setType("image/*"); - if (this.allowEdit) { + if (this.onlyGalleries) { intent.setAction(Intent.ACTION_PICK); - intent.putExtra("crop", "true"); - if (targetWidth > 0) { - intent.putExtra("outputX", targetWidth); - } - if (targetHeight > 0) { - intent.putExtra("outputY", targetHeight); - } - if (targetHeight > 0 && targetWidth > 0 && targetWidth == targetHeight) { - intent.putExtra("aspectX", 1); - intent.putExtra("aspectY", 1); + if (this.allowEdit) { + intent.putExtra("crop", "true"); + if (targetWidth > 0) { + intent.putExtra("outputX", targetWidth); + } + if (targetHeight > 0) { + intent.putExtra("outputY", targetHeight); + } + if (targetHeight > 0 && targetWidth > 0 && targetWidth == targetHeight) { + intent.putExtra("aspectX", 1); + intent.putExtra("aspectY", 1); + } } File photo = createCaptureFile(encodingType); croppedUri = Uri.fromFile(photo); diff --git a/www/Camera.js b/www/Camera.js index 555bb5efa..6ec82ee2d 100644 --- a/www/Camera.js +++ b/www/Camera.js @@ -59,9 +59,10 @@ cameraExport.getPicture = function(successCallback, errorCallback, options) { var saveToPhotoAlbum = !!options.saveToPhotoAlbum; var popoverOptions = getValue(options.popoverOptions, null); var cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK); + var onlyGalleries = !!options.onlyGalleries; var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType, - mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection]; + mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection, onlyGalleries]; exec(successCallback, errorCallback, "Camera", "takePicture", args); // XXX: commented out From 11519b17f0a4603703a815cbfc5e14e3cd1721da Mon Sep 17 00:00:00 2001 From: Serge Huijben Date: Tue, 31 Mar 2015 13:28:47 +0200 Subject: [PATCH 7/7] remove index.md from doc --- doc/index.md | 457 --------------------------------------------------- 1 file changed, 457 deletions(-) delete mode 100644 doc/index.md diff --git a/doc/index.md b/doc/index.md deleted file mode 100644 index 09bdfc7a4..000000000 --- a/doc/index.md +++ /dev/null @@ -1,457 +0,0 @@ - - -# org.apache.cordova.camera - -This plugin defines a global `navigator.camera` object, which provides an API for taking pictures and for choosing images from -the system's image library. - -Although the object is attached to the global scoped `navigator`, it is not available until after the `deviceready` event. - - document.addEventListener("deviceready", onDeviceReady, false); - function onDeviceReady() { - console.log(navigator.camera); - } - -## Installation - - cordova plugin add org.apache.cordova.camera - -## navigator.camera.getPicture - -Takes a photo using the camera, or retrieves a photo from the device's -image gallery. The image is passed to the success callback as a -base64-encoded `String`, or as the URI for the image file. The method -itself returns a `CameraPopoverHandle` object that can be used to -reposition the file selection popover. - - navigator.camera.getPicture( cameraSuccess, cameraError, cameraOptions ); - -### Description - -The `camera.getPicture` function opens the device's default camera -application that allows users to snap pictures. This behavior occurs -by default, when `Camera.sourceType` equals -`Camera.PictureSourceType.CAMERA`. Once the user snaps the photo, the -camera application closes and the application is restored. - -If `Camera.sourceType` is `Camera.PictureSourceType.PHOTOLIBRARY` or -`Camera.PictureSourceType.SAVEDPHOTOALBUM`, then a dialog displays -that allows users to select an existing image. The -`camera.getPicture` function returns a `CameraPopoverHandle` object, -which can be used to reposition the image selection dialog, for -example, when the device orientation changes. - -The return value is sent to the `cameraSuccess` callback function, in -one of the following formats, depending on the specified -`cameraOptions`: - -- A `String` containing the base64-encoded photo image. - -- A `String` representing the image file location on local storage (default). - -You can do whatever you want with the encoded image or URI, for -example: - -- Render the image in an `` tag, as in the example below - -- Save the data locally (`LocalStorage`, [Lawnchair](http://brianleroux.github.com/lawnchair/), etc.) - -- Post the data to a remote server - -__NOTE__: Photo resolution on newer devices is quite good. Photos -selected from the device's gallery are not downscaled to a lower -quality, even if a `quality` parameter is specified. To avoid common -memory problems, set `Camera.destinationType` to `FILE_URI` rather -than `DATA_URL`. - -### Supported Platforms - -- Amazon Fire OS -- Android -- BlackBerry 10 -- Browser -- Firefox OS -- iOS -- Tizen -- Windows Phone 7 and 8 -- Windows 8 - -### Preferences (iOS) - -- __CameraUsesGeolocation__ (boolean, defaults to false). For capturing JPEGs, set to true to get geolocation data in the EXIF header. This will trigger a request for geolocation permissions if set to true. - - - - -### Amazon Fire OS Quirks - -Amazon Fire OS uses intents to launch the camera activity on the device to capture -images, and on phones with low memory, the Cordova activity may be killed. In this -scenario, the image may not appear when the cordova activity is restored. - -### Android Quirks - -Android uses intents to launch the camera activity on the device to capture -images, and on phones with low memory, the Cordova activity may be killed. In this -scenario, the image may not appear when the Cordova activity is restored. - -### Browser Quirks - -Can only return photos as base64-encoded image. - -### Firefox OS Quirks - -Camera plugin is currently implemented using [Web Activities](https://hacks.mozilla.org/2013/01/introducing-web-activities/). - -### iOS Quirks - -Including a JavaScript `alert()` in either of the callback functions -can cause problems. Wrap the alert within a `setTimeout()` to allow -the iOS image picker or popover to fully close before the alert -displays: - - setTimeout(function() { - // do your thing here! - }, 0); - -### Windows Phone 7 Quirks - -Invoking the native camera application while the device is connected -via Zune does not work, and triggers an error callback. - -### Tizen Quirks - -Tizen only supports a `destinationType` of -`Camera.DestinationType.FILE_URI` and a `sourceType` of -`Camera.PictureSourceType.PHOTOLIBRARY`. - -### Example - -Take a photo and retrieve it as a base64-encoded image: - - navigator.camera.getPicture(onSuccess, onFail, { quality: 50, - destinationType: Camera.DestinationType.DATA_URL - }); - - function onSuccess(imageData) { - var image = document.getElementById('myImage'); - image.src = "data:image/jpeg;base64," + imageData; - } - - function onFail(message) { - alert('Failed because: ' + message); - } - -Take a photo and retrieve the image's file location: - - navigator.camera.getPicture(onSuccess, onFail, { quality: 50, - destinationType: Camera.DestinationType.FILE_URI }); - - function onSuccess(imageURI) { - var image = document.getElementById('myImage'); - image.src = imageURI; - } - - function onFail(message) { - alert('Failed because: ' + message); - } - -## CameraOptions - -Optional parameters to customize the camera settings. - - { quality : 75, - destinationType : Camera.DestinationType.DATA_URL, - sourceType : Camera.PictureSourceType.CAMERA, - allowEdit : true, - encodingType: Camera.EncodingType.JPEG, - targetWidth: 100, - targetHeight: 100, - popoverOptions: CameraPopoverOptions, - saveToPhotoAlbum: false }; - -### Options - -- __quality__: Quality of the saved image, expressed as a range of 0-100, where 100 is typically full resolution with no loss from file compression. The default is 50. _(Number)_ (Note that information about the camera's resolution is unavailable.) - -- __destinationType__: Choose the format of the return value. The default is FILE_URI. Defined in `navigator.camera.DestinationType` _(Number)_ - - Camera.DestinationType = { - DATA_URL : 0, // Return image as base64-encoded string - FILE_URI : 1, // Return image file URI - NATIVE_URI : 2 // Return image native URI (e.g., assets-library:// on iOS or content:// on Android) - }; - -- __sourceType__: Set the source of the picture. The default is CAMERA. Defined in `navigator.camera.PictureSourceType` _(Number)_ - - Camera.PictureSourceType = { - PHOTOLIBRARY : 0, - CAMERA : 1, - SAVEDPHOTOALBUM : 2 - }; - -- __allowEdit__: Allow simple editing of image before selection. _(Boolean)_ - -- __encodingType__: Choose the returned image file's encoding. Default is JPEG. Defined in `navigator.camera.EncodingType` _(Number)_ - - Camera.EncodingType = { - JPEG : 0, // Return JPEG encoded image - PNG : 1 // Return PNG encoded image - }; - -- __targetWidth__: Width in pixels to scale image. Must be used with __targetHeight__. Aspect ratio remains constant. _(Number)_ - -- __targetHeight__: Height in pixels to scale image. Must be used with __targetWidth__. Aspect ratio remains constant. _(Number)_ - -- __mediaType__: Set the type of media to select from. Only works when `PictureSourceType` is `PHOTOLIBRARY` or `SAVEDPHOTOALBUM`. Defined in `nagivator.camera.MediaType` _(Number)_ - - Camera.MediaType = { - PICTURE: 0, // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType - VIDEO: 1, // allow selection of video only, WILL ALWAYS RETURN FILE_URI - ALLMEDIA : 2 // allow selection from all media types - }; - -- __correctOrientation__: Rotate the image to correct for the orientation of the device during capture. _(Boolean)_ - -- __saveToPhotoAlbum__: Save the image to the photo album on the device after capture. _(Boolean)_ - -- __popoverOptions__: iOS-only options that specify popover location in iPad. Defined in `CameraPopoverOptions`. - -- __cameraDirection__: Choose the camera to use (front- or back-facing). The default is BACK. Defined in `navigator.camera.Direction` _(Number)_ - - Camera.Direction = { - BACK : 0, // Use the back-facing camera - FRONT : 1 // Use the front-facing camera - }; - -- __onlyGalleries__: Android-only Allow getImage to only get from gallery apps, excluding dropbox,drive,onedrive and filesystem explorers. _(Boolean)_ - -### Amazon Fire OS Quirks - -- Any `cameraDirection` value results in a back-facing photo. - -- Ignores the `allowEdit` parameter. - -- `Camera.PictureSourceType.PHOTOLIBRARY` and `Camera.PictureSourceType.SAVEDPHOTOALBUM` both display the same photo album. - -### Android Quirks - -- Any `cameraDirection` value results in a back-facing photo. - -- Ignores the `allowEdit` parameter. - -- `Camera.PictureSourceType.PHOTOLIBRARY` and `Camera.PictureSourceType.SAVEDPHOTOALBUM` both display the same photo album. - -### BlackBerry 10 Quirks - -- Ignores the `quality` parameter. - -- Ignores the `allowEdit` parameter. - -- `Camera.MediaType` is not supported. - -- Ignores the `correctOrientation` parameter. - -- Ignores the `cameraDirection` parameter. - -### Firefox OS Quirks - -- Ignores the `quality` parameter. - -- `Camera.DestinationType` is ignored and equals `1` (image file URI) - -- Ignores the `allowEdit` parameter. - -- Ignores the `PictureSourceType` parameter (user chooses it in a dialog window) - -- Ignores the `encodingType` - -- Ignores the `targetWidth` and `targetHeight` - -- `Camera.MediaType` is not supported. - -- Ignores the `correctOrientation` parameter. - -- Ignores the `cameraDirection` parameter. - -### iOS Quirks - -- Set `quality` below 50 to avoid memory errors on some devices. - -- When using `destinationType.FILE_URI`, photos are saved in the application's temporary directory. The contents of the application's temporary directory is deleted when the application ends. - -### Tizen Quirks - -- options not supported - -- always returns a FILE URI - -### Windows Phone 7 and 8 Quirks - -- Ignores the `allowEdit` parameter. - -- Ignores the `correctOrientation` parameter. - -- Ignores the `cameraDirection` parameter. - -- Ignores the `saveToPhotoAlbum` parameter. IMPORTANT: All images taken with the wp7/8 cordova camera API are always copied to the phone's camera roll. Depending on the user's settings, this could also mean the image is auto-uploaded to their OneDrive. This could potentially mean the image is available to a wider audience than your app intended. If this a blocker for your application, you will need to implement the CameraCaptureTask as documented on msdn : [http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh394006.aspx](http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh394006.aspx) -You may also comment or up-vote the related issue in the [issue tracker](https://issues.apache.org/jira/browse/CB-2083) - -- Ignores the `mediaType` property of `cameraOptions` as the Windows Phone SDK does not provide a way to choose videos from PHOTOLIBRARY. - - -## CameraError - -onError callback function that provides an error message. - - function(message) { - // Show a helpful message - } - -### Parameters - -- __message__: The message is provided by the device's native code. _(String)_ - - -## cameraSuccess - -onSuccess callback function that provides the image data. - - function(imageData) { - // Do something with the image - } - -### Parameters - -- __imageData__: Base64 encoding of the image data, _or_ the image file URI, depending on `cameraOptions` in effect. _(String)_ - -### Example - - // Show image - // - function cameraCallback(imageData) { - var image = document.getElementById('myImage'); - image.src = "data:image/jpeg;base64," + imageData; - } - - -## CameraPopoverHandle - -A handle to the popover dialog created by `navigator.camera.getPicture`. - -### Methods - -- __setPosition__: Set the position of the popover. - -### Supported Platforms - -- iOS - -### setPosition - -Set the position of the popover. - -__Parameters__: - -- `cameraPopoverOptions`: the `CameraPopoverOptions` that specify the new position - -### Example - - var cameraPopoverHandle = navigator.camera.getPicture(onSuccess, onFail, - { destinationType: Camera.DestinationType.FILE_URI, - sourceType: Camera.PictureSourceType.PHOTOLIBRARY, - popoverOptions: new CameraPopoverOptions(300, 300, 100, 100, Camera.PopoverArrowDirection.ARROW_ANY) - }); - - // Reposition the popover if the orientation changes. - window.onorientationchange = function() { - var cameraPopoverOptions = new CameraPopoverOptions(0, 0, 100, 100, Camera.PopoverArrowDirection.ARROW_ANY); - cameraPopoverHandle.setPosition(cameraPopoverOptions); - } - - -## CameraPopoverOptions - -iOS-only parameters that specify the anchor element location and arrow -direction of the popover when selecting images from an iPad's library -or album. - - { x : 0, - y : 32, - width : 320, - height : 480, - arrowDir : Camera.PopoverArrowDirection.ARROW_ANY - }; - -### CameraPopoverOptions - -- __x__: x pixel coordinate of screen element onto which to anchor the popover. _(Number)_ - -- __y__: y pixel coordinate of screen element onto which to anchor the popover. _(Number)_ - -- __width__: width, in pixels, of the screen element onto which to anchor the popover. _(Number)_ - -- __height__: height, in pixels, of the screen element onto which to anchor the popover. _(Number)_ - -- __arrowDir__: Direction the arrow on the popover should point. Defined in `Camera.PopoverArrowDirection` _(Number)_ - - Camera.PopoverArrowDirection = { - ARROW_UP : 1, // matches iOS UIPopoverArrowDirection constants - ARROW_DOWN : 2, - ARROW_LEFT : 4, - ARROW_RIGHT : 8, - ARROW_ANY : 15 - }; - -Note that the size of the popover may change to adjust to the -direction of the arrow and orientation of the screen. Make sure to -account for orientation changes when specifying the anchor element -location. - -## navigator.camera.cleanup - -Removes intermediate photos taken by the camera from temporary -storage. - - navigator.camera.cleanup( cameraSuccess, cameraError ); - -### Description - -Removes intermediate image files that are kept in temporary storage -after calling `camera.getPicture`. Applies only when the value of -`Camera.sourceType` equals `Camera.PictureSourceType.CAMERA` and the -`Camera.destinationType` equals `Camera.DestinationType.FILE_URI`. - -### Supported Platforms - -- iOS - -### Example - - navigator.camera.cleanup(onSuccess, onFail); - - function onSuccess() { - console.log("Camera cleanup success.") - } - - function onFail(message) { - alert('Failed because: ' + message); - } -