You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ag...@apache.org on 2015/03/11 16:35:23 UTC

[12/13] cordova-plugin-file git commit: CB-6428 android: Add support for file:///android_asset URLs

CB-6428 android: Add support for file:///android_asset URLs

Supports:
- listing
- copying of file


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

Branch: refs/heads/master
Commit: 356d334382fe7f827b2635e0c3f819453f746bab
Parents: d620226
Author: Andrew Grieve <ag...@chromium.org>
Authored: Tue Mar 10 16:25:23 2015 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Wed Mar 11 11:35:08 2015 -0400

----------------------------------------------------------------------
 plugin.xml                       |   1 +
 src/android/AssetFilesystem.java | 222 ++++++++++++++++++++++++++++++++++
 src/android/FileUtils.java       |   6 +-
 src/android/Filesystem.java      |  74 ++++++++++--
 src/android/LocalFilesystem.java |  72 ++---------
 tests/tests.js                   |  37 ++++++
 6 files changed, 337 insertions(+), 75 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/356d3343/plugin.xml
----------------------------------------------------------------------
diff --git a/plugin.xml b/plugin.xml
index 10c0c65..c4b9dcf 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -129,6 +129,7 @@ xmlns:android="http://schemas.android.com/apk/res/android"
         <source-file src="src/android/Filesystem.java" target-dir="src/org/apache/cordova/file" />
         <source-file src="src/android/LocalFilesystem.java" target-dir="src/org/apache/cordova/file" />
         <source-file src="src/android/ContentFilesystem.java" target-dir="src/org/apache/cordova/file" />
