cordova-android/framework/src/com/phonegap/FileUtils.java
macdonst e789349c23 Handle content:// uri's in resolveLocalFileSystemURI
Sometimes Android will hand you a content:// uri in the place of a file path. Particularily the Camera.getPicture() code will do this. I've updated the file utils code to handle this type of uri and return a real file path.
2011-07-19 04:10:55 +08:00

993 lines
33 KiB
Java
Executable File

/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010-2011, IBM Corporation
*/
package com.phonegap;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.channels.FileChannel;
import org.apache.commons.codec.binary.Base64;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.webkit.MimeTypeMap;
import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult;
import com.phonegap.file.EncodingException;
import com.phonegap.file.FileExistsException;
import com.phonegap.file.InvalidModificationException;
import com.phonegap.file.NoModificationAllowedException;
import com.phonegap.file.TypeMismatchException;
/**
* This class provides SD card file and directory services to JavaScript.
* Only files on the SD card can be accessed.
*/
public class FileUtils extends Plugin {
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 TEMPORARY = 0;
public static int PERSISTENT = 1;
public static int RESOURCE = 2;
public static int APPLICATION = 3;
FileReader f_in;
FileWriter f_out;
/**
* Constructor.
*/
public FileUtils() {
}
/**
* Executes the request and returns PluginResult.
*
* @param action The action to execute.
* @param args JSONArry of arguments for the plugin.
* @param callbackId The callback id used when calling back into JavaScript.
* @return A PluginResult object with a status and message.
*/
public PluginResult execute(String action, JSONArray args, String callbackId) {
PluginResult.Status status = PluginResult.Status.OK;
String result = "";
//System.out.println("FileUtils.execute("+action+")");
try {
try {
if (action.equals("testSaveLocationExists")) {
boolean b = DirectoryManager.testSaveLocationExists();
return new PluginResult(status, b);
}
else if (action.equals("getFreeDiskSpace")) {
long l = DirectoryManager.getFreeDiskSpace();
return new PluginResult(status, l);
}
else if (action.equals("testFileExists")) {
boolean b = DirectoryManager.testFileExists(args.getString(0));
return new PluginResult(status, b);
}
else if (action.equals("testDirectoryExists")) {
boolean b = DirectoryManager.testFileExists(args.getString(0));
return new PluginResult(status, b);
}
else if (action.equals("readAsText")) {
String s = this.readAsText(args.getString(0), args.getString(1));
return new PluginResult(status, s);
}
else if (action.equals("readAsDataURL")) {
String s = this.readAsDataURL(args.getString(0));
return new PluginResult(status, s);
}
else if (action.equals("write")) {
long fileSize = this.write(args.getString(0), args.getString(1), args.getInt(2));
return new PluginResult(status, fileSize);
}
else if (action.equals("truncate")) {
long fileSize = this.truncateFile(args.getString(0), args.getLong(1));
return new PluginResult(status, fileSize);
}
else if (action.equals("requestFileSystem")) {
long size = args.optLong(1);
if (size != 0) {
if (size > DirectoryManager.getFreeDiskSpace()) {
JSONObject error = new JSONObject().put("code", FileUtils.QUOTA_EXCEEDED_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
}
}
JSONObject obj = requestFileSystem(args.getInt(0));
return new PluginResult(status, obj, "window.localFileSystem._castFS");
}
else if (action.equals("resolveLocalFileSystemURI")) {
JSONObject obj = resolveLocalFileSystemURI(args.getString(0));
return new PluginResult(status, obj, "window.localFileSystem._castEntry");
}
else if (action.equals("getMetadata")) {
JSONObject obj = getMetadata(args.getString(0));
return new PluginResult(status, obj, "window.localFileSystem._castDate");
}
else if (action.equals("getFileMetadata")) {
JSONObject obj = getFileMetadata(args.getString(0));
return new PluginResult(status, obj, "window.localFileSystem._castDate");
}
else if (action.equals("getParent")) {
JSONObject obj = getParent(args.getString(0));
return new PluginResult(status, obj, "window.localFileSystem._castEntry");
}
else if (action.equals("getDirectory")) {
JSONObject obj = getFile(args.getString(0), args.getString(1), args.optJSONObject(2), true);
return new PluginResult(status, obj, "window.localFileSystem._castEntry");
}
else if (action.equals("getFile")) {
JSONObject obj = getFile(args.getString(0), args.getString(1), args.optJSONObject(2), false);
return new PluginResult(status, obj, "window.localFileSystem._castEntry");
}
else if (action.equals("remove")) {
boolean success;
success = remove(args.getString(0));
if (success) {
return new PluginResult(status);
} else {
JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
}
}
else if (action.equals("removeRecursively")) {
boolean success = removeRecursively(args.getString(0));
if (success) {
return new PluginResult(status);
} else {
JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
}
}
else if (action.equals("moveTo")) {
JSONObject entry = transferTo(args.getString(0), args.getJSONObject(1), args.optString(2), true);
return new PluginResult(status, entry, "window.localFileSystem._castEntry");
}
else if (action.equals("copyTo")) {
JSONObject entry = transferTo(args.getString(0), args.getJSONObject(1), args.optString(2), false);
return new PluginResult(status, entry, "window.localFileSystem._castEntry");
}
else if (action.equals("readEntries")) {
JSONArray entries = readEntries(args.getString(0));
return new PluginResult(status, entries, "window.localFileSystem._castEntries");
}
return new PluginResult(status, result);
} catch (FileNotFoundException e) {
JSONObject error = new JSONObject().put("code", FileUtils.NOT_FOUND_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
} catch (FileExistsException e) {
JSONObject error = new JSONObject().put("code", FileUtils.PATH_EXISTS_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
} catch (NoModificationAllowedException e) {
JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
} catch (JSONException e) {
JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
} catch (InvalidModificationException e) {
JSONObject error = new JSONObject().put("code", FileUtils.INVALID_MODIFICATION_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
} catch (MalformedURLException e) {
JSONObject error = new JSONObject().put("code", FileUtils.ENCODING_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
} catch (IOException e) {
JSONObject error = new JSONObject().put("code", FileUtils.INVALID_MODIFICATION_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
} catch (EncodingException e) {
JSONObject error = new JSONObject().put("code", FileUtils.ENCODING_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
} catch (TypeMismatchException e) {
JSONObject error = new JSONObject().put("code", FileUtils.TYPE_MISMATCH_ERR);
return new PluginResult(PluginResult.Status.ERROR, error);
}
} catch (JSONException e) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
}
}
/**
* Allows the user to look up the Entry for a file or directory referred to by a local URI.
*
* @param url 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 url) throws IOException, JSONException {
String decoded = URLDecoder.decode(url, "UTF-8");
File fp = null;
// Handle the special case where you get an Android content:// uri.
if (decoded.startsWith("content:")) {
Cursor cursor = this.ctx.managedQuery(Uri.parse(decoded), new String[] { MediaStore.Images.Media.DATA }, null, null, null);
// Note: MediaStore.Images/Audio/Video.Media.DATA is always "_data"
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
fp = new File(cursor.getString(column_index));
} else {
// Test to see if this is a valid URL first
@SuppressWarnings("unused")
URL testUrl = new URL(decoded);
if (decoded.startsWith("file://")) {
fp = new File(decoded.substring(7, decoded.length()));
} else {
fp = new File(decoded);
}
}
if (!fp.exists()) {
throw new FileNotFoundException();
}
if (!fp.canRead()) {
throw new IOException();
}
return getEntry(fp);
}
/**
* Read the list of files from this directory.
*
* @param fileName the directory to read from
* @return a JSONArray containing JSONObjects that represent Entry objects.
* @throws FileNotFoundException if the directory is not found.
* @throws JSONException
*/
private JSONArray readEntries(String fileName) throws FileNotFoundException, JSONException {
File fp = new File(fileName);
if (!fp.exists()) {
// The directory we are listing doesn't exist so we should fail.
throw new FileNotFoundException();
}
JSONArray entries = new JSONArray();
if (fp.isDirectory()) {
File[] files = fp.listFiles();
for (int i=0; i<files.length; i++) {
entries.put(getEntry(files[i]));
}
}
return entries;
}
/**
* A setup method that handles the move/copy of files/directories
*
* @param fileName to be copied/moved
* @param newParent is the location where the file will be copied/moved to
* @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 FileExistsException
* @throws NoModificationAllowedException
* @throws IOException
* @throws InvalidModificationException
* @throws EncodingException
* @throws JSONException
*/
private JSONObject transferTo(String fileName, JSONObject newParent, String newName, boolean move) throws JSONException, FileExistsException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException {
// Check for invalid file name
if (newName != null && newName.contains(":")) {
throw new EncodingException("Bad file name");
}
File source = new File(fileName);
if (!source.exists()) {
// The file/directory we are copying doesn't exist so we should fail.
throw new FileNotFoundException("The source does not exist");
}
File destinationDir = new File(newParent.getString("fullPath"));
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
File destination = createDestination(newName, source, destinationDir);
//Log.d(LOG_TAG, "Source: " + source.getAbsolutePath());
//Log.d(LOG_TAG, "Destin: " + destination.getAbsolutePath());
// Check to see if source and destination are the same file
if (source.getAbsolutePath().equals(destination.getAbsolutePath())) {
throw new InvalidModificationException("Can't copy a file onto itself");
}
if (source.isDirectory()) {
if (move) {
return moveDirectory(source, destination);
} else {
return copyDirectory(source, destination);
}
} else {
if (move) {
return moveFile(source, destination);
} else {
return copyFile(source, destination);
}
}
}
/**
* Creates the destination File object based on name passed in
*
* @param newName for the file directory to be called, if null use existing file name
* @param fp represents the source file
* @param destination represents the destination file
* @return a File object that represents the destination
*/
private File createDestination(String newName, File fp, File destination) {
File destFile = null;
// I know this looks weird but it is to work around a JSON bug.
if ("null".equals(newName) || "".equals(newName) ) {
newName = null;
}
if (newName != null) {
destFile = new File(destination.getAbsolutePath() + File.separator + newName);
} else {
destFile = new File(destination.getAbsolutePath() + File.separator + fp.getName());
}
return destFile;
}
/**
* Copy a file
*
* @param srcFile file to be copied
* @param destFile destination to be copied to
* @return a FileEntry object
* @throws IOException
* @throws InvalidModificationException
* @throws JSONException
*/
private JSONObject copyFile(File srcFile, File destFile) throws IOException, InvalidModificationException, JSONException {
// Renaming a file to an existing directory should fail
if (destFile.exists() && destFile.isDirectory()) {
throw new InvalidModificationException("Can't rename a file to a directory");
}
FileChannel input = new FileInputStream(srcFile).getChannel();
FileChannel output = new FileOutputStream(destFile).getChannel();
input.transferTo(0, input.size(), output);
input.close();
output.close();
/*
if (srcFile.length() != destFile.length()) {
return false;
}
*/
return getEntry(destFile);
}
/**
* Copy a directory
*
* @param srcDir directory to be copied
* @param destinationDir destination to be copied to
* @return a DirectoryEntry object
* @throws JSONException
* @throws IOException
* @throws NoModificationAllowedException
* @throws InvalidModificationException
*/
private JSONObject copyDirectory(File srcDir, File destinationDir) throws JSONException, IOException, NoModificationAllowedException, InvalidModificationException {
// Renaming a file to an existing directory should fail
if (destinationDir.exists() && destinationDir.isFile()) {
throw new InvalidModificationException("Can't rename a file to a directory");
}
// Check to make sure we are not copying the directory into itself
if (isCopyOnItself(srcDir.getAbsolutePath(), destinationDir.getAbsolutePath())) {
throw new InvalidModificationException("Can't copy itself into itself");
}
// See if the destination directory exists. If not create it.
if (!destinationDir.exists()) {
if (!destinationDir.mkdir()) {
// If we can't create the directory then fail
throw new NoModificationAllowedException("Couldn't create the destination direcotry");
}
}
for (File file : srcDir.listFiles()) {
if (file.isDirectory()) {
copyDirectory(file, destinationDir);
} else {
File destination = new File(destinationDir.getAbsoluteFile() + File.separator + file.getName());
copyFile(file, destination);
}
}
return getEntry(destinationDir);
}
/**
* Check to see if the user attempted to copy an entry into its parent without changing its name,
* or attempted to copy a directory into a directory that it contains directly or indirectly.
*
* @param srcDir
* @param destinationDir
* @return
*/
private boolean isCopyOnItself(String src, String dest) {
// This weird test is to determine if we are copying or moving a directory into itself.
// Copy /sdcard/myDir to /sdcard/myDir-backup is okay but
// Copy /sdcard/myDir to /sdcard/myDir/backup should thow an INVALID_MODIFICATION_ERR
if (dest.startsWith(src) && dest.indexOf(File.separator, src.length()-1) != -1) {
return true;
}
return false;
}
/**
* Move a file
*
* @param srcFile file to be copied
* @param destFile destination to be copied to
* @return a FileEntry object
* @throws IOException
* @throws InvalidModificationException
* @throws JSONException
*/
private JSONObject moveFile(File srcFile, File destFile) throws JSONException, InvalidModificationException {
// Renaming a file to an existing directory should fail
if (destFile.exists() && destFile.isDirectory()) {
throw new InvalidModificationException("Can't rename a file to a directory");
}
// Try to rename the file
if (!srcFile.renameTo(destFile)) {
// Trying to rename the file failed. Possibly because we moved across file system on the device.
// Now we have to do things the hard way
// 1) Copy all the old file
// 2) delete the src file
}
return getEntry(destFile);
}
/**
* Move a directory
*
* @param srcDir directory to be copied
* @param destinationDir destination to be copied to
* @return a DirectoryEntry object
* @throws JSONException
* @throws IOException
* @throws NoModificationAllowedException
* @throws InvalidModificationException
*/
private JSONObject moveDirectory(File srcDir, File destinationDir) throws JSONException, FileExistsException, NoModificationAllowedException, InvalidModificationException {
// Renaming a file to an existing directory should fail
if (destinationDir.exists() && destinationDir.isFile()) {
throw new InvalidModificationException("Can't rename a file to a directory");
}
// Check to make sure we are not copying the directory into itself
if (isCopyOnItself(srcDir.getAbsolutePath(), destinationDir.getAbsolutePath())) {
throw new InvalidModificationException("Can't move itself into itself");
}
// If the destination directory already exists and is empty then delete it. This is according to spec.
if (destinationDir.exists()) {
if (destinationDir.list().length > 0) {
throw new InvalidModificationException("directory is not empty");
}
}
// Try to rename the directory
if (!srcDir.renameTo(destinationDir)) {
// Trying to rename the directory failed. Possibly because we moved across file system on the device.
// Now we have to do things the hard way
// 1) Copy all the old files
// 2) delete the src directory
}
return getEntry(destinationDir);
}
/**
* 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.
*
* @param filePath the directory to be removed
* @return a boolean representing success of failure
* @throws FileExistsException
*/
private boolean removeRecursively(String filePath) throws FileExistsException {
File fp = new File(filePath);
// You can't delete the root directory.
if (atRootDirectory(filePath)) {
return false;
}
return removeDirRecursively(fp);
}
/**
* Loops through a directory deleting all the files.
*
* @param directory to be removed
* @return a boolean representing success of failure
* @throws FileExistsException
*/
private 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;
}
}
/**
* 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.
*
* @param filePath file or directory to be removed
* @return a boolean representing success of failure
* @throws NoModificationAllowedException
* @throws InvalidModificationException
*/
private boolean remove(String filePath) throws NoModificationAllowedException, InvalidModificationException {
File fp = new File(filePath);
// You can't delete the root directory.
if (atRootDirectory(filePath)) {
throw new NoModificationAllowedException("You can't delete the root directory");
}
// 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();
}
/**
* Creates or looks up a file.
*
* @param dirPath base directory
* @param fileName 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 dirPath, String fileName, 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 (fileName.contains(":")) {
throw new EncodingException("This file has a : in it's name");
}
File fp = createFileObject(dirPath, fileName);
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 getEntry(fp);
}
/**
* If the path starts with a '/' just return that file object. If not construct the file
* object from the path passed in and the file name.
*
* @param dirPath root directory
* @param fileName new file name
* @return
*/
private File createFileObject(String dirPath, String fileName) {
File fp = null;
if (fileName.startsWith("/")) {
fp = new File(fileName);
} else {
fp = new File(dirPath + File.separator + fileName);
}
return fp;
}
/**
* Look up the parent DirectoryEntry containing this Entry.
* If this Entry is the root of its filesystem, its parent is itself.
*
* @param filePath
* @return
* @throws JSONException
*/
private JSONObject getParent(String filePath) throws JSONException {
if (atRootDirectory(filePath)) {
return getEntry(filePath);
}
return getEntry(new File(filePath).getParent());
}
/**
* Checks to see if we are at the root directory. Useful since we are
* not allow to delete this directory.
*
* @param filePath to directory
* @return true if we are at the root, false otherwise.
*/
private boolean atRootDirectory(String filePath) {
if (filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + ctx.getPackageName() + "/cache") ||
filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath())) {
return true;
}
return false;
}
/**
* Look up metadata about this entry.
*
* @param filePath to entry
* @return a Metadata object
* @throws FileNotFoundException
* @throws JSONException
*/
private JSONObject getMetadata(String filePath) throws FileNotFoundException, JSONException {
File file = new File(filePath);
if (!file.exists()) {
throw new FileNotFoundException("Failed to find file in getMetadata");
}
JSONObject metadata = new JSONObject();
metadata.put("modificationTime", file.lastModified());
return metadata;
}
/**
* Returns a File that represents the current state of the file that this FileEntry represents.
*
* @param filePath to entry
* @return returns a JSONObject represent a W3C File object
* @throws FileNotFoundException
* @throws JSONException
*/
private JSONObject getFileMetadata(String filePath) throws FileNotFoundException, JSONException {
File file = new File(filePath);
if (!file.exists()) {
throw new FileNotFoundException("File: " + filePath + " does not exist.");
}
JSONObject metadata = new JSONObject();
metadata.put("size", file.length());
metadata.put("type", getMimeType(filePath));
metadata.put("name", file.getName());
metadata.put("fullPath", file.getAbsolutePath());
metadata.put("lastModifiedDate", file.lastModified());
return metadata;
}
/**
* 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();
if (type == TEMPORARY) {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
fs.put("name", "temporary");
fs.put("root", getEntry(Environment.getExternalStorageDirectory().getAbsolutePath() +
"/Android/data/" + ctx.getPackageName() + "/cache/"));
// Create the cache dir if it doesn't exist.
File fp = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +
"/Android/data/" + ctx.getPackageName() + "/cache/");
fp.mkdirs();
} else {
throw new IOException("SD Card not mounted");
}
}
else if (type == PERSISTENT) {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
fs.put("name", "persistent");
fs.put("root", getEntry(Environment.getExternalStorageDirectory()));
} else {
throw new IOException("SD Card not mounted");
}
}
else if (type == RESOURCE) {
fs.put("name", "resource");
}
else if (type == APPLICATION) {
fs.put("name", "application");
}
else {
throw new IOException("No filesystem of type requested");
}
return fs;
}
/**
* Returns a JSON Object representing a directory on the device's file system
*
* @param path to the directory
* @return
* @throws JSONException
*/
private JSONObject getEntry(File file) throws JSONException {
JSONObject entry = new JSONObject();
entry.put("isFile", file.isFile());
entry.put("isDirectory", file.isDirectory());
entry.put("name", file.getName());
entry.put("fullPath", file.getAbsolutePath());
// I can't add the next thing it as it would be an infinite loop
//entry.put("filesystem", null);
return entry;
}
/**
* Returns a JSON Object representing a directory on the device's file system
*
* @param path to the directory
* @return
* @throws JSONException
*/
private JSONObject getEntry(String path) throws JSONException {
return getEntry(new File(path));
}
/**
* Identifies if action to be executed returns a value and should be run synchronously.
*
* @param action The action to execute
* @return T=returns value
*/
public boolean isSynch(String action) {
if (action.equals("testSaveLocationExists")) {
return true;
}
else if (action.equals("getFreeDiskSpace")) {
return true;
}
else if (action.equals("testFileExists")) {
return true;
}
else if (action.equals("testDirectoryExists")) {
return true;
}
return false;
}
//--------------------------------------------------------------------------
// LOCAL METHODS
//--------------------------------------------------------------------------
/**
* Read content of text file.
*
* @param filename The name of the file.
* @param encoding The encoding to return contents as. Typical value is UTF-8.
* (see http://www.iana.org/assignments/character-sets)
* @return Contents of file.
* @throws FileNotFoundException, IOException
*/
public String readAsText(String filename, String encoding) throws FileNotFoundException, IOException {
byte[] bytes = new byte[1000];
BufferedInputStream bis = new BufferedInputStream(getPathFromUri(filename), 1024);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int numRead = 0;
while ((numRead = bis.read(bytes, 0, 1000)) >= 0) {
bos.write(bytes, 0, numRead);
}
return new String(bos.toByteArray(), encoding);
}
/**
* Read content of text file and return as base64 encoded data url.
*
* @param filename The name of the file.
* @return Contents of file = data:<media type>;base64,<data>
* @throws FileNotFoundException, IOException
*/
public String readAsDataURL(String filename) throws FileNotFoundException, IOException {
byte[] bytes = new byte[1000];
BufferedInputStream bis = new BufferedInputStream(getPathFromUri(filename), 1024);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int numRead = 0;
while ((numRead = bis.read(bytes, 0, 1000)) >= 0) {
bos.write(bytes, 0, numRead);
}
// Determine content type from file name
String contentType = null;
if (filename.startsWith("content:")) {
Uri fileUri = Uri.parse(filename);
contentType = this.ctx.getContentResolver().getType(fileUri);
}
else {
contentType = getMimeType(filename);
}
byte[] base64 = Base64.encodeBase64(bos.toByteArray());
String data = "data:" + contentType + ";base64," + new String(base64);
return data;
}
/**
* Looks up the mime type of a given file name.
*
* @param filename
* @return a mime type
*/
public static String getMimeType(String filename) {
MimeTypeMap map = MimeTypeMap.getSingleton();
return map.getMimeTypeFromExtension(map.getFileExtensionFromUrl(filename));
}
/**
* Write contents of file.
*
* @param filename The name of the file.
* @param data The contents of the file.
* @param offset The position to begin writing the file.
* @throws FileNotFoundException, IOException
*/
/**/
public long write(String filename, String data, int offset) throws FileNotFoundException, IOException {
boolean append = false;
if (offset > 0) {
this.truncateFile(filename, offset);
append = true;
}
byte [] rawData = data.getBytes();
ByteArrayInputStream in = new ByteArrayInputStream(rawData);
FileOutputStream out = new FileOutputStream(filename, append);
byte buff[] = new byte[rawData.length];
in.read(buff, 0, buff.length);
out.write(buff, 0, rawData.length);
out.flush();
out.close();
return data.length();
}
/**
* Truncate the file to size
*
* @param filename
* @param size
* @throws FileNotFoundException, IOException
*/
private long truncateFile(String filename, long size) throws FileNotFoundException, IOException {
RandomAccessFile raf = new RandomAccessFile(filename, "rw");
if (raf.length() >= size) {
FileChannel channel = raf.getChannel();
channel.truncate(size);
return size;
}
return raf.length();
}
/**
* Get an input stream based on file path or content:// uri
*
* @param path
* @return an input stream
* @throws FileNotFoundException
*/
private InputStream getPathFromUri(String path) throws FileNotFoundException {
if (path.startsWith("content")) {
Uri uri = Uri.parse(path);
return ctx.getContentResolver().openInputStream(uri);
}
else {
return new FileInputStream(path);
}
}
}