From 02811010cf62f1b21a06780d1e470d04bb24c50f Mon Sep 17 00:00:00 2001 From: Arjun Roychowdhury Date: Sat, 31 Oct 2015 08:21:38 -0400 Subject: removed unecessary files from git --- .../src/android/AssetFilesystem.java | 283 ------ .../src/android/ContentFilesystem.java | 215 ---- .../src/android/DirectoryManager.java | 133 --- .../src/android/EncodingException.java | 29 - .../src/android/FileExistsException.java | 29 - .../cordova-plugin-file/src/android/FileUtils.java | 1027 -------------------- .../src/android/Filesystem.java | 325 ------- .../src/android/InvalidModificationException.java | 30 - .../src/android/LocalFilesystem.java | 505 ---------- .../src/android/LocalFilesystemURL.java | 64 -- .../android/NoModificationAllowedException.java | 29 - .../src/android/TypeMismatchException.java | 30 - .../src/android/build-extras.gradle | 47 - 13 files changed, 2746 deletions(-) delete mode 100644 plugins/cordova-plugin-file/src/android/AssetFilesystem.java delete mode 100644 plugins/cordova-plugin-file/src/android/ContentFilesystem.java delete mode 100644 plugins/cordova-plugin-file/src/android/DirectoryManager.java delete mode 100644 plugins/cordova-plugin-file/src/android/EncodingException.java delete mode 100644 plugins/cordova-plugin-file/src/android/FileExistsException.java delete mode 100644 plugins/cordova-plugin-file/src/android/FileUtils.java delete mode 100644 plugins/cordova-plugin-file/src/android/Filesystem.java delete mode 100644 plugins/cordova-plugin-file/src/android/InvalidModificationException.java delete mode 100644 plugins/cordova-plugin-file/src/android/LocalFilesystem.java delete mode 100644 plugins/cordova-plugin-file/src/android/LocalFilesystemURL.java delete mode 100644 plugins/cordova-plugin-file/src/android/NoModificationAllowedException.java delete mode 100644 plugins/cordova-plugin-file/src/android/TypeMismatchException.java delete mode 100644 plugins/cordova-plugin-file/src/android/build-extras.gradle (limited to 'plugins/cordova-plugin-file/src/android') diff --git a/plugins/cordova-plugin-file/src/android/AssetFilesystem.java b/plugins/cordova-plugin-file/src/android/AssetFilesystem.java deleted file mode 100644 index f501b279..00000000 --- a/plugins/cordova-plugin-file/src/android/AssetFilesystem.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - 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 android.util.Log; - -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; -import java.io.ObjectInputStream; -import java.util.HashMap; -import java.util.Map; - -public class AssetFilesystem extends Filesystem { - - private final AssetManager assetManager; - - // A custom gradle hook creates the cdvasset.manifest file, which speeds up asset listing a tonne. - // See: http://stackoverflow.com/questions/16911558/android-assetmanager-list-incredibly-slow - private static Object listCacheLock = new Object(); - private static boolean listCacheFromFile; - private static Map listCache; - private static Map lengthCache; - - private void lazyInitCaches() { - synchronized (listCacheLock) { - if (listCache == null) { - ObjectInputStream ois = null; - try { - ois = new ObjectInputStream(assetManager.open("cdvasset.manifest")); - listCache = (Map) ois.readObject(); - lengthCache = (Map) ois.readObject(); - listCacheFromFile = true; - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - // Asset manifest won't exist if the gradle hook isn't set up correctly. - } finally { - if (ois != null) { - try { - ois.close(); - } catch (IOException e) { - } - } - } - if (listCache == null) { - Log.w("AssetFilesystem", "Asset manifest not found. Recursive copies and directory listing will be slow."); - listCache = new HashMap(); - } - } - } - } - - private String[] listAssets(String assetPath) throws IOException { - if (assetPath.startsWith("/")) { - assetPath = assetPath.substring(1); - } - lazyInitCaches(); - String[] ret = listCache.get(assetPath); - if (ret == null) { - if (listCacheFromFile) { - ret = new String[0]; - } else { - ret = assetManager.list(assetPath); - listCache.put(assetPath, ret); - } - } - return ret; - } - - private long getAssetSize(String assetPath) throws FileNotFoundException { - if (assetPath.startsWith("/")) { - assetPath = assetPath.substring(1); - } - lazyInitCaches(); - if (lengthCache != null) { - Long ret = lengthCache.get(assetPath); - if (ret == null) { - throw new FileNotFoundException("Asset not found: " + assetPath); - } - return ret; - } - CordovaResourceApi.OpenForReadResult offr = null; - try { - offr = resourceApi.openForRead(nativeUriForFullPath(assetPath)); - long length = offr.length; - if (length < 0) { - // available() doesn't always yield the file size, but for assets it does. - length = offr.inputStream.available(); - } - return length; - } catch (IOException e) { - throw new FileNotFoundException("File not found: " + assetPath); - } finally { - if (offr != null) { - try { - offr.inputStream.close(); - } catch (IOException e) { - } - } - } - } - - 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) { - try { - return listAssets(assetPath).length != 0; - } catch (IOException e) { - return false; - } - } - - @Override - public LocalFilesystemURL[] listChildren(LocalFilesystemURL inputURL) throws FileNotFoundException { - String pathNoSlashes = inputURL.path.substring(1); - if (pathNoSlashes.endsWith("/")) { - pathNoSlashes = pathNoSlashes.substring(0, pathNoSlashes.length() - 1); - } - - String[] files; - try { - files = listAssets(pathNoSlashes); - } catch (IOException e) { - throw new FileNotFoundException(); - } - - LocalFilesystemURL[] entries = new LocalFilesystemURL[files.length]; - for (int i = 0; i < files.length; ++i) { - entries[i] = localUrlforFullPath(new File(inputURL.path, files[i]).getPath()); - } - 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 = localUrlforFullPath(normalizePath(path)); - } else { - requestedURL = localUrlforFullPath(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 { - JSONObject metadata = new JSONObject(); - long size = inputURL.isDirectory ? 0 : getAssetSize(inputURL.path); - try { - metadata.put("size", size); - metadata.put("type", inputURL.isDirectory ? "text/directory" : resourceApi.getMimeType(toNativeUri(inputURL))); - metadata.put("name", new File(inputURL.path).getName()); - metadata.put("fullPath", inputURL.path); - metadata.put("lastModifiedDate", 0); - } catch (JSONException e) { - return null; - } - 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 NoModificationAllowedException { - throw new NoModificationAllowedException("Assets are read-only"); - } - -} diff --git a/plugins/cordova-plugin-file/src/android/ContentFilesystem.java b/plugins/cordova-plugin-file/src/android/ContentFilesystem.java deleted file mode 100644 index 883e7cf5..00000000 --- a/plugins/cordova-plugin-file/src/android/ContentFilesystem.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - 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 java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.OutputStream; - -import org.apache.cordova.CordovaResourceApi; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import android.content.ContentResolver; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.provider.MediaStore; -import android.provider.OpenableColumns; - -public class ContentFilesystem extends Filesystem { - - private final Context context; - - public ContentFilesystem(Context context, CordovaResourceApi resourceApi) { - super(Uri.parse("content://"), "content", resourceApi); - this.context = context; - } - - @Override - public Uri toNativeUri(LocalFilesystemURL inputURL) { - String authorityAndPath = inputURL.uri.getEncodedPath().substring(this.name.length() + 2); - if (authorityAndPath.length() < 2) { - return null; - } - String ret = "content://" + authorityAndPath; - String query = inputURL.uri.getEncodedQuery(); - if (query != null) { - ret += '?' + query; - } - String frag = inputURL.uri.getEncodedFragment(); - if (frag != null) { - ret += '#' + frag; - } - 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 { - Uri contentUri = toNativeUri(inputURL); - try { - 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 true; - } - - @Override - public boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL) - throws NoModificationAllowedException { - throw new NoModificationAllowedException("Cannot remove content url"); - } - - @Override - public LocalFilesystemURL[] listChildren(LocalFilesystemURL inputURL) throws FileNotFoundException { - throw new UnsupportedOperationException("readEntriesAtLocalURL() not supported for content:. Use resolveLocalFileSystemURL instead."); - } - - @Override - public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException { - 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 { - // 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(); - } - - JSONObject metadata = new JSONObject(); - try { - metadata.put("size", size); - metadata.put("type", mimeType); - metadata.put("name", name); - metadata.put("fullPath", inputURL.path); - metadata.put("lastModifiedDate", lastModified); - } catch (JSONException e) { - return null; - } - return metadata; - } - - @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"); - } - @Override - public long truncateFileAtURL(LocalFilesystemURL inputURL, long size) - throws NoModificationAllowedException { - throw new NoModificationAllowedException("Couldn't truncate file given its content URI"); - } - - protected Cursor openCursorForURL(Uri nativeUri) { - ContentResolver contentResolver = context.getContentResolver(); - try { - return contentResolver.query(nativeUri, null, null, null, null); - } catch (UnsupportedOperationException e) { - return null; - } - } - - private Long resourceSizeForCursor(Cursor cursor) { - int columnIndex = cursor.getColumnIndex(OpenableColumns.SIZE); - if (columnIndex != -1) { - String sizeStr = cursor.getString(columnIndex); - if (sizeStr != null) { - return Long.parseLong(sizeStr); - } - } - return null; - } - - 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 Long.parseLong(dateStr); - } - } - return null; - } - - @Override - public String filesystemPathForURL(LocalFilesystemURL url) { - File f = resourceApi.mapUriToFile(toNativeUri(url)); - return f == null ? null : f.getAbsolutePath(); - } - - @Override - public LocalFilesystemURL URLforFilesystemPath(String path) { - // Returns null as we don't support reverse mapping back to content:// URLs - return null; - } - - @Override - public boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL) { - return true; - } -} diff --git a/plugins/cordova-plugin-file/src/android/DirectoryManager.java b/plugins/cordova-plugin-file/src/android/DirectoryManager.java deleted file mode 100644 index bcc005b2..00000000 --- a/plugins/cordova-plugin-file/src/android/DirectoryManager.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - 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.os.Environment; -import android.os.StatFs; - -import java.io.File; - -/** - * This class provides file directory utilities. - * All file operations are performed on the SD card. - * - * It is used by the FileUtils class. - */ -public class DirectoryManager { - - @SuppressWarnings("unused") - private static final String LOG_TAG = "DirectoryManager"; - - /** - * Determine if a file or directory exists. - * @param name The name of the file to check. - * @return T=exists, F=not found - */ - public static boolean testFileExists(String name) { - boolean status; - - // If SD card exists - if ((testSaveLocationExists()) && (!name.equals(""))) { - File path = Environment.getExternalStorageDirectory(); - File newPath = constructFilePaths(path.toString(), name); - status = newPath.exists(); - } - // If no SD card - else { - status = false; - } - return status; - } - - /** - * Get the free disk space - * - * @return Size in KB or -1 if not available - */ - public static long getFreeDiskSpace(boolean checkInternal) { - String status = Environment.getExternalStorageState(); - long freeSpace = 0; - - // If SD card exists - if (status.equals(Environment.MEDIA_MOUNTED)) { - freeSpace = freeSpaceCalculation(Environment.getExternalStorageDirectory().getPath()); - } - else if (checkInternal) { - freeSpace = freeSpaceCalculation("/"); - } - // If no SD card and we haven't been asked to check the internal directory then return -1 - else { - return -1; - } - - return freeSpace; - } - - /** - * Given a path return the number of free KB - * - * @param path to the file system - * @return free space in KB - */ - private static long freeSpaceCalculation(String path) { - StatFs stat = new StatFs(path); - long blockSize = stat.getBlockSize(); - long availableBlocks = stat.getAvailableBlocks(); - return availableBlocks * blockSize / 1024; - } - - /** - * Determine if SD card exists. - * - * @return T=exists, F=not found - */ - public static boolean testSaveLocationExists() { - String sDCardStatus = Environment.getExternalStorageState(); - boolean status; - - // If SD card is mounted - if (sDCardStatus.equals(Environment.MEDIA_MOUNTED)) { - status = true; - } - - // If no SD card - else { - status = false; - } - return status; - } - - /** - * Create a new file object from two file paths. - * - * @param file1 Base file path - * @param file2 Remaining file path - * @return File object - */ - private static File constructFilePaths (String file1, String file2) { - File newPath; - if (file2.startsWith(file1)) { - newPath = new File(file2); - } - else { - newPath = new File(file1 + "/" + file2); - } - return newPath; - } -} diff --git a/plugins/cordova-plugin-file/src/android/EncodingException.java b/plugins/cordova-plugin-file/src/android/EncodingException.java deleted file mode 100644 index e9e1653b..00000000 --- a/plugins/cordova-plugin-file/src/android/EncodingException.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - 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; - -@SuppressWarnings("serial") -public class EncodingException extends Exception { - - public EncodingException(String message) { - super(message); - } - -} diff --git a/plugins/cordova-plugin-file/src/android/FileExistsException.java b/plugins/cordova-plugin-file/src/android/FileExistsException.java deleted file mode 100644 index 5c4d83dc..00000000 --- a/plugins/cordova-plugin-file/src/android/FileExistsException.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - 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; - -@SuppressWarnings("serial") -public class FileExistsException extends Exception { - - public FileExistsException(String msg) { - super(msg); - } - -} diff --git a/plugins/cordova-plugin-file/src/android/FileUtils.java b/plugins/cordova-plugin-file/src/android/FileUtils.java deleted file mode 100644 index f57d26b3..00000000 --- a/plugins/cordova-plugin-file/src/android/FileUtils.java +++ /dev/null @@ -1,1027 +0,0 @@ -/* - 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.app.Activity; -import android.content.Context; -import android.net.Uri; -import android.os.Environment; -import android.util.Base64; -import android.util.Log; - -import org.apache.cordova.CallbackContext; -import org.apache.cordova.CordovaInterface; -import org.apache.cordova.CordovaPlugin; -import org.apache.cordova.CordovaWebView; -import org.apache.cordova.PluginResult; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; - -/** - * This class provides file and directory services to JavaScript. - */ -public class FileUtils extends CordovaPlugin { - private static final String LOG_TAG = "FileUtils"; - - public static int NOT_FOUND_ERR = 1; - public static int SECURITY_ERR = 2; - public static int ABORT_ERR = 3; - - public static int NOT_READABLE_ERR = 4; - public static int ENCODING_ERR = 5; - public static int NO_MODIFICATION_ALLOWED_ERR = 6; - public static int INVALID_STATE_ERR = 7; - public static int SYNTAX_ERR = 8; - public static int INVALID_MODIFICATION_ERR = 9; - public static int QUOTA_EXCEEDED_ERR = 10; - public static int TYPE_MISMATCH_ERR = 11; - public static int PATH_EXISTS_ERR = 12; - - public static int UNKNOWN_ERR = 1000; - - private boolean configured = false; - - // This field exists only to support getEntry, below, which has been deprecated - private static FileUtils filePlugin; - - private interface FileOp { - void run(JSONArray args) throws Exception; - } - - private ArrayList filesystems; - - public void registerFilesystem(Filesystem fs) { - if (fs != null && filesystemForName(fs.name)== null) { - this.filesystems.add(fs); - } - } - - private Filesystem filesystemForName(String name) { - for (Filesystem fs:filesystems) { - if (fs != null && fs.name != null && fs.name.equals(name)) { - return fs; - } - } - return null; - } - - protected String[] getExtraFileSystemsPreference(Activity activity) { - String fileSystemsStr = activity.getIntent().getStringExtra("androidextrafilesystems"); - if (fileSystemsStr == null) { - fileSystemsStr = "files,files-external,documents,sdcard,cache,cache-external,root"; - } - return fileSystemsStr.split(","); - } - - protected void registerExtraFileSystems(String[] filesystems, HashMap availableFileSystems) { - HashSet installedFileSystems = new HashSet(); - - /* Register filesystems in order */ - for (String fsName : filesystems) { - if (!installedFileSystems.contains(fsName)) { - String fsRoot = availableFileSystems.get(fsName); - if (fsRoot != null) { - File newRoot = new File(fsRoot); - if (newRoot.mkdirs() || newRoot.isDirectory()) { - registerFilesystem(new LocalFilesystem(fsName, webView.getContext(), webView.getResourceApi(), newRoot)); - installedFileSystems.add(fsName); - } else { - Log.d(LOG_TAG, "Unable to create root dir for filesystem \"" + fsName + "\", skipping"); - } - } else { - Log.d(LOG_TAG, "Unrecognized extra filesystem identifier: " + fsName); - } - } - } - } - - protected HashMap getAvailableFileSystems(Activity activity) { - Context context = activity.getApplicationContext(); - HashMap availableFileSystems = new HashMap(); - - availableFileSystems.put("files", context.getFilesDir().getAbsolutePath()); - availableFileSystems.put("documents", new File(context.getFilesDir(), "Documents").getAbsolutePath()); - availableFileSystems.put("cache", context.getCacheDir().getAbsolutePath()); - availableFileSystems.put("root", "/"); - if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - try { - availableFileSystems.put("files-external", context.getExternalFilesDir(null).getAbsolutePath()); - availableFileSystems.put("sdcard", Environment.getExternalStorageDirectory().getAbsolutePath()); - availableFileSystems.put("cache-external", context.getExternalCacheDir().getAbsolutePath()); - } - catch(NullPointerException e) { - Log.d(LOG_TAG, "External storage unavailable, check to see if USB Mass Storage Mode is on"); - } - } - - return availableFileSystems; - } - - @Override - public void initialize(CordovaInterface cordova, CordovaWebView webView) { - super.initialize(cordova, webView); - this.filesystems = new ArrayList(); - - String tempRoot = null; - String persistentRoot = null; - - Activity activity = cordova.getActivity(); - String packageName = activity.getPackageName(); - - String location = activity.getIntent().getStringExtra("androidpersistentfilelocation"); - if (location == null) { - location = "compatibility"; - } - tempRoot = activity.getCacheDir().getAbsolutePath(); - if ("internal".equalsIgnoreCase(location)) { - persistentRoot = activity.getFilesDir().getAbsolutePath() + "/files/"; - this.configured = true; - } else if ("compatibility".equalsIgnoreCase(location)) { - /* - * Fall-back to compatibility mode -- this is the logic implemented in - * earlier versions of this plugin, and should be maintained here so - * that apps which were originally deployed with older versions of the - * plugin can continue to provide access to files stored under those - * versions. - */ - if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - persistentRoot = Environment.getExternalStorageDirectory().getAbsolutePath(); - tempRoot = Environment.getExternalStorageDirectory().getAbsolutePath() + - "/Android/data/" + packageName + "/cache/"; - } else { - persistentRoot = "/data/data/" + packageName; - } - this.configured = true; - } - - if (this.configured) { - // Create the directories if they don't exist. - File tmpRootFile = new File(tempRoot); - File persistentRootFile = new File(persistentRoot); - tmpRootFile.mkdirs(); - persistentRootFile.mkdirs(); - - // Register initial filesystems - // Note: The temporary and persistent filesystems need to be the first two - // registered, so that they will match window.TEMPORARY and window.PERSISTENT, - // per spec. - this.registerFilesystem(new LocalFilesystem("temporary", webView.getContext(), webView.getResourceApi(), tmpRootFile)); - this.registerFilesystem(new LocalFilesystem("persistent", webView.getContext(), webView.getResourceApi(), persistentRootFile)); - this.registerFilesystem(new ContentFilesystem(webView.getContext(), webView.getResourceApi())); - this.registerFilesystem(new AssetFilesystem(webView.getContext().getAssets(), webView.getResourceApi())); - - registerExtraFileSystems(getExtraFileSystemsPreference(activity), getAvailableFileSystems(activity)); - - // Initialize static plugin reference for deprecated getEntry method - if (filePlugin == null) { - FileUtils.filePlugin = this; - } - } else { - Log.e(LOG_TAG, "File plugin configuration error: Please set AndroidPersistentFileLocation in config.xml to one of \"internal\" (for new applications) or \"compatibility\" (for compatibility with previous versions)"); - activity.finish(); - } - } - - public static FileUtils getFilePlugin() { - return filePlugin; - } - - private Filesystem filesystemForURL(LocalFilesystemURL localURL) { - if (localURL == null) return null; - return filesystemForName(localURL.fsName); - } - - @Override - public Uri remapUri(Uri uri) { - // Remap only cdvfile: URLs (not content:). - if (!LocalFilesystemURL.FILESYSTEM_PROTOCOL.equals(uri.getScheme())) { - return null; - } - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(uri); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - return null; - } - String path = fs.filesystemPathForURL(inputURL); - if (path != null) { - return Uri.parse("file://" + fs.filesystemPathForURL(inputURL)); - } - return null; - } catch (IllegalArgumentException e) { - return null; - } - } - - public boolean execute(String action, final String rawArgs, final CallbackContext callbackContext) { - if (!configured) { - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, "File plugin is not configured. Please see the README.md file for details on how to update config.xml")); - return true; - } - if (action.equals("testSaveLocationExists")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) { - boolean b = DirectoryManager.testSaveLocationExists(); - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, b)); - } - }, rawArgs, callbackContext); - } - else if (action.equals("getFreeDiskSpace")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) { - long l = DirectoryManager.getFreeDiskSpace(false); - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, l)); - } - }, rawArgs, callbackContext); - } - else if (action.equals("testFileExists")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException { - String fname=args.getString(0); - boolean b = DirectoryManager.testFileExists(fname); - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, b)); - } - }, rawArgs, callbackContext); - } - else if (action.equals("testDirectoryExists")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException { - String fname=args.getString(0); - boolean b = DirectoryManager.testFileExists(fname); - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, b)); - } - }, rawArgs, callbackContext); - } - else if (action.equals("readAsText")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, MalformedURLException { - String encoding = args.getString(1); - int start = args.getInt(2); - int end = args.getInt(3); - String fname=args.getString(0); - readFileAs(fname, start, end, callbackContext, encoding, PluginResult.MESSAGE_TYPE_STRING); - } - }, rawArgs, callbackContext); - } - else if (action.equals("readAsDataURL")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, MalformedURLException { - int start = args.getInt(1); - int end = args.getInt(2); - String fname=args.getString(0); - readFileAs(fname, start, end, callbackContext, null, -1); - } - }, rawArgs, callbackContext); - } - else if (action.equals("readAsArrayBuffer")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, MalformedURLException { - int start = args.getInt(1); - int end = args.getInt(2); - String fname=args.getString(0); - readFileAs(fname, start, end, callbackContext, null, PluginResult.MESSAGE_TYPE_ARRAYBUFFER); - } - }, rawArgs, callbackContext); - } - else if (action.equals("readAsBinaryString")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, MalformedURLException { - int start = args.getInt(1); - int end = args.getInt(2); - String fname=args.getString(0); - readFileAs(fname, start, end, callbackContext, null, PluginResult.MESSAGE_TYPE_BINARYSTRING); - } - }, rawArgs, callbackContext); - } - else if (action.equals("write")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, FileNotFoundException, IOException, NoModificationAllowedException { - String fname=args.getString(0); - String data=args.getString(1); - int offset=args.getInt(2); - Boolean isBinary=args.getBoolean(3); - long fileSize = write(fname, data, offset, isBinary); - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, fileSize)); - } - }, rawArgs, callbackContext); - } - else if (action.equals("truncate")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, FileNotFoundException, IOException, NoModificationAllowedException { - String fname=args.getString(0); - int offset=args.getInt(1); - long fileSize = truncateFile(fname, offset); - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, fileSize)); - } - }, rawArgs, callbackContext); - } - else if (action.equals("requestAllFileSystems")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws IOException, JSONException { - callbackContext.success(requestAllFileSystems()); - } - }, rawArgs, callbackContext); - } else if (action.equals("requestAllPaths")) { - cordova.getThreadPool().execute( - new Runnable() { - public void run() { - try { - callbackContext.success(requestAllPaths()); - } catch (JSONException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - ); - } else if (action.equals("requestFileSystem")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws IOException, JSONException { - int fstype=args.getInt(0); - long size = args.optLong(1); - if (size != 0 && size > (DirectoryManager.getFreeDiskSpace(true) * 1024)) { - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, FileUtils.QUOTA_EXCEEDED_ERR)); - } else { - JSONObject obj = requestFileSystem(fstype); - callbackContext.success(obj); - } - } - }, rawArgs, callbackContext); - } - else if (action.equals("resolveLocalFileSystemURI")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws IOException, JSONException { - String fname=args.getString(0); - JSONObject obj = resolveLocalFileSystemURI(fname); - callbackContext.success(obj); - } - }, rawArgs, callbackContext); - } - else if (action.equals("getFileMetadata")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws FileNotFoundException, JSONException, MalformedURLException { - String fname=args.getString(0); - JSONObject obj = getFileMetadata(fname); - callbackContext.success(obj); - } - }, rawArgs, callbackContext); - } - else if (action.equals("getParent")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, IOException { - String fname=args.getString(0); - JSONObject obj = getParent(fname); - callbackContext.success(obj); - } - }, rawArgs, callbackContext); - } - else if (action.equals("getDirectory")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException { - String dirname=args.getString(0); - String path=args.getString(1); - JSONObject obj = getFile(dirname, path, args.optJSONObject(2), true); - callbackContext.success(obj); - } - }, rawArgs, callbackContext); - } - else if (action.equals("getFile")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException { - String dirname=args.getString(0); - String path=args.getString(1); - JSONObject obj = getFile(dirname, path, args.optJSONObject(2), false); - callbackContext.success(obj); - } - }, rawArgs, callbackContext); - } - else if (action.equals("remove")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, NoModificationAllowedException, InvalidModificationException, MalformedURLException { - String fname=args.getString(0); - boolean success = remove(fname); - if (success) { - callbackContext.success(); - } else { - callbackContext.error(FileUtils.NO_MODIFICATION_ALLOWED_ERR); - } - } - }, rawArgs, callbackContext); - } - else if (action.equals("removeRecursively")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, FileExistsException, MalformedURLException, NoModificationAllowedException { - String fname=args.getString(0); - boolean success = removeRecursively(fname); - if (success) { - callbackContext.success(); - } else { - callbackContext.error(FileUtils.NO_MODIFICATION_ALLOWED_ERR); - } - } - }, rawArgs, callbackContext); - } - else if (action.equals("moveTo")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException, FileExistsException { - String fname=args.getString(0); - String newParent=args.getString(1); - String newName=args.getString(2); - JSONObject entry = transferTo(fname, newParent, newName, true); - callbackContext.success(entry); - } - }, rawArgs, callbackContext); - } - else if (action.equals("copyTo")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException, FileExistsException { - String fname=args.getString(0); - String newParent=args.getString(1); - String newName=args.getString(2); - JSONObject entry = transferTo(fname, newParent, newName, false); - callbackContext.success(entry); - } - }, rawArgs, callbackContext); - } - else if (action.equals("readEntries")) { - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws FileNotFoundException, JSONException, MalformedURLException { - String fname=args.getString(0); - JSONArray entries = readEntries(fname); - callbackContext.success(entries); - } - }, rawArgs, callbackContext); - } - else if (action.equals("_getLocalFilesystemPath")) { - // Internal method for testing: Get the on-disk location of a local filesystem url. - // [Currently used for testing file-transfer] - threadhelper( new FileOp( ){ - public void run(JSONArray args) throws FileNotFoundException, JSONException, MalformedURLException { - String localURLstr = args.getString(0); - String fname = filesystemPathForURL(localURLstr); - callbackContext.success(fname); - } - }, rawArgs, callbackContext); - } - else { - return false; - } - 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 - * interface to these methods. - */ - - public String filesystemPathForURL(String localURLstr) throws MalformedURLException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(localURLstr); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - return fs.filesystemPathForURL(inputURL); - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - } - - public LocalFilesystemURL filesystemURLforLocalPath(String localPath) { - LocalFilesystemURL localURL = null; - int shortestFullPath = 0; - - // Try all installed filesystems. Return the best matching URL - // (determined by the shortest resulting URL) - for (Filesystem fs: filesystems) { - 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(); - } - } - } - return localURL; - } - - - /* helper to execute functions async and handle the result codes - * - */ - private void threadhelper(final FileOp f, final String rawArgs, final CallbackContext callbackContext){ - cordova.getThreadPool().execute(new Runnable() { - public void run() { - try { - JSONArray args = new JSONArray(rawArgs); - f.run(args); - } catch ( Exception e) { - if( e instanceof EncodingException){ - callbackContext.error(FileUtils.ENCODING_ERR); - } else if(e instanceof FileNotFoundException) { - callbackContext.error(FileUtils.NOT_FOUND_ERR); - } else if(e instanceof FileExistsException) { - callbackContext.error(FileUtils.PATH_EXISTS_ERR); - } else if(e instanceof NoModificationAllowedException ) { - callbackContext.error(FileUtils.NO_MODIFICATION_ALLOWED_ERR); - } else if(e instanceof InvalidModificationException ) { - callbackContext.error(FileUtils.INVALID_MODIFICATION_ERR); - } else if(e instanceof MalformedURLException ) { - callbackContext.error(FileUtils.ENCODING_ERR); - } else if(e instanceof IOException ) { - callbackContext.error(FileUtils.INVALID_MODIFICATION_ERR); - } else if(e instanceof EncodingException ) { - callbackContext.error(FileUtils.ENCODING_ERR); - } else if(e instanceof TypeMismatchException ) { - callbackContext.error(FileUtils.TYPE_MISMATCH_ERR); - } else if(e instanceof JSONException ) { - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); - } else { - e.printStackTrace(); - callbackContext.error(FileUtils.UNKNOWN_ERR); - } - } - } - }); - } - - /** - * Allows the user to look up the Entry for a file or directory referred to by a local URI. - * - * @param uriString of the file/directory to look up - * @return a JSONObject representing a Entry from the filesystem - * @throws MalformedURLException if the url is not valid - * @throws FileNotFoundException if the file does not exist - * @throws IOException if the user can't read the file - * @throws JSONException - */ - private JSONObject resolveLocalFileSystemURI(String uriString) throws IOException, JSONException { - if (uriString == null) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - Uri uri = Uri.parse(uriString); - - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(uri); - if (inputURL == null) { - /* Check for file://, content:// urls */ - inputURL = resolveNativeUri(uri); - } - - try { - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - if (fs.exists(inputURL)) { - return fs.getEntryForLocalURL(inputURL); - } - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - throw new FileNotFoundException(); - } - - /** - * Read the list of files from this directory. - * - * @return a JSONArray containing JSONObjects that represent Entry objects. - * @throws FileNotFoundException if the directory is not found. - * @throws JSONException - * @throws MalformedURLException - */ - private JSONArray readEntries(String baseURLstr) throws FileNotFoundException, JSONException, MalformedURLException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - return fs.readEntriesAtLocalURL(inputURL); - - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - } - - /** - * A setup method that handles the move/copy of files/directories - * - * @param newName for the file directory to be called, if null use existing file name - * @param move if false do a copy, if true do a move - * @return a Entry object - * @throws NoModificationAllowedException - * @throws IOException - * @throws InvalidModificationException - * @throws EncodingException - * @throws JSONException - * @throws FileExistsException - */ - private JSONObject transferTo(String srcURLstr, String destURLstr, String newName, boolean move) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException, FileExistsException { - if (srcURLstr == null || destURLstr == null) { - // either no source or no destination provided - throw new FileNotFoundException(); - } - - LocalFilesystemURL srcURL = LocalFilesystemURL.parse(srcURLstr); - LocalFilesystemURL destURL = LocalFilesystemURL.parse(destURLstr); - - Filesystem srcFs = this.filesystemForURL(srcURL); - Filesystem destFs = this.filesystemForURL(destURL); - - // Check for invalid file name - if (newName != null && newName.contains(":")) { - throw new EncodingException("Bad file name"); - } - - return destFs.copyFileToURL(destURL, newName, srcFs, srcURL, move); - } - - /** - * Deletes a directory and all of its contents, if any. In the event of an error - * [e.g. trying to delete a directory that contains a file that cannot be removed], - * some of the contents of the directory may be deleted. - * It is an error to attempt to delete the root directory of a filesystem. - * - * @return a boolean representing success of failure - * @throws FileExistsException - * @throws NoModificationAllowedException - * @throws MalformedURLException - */ - private boolean removeRecursively(String baseURLstr) throws FileExistsException, NoModificationAllowedException, MalformedURLException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr); - // You can't delete the root directory. - if ("".equals(inputURL.path) || "/".equals(inputURL.path)) { - throw new NoModificationAllowedException("You can't delete the root directory"); - } - - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - return fs.recursiveRemoveFileAtLocalURL(inputURL); - - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - } - - - /** - * Deletes a file or directory. It is an error to attempt to delete a directory that is not empty. - * It is an error to attempt to delete the root directory of a filesystem. - * - * @return a boolean representing success of failure - * @throws NoModificationAllowedException - * @throws InvalidModificationException - * @throws MalformedURLException - */ - private boolean remove(String baseURLstr) throws NoModificationAllowedException, InvalidModificationException, MalformedURLException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr); - // You can't delete the root directory. - if ("".equals(inputURL.path) || "/".equals(inputURL.path)) { - - throw new NoModificationAllowedException("You can't delete the root directory"); - } - - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - return fs.removeFileAtLocalURL(inputURL); - - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - } - - /** - * Creates or looks up a file. - * - * @param baseURLstr base directory - * @param path file/directory to lookup or create - * @param options specify whether to create or not - * @param directory if true look up directory, if false look up file - * @return a Entry object - * @throws FileExistsException - * @throws IOException - * @throws TypeMismatchException - * @throws EncodingException - * @throws JSONException - */ - private JSONObject getFile(String baseURLstr, String path, JSONObject options, boolean directory) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - return fs.getFileForLocalURL(inputURL, path, options, directory); - - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - - } - - /** - * Look up the parent DirectoryEntry containing this Entry. - * If this Entry is the root of its filesystem, its parent is itself. - */ - private JSONObject getParent(String baseURLstr) throws JSONException, IOException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - return fs.getParentForLocalURL(inputURL); - - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - } - - /** - * Returns a File that represents the current state of the file that this FileEntry represents. - * - * @return returns a JSONObject represent a W3C File object - */ - private JSONObject getFileMetadata(String baseURLstr) throws FileNotFoundException, JSONException, MalformedURLException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(baseURLstr); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - return fs.getFileMetadataForLocalURL(inputURL); - - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - } - - /** - * Requests a filesystem in which to store application data. - * - * @param type of file system requested - * @return a JSONObject representing the file system - * @throws IOException - * @throws JSONException - */ - private JSONObject requestFileSystem(int type) throws IOException, JSONException { - JSONObject fs = new JSONObject(); - Filesystem rootFs = null; - try { - rootFs = this.filesystems.get(type); - } catch (ArrayIndexOutOfBoundsException e) { - // Pass null through - } - if (rootFs == null) { - throw new IOException("No filesystem of type requested"); - } - fs.put("name", rootFs.name); - fs.put("root", rootFs.getRootEntry()); - return fs; - } - - - /** - * Requests a filesystem in which to store application data. - * - * @return a JSONObject representing the file system - */ - private JSONArray requestAllFileSystems() throws IOException, JSONException { - JSONArray ret = new JSONArray(); - for (Filesystem fs : filesystems) { - ret.put(fs.getRootEntry()); - } - return ret; - } - - private static String toDirUrl(File f) { - return Uri.fromFile(f).toString() + '/'; - } - - private JSONObject requestAllPaths() throws JSONException { - Context context = cordova.getActivity(); - JSONObject ret = new JSONObject(); - ret.put("applicationDirectory", "file:///android_asset/"); - ret.put("applicationStorageDirectory", toDirUrl(context.getFilesDir().getParentFile())); - ret.put("dataDirectory", toDirUrl(context.getFilesDir())); - ret.put("cacheDirectory", toDirUrl(context.getCacheDir())); - if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - try { - ret.put("externalApplicationStorageDirectory", toDirUrl(context.getExternalFilesDir(null).getParentFile())); - ret.put("externalDataDirectory", toDirUrl(context.getExternalFilesDir(null))); - ret.put("externalCacheDirectory", toDirUrl(context.getExternalCacheDir())); - ret.put("externalRootDirectory", toDirUrl(Environment.getExternalStorageDirectory())); - } - catch(NullPointerException e) { - /* If external storage is unavailable, context.getExternal* returns null */ - Log.d(LOG_TAG, "Unable to access these paths, most liklely due to USB storage"); - } - } - return ret; - } - - /** - * Returns a JSON object representing the given File. Internal APIs should be modified - * to use URLs instead of raw FS paths wherever possible, when interfacing with this plugin. - * - * @param file the File to convert - * @return a JSON representation of the given File - * @throws JSONException - */ - public JSONObject getEntryForFile(File file) throws JSONException { - JSONObject entry; - - for (Filesystem fs : filesystems) { - entry = fs.makeEntryForFile(file); - if (entry != null) { - return entry; - } - } - return null; - } - - /** - * Returns a JSON object representing the given File. Deprecated, as this is only used by - * FileTransfer, and because it is a static method that should really be an instance method, - * since it depends on the actual filesystem roots in use. Internal APIs should be modified - * to use URLs instead of raw FS paths wherever possible, when interfacing with this plugin. - * - * @param file the File to convert - * @return a JSON representation of the given File - * @throws JSONException - */ - @Deprecated - public static JSONObject getEntry(File file) throws JSONException { - if (getFilePlugin() != null) { - return getFilePlugin().getEntryForFile(file); - } - return null; - } - - /** - * Read the contents of a file. - * This is done in a background thread; the result is sent to the callback. - * - * @param start Start position in the file. - * @param end End position to stop at (exclusive). - * @param callbackContext The context through which to send the result. - * @param encoding The encoding to return contents as. Typical value is UTF-8. (see http://www.iana.org/assignments/character-sets) - * @param resultType The desired type of data to send to the callback. - * @return Contents of file. - */ - public void readFileAs(final String srcURLstr, final int start, final int end, final CallbackContext callbackContext, final String encoding, final int resultType) throws MalformedURLException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(srcURLstr); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - - fs.readFileAtURL(inputURL, start, end, new Filesystem.ReadFileCallback() { - public void handleData(InputStream inputStream, String contentType) { - try { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - 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); - } - - PluginResult result; - switch (resultType) { - case PluginResult.MESSAGE_TYPE_STRING: - result = new PluginResult(PluginResult.Status.OK, os.toString(encoding)); - break; - case PluginResult.MESSAGE_TYPE_ARRAYBUFFER: - result = new PluginResult(PluginResult.Status.OK, os.toByteArray()); - break; - case PluginResult.MESSAGE_TYPE_BINARYSTRING: - result = new PluginResult(PluginResult.Status.OK, os.toByteArray(), true); - break; - default: // Base64. - byte[] base64 = Base64.encode(os.toByteArray(), Base64.NO_WRAP); - String s = "data:" + contentType + ";base64," + new String(base64, "US-ASCII"); - result = new PluginResult(PluginResult.Status.OK, s); - } - - callbackContext.sendPluginResult(result); - } catch (IOException e) { - Log.d(LOG_TAG, e.getLocalizedMessage()); - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, NOT_READABLE_ERR)); - } - } - }); - - - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } catch (FileNotFoundException e) { - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, NOT_FOUND_ERR)); - } catch (IOException e) { - Log.d(LOG_TAG, e.getLocalizedMessage()); - callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, NOT_READABLE_ERR)); - } - } - - - /** - * Write contents of file. - * - * @param data The contents of the file. - * @param offset The position to begin writing the file. - * @param isBinary True if the file contents are base64-encoded binary data - */ - /**/ - public long write(String srcURLstr, String data, int offset, boolean isBinary) throws FileNotFoundException, IOException, NoModificationAllowedException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(srcURLstr); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - - long x = fs.writeToFileAtURL(inputURL, data, offset, isBinary); Log.d("TEST",srcURLstr + ": "+x); return x; - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - - } - - /** - * Truncate the file to size - */ - private long truncateFile(String srcURLstr, long size) throws FileNotFoundException, IOException, NoModificationAllowedException { - try { - LocalFilesystemURL inputURL = LocalFilesystemURL.parse(srcURLstr); - Filesystem fs = this.filesystemForURL(inputURL); - if (fs == null) { - throw new MalformedURLException("No installed handlers for this URL"); - } - - return fs.truncateFileAtURL(inputURL, size); - } catch (IllegalArgumentException e) { - throw new MalformedURLException("Unrecognized filesystem URL"); - } - } -} diff --git a/plugins/cordova-plugin-file/src/android/Filesystem.java b/plugins/cordova-plugin-file/src/android/Filesystem.java deleted file mode 100644 index faf31d2a..00000000 --- a/plugins/cordova-plugin-file/src/android/Filesystem.java +++ /dev/null @@ -1,325 +0,0 @@ -/* - 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.net.Uri; - -import java.io.File; -import java.io.FileNotFoundException; -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; -import org.json.JSONException; -import org.json.JSONObject; - -public abstract class Filesystem { - - protected final Uri rootUri; - protected final CordovaResourceApi resourceApi; - public final String name; - private JSONObject rootEntry; - - public Filesystem(Uri rootUri, String name, CordovaResourceApi resourceApi) { - this.rootUri = rootUri; - this.name = name; - this.resourceApi = resourceApi; - } - - public interface ReadFileCallback { - public void handleData(InputStream inputStream, String contentType) throws IOException; - } - - public static JSONObject makeEntryForURL(LocalFilesystemURL inputURL, Uri nativeURL) { - try { - 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]; - - 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", inputURL.fsName); - // Backwards compatibility - entry.put("filesystem", "temporary".equals(inputURL.fsName) ? 0 : 1); - - String nativeUrlStr = nativeURL.toString(); - if (inputURL.isDirectory && !nativeUrlStr.endsWith("/")) { - nativeUrlStr += "/"; - } - entry.put("nativeURL", nativeUrlStr); - return entry; - } catch (JSONException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } - - public JSONObject makeEntryForURL(LocalFilesystemURL inputURL) { - Uri nativeUri = toNativeUri(inputURL); - return nativeUri == null ? null : makeEntryForURL(inputURL, nativeUri); - } - - public JSONObject makeEntryForNativeUri(Uri nativeUri) { - LocalFilesystemURL inputUrl = toLocalUri(nativeUri); - return inputUrl == null ? null : makeEntryForURL(inputUrl, nativeUri); - } - - public JSONObject getEntryForLocalURL(LocalFilesystemURL inputURL) throws IOException { - 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; - - abstract boolean removeFileAtLocalURL(LocalFilesystemURL inputURL) throws InvalidModificationException, NoModificationAllowedException; - - abstract boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL) throws FileExistsException, NoModificationAllowedException; - - abstract LocalFilesystemURL[] listChildren(LocalFilesystemURL inputURL) throws FileNotFoundException; - - public final JSONArray readEntriesAtLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException { - LocalFilesystemURL[] children = listChildren(inputURL); - JSONArray entries = new JSONArray(); - if (children != null) { - for (LocalFilesystemURL url : children) { - entries.put(makeEntryForURL(url)); - } - } - return entries; - } - - abstract JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException; - - public Uri getRootUri() { - 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; - } - - public LocalFilesystemURL localUrlforFullPath(String fullPath) { - Uri nativeUri = nativeUriForFullPath(fullPath); - if (nativeUri != null) { - return toLocalUri(nativeUri); - } - return null; - } - - /** - * 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 components = new ArrayList(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; - } - - public JSONObject getParentForLocalURL(LocalFilesystemURL inputURL) throws IOException { - Uri parentUri = inputURL.uri; - String parentPath = new File(inputURL.uri.getPath()).getParent(); - if (!"/".equals(parentPath)) { - parentUri = inputURL.uri.buildUpon().path(parentPath + '/').build(); - } - return getEntryForLocalURL(LocalFilesystemURL.parse(parentUri)); - } - - protected LocalFilesystemURL makeDestinationURL(String newName, LocalFilesystemURL srcURL, LocalFilesystemURL destURL, boolean isDirectory) { - // I know this looks weird but it is to work around a JSON bug. - if ("null".equals(newName) || "".equals(newName)) { - newName = srcURL.uri.getLastPathSegment();; - } - - String newDest = destURL.uri.toString(); - if (newDest.endsWith("/")) { - newDest = newDest + newName; - } else { - newDest = newDest + "/" + newName; - } - if (isDirectory) { - newDest += '/'; - } - 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. - */ - public JSONObject copyFileToURL(LocalFilesystemURL destURL, String newName, - Filesystem srcFs, LocalFilesystemURL srcURL, boolean move) throws IOException, InvalidModificationException, JSONException, NoModificationAllowedException, FileExistsException { - // First, check to see that we can do it - if (move && !srcFs.canRemoveFileAtLocalURL(srcURL)) { - throw new NoModificationAllowedException("Cannot move file at source URL"); - } - final LocalFilesystemURL destination = makeDestinationURL(newName, srcURL, destURL, srcURL.isDirectory); - - 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); - } - - public OutputStream getOutputStreamForURL(LocalFilesystemURL inputURL) throws IOException { - return resourceApi.openOutputStream(toNativeUri(inputURL)); - } - - 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); - } - InputStream inputStream = ofrr.inputStream; - if (end < ofrr.length) { - inputStream = new LimitedInputStream(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; - - abstract long truncateFileAtURL(LocalFilesystemURL inputURL, long size) - throws IOException, NoModificationAllowedException; - - // This method should return null if filesystem urls cannot be mapped to paths - abstract String filesystemPathForURL(LocalFilesystemURL url); - - abstract LocalFilesystemURL URLforFilesystemPath(String path); - - abstract boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL); - - protected class LimitedInputStream extends FilterInputStream { - long numBytesToRead; - public LimitedInputStream(InputStream in, long numBytesToRead) { - super(in); - this.numBytesToRead = numBytesToRead; - } - @Override - public int read() throws IOException { - if (numBytesToRead <= 0) { - return -1; - } - numBytesToRead--; - return in.read(); - } - @Override - public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { - if (numBytesToRead <= 0) { - return -1; - } - int bytesToRead = byteCount; - if (byteCount > numBytesToRead) { - bytesToRead = (int)numBytesToRead; // Cast okay; long is less than int here. - } - int numBytesRead = in.read(buffer, byteOffset, bytesToRead); - numBytesToRead -= numBytesRead; - return numBytesRead; - } - } -} diff --git a/plugins/cordova-plugin-file/src/android/InvalidModificationException.java b/plugins/cordova-plugin-file/src/android/InvalidModificationException.java deleted file mode 100644 index 8f6bec59..00000000 --- a/plugins/cordova-plugin-file/src/android/InvalidModificationException.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - 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; - -@SuppressWarnings("serial") -public class InvalidModificationException extends Exception { - - public InvalidModificationException(String message) { - super(message); - } - -} diff --git a/plugins/cordova-plugin-file/src/android/LocalFilesystem.java b/plugins/cordova-plugin-file/src/android/LocalFilesystem.java deleted file mode 100644 index 3b1ecca8..00000000 --- a/plugins/cordova-plugin-file/src/android/LocalFilesystem.java +++ /dev/null @@ -1,505 +0,0 @@ -/* - 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 java.io.ByteArrayInputStream; -import java.io.File; -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 org.apache.cordova.CordovaResourceApi; -import org.json.JSONException; -import org.json.JSONObject; - -import android.os.Build; -import android.os.Environment; -import android.util.Base64; -import android.net.Uri; -import android.content.Context; -import android.content.Intent; - -public class LocalFilesystem extends Filesystem { - private final Context context; - - public LocalFilesystem(String name, Context context, CordovaResourceApi resourceApi, File fsRoot) { - super(Uri.fromFile(fsRoot).buildUpon().appendEncodedPath("").build(), name, resourceApi); - this.context = context; - } - - public String filesystemPathForFullPath(String fullPath) { - return new File(rootUri.getPath(), fullPath).toString(); - } - - @Override - public String filesystemPathForURL(LocalFilesystemURL url) { - return filesystemPathForFullPath(url.path); - } - - private String fullPathForFilesystemPath(String absolutePath) { - if (absolutePath != null && absolutePath.startsWith(rootUri.getPath())) { - return absolutePath.substring(rootUri.getPath().length() - 1); - } - 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 localUrlforFullPath(fullPathForFilesystemPath(path)); - } - - @Override - public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL, - String path, JSONObject options, boolean directory) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException { - boolean create = false; - boolean exclusive = false; - - if (options != null) { - create = options.optBoolean("create"); - if (create) { - exclusive = options.optBoolean("exclusive"); - } - } - - // Check for a ":" character in the file to line up with BB and iOS - if (path.contains(":")) { - throw new EncodingException("This path has an invalid \":\" in it."); - } - - LocalFilesystemURL requestedURL; - - // Check whether the supplied path is absolute or relative - if (directory && !path.endsWith("/")) { - path += "/"; - } - if (path.startsWith("/")) { - requestedURL = localUrlforFullPath(normalizePath(path)); - } else { - requestedURL = localUrlforFullPath(normalizePath(inputURL.path + "/" + path)); - } - - File fp = new File(this.filesystemPathForURL(requestedURL)); - - if (create) { - if (exclusive && fp.exists()) { - throw new FileExistsException("create/exclusive fails"); - } - if (directory) { - fp.mkdir(); - } else { - fp.createNewFile(); - } - if (!fp.exists()) { - throw new FileExistsException("create fails"); - } - } - else { - if (!fp.exists()) { - throw new FileNotFoundException("path does not exist"); - } - 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"); - } - } - } - - // Return the directory - return makeEntryForURL(requestedURL); - } - - @Override - public boolean removeFileAtLocalURL(LocalFilesystemURL inputURL) throws InvalidModificationException { - - File fp = new File(filesystemPathForURL(inputURL)); - - // You can't delete a directory that is not empty - if (fp.isDirectory() && fp.list().length > 0) { - throw new InvalidModificationException("You can't delete a directory that is not empty."); - } - - return fp.delete(); - } - - @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); - } - - protected boolean removeDirRecursively(File directory) throws FileExistsException { - if (directory.isDirectory()) { - for (File file : directory.listFiles()) { - removeDirRecursively(file); - } - } - - if (!directory.delete()) { - throw new FileExistsException("could not delete: " + directory.getName()); - } else { - return true; - } - } - - @Override - public LocalFilesystemURL[] listChildren(LocalFilesystemURL inputURL) throws FileNotFoundException { - File fp = new File(filesystemPathForURL(inputURL)); - - if (!fp.exists()) { - // The directory we are listing doesn't exist so we should fail. - throw new FileNotFoundException(); - } - - File[] files = fp.listFiles(); - if (files == null) { - // inputURL is a directory - return null; - } - LocalFilesystemURL[] entries = new LocalFilesystemURL[files.length]; - for (int i = 0; i < files.length; i++) { - entries[i] = URLforFilesystemPath(files[i].getPath()); - } - - return entries; - } - - @Override - public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException { - File file = new File(filesystemPathForURL(inputURL)); - - if (!file.exists()) { - throw new FileNotFoundException("File at " + inputURL.uri + " does not exist."); - } - - JSONObject metadata = new JSONObject(); - try { - // Ensure that directories report a size of 0 - 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.path); - metadata.put("lastModifiedDate", file.lastModified()); - } catch (JSONException e) { - return null; - } - return metadata; - } - - private void copyFile(Filesystem srcFs, LocalFilesystemURL srcURL, File destFile, boolean move) throws IOException, InvalidModificationException, NoModificationAllowedException { - if (move) { - String realSrcPath = srcFs.filesystemPathForURL(srcURL); - if (realSrcPath != null) { - File srcFile = new File(realSrcPath); - if (srcFile.renameTo(destFile)) { - return; - } - // Trying to rename the file failed. Possibly because we moved across file system on the device. - } - } - - CordovaResourceApi.OpenForReadResult offr = resourceApi.openForRead(srcFs.toNativeUri(srcURL)); - copyResource(offr, new FileOutputStream(destFile)); - - if (move) { - srcFs.removeFileAtLocalURL(srcURL); - } - } - - private void copyDirectory(Filesystem srcFs, LocalFilesystemURL srcURL, File dstDir, boolean move) throws IOException, NoModificationAllowedException, InvalidModificationException, FileExistsException { - if (move) { - String realSrcPath = srcFs.filesystemPathForURL(srcURL); - if (realSrcPath != null) { - File srcDir = new File(realSrcPath); - // If the destination directory already exists and is empty then delete it. This is according to spec. - if (dstDir.exists()) { - if (dstDir.list().length > 0) { - throw new InvalidModificationException("directory is not empty"); - } - dstDir.delete(); - } - // Try to rename the directory - if (srcDir.renameTo(dstDir)) { - return; - } - // Trying to rename the file failed. Possibly because we moved across file system on the device. - } - } - - if (dstDir.exists()) { - if (dstDir.list().length > 0) { - throw new InvalidModificationException("directory is not empty"); - } - } else { - if (!dstDir.mkdir()) { - // If we can't create the directory then fail - throw new NoModificationAllowedException("Couldn't create the destination directory"); - } - } - - LocalFilesystemURL[] children = srcFs.listChildren(srcURL); - for (LocalFilesystemURL childLocalUrl : children) { - File target = new File(dstDir, new File(childLocalUrl.path).getName()); - if (childLocalUrl.isDirectory) { - copyDirectory(srcFs, childLocalUrl, target, false); - } else { - copyFile(srcFs, childLocalUrl, target, false); - } - } - - if (move) { - srcFs.recursiveRemoveFileAtLocalURL(srcURL); - } - } - - @Override - public JSONObject copyFileToURL(LocalFilesystemURL destURL, String newName, - Filesystem srcFs, LocalFilesystemURL srcURL, boolean move) throws IOException, InvalidModificationException, JSONException, NoModificationAllowedException, FileExistsException { - - // Check to see if the destination directory exists - String newParent = this.filesystemPathForURL(destURL); - File destinationDir = new File(newParent); - if (!destinationDir.exists()) { - // The destination does not exist so we should fail. - throw new FileNotFoundException("The source does not exist"); - } - - // Figure out where we should be copying to - final LocalFilesystemURL destinationURL = makeDestinationURL(newName, srcURL, destURL, srcURL.isDirectory); - - Uri dstNativeUri = toNativeUri(destinationURL); - Uri srcNativeUri = srcFs.toNativeUri(srcURL); - // Check to see if source and destination are the same file - if (dstNativeUri.equals(srcNativeUri)) { - throw new InvalidModificationException("Can't copy onto itself"); - } - - if (move && !srcFs.canRemoveFileAtLocalURL(srcURL)) { - throw new InvalidModificationException("Source URL is read-only (cannot move)"); - } - - File destFile = new File(dstNativeUri.getPath()); - if (destFile.exists()) { - if (!srcURL.isDirectory && destFile.isDirectory()) { - throw new InvalidModificationException("Can't copy/move a file to an existing directory"); - } else if (srcURL.isDirectory && destFile.isFile()) { - throw new InvalidModificationException("Can't copy/move a directory to an existing file"); - } - } - - if (srcURL.isDirectory) { - // E.g. Copy /sdcard/myDir to /sdcard/myDir/backup - if (dstNativeUri.toString().startsWith(srcNativeUri.toString() + '/')) { - throw new InvalidModificationException("Can't copy directory into itself"); - } - copyDirectory(srcFs, srcURL, destFile, move); - } else { - copyFile(srcFs, srcURL, destFile, move); - } - return makeEntryForURL(destinationURL); - } - - @Override - public long writeToFileAtURL(LocalFilesystemURL inputURL, String data, - int offset, boolean isBinary) throws IOException, NoModificationAllowedException { - - boolean append = false; - if (offset > 0) { - this.truncateFileAtURL(inputURL, offset); - append = true; - } - - byte[] rawData; - if (isBinary) { - rawData = Base64.decode(data, Base64.DEFAULT); - } else { - rawData = data.getBytes(); - } - ByteArrayInputStream in = new ByteArrayInputStream(rawData); - try - { - byte buff[] = new byte[rawData.length]; - String absolutePath = filesystemPathForURL(inputURL); - FileOutputStream out = new FileOutputStream(absolutePath, append); - try { - in.read(buff, 0, buff.length); - out.write(buff, 0, rawData.length); - out.flush(); - } finally { - // Always close the output - out.close(); - } - if (isPublicDirectory(absolutePath)) { - broadcastNewFile(Uri.fromFile(new File(absolutePath))); - } - } - catch (NullPointerException e) - { - // This is a bug in the Android implementation of the Java Stack - NoModificationAllowedException realException = new NoModificationAllowedException(inputURL.toString()); - throw realException; - } - - return rawData.length; - } - - private boolean isPublicDirectory(String absolutePath) { - // TODO: should expose a way to scan app's private files (maybe via a flag). - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // Lollipop has a bug where SD cards are null. - for (File f : context.getExternalMediaDirs()) { - if(f != null && absolutePath.startsWith(f.getAbsolutePath())) { - return true; - } - } - } - - String extPath = Environment.getExternalStorageDirectory().getAbsolutePath(); - return absolutePath.startsWith(extPath); - } - - /** - * Send broadcast of new file so files appear over MTP - */ - private void broadcastNewFile(Uri nativeUri) { - Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, nativeUri); - context.sendBroadcast(intent); - } - - @Override - public long truncateFileAtURL(LocalFilesystemURL inputURL, long size) throws IOException { - File file = new File(filesystemPathForURL(inputURL)); - - if (!file.exists()) { - throw new FileNotFoundException("File at " + inputURL.uri + " does not exist."); - } - - RandomAccessFile raf = new RandomAccessFile(filesystemPathForURL(inputURL), "rw"); - try { - if (raf.length() >= size) { - FileChannel channel = raf.getChannel(); - channel.truncate(size); - return size; - } - - return raf.length(); - } finally { - raf.close(); - } - - - } - - @Override - public boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL) { - String path = filesystemPathForURL(inputURL); - File file = new File(path); - return file.exists(); - } - - // This is a copy & paste from CordovaResource API that is required since CordovaResourceApi - // has a bug pre-4.0.0. - // TODO: Once cordova-android@4.0.0 is released, delete this copy and make the plugin depend on - // 4.0.0 with an engine tag. - private static void copyResource(CordovaResourceApi.OpenForReadResult input, OutputStream outputStream) throws IOException { - try { - InputStream inputStream = input.inputStream; - if (inputStream instanceof FileInputStream && outputStream instanceof FileOutputStream) { - FileChannel inChannel = ((FileInputStream)input.inputStream).getChannel(); - FileChannel outChannel = ((FileOutputStream)outputStream).getChannel(); - long offset = 0; - long length = input.length; - if (input.assetFd != null) { - offset = input.assetFd.getStartOffset(); - } - // transferFrom()'s 2nd arg is a relative position. Need to set the absolute - // position first. - inChannel.position(offset); - outChannel.transferFrom(inChannel, 0, length); - } else { - final int BUFFER_SIZE = 8192; - byte[] buffer = new byte[BUFFER_SIZE]; - - for (;;) { - int bytesRead = inputStream.read(buffer, 0, BUFFER_SIZE); - - if (bytesRead <= 0) { - break; - } - outputStream.write(buffer, 0, bytesRead); - } - } - } finally { - input.inputStream.close(); - if (outputStream != null) { - outputStream.close(); - } - } - } -} diff --git a/plugins/cordova-plugin-file/src/android/LocalFilesystemURL.java b/plugins/cordova-plugin-file/src/android/LocalFilesystemURL.java deleted file mode 100644 index 74f43db6..00000000 --- a/plugins/cordova-plugin-file/src/android/LocalFilesystemURL.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - 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.net.Uri; - -public class LocalFilesystemURL { - - public static final String FILESYSTEM_PROTOCOL = "cdvfile"; - - public final Uri uri; - public final String fsName; - public final String path; - public final boolean isDirectory; - - private LocalFilesystemURL(Uri uri, String fsName, String fsPath, boolean isDirectory) { - this.uri = uri; - this.fsName = fsName; - this.path = fsPath; - this.isDirectory = isDirectory; - } - - public static LocalFilesystemURL parse(Uri uri) { - if (!FILESYSTEM_PROTOCOL.equals(uri.getScheme())) { - return null; - } - String path = uri.getPath(); - if (path.length() < 1) { - return null; - } - int firstSlashIdx = path.indexOf('/', 1); - if (firstSlashIdx < 0) { - return null; - } - 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) { - return parse(Uri.parse(uri)); - } - - public String toString() { - return uri.toString(); - } -} diff --git a/plugins/cordova-plugin-file/src/android/NoModificationAllowedException.java b/plugins/cordova-plugin-file/src/android/NoModificationAllowedException.java deleted file mode 100644 index 627eafb5..00000000 --- a/plugins/cordova-plugin-file/src/android/NoModificationAllowedException.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - 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; - -@SuppressWarnings("serial") -public class NoModificationAllowedException extends Exception { - - public NoModificationAllowedException(String message) { - super(message); - } - -} diff --git a/plugins/cordova-plugin-file/src/android/TypeMismatchException.java b/plugins/cordova-plugin-file/src/android/TypeMismatchException.java deleted file mode 100644 index 1315f9a9..00000000 --- a/plugins/cordova-plugin-file/src/android/TypeMismatchException.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - 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; - -@SuppressWarnings("serial") -public class TypeMismatchException extends Exception { - - public TypeMismatchException(String message) { - super(message); - } - -} diff --git a/plugins/cordova-plugin-file/src/android/build-extras.gradle b/plugins/cordova-plugin-file/src/android/build-extras.gradle deleted file mode 100644 index a0a7844a..00000000 --- a/plugins/cordova-plugin-file/src/android/build-extras.gradle +++ /dev/null @@ -1,47 +0,0 @@ -/* - 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. - */ -ext.postBuildExtras = { - def inAssetsDir = file("assets") - def outAssetsDir = inAssetsDir - def outFile = new File(outAssetsDir, "cdvasset.manifest") - - def newTask = task("cdvCreateAssetManifest") << { - def contents = new HashMap() - def sizes = new HashMap() - contents[""] = inAssetsDir.list() - def tree = fileTree(dir: inAssetsDir) - tree.visit { fileDetails -> - if (fileDetails.isDirectory()) { - contents[fileDetails.relativePath.toString()] = fileDetails.file.list() - } else { - sizes[fileDetails.relativePath.toString()] = fileDetails.file.length() - } - } - - outAssetsDir.mkdirs() - outFile.withObjectOutputStream { oos -> - oos.writeObject(contents) - oos.writeObject(sizes) - } - } - newTask.inputs.dir inAssetsDir - newTask.outputs.file outFile - def preBuildTask = tasks["preBuild"] - preBuildTask.dependsOn(newTask) -} -- cgit v1.2.3