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.CordovaInterface;
import org.apache.cordova.CordovaResourceApi;
import org.apache.cordova.CordovaWebView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
public class ContentFilesystem extends Filesystem {
private CordovaInterface cordova;
private CordovaResourceApi resourceApi;
public ContentFilesystem(String name, CordovaInterface cordova, CordovaWebView webView) {
this.name = name;
this.cordova = cordova;
this.resourceApi = new CordovaResourceApi(webView.getContext(), webView.pluginManager);
}
@Override
public JSONObject getEntryForLocalURL(LocalFilesystemURL inputURL) throws IOException {
// Get the cursor to validate that the file exists
Cursor cursor = openCursorForURL(inputURL);
try {
if (cursor == null || !cursor.moveToFirst()) {
throw new FileNotFoundException();
}
} finally {
if (cursor != null)
cursor.close();
}
try {
return makeEntryForPath(inputURL.fullPath, inputURL.filesystemName, false /*fp.isDirectory()*/, inputURL.URL.toString());
} catch (JSONException e) {
throw new IOException();
}
}
@Override
public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL,
String fileName, JSONObject options, boolean directory) throws IOException, TypeMismatchException, JSONException {
if (options != null) {
if (options.optBoolean("create")) {
throw new IOException("Cannot create content url");
}
}
LocalFilesystemURL requestedURL = new LocalFilesystemURL(Uri.withAppendedPath(inputURL.URL, fileName));
File fp = new File(this.filesystemPathForURL(requestedURL));
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 makeEntryForPath(requestedURL.fullPath, requestedURL.filesystemName, directory, Uri.fromFile(fp).toString());
}
@Override
public boolean removeFileAtLocalURL(LocalFilesystemURL inputURL)
throws NoModificationAllowedException {
String filePath = filesystemPathForURL(inputURL);
File file = new File(filePath);
try {
this.cordova.getActivity().getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
MediaStore.Images.Media.DATA + " = ?",
new String[] { filePath });
} 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.
}
return file.delete();
}
@Override
public boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL)
throws NoModificationAllowedException {
throw new NoModificationAllowedException("Cannot remove content url");
}
@Override
public JSONArray readEntriesAtLocalURL(LocalFilesystemURL inputURL)
throws FileNotFoundException {
// TODO Auto-generated method stub
return null;
}
@Override
public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException {
Integer size = null;
Integer lastModified = null;
Cursor cursor = openCursorForURL(inputURL);
try {
if (cursor != null && cursor.moveToFirst()) {
size = resourceSizeForCursor(cursor);
lastModified = lastModifiedDateForCursor(cursor);
} else {
throw new FileNotFoundException();
}
} finally {
if (cursor != null)
cursor.close();
}
JSONObject metadata = new JSONObject();
try {
metadata.put("size", size);
metadata.put("type", resourceApi.getMimeType(inputURL.URL));
metadata.put("name", inputURL.filesystemName);
metadata.put("fullPath", inputURL.fullPath);
metadata.put("lastModifiedDate", lastModified);
} catch (JSONException e) {
return null;
}
return metadata;
}
@Override
public JSONObject copyFileToURL(LocalFilesystemURL destURL, String newName,
Filesystem srcFs, LocalFilesystemURL srcURL, boolean move)
throws IOException, InvalidModificationException, JSONException,
NoModificationAllowedException, FileExistsException {
if (LocalFilesystem.class.isInstance(srcFs)) {
/* Same FS, we can shortcut with CordovaResourceApi operations */
// Figure out where we should be copying to
final LocalFilesystemURL destinationURL = makeDestinationURL(newName, srcURL, destURL);
OutputStream os = resourceApi.openOutputStream(destURL.URL);
CordovaResourceApi.OpenForReadResult ofrr = resourceApi.openForRead(srcURL.URL);
if (move && !srcFs.canRemoveFileAtLocalURL(srcURL)) {
throw new NoModificationAllowedException("Cannot move file at source URL");
}
try {
resourceApi.copyResource(ofrr, os);
} catch (IOException e) {
throw new IOException("Cannot read file at source URL");
}
if (move) {
srcFs.removeFileAtLocalURL(srcURL);
}
return makeEntryForURL(destinationURL, false, destinationURL.URL.toString());
} else {
// Need to copy the hard way
return super.copyFileToURL(destURL, newName, srcFs, srcURL, move);
}
}
@Override
public void readFileAtURL(LocalFilesystemURL inputURL, long start, long end,
ReadFileCallback readFileCallback) throws IOException {
CordovaResourceApi.OpenForReadResult ofrr = resourceApi.openForRead(inputURL.URL);
if (end < 0) {
end = ofrr.length;
}
long numBytesToRead = end - start;
try {
if (start > 0) {
ofrr.inputStream.skip(start);
}
LimitedInputStream inputStream = new LimitedInputStream(ofrr.inputStream, numBytesToRead);
readFileCallback.handleData(inputStream, ofrr.mimeType);
} finally {
ofrr.inputStream.close();
}
}
@Override
public long writeToFileAtURL(LocalFilesystemURL inputURL, String data,
int offset, boolean isBinary) throws NoModificationAllowedException {
throw new NoModificationAllowedException("Couldn't write to file given its content URI");
}
@Override
public long truncateFileAtURL(LocalFilesystemURL inputURL, long size)
throws NoModificationAllowedException {
throw new NoModificationAllowedException("Couldn't truncate file given its content URI");
}
protected Cursor openCursorForURL(LocalFilesystemURL url) {
ContentResolver contentResolver = this.cordova.getActivity().getContentResolver();
Cursor cursor = contentResolver.query(url.URL, null, null, null, null);
return cursor;
}
protected String filesystemPathForCursor(Cursor cursor) {
final String[] LOCAL_FILE_PROJECTION = { MediaStore.Images.Media.DATA };
int columnIndex = cursor.getColumnIndex(LOCAL_FILE_PROJECTION[0]);
if (columnIndex != -1) {
return cursor.getString(columnIndex);
}
return null;
}
protected Integer resourceSizeForCursor(Cursor cursor) {
int columnIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
if (columnIndex != -1) {
String sizeStr = cursor.getString(columnIndex);
if (sizeStr != null) {
return Integer.parseInt(sizeStr,10);
}
}
return null;
}
protected Integer lastModifiedDateForCursor(Cursor cursor) {
final String[] LOCAL_FILE_PROJECTION = { MediaStore.MediaColumns.DATE_MODIFIED };
int columnIndex = cursor.getColumnIndex(LOCAL_FILE_PROJECTION[0]);
if (columnIndex != -1) {
String dateStr = cursor.getString(columnIndex);
if (dateStr != null) {
return Integer.parseInt(dateStr,10);
}
}
return null;
}
@Override
public String filesystemPathForURL(LocalFilesystemURL url) {
Cursor cursor = openCursorForURL(url);
try {
if (cursor != null && cursor.moveToFirst()) {
return filesystemPathForCursor(cursor);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
@Override
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) {
String path = filesystemPathForURL(inputURL);
File file = new File(path);
return file.exists();
}
@Override
OutputStream getOutputStreamForURL(LocalFilesystemURL inputURL)
throws IOException {
OutputStream os = resourceApi.openOutputStream(inputURL.URL);
return os;
}
}