I\'m trying to set the source of an img tag in my app based on the image chosen from the device image gallery using the PhoneGap/Cordova Camera Plugin.
It has worked
Something broke in Android 4.4 with the URI encoding of images.
A bug has been filed against Cordova here: https://issues.apache.org/jira/browse/CB-5398
In the docs for getPicture, under the Android Quicks section, it discusses this problem and points to a StackOverflow question with a workaround (edit the Camera plugin java code to force it to open the Gallery app instead of the Storage Access Framework app.)
It seems another thing you could do is set the destination type to DATA_URL.
A kind of really-very-dirty workaround works for me while this bug is fixed. Use in case of extreme necessity :)
if (imageURI.substring(0,21)=="content://com.android") {
photo_split=imageURI.split("%3A");
imageURI="content://media/external/images/media/"+photo_split[1];
}
Adobe assures me that that this problem will be fixed in 3.5.0. It is not fixed in 3.4. As 3.5.0 is scheduled to be release in mid-May, I'm just going to wait until then.
Adobe claims that this was not a change that could be made on the plugin level. It was a basic change in the cordova code. It's too bad it took so long for them to come up with this fix.
UPDATE: Cordova 3.5.0 was released on May 9. You can download it bia node and see if the problems are in fact solved.
Here is a simple fix to this problem:
replace this:
content://com.android.providers.media.documents/document/image%3A352
by this:
content://com.android.providers.media.documents/document/image%253A352
if you are using JavaScript you can use this code:
var path = content://com.android.providers.media.documents/document/image%3A352;
path = path.replace("%", "%25");
this technique force the uri to let pass "%3A" as it is, without changing it to ":", hope it will work for you !
Not much more robust just a few more checks for special conditions like "content:" with no extensions and so. Also, since I need to upload it, I am detecting the extension, or create a .jpg extension if the file does not have one:
// Android 4.4 cordova workarounds ... returns new kind or URL for content from chooser
//if (imageUrl.substring(0,21)=="content://com.android") {
if(imageUrl.indexOf('content://') != -1 && imageUrl.indexOf("%3A") != -1){
//"PlainFileUrl = content://com.android.providers.media.documents/document/image%3A14",
photo_split=imageUrl.split("%3A");
imageUrl="content://media/external/images/media/"+photo_split[1];
}
// workaround end
var fileName = imageUrl.substr(imageUrl.lastIndexOf('/') + 1);
var extension;
// check for content: protocol to make sure is not
// a file with no extension
if (imageUrl.indexOf('content://') != -1) {
if(imageUrl.lastIndexOf('.') > imageUrl.lastIndexOf('/')){
extension = imageUrl.substr(imageUrl.lastIndexOf('.') + 1);
}else{
extension = "jpg";
fileName = fileName + ".jpg";
LogService.log("Created File Extension jpg");
}
} else {
if (imageUrl.lastIndexOf('.') == -1 || (imageUrl.lastIndexOf('.') < imageUrl.lastIndexOf('/')) ) {
extension = "invalid";
} else {
extension = imageUrl.substr(imageUrl.lastIndexOf('.') + 1);
}
}
Whenever some uri
is passed into <img src="uri" />
it's implicitly decoded from
content://com.android.providers.media.documents/document/image%3A9888 (1)
into
content://com.android.providers.media.documents/document/image:9888 (2)
However, after returning from Intent.ACTION_OPEN_DOCUMENT
or Intent.ACTION_GET_CONTENT
Android provides you with the read permission for (1), not (2). In this case WebView
will expectantly log an error:
java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaDocumentsProvider uri content://com.android.providers.media.documents/document/image:9888 from pid=13163, uid=10165 requires android.permission.MANAGE_DOCUMENTS, or grantUriPermission()
or
Unable to open content URL
Code snippet
All you need to resolve the issue is
String uriToUseInWebView = transformForWebView(uri.toString());
private String transformForWebView(String uri) {
for (int i = 0; i < timesDecodingWebView(); i++)
uri = uri.replace("%", Uri.encode("%"));
return uri;
}
private int timesDecodingWebView() {
if (Build.VERSION.RELEASE.equals("4.4.2")) {
return 2;
} else {
return 1;
}
}
in your Java code before passing uri
into HTML/JS to ensure that (1) will be actually loaded.
I've tested that on 4.4.2, 4.4.4 and 5.0. The funny part is that Android 4.4.2 decodes the uri
internally twice.