+        <source-file src="src/android/AssetFilesystem.java" target-dir="src/org/apache/cordova/file" />
 
         <!-- android specific file apis -->
         <js-module src="www/android/FileSystem.js" name="androidFileSystem">

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/356d3343/src/android/AssetFilesystem.java
----------------------------------------------------------------------
diff --git a/src/android/AssetFilesystem.java b/src/android/AssetFilesystem.java
new file mode 100644
index 0000000..7a54e43
--- /dev/null
+++ b/src/android/AssetFilesystem.java
@@ -0,0 +1,222 @@
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+ */
+package org.apache.cordova.file;
+
+import android.content.res.AssetManager;
+import android.net.Uri;
+
+import org.apache.cordova.CordovaResourceApi;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+public class AssetFilesystem extends Filesystem {
+
+    private final AssetManager assetManager;
+
+    public AssetFilesystem(AssetManager assetManager, CordovaResourceApi resourceApi) {
+        super(Uri.parse("file:///android_asset/"), "assets", resourceApi);
+        this.assetManager = assetManager;
+	}
+
+    @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 (isDirectory(subPath) || inputURL.getPath().endsWith("/")) {
+            // Add trailing / for directories.
+            b.appendEncodedPath("");
+        }
+        return LocalFilesystemURL.parse(b.build());
+    }
+
+    private Boolean isDirectory(String assetPath) {
+        if (assetPath.startsWith("/")) {
+            assetPath = assetPath.substring(1);
+        }
+        try {
+            return assetManager.list(assetPath).length != 0;
+        } catch (IOException e) {
+        }
+        return false;
+    }
+
+    private LocalFilesystemURL URLforFullPath(String fullPath) {
+        Uri nativeUri = nativeUriForFullPath(fullPath);
+        if (nativeUri != null) {
+            return toLocalUri(nativeUri);
+        }
+        return null;
+    }
+
+
+    @Override
+	public JSONArray readEntriesAtLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException {
+        String[] files;
+        String pathNoSlashes = inputURL.path.substring(1);
+        if (pathNoSlashes.endsWith("/")) {
+            pathNoSlashes = pathNoSlashes.substring(0, pathNoSlashes.length() - 1);
+        }
+
+        try {
+            files = assetManager.list(pathNoSlashes);
+        } catch (IOException e) {
+            throw new FileNotFoundException();
+        }
+
+        JSONArray entries = new JSONArray();
+        if (files != null) {
+            for (String file : files) {
+                Uri newNativeUri = nativeUriForFullPath(new File(inputURL.path, file).getPath());
+                entries.put(makeEntryForNativeUri(newNativeUri));
+            }
+        }
+
+        return entries;
+	}
+
+    @Override
+    public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL,
+                                         String path, JSONObject options, boolean directory)
+            throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException {
+        if (options != null && options.optBoolean("create")) {
+            throw new UnsupportedOperationException("Assets are read-only");
+        }
+
+        // Check whether the supplied path is absolute or relative
+        if (directory && !path.endsWith("/")) {
+            path += "/";
+        }
+
+        LocalFilesystemURL requestedURL;
+        if (path.startsWith("/")) {
+            requestedURL = URLforFullPath(normalizePath(path));
+        } else {
+            requestedURL = URLforFullPath(normalizePath(inputURL.path + "/" + path));
+        }
+
+        // Throws a FileNotFoundException if it doesn't exist.
+        getFileMetadataForLocalURL(requestedURL);
+
+        boolean isDir = isDirectory(requestedURL.path);
+        if (directory && !isDir) {
+            throw new TypeMismatchException("path doesn't exist or is file");
+        } else if (!directory && isDir) {
+            throw new TypeMismatchException("path doesn't exist or is directory");
+        }
+
+        // Return the directory
+        return makeEntryForURL(requestedURL);
+    }
+
+
+    @Override
+	public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException {
+        CordovaResourceApi.OpenForReadResult offr;
+        try {
+            offr = inputURL.isDirectory ? null : resourceApi.openForRead(toNativeUri(inputURL));
+        } catch (IOException e) {
+            throw new FileNotFoundException("File not found: " + inputURL);
+        }
+        JSONObject metadata = new JSONObject();
+        try {
+        	metadata.put("size", inputURL.isDirectory ? 0 : offr.length);
+        	metadata.put("type", inputURL.isDirectory ? "text/directory" : offr.mimeType);
+        	metadata.put("name", new File(inputURL.path).getName());
+        	metadata.put("fullPath", inputURL.path);
+        	metadata.put("lastModifiedDate", 0);
+        } catch (JSONException e) {
+            return null;
+        } finally {
+            if (offr != null) {
+                try {
+                    offr.inputStream.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        return metadata;
+	}
+
+	@Override
+	public boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL) {
+		return false;
+	}
+
+    @Override
+    long writeToFileAtURL(LocalFilesystemURL inputURL, String data, int offset, boolean isBinary) throws NoModificationAllowedException, IOException {
+        throw new NoModificationAllowedException("Assets are read-only");
+    }
+
+    @Override
+    long truncateFileAtURL(LocalFilesystemURL inputURL, long size) throws IOException, NoModificationAllowedException {
+        throw new NoModificationAllowedException("Assets are read-only");
+    }
+
+    @Override
+    String filesystemPathForURL(LocalFilesystemURL url) {
+        return null;
+    }
+
+    @Override
+    LocalFilesystemURL URLforFilesystemPath(String path) {
+        return null;
+    }
+
+    @Override
+    boolean removeFileAtLocalURL(LocalFilesystemURL inputURL) throws InvalidModificationException, NoModificationAllowedException {
+        throw new NoModificationAllowedException("Assets are read-only");
+    }
+
+    @Override
+    boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL) throws FileExistsException, NoModificationAllowedException {
+        throw new NoModificationAllowedException("Assets are read-only");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/356d3343/src/android/FileUtils.java
----------------------------------------------------------------------
diff --git a/src/android/FileUtils.java b/src/android/FileUtils.java
index a810726..67ebbe1 100644
--- a/src/android/FileUtils.java
+++ b/src/android/FileUtils.java
@@ -194,6 +194,7 @@ public class FileUtils extends CordovaPlugin {
     		this.registerFilesystem(new LocalFilesystem("temporary", webView.getContext(), webView.getResourceApi(), tempRoot));
     		this.registerFilesystem(new LocalFilesystem("persistent", webView.getContext(), webView.getResourceApi(), persistentRoot));
     		this.registerFilesystem(new ContentFilesystem(webView.getContext(), webView.getResourceApi()));
+            this.registerFilesystem(new AssetFilesystem(webView.getContext().getAssets(), webView.getResourceApi()));
 
             registerExtraFileSystems(getExtraFileSystemsPreference(activity), getAvailableFileSystems(activity));
 
@@ -623,10 +624,13 @@ public class FileUtils extends CordovaPlugin {
         	if (fs == null) {
         		throw new MalformedURLException("No installed handlers for this URL");
         	}
-        	return fs.getEntryForLocalURL(inputURL);
+            if (fs.exists(inputURL)) {
+                return fs.getEntryForLocalURL(inputURL);
+            }
         } catch (IllegalArgumentException e) {
         	throw new MalformedURLException("Unrecognized filesystem URL");
         }
+        throw new FileNotFoundException();
     }   
     
     /**

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/356d3343/src/android/Filesystem.java
----------------------------------------------------------------------
diff --git a/src/android/Filesystem.java b/src/android/Filesystem.java
index 2d64942..724976d 100644
--- a/src/android/Filesystem.java
+++ b/src/android/Filesystem.java
@@ -26,6 +26,8 @@ import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
 
 import org.apache.cordova.CordovaResourceApi;
 import org.json.JSONArray;
@@ -37,13 +39,12 @@ public abstract class Filesystem {
     protected final Uri rootUri;
     protected final CordovaResourceApi resourceApi;
     public final String name;
-    private final JSONObject rootEntry;
+    private JSONObject rootEntry;
 
     public Filesystem(Uri rootUri, String name, CordovaResourceApi resourceApi) {
         this.rootUri = rootUri;
         this.name = name;
         this.resourceApi = resourceApi;
-        rootEntry = makeEntryForNativeUri(rootUri);
     }
 
     public interface ReadFileCallback {
@@ -94,6 +95,10 @@ public abstract class Filesystem {
         return makeEntryForURL(inputURL);
     }
 
+    public JSONObject makeEntryForFile(File file) {
+        return makeEntryForNativeUri(Uri.fromFile(file));
+    }
+
     abstract JSONObject getFileForLocalURL(LocalFilesystemURL inputURL, String path,
 			JSONObject options, boolean directory) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException;
 
@@ -109,10 +114,67 @@ public abstract class Filesystem {
         return rootUri;
     }
 
+    public boolean exists(LocalFilesystemURL inputURL) {
+        try {
+            getFileMetadataForLocalURL(inputURL);
+        } catch (FileNotFoundException e) {
+            return false;
+        }
+        return true;
+    }
+
+    public 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;
+    }
+
+    /**
+     * Removes multiple repeated //s, and collapses processes ../s.
+     */
+    protected static 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.replaceFirst("/+", "");
+        }
+        ArrayList<String> components = new ArrayList<String>(Arrays.asList(rawPath.split("/+")));
+        for (int index = 0; index < components.size(); ++index) {
+            if (components.get(index).equals("..")) {
+                components.remove(index);
+                if (index > 0) {
+                    components.remove(index-1);
+                    --index;
+                }
+            }
+        }
+        StringBuilder normalizedPath = new StringBuilder();
+        for(String component: components) {
+            normalizedPath.append("/");
+            normalizedPath.append(component);
+        }
+        if (isAbsolutePath) {
+            return normalizedPath.toString();
+        } else {
+            return normalizedPath.toString().substring(1);
+        }
+    }
+
+
+
     public abstract Uri toNativeUri(LocalFilesystemURL inputURL);
     public abstract LocalFilesystemURL toLocalUri(Uri inputURL);
 
     public JSONObject getRootEntry() {
+        if (rootEntry == null) {
+            rootEntry = makeEntryForNativeUri(rootUri);
+        }
         return rootEntry;
     }
 
