CB-2432 CB-3185 CB-5975: Correctly handle content:// urls especially when non-local-to-device
Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/commit/c56b3303 Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/tree/c56b3303 Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/diff/c56b3303 Branch: refs/heads/master Commit: c56b33035edcfae3327369cb64c4e6818e3d84b4 Parents: c2df6d6 Author: Ian Clelland <[email protected]> Authored: Fri Feb 14 10:15:12 2014 -0500 Committer: Ian Clelland <[email protected]> Committed: Fri Feb 14 10:15:12 2014 -0500 ---------------------------------------------------------------------- src/android/ContentFilesystem.java | 121 ++++++++++++++++++++----------- src/android/FileUtils.java | 6 +- src/android/LocalFilesystemURL.java | 4 +- www/android/FileSystem.js | 2 +- 4 files changed, 86 insertions(+), 47 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/c56b3303/src/android/ContentFilesystem.java ---------------------------------------------------------------------- diff --git a/src/android/ContentFilesystem.java b/src/android/ContentFilesystem.java index f116b31..5c34ea2 100644 --- a/src/android/ContentFilesystem.java +++ b/src/android/ContentFilesystem.java @@ -16,6 +16,7 @@ import android.content.ContentResolver; import android.database.Cursor; import android.net.Uri; import android.provider.MediaStore; +import android.provider.OpenableColumns; public class ContentFilesystem extends Filesystem { @@ -29,27 +30,22 @@ public class ContentFilesystem extends Filesystem { } @Override - @SuppressWarnings("deprecation") public JSONObject getEntryForLocalURL(LocalFilesystemURL inputURL) throws IOException { - File fp = null; - - Cursor cursor = this.cordova.getActivity().managedQuery(inputURL.URL, new String[] { MediaStore.Images.Media.DATA }, null, null, null); - // Note: MediaStore.Images/Audio/Video.Media.DATA is always "_data" - int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); - cursor.moveToFirst(); - fp = new File(cursor.getString(column_index)); - - if (!fp.exists()) { - throw new FileNotFoundException(); - } - if (!fp.canRead()) { - throw new IOException(); - } - try { - return makeEntryForPath(inputURL.fullPath, inputURL.filesystemName, fp.isDirectory()); - } catch (JSONException e) { - throw new IOException(); - } + // Get the cursor to validate that the file exists + Cursor cursor = openCursorForURL(inputURL); + try { + if (cursor == null || !cursor.moveToFirst()) { + throw new FileNotFoundException(); + } + } finally { + if (cursor != null) + cursor.close(); + } + try { + return makeEntryForPath(inputURL.fullPath, inputURL.filesystemName, false /*fp.isDirectory()*/); + } catch (JSONException e) { + throw new IOException(); + } } @Override @@ -112,18 +108,28 @@ public class ContentFilesystem extends Filesystem { @Override public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException { - String path = filesystemPathForURL(inputURL); - if (path == null) { - throw new FileNotFoundException(); - } - File file = new File(path); + Integer size = null; + Integer lastModified = null; + Cursor cursor = openCursorForURL(inputURL); + try { + if (cursor != null && cursor.moveToFirst()) { + size = resourceSizeForCursor(cursor); + lastModified = lastModifiedDateForCursor(cursor); + } else { + throw new FileNotFoundException(); + } + } finally { + if (cursor != null) + cursor.close(); + } + JSONObject metadata = new JSONObject(); try { - metadata.put("size", file.length()); + metadata.put("size", size); metadata.put("type", resourceApi.getMimeType(inputURL.URL)); - metadata.put("name", file.getName()); + metadata.put("name", inputURL.filesystemName); metadata.put("fullPath", inputURL.fullPath); - metadata.put("lastModifiedDate", file.lastModified()); + metadata.put("lastModifiedDate", lastModified); } catch (JSONException e) { return null; } @@ -191,25 +197,56 @@ public class ContentFilesystem extends Filesystem { throw new NoModificationAllowedException("Couldn't truncate file given its content URI"); } - @Override - public String filesystemPathForURL(LocalFilesystemURL url) { + protected Cursor openCursorForURL(LocalFilesystemURL url) { + ContentResolver contentResolver = this.cordova.getActivity().getContentResolver(); + Cursor cursor = contentResolver.query(url.URL, null, null, null, null); + return cursor; + } + + protected String filesystemPathForCursor(Cursor cursor) { final String[] LOCAL_FILE_PROJECTION = { MediaStore.Images.Media.DATA }; + int columnIndex = cursor.getColumnIndex(LOCAL_FILE_PROJECTION[0]); + if (columnIndex != -1) { + return cursor.getString(columnIndex); + } + return null; + } - ContentResolver contentResolver = this.cordova.getActivity().getContentResolver(); - Cursor cursor = contentResolver.query(url.URL, LOCAL_FILE_PROJECTION, null, null, null); - if (cursor != null) { - try { - int columnIndex = cursor.getColumnIndex(LOCAL_FILE_PROJECTION[0]); - if (columnIndex != -1 && cursor.getCount() > 0) { - cursor.moveToFirst(); - String path = cursor.getString(columnIndex); - return path; - } - } finally { - cursor.close(); + protected Integer resourceSizeForCursor(Cursor cursor) { + int columnIndex = cursor.getColumnIndex(OpenableColumns.SIZE); + if (columnIndex != -1) { + String sizeStr = cursor.getString(columnIndex); + if (sizeStr != null) { + return Integer.parseInt(sizeStr,10); + } + } + return null; + } + + protected Integer lastModifiedDateForCursor(Cursor cursor) { + final String[] LOCAL_FILE_PROJECTION = { MediaStore.MediaColumns.DATE_MODIFIED }; + int columnIndex = cursor.getColumnIndex(LOCAL_FILE_PROJECTION[0]); + if (columnIndex != -1) { + String dateStr = cursor.getString(columnIndex); + if (dateStr != null) { + return Integer.parseInt(dateStr,10); } } return null; + } + + @Override + public String filesystemPathForURL(LocalFilesystemURL url) { + Cursor cursor = openCursorForURL(url); + try { + if (cursor != null && cursor.moveToFirst()) { + return filesystemPathForCursor(cursor); + } + } finally { + if (cursor != null) + cursor.close(); + } + return null; } @Override http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/c56b3303/src/android/FileUtils.java ---------------------------------------------------------------------- diff --git a/src/android/FileUtils.java b/src/android/FileUtils.java index 6acc101..ca643b3 100644 --- a/src/android/FileUtils.java +++ b/src/android/FileUtils.java @@ -501,14 +501,14 @@ public class FileUtils extends CordovaPlugin { * @throws JSONException */ private JSONObject resolveLocalFileSystemURI(String url) throws IOException, JSONException { - String decoded = URLDecoder.decode(url, "UTF-8"); LocalFilesystemURL inputURL; if (url == null) { throw new MalformedURLException("Unrecognized filesystem URL"); } /* Backwards-compatibility: Check for file:// urls */ - if (decoded.startsWith("file://")) { + if (url.startsWith("file://")) { + String decoded = URLDecoder.decode(url, "UTF-8"); /* This looks like a file url. Get the path, and see if any handlers recognize it. */ String path; int questionMark = decoded.indexOf("?"); @@ -519,7 +519,7 @@ public class FileUtils extends CordovaPlugin { } inputURL = this.filesystemURLforLocalPath(path); } else { - inputURL = new LocalFilesystemURL(decoded); + inputURL = new LocalFilesystemURL(url); } try { http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/c56b3303/src/android/LocalFilesystemURL.java ---------------------------------------------------------------------- diff --git a/src/android/LocalFilesystemURL.java b/src/android/LocalFilesystemURL.java index 8583a8f..f18a03a 100644 --- a/src/android/LocalFilesystemURL.java +++ b/src/android/LocalFilesystemURL.java @@ -26,7 +26,9 @@ public class LocalFilesystemURL { } return path.substring(path.indexOf('/', 1)); } else if ("content".equals(URL.getScheme())) { - return '/' + URL.getHost() + URL.getPath(); + String path = '/' + URL.getHost() + URL.getPath(); + // Re-encode path component to handle Android 4.4+ Content URLs + return Uri.encode(path,"/"); } return null; } http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/c56b3303/www/android/FileSystem.js ---------------------------------------------------------------------- diff --git a/www/android/FileSystem.js b/www/android/FileSystem.js index 73332f8..fde9c7a 100644 --- a/www/android/FileSystem.js +++ b/www/android/FileSystem.js @@ -24,7 +24,7 @@ FILESYSTEM_PROTOCOL = "cdvfile"; module.exports = { __format__: function(fullPath) { if (this.name === 'content') { - return 'content:/' + encodeURI(fullPath); + return 'content:/' + fullPath; } var path = ('/'+this.name+(fullPath[0]==='/'?'':'/')+encodeURI(fullPath)).replace('//','/'); return FILESYSTEM_PROTOCOL + '://localhost' + path;
