cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From agri...@apache.org
Subject [01/13] cordova-plugin-file git commit: CB-8642 android: Fix content URIs not working with resolve / copy
Date Wed, 11 Mar 2015 15:35:12 GMT
Repository: cordova-plugin-file
Updated Branches:
  refs/heads/master 2ed379c7c -> 80cf1de40


CB-8642 android: Fix content URIs not working with resolve / copy

The issue was fairly fundamental to our cdvfile:// scheme and required
some refactorings:
- Move isDirectory into LocalFilesystemURL
- Add toNativeUri() / toLocalUri() for converting between cdvfile: / file: / content:
- Made toInternalUrl() maintain query params for content: uris
- Moved copyFileToURL(), readFileAtURL(), getOutputStreamForURL() into base Filesystem class

Also added a ContentProvider to the test plugin and unit tests!


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/d6202261
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/tree/d6202261
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/diff/d6202261

Branch: refs/heads/master
Commit: d6202261fec395854050e771d262afcf4d4e86d9
Parents: 9d78908
Author: Andrew Grieve <agrieve@chromium.org>
Authored: Tue Mar 10 13:54:33 2015 -0400
Committer: Andrew Grieve <agrieve@chromium.org>
Committed: Wed Mar 11 11:35:07 2015 -0400

----------------------------------------------------------------------
 src/android/ContentFilesystem.java         | 197 +++++++++---------------
 src/android/FileUtils.java                 |  50 +++---
 src/android/Filesystem.java                | 124 ++++++++-------
 src/android/LocalFilesystem.java           | 141 ++++++++---------
 src/android/LocalFilesystemURL.java        |  27 ++--
 tests/plugin.xml                           |  10 ++
 tests/src/android/TestContentProvider.java |  78 ++++++++++
 tests/tests.js                             |  75 ++++++---
 www/Entry.js                               |   2 +-
 www/FileSystem.js                          |   2 +-
 www/android/FileSystem.js                  |  13 +-
 11 files changed, 404 insertions(+), 315 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/d6202261/src/android/ContentFilesystem.java
----------------------------------------------------------------------
diff --git a/src/android/ContentFilesystem.java b/src/android/ContentFilesystem.java
index a4a2227..25c6489 100644
--- a/src/android/ContentFilesystem.java
+++ b/src/android/ContentFilesystem.java
@@ -23,14 +23,7 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.OutputStream;
 
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.InvocationTargetException;
-
-import org.apache.cordova.CordovaInterface;
 import org.apache.cordova.CordovaResourceApi;
-import org.apache.cordova.CordovaWebView;
-import org.apache.cordova.PluginManager;
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -50,68 +43,67 @@ public class ContentFilesystem extends Filesystem {
 		super(Uri.parse("content://"), "content", resourceApi);
         this.context = context;
 	}
-	
-	@Override
-	public JSONObject getEntryForLocalURL(LocalFilesystemURL inputURL) throws IOException {
-	    if ("/".equals(inputURL.uri.getPath())) {
-            return LocalFilesystem.makeEntryForURL(inputURL, true, inputURL.uri);
-	    }
 
-		// Get the cursor to validate that the file exists
-		Cursor cursor = openCursorForURL(inputURL);
-		File file = resourceApi.mapUriToFile(inputURL.uri);
-        Uri nativeUrl;
-		if (file == null) {
-            nativeUrl = inputURL.uri;
-		} else {
-            nativeUrl = Uri.fromFile(file);
-		}
-        return makeEntryForURL(inputURL, false /*fp.isDirectory()*/, nativeUrl);
-	}
-	
     @Override