@@ -235,12 +297,4 @@ public abstract class Filesystem {
             return numBytesRead;
         }
     }
-
-    /* Create a FileEntry or DirectoryEntry given an actual file on device.
-     * Return null if the file does not exist within this filesystem.
-     */
-	public JSONObject makeEntryForFile(File file) throws JSONException {
-		return null;
-	}
-
 }

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/356d3343/src/android/LocalFilesystem.java
----------------------------------------------------------------------
diff --git a/src/android/LocalFilesystem.java b/src/android/LocalFilesystem.java
index 2754b41..83c1384 100644
--- a/src/android/LocalFilesystem.java
+++ b/src/android/LocalFilesystem.java
@@ -66,18 +66,6 @@ public class LocalFilesystem extends Filesystem {
 		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) {
         Uri nativeUri = nativeUriForFullPath(fullPath);
 	    if (nativeUri != null) {
@@ -128,56 +116,6 @@ public class LocalFilesystem extends Filesystem {
 	    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.replaceFirst("/+", "");
-	    }
-	    ArrayList<String> components = new ArrayList<String>(Arrays.asList(rawPath.split("/+")));
-	    for (int index = 0; index < components.size(); ++index) {
-	        if (components.get(index).equals("..")) {
-	            components.remove(index);
-	            if (index > 0) {
-	                components.remove(index-1);
-	                --index;
-	            }
-	        }
-	    }
-	    StringBuilder normalizedPath = new StringBuilder();
-	    for(String component: components) {
-	    	normalizedPath.append("/");
-	    	normalizedPath.append(component);
-	    }
-	    if (isAbsolutePath) {
-	    	return normalizedPath.toString();
-	    } else {
-	    	return normalizedPath.toString().substring(1);
-	    }
-	}
-
-	
-	@Override
-    public JSONObject makeEntryForFile(File file) {
-        return makeEntryForNativeUri(Uri.fromFile(file));
-    }
-
-	@Override
-	public JSONObject getEntryForLocalURL(LocalFilesystemURL inputURL) throws IOException {
-      File fp = new File(filesystemPathForURL(inputURL));
-
-      if (!fp.exists()) {
-          throw new FileNotFoundException();
-      }
-      if (!fp.canRead()) {
-          throw new IOException();
-      }
-      return super.getEntryForLocalURL(inputURL);
-	}
-
 	@Override
 	public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL,
 			String path, JSONObject options, boolean directory) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException {
@@ -203,7 +141,7 @@ public class LocalFilesystem extends Filesystem {
             path += "/";
         }
         if (path.startsWith("/")) {
-        	requestedURL = URLforFilesystemPath(normalizePath(path));
+        	requestedURL = URLforFullPath(normalizePath(path));
         } else {
         	requestedURL = URLforFullPath(normalizePath(inputURL.path + "/" + path));
         }
@@ -255,7 +193,13 @@ public class LocalFilesystem extends Filesystem {
         return fp.delete();
 	}
 
-	@Override
+    @Override
+    public boolean exists(LocalFilesystemURL inputURL) {
+        File fp = new File(filesystemPathForURL(inputURL));
+        return fp.exists();
+    }
+
+    @Override
 	public boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL) throws FileExistsException {
         File directory = new File(filesystemPathForURL(inputURL));
     	return removeDirRecursively(directory);

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/356d3343/tests/tests.js
----------------------------------------------------------------------
diff --git a/tests/tests.js b/tests/tests.js
index adaa9ab..5735af1 100644
--- a/tests/tests.js
+++ b/tests/tests.js
@@ -3443,6 +3443,43 @@ exports.defineAutoTests = function () {
                     }, failed.bind(null, done, 'resolveLocalFileSystemURL failed for content provider'));
                 });
             });
+            describe('asset: URLs', function() {
+                it("file.spec.141 filePaths.applicationStorage", function() {
+                    expect(cordova.file.applicationDirectory).toEqual('file:///android_asset/');
+                });
+                it("file.spec.142 assets should be enumerable", function(done) {
+                    resolveLocalFileSystemURL('file:///android_asset/www/', function(entry) {
+                        var reader = entry.createReader();
+                        reader.readEntries(function (entries) {
+                            expect(entries.length).not.toBe(0);
+                            done();
+                        }, failed.bind(null, done, 'reader.readEntries - Error during reading of entries from assets directory'));
+                    }, failed.bind(null, done, 'resolveLocalFileSystemURL failed for assets'));
+                });
+                it("file.spec.143 copyTo: asset -> temporary", function(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('file:///android_asset/www/index.html', function(entry) {
+                            expect(entry.filesystem.name).toEqual('assets');
+                            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 assets'));
+                    };
+                    // 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);
+                });
+            });
         }
     });
 


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