-	public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL,
-			String fileName, JSONObject options, boolean directory) throws IOException, TypeMismatchException, JSONException {
-        if (options != null) {
-            if (options.optBoolean("create")) {
-        		throw new IOException("Cannot create content url");
-            }
+    public Uri toNativeUri(LocalFilesystemURL inputURL) {
+        String authorityAndPath = inputURL.uri.getEncodedPath().substring(this.name.length() + 2);
+        if (authorityAndPath.length() < 2) {
+            return null;
         }
-        LocalFilesystemURL requestedURL = LocalFilesystemURL.parse(Uri.withAppendedPath(inputURL.uri, fileName));
-        File fp = new File(this.filesystemPathForURL(requestedURL));
-        if (!fp.exists()) {
-            throw new FileNotFoundException("path does not exist");
+        String ret = "content://" + authorityAndPath;
+        String query = inputURL.uri.getEncodedQuery();
+        if (query != null) {
+            ret += '?' + query;
         }
-        if (directory) {
-            if (fp.isFile()) {
-                throw new TypeMismatchException("path doesn't exist or is file");
-            }
-        } else {
-            if (fp.isDirectory()) {
-                throw new TypeMismatchException("path doesn't exist or is directory");
-            }
+        String frag = inputURL.uri.getEncodedFragment();
+        if (frag != null) {
+            ret += '#' + frag;
         }
-        // Return the directory
-        return makeEntryForURL(requestedURL, directory, Uri.fromFile(fp));
+        return Uri.parse(ret);
+    }
+
+    @Override
+    public LocalFilesystemURL toLocalUri(Uri inputURL) {
+        if (!"content".equals(inputURL.getScheme())) {
+            return null;
+        }
+        String subPath = inputURL.getEncodedPath();
+        if (subPath.length() > 0) {
+            subPath = subPath.substring(1);
+        }
+        Uri.Builder b = new Uri.Builder()
+            .scheme(LocalFilesystemURL.FILESYSTEM_PROTOCOL)
+            .authority("localhost")
+            .path(name)
+            .appendPath(inputURL.getAuthority());
+        if (subPath.length() > 0) {
+            b.appendEncodedPath(subPath);
+        }
+        Uri localUri = b.encodedQuery(inputURL.getEncodedQuery())
+            .encodedFragment(inputURL.getEncodedFragment())
+            .build();
+        return LocalFilesystemURL.parse(localUri);
+    }
 
+    @Override
+	public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL,
+			String fileName, JSONObject options, boolean directory) throws IOException, TypeMismatchException, JSONException {
+        throw new UnsupportedOperationException("getFile() not supported for content:. Use resolveLocalFileSystemURL instead.");
 	}
 
 	@Override
 	public boolean removeFileAtLocalURL(LocalFilesystemURL inputURL)
 			throws NoModificationAllowedException {
-
-		String filePath = filesystemPathForURL(inputURL);
-		File file = new File(filePath);
+        Uri contentUri = toNativeUri(inputURL);
 		try {
-            context.getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
-					MediaStore.Images.Media.DATA + " = ?",
-					new String[] { filePath });
+            context.getContentResolver().delete(contentUri, null, null);
 		} catch (UnsupportedOperationException t) {
 			// Was seeing this on the File mobile-spec tests on 4.0.3 x86 emulator.
 			// The ContentResolver applies only when the file was registered in the
 			// first case, which is generally only the case with images.
+            throw new NoModificationAllowedException("Deleting not supported for content uri: " + contentUri);
 		}
-		return file.delete();
+        return true;
 	}
 
 	@Override
@@ -123,22 +115,27 @@ public class ContentFilesystem extends Filesystem {
 	@Override
 	public JSONArray readEntriesAtLocalURL(LocalFilesystemURL inputURL)
 			throws FileNotFoundException {
-		// TODO Auto-generated method stub
-		return null;
+        throw new UnsupportedOperationException("readEntriesAtLocalURL() not supported for content:. Use resolveLocalFileSystemURL instead.");
 	}
 
 	@Override
 	public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException {
-		Integer size = null;
-		Integer lastModified = null;
-        Cursor cursor = openCursorForURL(inputURL);
+        long size = -1;
+        long lastModified = 0;
+        Uri nativeUri = toNativeUri(inputURL);
+        String mimeType = resourceApi.getMimeType(nativeUri);
+        Cursor cursor = openCursorForURL(nativeUri);
         try {
         	if (cursor != null && cursor.moveToFirst()) {
         		size = resourceSizeForCursor(cursor);
         		lastModified = lastModifiedDateForCursor(cursor);
         	} else {
-    			throw new FileNotFoundException();
+                // Some content providers don't support cursors at all!
+                CordovaResourceApi.OpenForReadResult offr = resourceApi.openForRead(nativeUri);
+    			size = offr.length;
         	}
+        } catch (IOException e) {
+            throw new FileNotFoundException();
         } finally {
         	if (cursor != null)
         		cursor.close();
@@ -147,9 +144,9 @@ public class ContentFilesystem extends Filesystem {
         JSONObject metadata = new JSONObject();
         try {
         	metadata.put("size", size);
-        	metadata.put("type", resourceApi.getMimeType(inputURL.uri));
+        	metadata.put("type", mimeType);
         	metadata.put("name", name);
-        	metadata.put("fullPath", inputURL.pathAndQuery);
+        	metadata.put("fullPath", inputURL.path);
         	metadata.put("lastModifiedDate", lastModified);
         } catch (JSONException e) {
         	return null;
@@ -158,56 +155,6 @@ public class ContentFilesystem extends Filesystem {
 	}
 
 	@Override
-	public JSONObject copyFileToURL(LocalFilesystemURL destURL, String newName,
-			Filesystem srcFs, LocalFilesystemURL srcURL, boolean move)
-                    throws IOException, InvalidModificationException, JSONException,
-                    NoModificationAllowedException, FileExistsException {
-        if (LocalFilesystem.class.isInstance(srcFs)) {
-            /* Same FS, we can shortcut with CordovaResourceApi operations */
-            // Figure out where we should be copying to
-            final LocalFilesystemURL destinationURL = makeDestinationURL(newName, srcURL, destURL);
-
-            OutputStream os = resourceApi.openOutputStream(destURL.uri);
-            CordovaResourceApi.OpenForReadResult ofrr = resourceApi.openForRead(srcURL.uri);
-            if (move && !srcFs.canRemoveFileAtLocalURL(srcURL)) {
-                throw new NoModificationAllowedException("Cannot move file at source URL");
-            }
-            try {
-                resourceApi.copyResource(ofrr, os);
-            } catch (IOException e) {
-                throw new IOException("Cannot read file at source URL");
-            }
-            if (move) {
-                srcFs.removeFileAtLocalURL(srcURL);
-            }
-            return makeEntryForURL(destinationURL, false, destinationURL.uri);
-        } else {
-            // Need to copy the hard way
-            return super.copyFileToURL(destURL, newName, srcFs, srcURL, move);
-		}
-	}
-
-    
-	@Override
-    public void readFileAtURL(LocalFilesystemURL inputURL, long start, long end,
-			ReadFileCallback readFileCallback) throws IOException {
-		CordovaResourceApi.OpenForReadResult ofrr = resourceApi.openForRead(inputURL.uri);
-        if (end < 0) {
-            end = ofrr.length;
-        }
-        long numBytesToRead = end - start;
-		try {
-			if (start > 0) {
-                ofrr.inputStream.skip(start);
-			}
-            LimitedInputStream inputStream = new LimitedInputStream(ofrr.inputStream, numBytesToRead);
-            readFileCallback.handleData(inputStream, ofrr.mimeType);
-		} finally {
-            ofrr.inputStream.close();
-		}
-	}
-
-	@Override
 	public long writeToFileAtURL(LocalFilesystemURL inputURL, String data,
 			int offset, boolean isBinary) throws NoModificationAllowedException {
         throw new NoModificationAllowedException("Couldn't write to file given its content URI");
@@ -218,30 +165,33 @@ public class ContentFilesystem extends Filesystem {
         throw new NoModificationAllowedException("Couldn't truncate file given its content URI");
 	}
 
-	protected Cursor openCursorForURL(LocalFilesystemURL url) {
+	protected Cursor openCursorForURL(Uri nativeUri) {
         ContentResolver contentResolver = context.getContentResolver();
-        Cursor cursor = contentResolver.query(url.uri, null, null, null, null);
-        return cursor;
+        try {
+            return contentResolver.query(nativeUri, null, null, null, null);
+        } catch (UnsupportedOperationException e) {
+            return null;
+        }
 	}
 
-	protected Integer resourceSizeForCursor(Cursor cursor) {
+	private Long 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 Long.parseLong(sizeStr);
             }
         }
         return null;
 	}
 	
-	protected Integer lastModifiedDateForCursor(Cursor cursor) {
+	protected Long 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 Long.parseLong(dateStr);
             }
         }
         return null;
@@ -249,7 +199,7 @@ public class ContentFilesystem extends Filesystem {
 
     @Override
     public String filesystemPathForURL(LocalFilesystemURL url) {
-        File f = resourceApi.mapUriToFile(url.uri);
+        File f = resourceApi.mapUriToFile(toNativeUri(url));
         return f == null ? null : f.getAbsolutePath();
     }
 
@@ -261,15 +211,6 @@ public class ContentFilesystem extends Filesystem {
 
 	@Override
 	public boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL) {
-		String path = filesystemPathForURL(inputURL);
-		File file = new File(path);
-		return file.exists();
+		return true;
 	}
-
-	@Override
-	OutputStream getOutputStreamForURL(LocalFilesystemURL inputURL)
-			throws IOException {
-		OutputStream os = resourceApi.openOutputStream(inputURL.uri);
-		return os;
-    }
 }

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/d6202261/src/android/FileUtils.java
----------------------------------------------------------------------
diff --git a/src/android/FileUtils.java b/src/android/FileUtils.java
index 2a1e965..a810726 100644
--- a/src/android/FileUtils.java
+++ b/src/android/FileUtils.java
@@ -41,7 +41,6 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
-import java.net.URLDecoder;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -503,6 +502,24 @@ public class FileUtils extends CordovaPlugin {
         return true;
     }
 
+    public LocalFilesystemURL resolveNativeUri(Uri nativeUri) {
+        LocalFilesystemURL localURL = null;
+
+        // Try all installed filesystems. Return the best matching URL
+        // (determined by the shortest resulting URL)
+        for (Filesystem fs : filesystems) {
+            LocalFilesystemURL url = fs.toLocalUri(nativeUri);
+            if (url != null) {
+                // A shorter fullPath implies that the filesystem is a better
+                // match for the local path than the previous best.
+                if (localURL == null || (url.uri.toString().length() < localURL.toString().length())) {
+                    localURL = url;
+                }
+            }
+        }
+        return localURL;
+    }
+
     /*
      * These two native-only methods can be used by other plugins to translate between
      * device file system paths and URLs. By design, there is no direct JavaScript
@@ -529,15 +546,13 @@ public class FileUtils extends CordovaPlugin {
         // Try all installed filesystems. Return the best matching URL
         // (determined by the shortest resulting URL)
         for (Filesystem fs: filesystems) {
-            if (fs != null) {
-                LocalFilesystemURL url = fs.URLforFilesystemPath(localPath);
-                if (url != null) {
-                    // A shorter fullPath implies that the filesystem is a better
-                    // match for the local path than the previous best.
-                    if (localURL == null || (url.pathAndQuery.length() < shortestFullPath)) {
-                        localURL = url;
-                        shortestFullPath = url.pathAndQuery.length();
-                    }
+            LocalFilesystemURL url = fs.URLforFilesystemPath(localPath);
+            if (url != null) {
+                // A shorter fullPath implies that the filesystem is a better
+                // match for the local path than the previous best.
+                if (localURL == null || (url.path.length() < shortestFullPath)) {
+                    localURL = url;
+                    shortestFullPath = url.path.length();
                 }
             }
         }
@@ -592,18 +607,15 @@ public class FileUtils extends CordovaPlugin {
      * @throws JSONException
      */
     private JSONObject resolveLocalFileSystemURI(String uriString) throws IOException, JSONException {
-    	LocalFilesystemURL inputURL;
     	if (uriString == null) {
     		throw new MalformedURLException("Unrecognized filesystem URL");
     	}
     	Uri uri = Uri.parse(uriString);
 
-		/* Backwards-compatibility: Check for file:// urls */
-        if ("file".equals(uri.getScheme())) {
-            String path = uri.getPath();
-    		inputURL = this.filesystemURLforLocalPath(path);
-    	} else {
-    		inputURL = LocalFilesystemURL.parse(uri);
+        LocalFilesystemURL inputURL = LocalFilesystemURL.parse(uri);
+        if (inputURL == null) {
+    		/* Check for file://, content:// urls */
+    		inputURL = resolveNativeUri(uri);
     	}
 
         try {
@@ -687,7 +699,7 @@ public class FileUtils extends CordovaPlugin {
         try {
         	LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr);
         	// You can't delete the root directory.
-        	if ("".equals(inputURL.pathAndQuery) || "/".equals(inputURL.pathAndQuery)) {
+        	if ("".equals(inputURL.path) || "/".equals(inputURL.path)) {
         		throw new NoModificationAllowedException("You can't delete the root directory");
         	}
 
@@ -716,7 +728,7 @@ public class FileUtils extends CordovaPlugin {
         try {
         	LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr);
         	// You can't delete the root directory.
-        	if ("".equals(inputURL.pathAndQuery) || "/".equals(inputURL.pathAndQuery)) {
+        	if ("".equals(inputURL.path) || "/".equals(inputURL.path)) {
 
         		throw new NoModificationAllowedException("You can't delete the root directory");
         	}

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/d6202261/src/android/Filesystem.java
----------------------------------------------------------------------
diff --git a/src/android/Filesystem.java b/src/android/Filesystem.java
index 64b78b8..2d64942 100644
--- a/src/android/Filesystem.java
+++ b/src/android/Filesystem.java
@@ -43,34 +43,36 @@ public abstract class Filesystem {
         this.rootUri = rootUri;
         this.name = name;
         this.resourceApi = resourceApi;
-        rootEntry = makeEntryForPath("/", name, true, rootUri.toString());
+        rootEntry = makeEntryForNativeUri(rootUri);
     }
 
     public interface ReadFileCallback {
 		public void handleData(InputStream inputStream, String contentType) throws IOException;
 	}
 
-	public static JSONObject makeEntryForPath(String path, String fsName, Boolean isDir, String nativeURL) {
+    public static JSONObject makeEntryForURL(LocalFilesystemURL inputURL, Uri nativeURL) {
         try {
-            JSONObject entry = new JSONObject();
-
+            String path = inputURL.path;
             int end = path.endsWith("/") ? 1 : 0;
             String[] parts = path.substring(0, path.length() - end).split("/+");
             String fileName = parts[parts.length - 1];
-            entry.put("isFile", !isDir);
-            entry.put("isDirectory", isDir);
+
+            JSONObject entry = new JSONObject();
+            entry.put("isFile", !inputURL.isDirectory);
+            entry.put("isDirectory", inputURL.isDirectory);
             entry.put("name", fileName);
             entry.put("fullPath", path);
             // The file system can't be specified, as it would lead to an infinite loop,
             // but the filesystem name can be.
-            entry.put("filesystemName", fsName);
+            entry.put("filesystemName", inputURL.fsName);
             // Backwards compatibility
-            entry.put("filesystem", "temporary".equals(fsName) ? 0 : 1);
+            entry.put("filesystem", "temporary".equals(inputURL.fsName) ? 0 : 1);
 
-            if (isDir && !nativeURL.endsWith("/")) {
-                nativeURL += "/";
+            String nativeUrlStr = nativeURL.toString();
+            if (inputURL.isDirectory && !nativeUrlStr.endsWith("/")) {
+                nativeUrlStr += "/";
             }
-            entry.put("nativeURL", nativeURL);
+            entry.put("nativeURL", nativeUrlStr);
             return entry;
         } catch (JSONException e) {
             e.printStackTrace();
@@ -78,13 +80,21 @@ public abstract class Filesystem {
         }
     }
 
-    public static JSONObject makeEntryForURL(LocalFilesystemURL inputURL, Boolean isDir, Uri nativeURL) {
-        return makeEntryForPath(inputURL.pathAndQuery, inputURL.fsName, isDir, nativeURL.toString());
+    public JSONObject makeEntryForURL(LocalFilesystemURL inputURL) {
+        Uri nativeUri = toNativeUri(inputURL);
+        return makeEntryForURL(inputURL, nativeUri);
+    }
+
+    public JSONObject makeEntryForNativeUri(Uri nativeUri) {
+        LocalFilesystemURL inputUrl = toLocalUri(nativeUri);
+        return makeEntryForURL(inputUrl, nativeUri);
     }
 
-	abstract JSONObject getEntryForLocalURL(LocalFilesystemURL inputURL) throws IOException;
+    public JSONObject getEntryForLocalURL(LocalFilesystemURL inputURL) throws IOException {
+        return makeEntryForURL(inputURL);
+    }
 
-	abstract JSONObject getFileForLocalURL(LocalFilesystemURL inputURL, String path,
+    abstract JSONObject getFileForLocalURL(LocalFilesystemURL inputURL, String path,
 			JSONObject options, boolean directory) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException;
 
 	abstract boolean removeFileAtLocalURL(LocalFilesystemURL inputURL) throws InvalidModificationException, NoModificationAllowedException;
@@ -99,6 +109,9 @@ public abstract class Filesystem {
         return rootUri;
     }
 
+    public abstract Uri toNativeUri(LocalFilesystemURL inputURL);
+    public abstract LocalFilesystemURL toLocalUri(Uri inputURL);
+
     public JSONObject getRootEntry() {
         return rootEntry;
     }
@@ -126,57 +139,60 @@ public abstract class Filesystem {
         }
         return LocalFilesystemURL.parse(newDest);
     }
-    
+
 	/* Read a source URL (possibly from a different filesystem, srcFs,) and copy it to
 	 * the destination URL on this filesystem, optionally with a new filename.
 	 * If move is true, then this method should either perform an atomic move operation
 	 * or remove the source file when finished.
 	 */
-    JSONObject copyFileToURL(LocalFilesystemURL destURL, String newName,
+    public JSONObject copyFileToURL(LocalFilesystemURL destURL, String newName,
             Filesystem srcFs, LocalFilesystemURL srcURL, boolean move) throws IOException, InvalidModificationException, JSONException, NoModificationAllowedException, FileExistsException {
-        // This is "the hard way" -- transfer data between arbitrary filesystem urls/
-        // Gets an input stream from src, and writes its contents to an output stream
-        // from dest.
-
         // First, check to see that we can do it
-        if (!move || srcFs.canRemoveFileAtLocalURL(srcURL)) {
-            final LocalFilesystemURL destination = makeDestinationURL(newName, srcURL, destURL);
-            srcFs.readFileAtURL(srcURL, 0, -1, new ReadFileCallback() {
-                public void handleData(InputStream inputStream, String contentType) throws IOException {
-                    if (inputStream != null) {
-                        //write data to file
-                        OutputStream os = getOutputStreamForURL(destination);
-                        final int BUFFER_SIZE = 8192;
-                        byte[] buffer = new byte[BUFFER_SIZE];
-
-                        for (;;) {
-                            int bytesRead = inputStream.read(buffer, 0, BUFFER_SIZE);
-
-                            if (bytesRead <= 0) {
-                                break;
-                            }
-                            os.write(buffer, 0, bytesRead);
-                        }
-                        os.close();
-                    } else {
-                        throw new IOException("Cannot read file at source URL");
-                    }
-                }
-            });
-            if (move) {
-                // Delete original
-                srcFs.removeFileAtLocalURL(srcURL);
-            }
-            return getEntryForLocalURL(destination);
-        } else {
+        if (move && !srcFs.canRemoveFileAtLocalURL(srcURL)) {
             throw new NoModificationAllowedException("Cannot move file at source URL");
         }
+        final LocalFilesystemURL destination = makeDestinationURL(newName, srcURL, destURL);
+
+        Uri srcNativeUri = srcFs.toNativeUri(srcURL);
+
+        CordovaResourceApi.OpenForReadResult ofrr = resourceApi.openForRead(srcNativeUri);
+        OutputStream os = null;
+        try {
+            os = getOutputStreamForURL(destination);
+        } catch (IOException e) {
+            ofrr.inputStream.close();
+            throw e;
+        }
+        // Closes streams.
+        resourceApi.copyResource(ofrr, os);
+
+        if (move) {
+            srcFs.removeFileAtLocalURL(srcURL);
+        }
+        return getEntryForLocalURL(destination);
     }
 
-    abstract OutputStream getOutputStreamForURL(LocalFilesystemURL inputURL) throws IOException;
+    public OutputStream getOutputStreamForURL(LocalFilesystemURL inputURL) throws IOException {
+        return resourceApi.openOutputStream(toNativeUri(inputURL));
+    }
 
-    abstract void readFileAtURL(LocalFilesystemURL inputURL, long start, long end,
-			ReadFileCallback readFileCallback) throws IOException;
+    public void readFileAtURL(LocalFilesystemURL inputURL, long start, long end,
+                              ReadFileCallback readFileCallback) throws IOException {
+        CordovaResourceApi.OpenForReadResult ofrr = resourceApi.openForRead(toNativeUri(inputURL));
+        if (end < 0) {
+            end = ofrr.length;
+        }
+        long numBytesToRead = end - start;
+        try {
+            if (start > 0) {
+                ofrr.inputStream.skip(start);
+            }
+            LimitedInputStream inputStream = new LimitedInputStream(ofrr.inputStream, numBytesToRead);
+            readFileCallback.handleData(inputStream, ofrr.mimeType);
+        } finally {
+            ofrr.inputStream.close();
+        }
+    }
 
 	abstract long writeToFileAtURL(LocalFilesystemURL inputURL, String data, int offset,
 			boolean isBinary) throws NoModificationAllowedException, IOException;

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/d6202261/src/android/LocalFilesystem.java
----------------------------------------------------------------------
diff --git a/src/android/LocalFilesystem.java b/src/android/LocalFilesystem.java
index 805eacd..2754b41 100644
--- a/src/android/LocalFilesystem.java
+++ b/src/android/LocalFilesystem.java
@@ -24,14 +24,11 @@ import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.io.RandomAccessFile;
 import java.nio.channels.FileChannel;
 import java.util.ArrayList;
 import java.util.Arrays;
 
-import org.apache.cordova.CordovaInterface;
 import org.apache.cordova.CordovaResourceApi;
 import org.json.JSONArray;
 import org.json.JSONException;
@@ -41,13 +38,12 @@ import android.util.Base64;
 import android.net.Uri;
 import android.content.Context;
 import android.content.Intent;
-import android.app.Activity;
 
 public class LocalFilesystem extends Filesystem {
     private final Context context;
 
     public LocalFilesystem(String name, Context context, CordovaResourceApi resourceApi, String rootPath) {
-        this(name, context, resourceApi, Uri.fromFile(new File(rootPath)));
+        this(name, context, resourceApi, Uri.fromFile(new File(rootPath)).buildUpon().appendPath("").build());
     }
 	public LocalFilesystem(String name, Context context, CordovaResourceApi resourceApi, Uri rootUri) {
         super(rootUri, name, resourceApi);
@@ -55,50 +51,91 @@ public class LocalFilesystem extends Filesystem {
 	}
 
     public String filesystemPathForFullPath(String fullPath) {
-	    String path = new File(rootUri.getPath(), fullPath).toString();
-        int questionMark = path.indexOf("?");
-        if (questionMark >= 0) {
-          path = path.substring(0, questionMark);
-        }
-	    if (path.length() > 1 && path.endsWith("/")) {
-	      path = path.substring(0, path.length()-1);
-	    }
-	    return path;
+	    return new File(rootUri.getPath(), fullPath).toString();
 	}
 	
 	@Override
 	public String filesystemPathForURL(LocalFilesystemURL url) {
-		return filesystemPathForFullPath(url.pathAndQuery);
+		return filesystemPathForFullPath(url.path);
 	}
 
 	private String fullPathForFilesystemPath(String absolutePath) {
 		if (absolutePath != null && absolutePath.startsWith(rootUri.getPath())) {
-			return absolutePath.substring(rootUri.getPath().length());
+			return absolutePath.substring(rootUri.getPath().length() - 1);
 		}
 		return null;
 	}
 
+    private Uri nativeUriForFullPath(String fullPath) {
+        Uri ret = null;
+        if (fullPath != null) {
+            String encodedPath = Uri.fromFile(new File(fullPath)).getEncodedPath();
+            if (encodedPath.startsWith("/")) {
+                encodedPath = encodedPath.substring(1);
+            }
+            ret = rootUri.buildUpon().appendEncodedPath(encodedPath).build();
+        }
+        return ret;
+    }
+
 	protected LocalFilesystemURL URLforFullPath(String fullPath) {
-	    if (fullPath != null) {
-	    	if (fullPath.startsWith("/")) {
-	    		return LocalFilesystemURL.parse(LocalFilesystemURL.FILESYSTEM_PROTOCOL + "://localhost/"+this.name+fullPath);
-	    	}
-	        return LocalFilesystemURL.parse(LocalFilesystemURL.FILESYSTEM_PROTOCOL + "://localhost/"+this.name+"/"+fullPath);
+        Uri nativeUri = nativeUriForFullPath(fullPath);
+	    if (nativeUri != null) {
+            return toLocalUri(nativeUri);
 	    }
 	    return null;
-		
 	}
+
+    @Override
+    public Uri toNativeUri(LocalFilesystemURL inputURL) {
+        return nativeUriForFullPath(inputURL.path);
+    }
+
+    @Override
+    public LocalFilesystemURL toLocalUri(Uri inputURL) {
+        if (!"file".equals(inputURL.getScheme())) {
+            return null;
+        }
+        File f = new File(inputURL.getPath());
+        // Removes and duplicate /s (e.g. file:///a//b/c)
+        Uri resolvedUri = Uri.fromFile(f);
+        String rootUriNoTrailingSlash = rootUri.getEncodedPath();
+        rootUriNoTrailingSlash = rootUriNoTrailingSlash.substring(0, rootUriNoTrailingSlash.length() - 1);
+        if (!resolvedUri.getEncodedPath().startsWith(rootUriNoTrailingSlash)) {
+            return null;
+        }
+        String subPath = resolvedUri.getEncodedPath().substring(rootUriNoTrailingSlash.length());
+        // Strip leading slash
+        if (!subPath.isEmpty()) {
+            subPath = subPath.substring(1);
+        }
+        Uri.Builder b = new Uri.Builder()
+            .scheme(LocalFilesystemURL.FILESYSTEM_PROTOCOL)
+            .authority("localhost")
+            .path(name);
+        if (!subPath.isEmpty()) {
+            b.appendEncodedPath(subPath);
+        }
+        if (f.isDirectory() || inputURL.getPath().endsWith("/")) {
+            // Add trailing / for directories.
+            b.appendEncodedPath("");
+        }
+        return LocalFilesystemURL.parse(b.build());
+    }
 	
 	@Override
 	public LocalFilesystemURL URLforFilesystemPath(String path) {
 	    return this.URLforFullPath(this.fullPathForFilesystemPath(path));
 	}
 
+    /**
+     * Removes multiple repeated //s, and collapses processes ../s.
+     */
 	protected String normalizePath(String rawPath) {
 	    // If this is an absolute path, trim the leading "/" and replace it later
 	    boolean isAbsolutePath = rawPath.startsWith("/");
 	    if (isAbsolutePath) {
-	        rawPath = rawPath.substring(1);
+	        rawPath = rawPath.replaceFirst("/+", "");
 	    }
 	    ArrayList<String> components = new ArrayList<String>(Arrays.asList(rawPath.split("/+")));
 	    for (int index = 0; index < components.size(); ++index) {
@@ -120,18 +157,12 @@ public class LocalFilesystem extends Filesystem {
 	    } else {
 	    	return normalizedPath.toString().substring(1);
 	    }
-
-
 	}
 
 	
 	@Override
-    public JSONObject makeEntryForFile(File file) throws JSONException {
-    	String path = this.fullPathForFilesystemPath(file.getAbsolutePath());
-    	if (path != null) {
-    		return makeEntryForPath(path, this.name, file.isDirectory(), Uri.fromFile(file).toString());
-    	}
-    	return null;
+    public JSONObject makeEntryForFile(File file) {
+        return makeEntryForNativeUri(Uri.fromFile(file));
     }
 
 	@Override
@@ -144,7 +175,7 @@ public class LocalFilesystem extends Filesystem {
       if (!fp.canRead()) {
           throw new IOException();
       }
-      return LocalFilesystem.makeEntryForURL(inputURL, fp.isDirectory(),  Uri.fromFile(fp));
+      return super.getEntryForLocalURL(inputURL);
 	}
 
 	@Override
@@ -168,10 +199,13 @@ public class LocalFilesystem extends Filesystem {
         LocalFilesystemURL requestedURL;
         
         // Check whether the supplied path is absolute or relative
+        if (directory && !path.endsWith("/")) {
+            path += "/";
+        }
         if (path.startsWith("/")) {
-        	requestedURL = URLforFilesystemPath(path);
+        	requestedURL = URLforFilesystemPath(normalizePath(path));
         } else {
-        	requestedURL = URLforFullPath(normalizePath(inputURL.pathAndQuery + "/" + path));
+        	requestedURL = URLforFullPath(normalizePath(inputURL.path + "/" + path));
         }
         
         File fp = new File(this.filesystemPathForURL(requestedURL));
@@ -205,7 +239,7 @@ public class LocalFilesystem extends Filesystem {
         }
 
         // Return the directory
-        return makeEntryForPath(requestedURL.pathAndQuery, requestedURL.fsName, directory, Uri.fromFile(fp).toString());
+        return makeEntryForURL(requestedURL);
 	}
 
 	@Override
@@ -256,7 +290,7 @@ public class LocalFilesystem extends Filesystem {
             File[] files = fp.listFiles();
             for (int i = 0; i < files.length; i++) {
                 if (files[i].canRead()) {
-                    entries.put(makeEntryForPath(fullPathForFilesystemPath(files[i].getAbsolutePath()), inputURL.fsName, files[i].isDirectory(), Uri.fromFile(files[i]).toString()));
+                    entries.put(makeEntryForFile(files[i]));
                 }
             }
         }
@@ -278,7 +312,7 @@ public class LocalFilesystem extends Filesystem {
         	metadata.put("size", file.isDirectory() ? 0 : file.length());
         	metadata.put("type", resourceApi.getMimeType(Uri.fromFile(file)));
         	metadata.put("name", file.getName());
-        	metadata.put("fullPath", inputURL.pathAndQuery);
+        	metadata.put("fullPath", inputURL.path);
         	metadata.put("lastModifiedDate", file.lastModified());
         } catch (JSONException e) {
         	return null;
@@ -514,30 +548,6 @@ public class LocalFilesystem extends Filesystem {
             return super.copyFileToURL(destURL, newName, srcFs, srcURL, move);
     	}
 	}
-
-	@Override
-    public void readFileAtURL(LocalFilesystemURL inputURL, long start, long end,
-			ReadFileCallback readFileCallback) throws IOException {
-
-		File file = new File(this.filesystemPathForURL(inputURL));
-        String contentType = resourceApi.getMimeType(Uri.fromFile(file));
-		
-        if (end < 0) {
-            end = file.length();
-        }
-        long numBytesToRead = end - start;
-
-        InputStream rawInputStream = new FileInputStream(file);
-		try {
-			if (start > 0) {
-                rawInputStream.skip(start);
-			}
-            LimitedInputStream inputStream = new LimitedInputStream(rawInputStream, numBytesToRead);
-            readFileCallback.handleData(inputStream, contentType);
-		} finally {
-            rawInputStream.close();
-		}
-	}
     
 	@Override
 	public long writeToFileAtURL(LocalFilesystemURL inputURL, String data,
@@ -624,13 +634,4 @@ public class LocalFilesystem extends Filesystem {
 		File file = new File(path);
 		return file.exists();
 	}
-
-	@Override
-	OutputStream getOutputStreamForURL(LocalFilesystemURL inputURL) throws FileNotFoundException {
-		String path = filesystemPathForURL(inputURL);
-		File file = new File(path);
-		FileOutputStream os = new FileOutputStream(file);
-		return os;
-	}
-
 }

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/d6202261/src/android/LocalFilesystemURL.java
----------------------------------------------------------------------
diff --git a/src/android/LocalFilesystemURL.java b/src/android/LocalFilesystemURL.java
index 473b587..b96b6ee 100644
--- a/src/android/LocalFilesystemURL.java
+++ b/src/android/LocalFilesystemURL.java
@@ -18,8 +18,6 @@
  */
 package org.apache.cordova.file;
 
-import java.util.List;
-
 import android.net.Uri;
 
 public class LocalFilesystemURL {
@@ -28,29 +26,32 @@ public class LocalFilesystemURL {
 
     public final Uri uri;
     public final String fsName;
-    public final String pathAndQuery;
+    public final String path;
+    public final boolean isDirectory;
 
-	private LocalFilesystemURL(Uri uri, String fsName, String fsPath) {
+	private LocalFilesystemURL(Uri uri, String fsName, String fsPath, boolean isDirectory) {
 		this.uri = uri;
         this.fsName = fsName;
-        this.pathAndQuery = fsPath;
+        this.path = fsPath;
+        this.isDirectory = isDirectory;
 	}
 
     public static LocalFilesystemURL parse(Uri uri) {
         if (!FILESYSTEM_PROTOCOL.equals(uri.getScheme())) {
             return null;
         }
-        List<String> pathComponents = uri.getPathSegments();
-        if (pathComponents == null || pathComponents.size() == 0) {
+        String path = uri.getPath();
+        if (path.length() < 1) {
             return null;
         }
-        String fsName = pathComponents.get(0);
-        String pathAndQuery = uri.getPath();
-        pathAndQuery = pathAndQuery.substring(pathAndQuery.indexOf('/', 1));
-        if (uri.getQuery() != null) {
-            pathAndQuery = pathAndQuery + "?" + uri.getQuery();
+        int firstSlashIdx = path.indexOf('/', 1);
+        if (firstSlashIdx < 0) {
+            return null;
         }
-        return new LocalFilesystemURL(uri, fsName, pathAndQuery);
+        String fsName = path.substring(1, firstSlashIdx);
+        path = path.substring(firstSlashIdx);
+        boolean isDirectory = path.charAt(path.length() - 1) == '/';
+        return new LocalFilesystemURL(uri, fsName, path, isDirectory);
     }
 
     public static LocalFilesystemURL parse(String uri) {

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/d6202261/tests/plugin.xml
----------------------------------------------------------------------
diff --git a/tests/plugin.xml b/tests/plugin.xml
index ba1b01c..80adb4e 100644
--- a/tests/plugin.xml
+++ b/tests/plugin.xml
@@ -29,4 +29,14 @@
 
     <js-module src="tests.js" name="tests">
     </js-module>
+
+    <platform name="android">
+        <source-file src="src/android/TestContentProvider.java" target-dir="src/org/apache/cordova/file/test" />
+        <config-file target="AndroidManifest.xml" parent="/*/application">
+            <provider
+                android:name="org.apache.cordova.file.test.TestContentProvider"
+                android:authorities="org.apache.cordova.file.testprovider"
+                android:exported="false" />
+        </config-file>
+    </platform>
 </plugin>

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/d6202261/tests/src/android/TestContentProvider.java
----------------------------------------------------------------------
diff --git a/tests/src/android/TestContentProvider.java b/tests/src/android/TestContentProvider.java
new file mode 100644
index 0000000..1aa4783
--- /dev/null
+++ b/tests/src/android/TestContentProvider.java
@@ -0,0 +1,78 @@
+package org.apache.cordova.file.test;
+
+import android.content.ContentProvider;
+import android.net.Uri;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.os.ParcelFileDescriptor;
+
+import org.apache.cordova.CordovaResourceApi;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+public class TestContentProvider extends ContentProvider {
+
+    @Override
+    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+        String fileName = uri.getQueryParameter("realPath");
+        if (fileName == null) {
+            fileName = uri.getPath();
+        }
+        if (fileName == null || fileName.length() < 1) {
+            throw new FileNotFoundException();
+        }
+        CordovaResourceApi resourceApi = new CordovaResourceApi(getContext(), null);
+        try {
+            File f = File.createTempFile("test-content-provider", ".tmp");
+            resourceApi.copyResource(Uri.parse("file:///android_asset" + fileName), Uri.fromFile(f));
+            FileInputStream fis = new FileInputStream(f);
+            String thisIsDumb = fis.getFD().toString();
+            int fd = Integer.parseInt(thisIsDumb.substring("FileDescriptor[".length(), thisIsDumb.length() - 1));
+            return ParcelFileDescriptor.adoptFd(fd);
+        } catch (FileNotFoundException e) {
+            throw e;
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new FileNotFoundException("IO error: " + e.toString());
+        }
+    }
+
+    @Override
+    public boolean onCreate() {
+        return false;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return "text/html";
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException();
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/d6202261/tests/tests.js
----------------------------------------------------------------------
diff --git a/tests/tests.js b/tests/tests.js
index 1478aaf..adaa9ab 100644
--- a/tests/tests.js
+++ b/tests/tests.js
@@ -2090,8 +2090,7 @@ exports.defineAutoTests = function () {
                             expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
                         }
                         // cleanup
-                        deleteEntry(file1);
-                        done();
+                        deleteEntry(file1, done);
                     });
                 }, failed.bind(null, done, 'createFile - Error creating file: ' + file1));
             });
@@ -2904,8 +2903,7 @@ exports.defineAutoTests = function () {
                         expect(fileEntry.name).toBe(fileName);
                         expect(fileEntry.fullPath).toCanonicallyMatch(root.fullPath +'/' + fileName);
                         // cleanup
-                        deleteEntry(fileName);
-                        done();
+                        deleteEntry(fileName, done);
                     }, failed.bind(null, done, 'root.getFile - Error getting file: ../' + fileName));
                 }, failed.bind(null, done, 'createFile - Error creating file: ../' + fileName));
             });
@@ -3131,8 +3129,7 @@ exports.defineAutoTests = function () {
                         : expect(entry.filesystem.name).toEqual("persistent");
                     // cleanup
                     deleteEntry(entry.name);
-                    deleteEntry(sourceEntry.name);
-                    done();
+                    deleteEntry(sourceEntry.name, done);
                 },
                 createSourceAndTransfer = function () {
                     temp_root.getFile(file1, {
@@ -3166,8 +3163,7 @@ exports.defineAutoTests = function () {
                         : expect(entry.filesystem.name).toEqual("temporary");
                     // cleanup
                     deleteEntry(entry.name);
-                    deleteEntry(sourceEntry.name);
-                    done();
+                    deleteEntry(sourceEntry.name, done);
                 },
                 createSourceAndTransfer = function () {
                     persistent_root.getFile(file1, {
@@ -3204,8 +3200,7 @@ exports.defineAutoTests = function () {
                         : expect(entry.filesystem.name).toEqual("persistent");
                     // cleanup
                     deleteEntry(entry.name);
-                    deleteEntry(sourceEntry.name);
-                    done();
+                    deleteEntry(sourceEntry.name, done);
                 },
                 createSourceAndTransfer = function () {
                     temp_root.getFile(file1, {
@@ -3239,8 +3234,7 @@ exports.defineAutoTests = function () {
                         : expect(entry.filesystem.name).toEqual("temporary");
                     // cleanup
                     deleteEntry(entry.name);
-                    deleteEntry(sourceEntry.name);
-                    done();
+                    deleteEntry(sourceEntry.name, done);
                 },
                 createSourceAndTransfer = function () {
                     persistent_root.getFile(file1, {
@@ -3314,8 +3308,7 @@ exports.defineAutoTests = function () {
                     expect(directory.name).toCanonicallyMatch(nestedName);
                     expect(directory.fullPath).toCanonicallyMatch('/' + path + '/');
                     deleteEntry(directory.name);
-                    deleteEntry(parentName);
-                    done();
+                    deleteEntry(parentName, done);
                 };
 
                 createDirectory(parentName, function() {
@@ -3334,8 +3327,7 @@ exports.defineAutoTests = function () {
                     expect(fileEntry.isFile).toBe(true);
                     expect(fileEntry.isDirectory).toBe(false);
                     expect(fileEntry.name).toCanonicallyMatch(secondFileName);
-                    deleteEntry(fileEntry.name);
-                    done();
+                    deleteEntry(fileEntry.name, done);
                 };
 
                 createFile(deletedFileName, function (deletedFile) {
@@ -3359,8 +3351,7 @@ exports.defineAutoTests = function () {
                     expect(directory.isFile).toBe(false);
                     expect(directory.isDirectory).toBe(true);
                     expect(directory.name).toCanonicallyMatch(secondDirName);
-                    deleteEntry(directory.name);
-                    done();
+                    deleteEntry(directory.name, done);
                 };
 
                 createDirectory(deletedDirName, function (deletedDir) {
@@ -3383,8 +3374,7 @@ exports.defineAutoTests = function () {
                     expect(directory.isFile).toBe(false);
                     expect(directory.isDirectory).toBe(true);
                     expect(directory.name).toCanonicallyMatch(parentName);
-                    deleteEntry(directory.name);
-                    done();
+                    deleteEntry(directory.name, done);
                 };
 
                 createDirectory(parentName, function(parent){
@@ -3405,8 +3395,7 @@ exports.defineAutoTests = function () {
                     expect(directory.isFile).toBe(false);
                     expect(directory.isDirectory).toBe(true);
                     expect(directory.name).toCanonicallyMatch(dirName);
-                    deleteEntry(directory.name);
-                    done();
+                    deleteEntry(directory.name, done);
                 };
 
                 createDirectory(dirName, function(){
@@ -3415,9 +3404,47 @@ exports.defineAutoTests = function () {
                 }, failed.bind(this, done, 'root.getDirectory - Error creating directory : ' + dirName));
             });
         });
-        // IndexedDB-based impl
+        // Content and Asset URLs
+        if (cordova.platformId == 'android') {
+            describe('content: URLs', function() {
+                function testContentCopy(src, done) {
+                    var file2 = "entry.copy.file2b",
+                    fullPath = joinURL(temp_root.fullPath, file2),
+                    validateFile = function (entry) {
+                        expect(entry.isFile).toBe(true);
+                        expect(entry.isDirectory).toBe(false);
+                        expect(entry.name).toCanonicallyMatch(file2);
+                        expect(entry.fullPath).toCanonicallyMatch(fullPath);
+                        expect(entry.filesystem.name).toEqual("temporary");
+                        // cleanup
+                        deleteEntry(entry.name, done);
+                    },
+                    transfer = function () {
+                        resolveLocalFileSystemURL(src, function(entry) {
+                            expect(entry).toBeDefined();
+                            expect(entry.filesystem.name).toEqual("content");
+                            entry.copyTo(temp_root, file2, validateFile, failed.bind(null, done, 'entry.copyTo - Error copying file: ' + entry.toURL() + ' to TEMPORAL root as: ' + file2));
+                        }, failed.bind(null, done, 'resolveLocalFileSystemURL failed for content provider'));
+                    };
+                    // Delete any existing file to start things off
+                    temp_root.getFile(file2, {}, function (entry) {
+                        entry.remove(transfer, failed.bind(null, done, 'entry.remove - Error removing file: ' + file2));
+                    }, transfer);
+                }
+                it("file.spec.138 copyTo: content", function(done) {
+                    testContentCopy('content://org.apache.cordova.file.testprovider/www/index.html', done);
+                });
+                it("file.spec.139 copyTo: content /w space and query", function(done) {
+                    testContentCopy('content://org.apache.cordova.file.testprovider/?name=foo%20bar&realPath=%2Fwww%2Findex.html', done);
+                });
+                it("file.spec.140 delete: content should fail", function(done) {
+                    resolveLocalFileSystemURL('content://org.apache.cordova.file.testprovider/www/index.html', function(entry) {
+                        entry.remove(failed.bind(null, done, 'expected delete to fail'), done);
+                    }, failed.bind(null, done, 'resolveLocalFileSystemURL failed for content provider'));
+                });
+            });
+        }
     });
-    //File API describe
 
 };
 //******************************************************************************************

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/d6202261/www/Entry.js
----------------------------------------------------------------------
diff --git a/www/Entry.js b/www/Entry.js
index 12f6227..c23f8e2 100644
--- a/www/Entry.js
+++ b/www/Entry.js
@@ -180,7 +180,7 @@ Entry.prototype.copyTo = function(parent, newName, successCallback, errorCallbac
  */
 Entry.prototype.toInternalURL = function() {
     if (this.filesystem && this.filesystem.__format__) {
-      return this.filesystem.__format__(this.fullPath);
+      return this.filesystem.__format__(this.fullPath, this.nativeURL);
     }
 };
 

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/d6202261/www/FileSystem.js
----------------------------------------------------------------------
diff --git a/www/FileSystem.js b/www/FileSystem.js
index efa6740..36bffbb 100644
--- a/www/FileSystem.js
+++ b/www/FileSystem.js
@@ -37,7 +37,7 @@ var FileSystem = function(name, root) {
     }
 };
 
-FileSystem.prototype.__format__ = function(fullPath) {
+FileSystem.prototype.__format__ = function(fullPath, nativeUrl) {
     return fullPath;
 };
 

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/d6202261/www/android/FileSystem.js
----------------------------------------------------------------------
diff --git a/www/android/FileSystem.js b/www/android/FileSystem.js
index fde9c7a..518e4c4 100644
--- a/www/android/FileSystem.js
+++ b/www/android/FileSystem.js
@@ -22,12 +22,15 @@
 FILESYSTEM_PROTOCOL = "cdvfile";
 
 module.exports = {
-    __format__: function(fullPath) {
-        if (this.name === 'content') {
-            return 'content:/' + fullPath;
+    __format__: function(fullPath, nativeUrl) {
+        var path = '/' + this.name + '/' + encodeURI(fullPath);
+        path = path.replace('//','/');
+        var ret = FILESYSTEM_PROTOCOL + '://localhost' + path;
+        var m = /\?.*/.exec(nativeUrl);
+        if (m) {
+          ret += m[0];
         }
-        var path = ('/'+this.name+(fullPath[0]==='/'?'':'/')+encodeURI(fullPath)).replace('//','/');
-        return FILESYSTEM_PROTOCOL + '://localhost' + path;
+        return ret;
     }
 };
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org


Mime
View raw message