From 7c8db0ea4483f4cbf0de4ee591b41f14040ec822 Mon Sep 17 00:00:00 2001 From: William Shen Date: Wed, 23 Nov 2011 10:39:20 -0800 Subject: [PATCH 01/18] fixing redundant assignment of variable --- framework/src/com/phonegap/ContactAccessorSdk5.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/framework/src/com/phonegap/ContactAccessorSdk5.java b/framework/src/com/phonegap/ContactAccessorSdk5.java index 4e1ca9f6..4f625062 100644 --- a/framework/src/com/phonegap/ContactAccessorSdk5.java +++ b/framework/src/com/phonegap/ContactAccessorSdk5.java @@ -1603,10 +1603,8 @@ public class ContactAccessorSdk5 extends ContactAccessor { } } catch (RemoteException e) { Log.e(LOG_TAG, e.getMessage(), e); - newId = null; } catch (OperationApplicationException e) { Log.e(LOG_TAG, e.getMessage(), e); - newId = null; } return newId; } From cd5bf6195e3a78ac000f089664481f56a8956e07 Mon Sep 17 00:00:00 2001 From: William Shen Date: Wed, 23 Nov 2011 10:52:43 -0800 Subject: [PATCH 02/18] cleaning up handling of exceptions. removing unnecessary exception declaration in FileUtils. removing try-catch that should not have been done in HttpHandler --- framework/src/com/phonegap/FileUtils.java | 2 +- framework/src/com/phonegap/HttpHandler.java | 31 ++++++++++----------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/framework/src/com/phonegap/FileUtils.java b/framework/src/com/phonegap/FileUtils.java index 00aa9ce3..5f948566 100755 --- a/framework/src/com/phonegap/FileUtils.java +++ b/framework/src/com/phonegap/FileUtils.java @@ -531,7 +531,7 @@ public class FileUtils extends Plugin { * @throws NoModificationAllowedException * @throws InvalidModificationException */ - private JSONObject moveDirectory(File srcDir, File destinationDir) throws JSONException, FileExistsException, NoModificationAllowedException, InvalidModificationException { + private JSONObject moveDirectory(File srcDir, File destinationDir) throws JSONException, 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"); diff --git a/framework/src/com/phonegap/HttpHandler.java b/framework/src/com/phonegap/HttpHandler.java index 156d5a2c..78b8df50 100755 --- a/framework/src/com/phonegap/HttpHandler.java +++ b/framework/src/com/phonegap/HttpHandler.java @@ -20,6 +20,7 @@ package com.phonegap; import java.io.EOFException; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import org.apache.http.HttpEntity; @@ -56,27 +57,25 @@ public class HttpHandler { return entity; } - private void writeToDisk(HttpEntity entity, String file) throws EOFException + private void writeToDisk(HttpEntity entity, String file) throws IllegalStateException, IOException /** * writes a HTTP entity to the specified filename and location on disk */ { int i=0; String FilePath="/sdcard/" + file; - try { - InputStream in = entity.getContent(); - byte buff[] = new byte[1024]; - FileOutputStream out= - new FileOutputStream(FilePath); - do { - int numread = in.read(buff); - if (numread <= 0) - break; - out.write(buff, 0, numread); - i++; - } while (true); - out.flush(); - out.close(); - } catch (Exception e) { e.printStackTrace(); } + InputStream in = entity.getContent(); + byte buff[] = new byte[1024]; + FileOutputStream out= + new FileOutputStream(FilePath); + do { + int numread = in.read(buff); + if (numread <= 0) + break; + out.write(buff, 0, numread); + i++; + } while (true); + out.flush(); + out.close(); } } From 7a6a59383bf2e6e67a6b49cd89ac0aa5f243e868 Mon Sep 17 00:00:00 2001 From: William Shen Date: Wed, 23 Nov 2011 11:03:14 -0800 Subject: [PATCH 03/18] removed a couple extra semicolons --- framework/src/com/phonegap/ContactAccessorSdk5.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/src/com/phonegap/ContactAccessorSdk5.java b/framework/src/com/phonegap/ContactAccessorSdk5.java index 4f625062..9e9cee20 100644 --- a/framework/src/com/phonegap/ContactAccessorSdk5.java +++ b/framework/src/com/phonegap/ContactAccessorSdk5.java @@ -1054,7 +1054,7 @@ public class ContactAccessorSdk5 extends ContactAccessor { if (organizations != null) { for (int i=0; i Date: Wed, 23 Nov 2011 11:25:53 -0800 Subject: [PATCH 04/18] removed unused import --- framework/src/com/phonegap/HttpHandler.java | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/src/com/phonegap/HttpHandler.java b/framework/src/com/phonegap/HttpHandler.java index 78b8df50..3893d2a9 100755 --- a/framework/src/com/phonegap/HttpHandler.java +++ b/framework/src/com/phonegap/HttpHandler.java @@ -18,7 +18,6 @@ */ package com.phonegap; -import java.io.EOFException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; From 8904c67fb5e4f7ab17f0db1325c645c974ee7bb3 Mon Sep 17 00:00:00 2001 From: Alexander Keller Date: Sat, 26 Nov 2011 18:16:44 +0100 Subject: [PATCH 05/18] added download() to the filetransfer --- framework/assets/js/filetransfer.js | 11 + framework/src/com/phonegap/FileTransfer.java | 559 ++++++++++--------- 2 files changed, 313 insertions(+), 257 deletions(-) diff --git a/framework/assets/js/filetransfer.js b/framework/assets/js/filetransfer.js index 690cfcd1..4c423064 100644 --- a/framework/assets/js/filetransfer.js +++ b/framework/assets/js/filetransfer.js @@ -83,6 +83,17 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro PhoneGap.exec(successCallback, errorCallback, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, debug, chunkedMode]); }; +/** + * Downloads a file form a given URL and saves it to the specified directory. + * @param sourceUrl {String} URL of the server to receive the file + * @param targetFile {String} Full path of the file on the device + * @param successCallback (Function} Callback to be invoked when upload has completed + * @param errorCallback {Function} Callback to be invoked upon error + */ +FileTransfer.prototype.download = function(sourceUrl, targetFile, successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, 'FileTransfer', 'download', [targetFile, sourceUrl]); +}; + /** * Options to customize the HTTP request used to upload files. * @constructor diff --git a/framework/src/com/phonegap/FileTransfer.java b/framework/src/com/phonegap/FileTransfer.java index 8d014a22..fabe14dd 100644 --- a/framework/src/com/phonegap/FileTransfer.java +++ b/framework/src/com/phonegap/FileTransfer.java @@ -20,8 +20,10 @@ package com.phonegap; import java.io.DataInputStream; import java.io.DataOutputStream; +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.net.HttpURLConnection; @@ -52,98 +54,100 @@ import com.phonegap.api.PluginResult; public class FileTransfer extends Plugin { - private static final String LOG_TAG = "FileUploader"; - private static final String LINE_START = "--"; - private static final String LINE_END = "\r\n"; - private static final String BOUNDRY = "*****"; + private static final String LOG_TAG = "FileUploader"; + private static final String LINE_START = "--"; + private static final String LINE_END = "\r\n"; + private static final String BOUNDRY = "*****"; - public static int FILE_NOT_FOUND_ERR = 1; + public static int FILE_NOT_FOUND_ERR = 1; public static int INVALID_URL_ERR = 2; public static int CONNECTION_ERR = 3; - + private SSLSocketFactory defaultSSLSocketFactory = null; private HostnameVerifier defaultHostnameVerifier = null; - /* (non-Javadoc) - * @see com.phonegap.api.Plugin#execute(java.lang.String, org.json.JSONArray, java.lang.String) - */ - @Override - public PluginResult execute(String action, JSONArray args, String callbackId) { - String file = null; - String server = null; - try { - file = args.getString(0); - server = args.getString(1); - } - catch (JSONException e) { - Log.d(LOG_TAG, "Missing filename or server name"); - return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing filename or server name"); - } - - // Setup the options - String fileKey = null; - String fileName = null; - String mimeType = null; - - fileKey = getArgument(args, 2, "file"); - fileName = getArgument(args, 3, "image.jpg"); - mimeType = getArgument(args, 4, "image/jpeg"); + /* (non-Javadoc) + * @see com.phonegap.api.Plugin#execute(java.lang.String, org.json.JSONArray, java.lang.String) + */ + @Override + public PluginResult execute(String action, JSONArray args, String callbackId) { + String file = null; + String server = null; + try { + file = args.getString(0); + server = args.getString(1); + } + catch (JSONException e) { + Log.d(LOG_TAG, "Missing filename or server name"); + return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing filename or server name"); + } - try { - JSONObject params = args.optJSONObject(5); - boolean trustEveryone = args.optBoolean(6); - boolean chunkedMode = args.getBoolean(7); + try { + if (action.equals("upload")) { + // Setup the options + String fileKey = null; + String fileName = null; + String mimeType = null; - if (action.equals("upload")) { - FileUploadResult r = upload(file, server, fileKey, fileName, mimeType, params, trustEveryone, chunkedMode); - Log.d(LOG_TAG, "****** About to return a result from upload"); - return new PluginResult(PluginResult.Status.OK, r.toJSONObject()); - } else { - return new PluginResult(PluginResult.Status.INVALID_ACTION); - } - } catch (FileNotFoundException e) { - Log.e(LOG_TAG, e.getMessage(), e); - JSONObject error = createFileUploadError(FILE_NOT_FOUND_ERR); + fileKey = getArgument(args, 2, "file"); + fileName = getArgument(args, 3, "image.jpg"); + mimeType = getArgument(args, 4, "image/jpeg"); + JSONObject params = args.optJSONObject(5); + boolean trustEveryone = args.optBoolean(6); + boolean chunkedMode = args.getBoolean(7); + FileUploadResult r = upload(file, server, fileKey, fileName, mimeType, params, trustEveryone, chunkedMode); + Log.d(LOG_TAG, "****** About to return a result from upload"); + return new PluginResult(PluginResult.Status.OK, r.toJSONObject()); + } else if (action.equals("download")) { + String r = download(file, server); + Log.d(LOG_TAG, "****** About to return a result from download"); + return new PluginResult(PluginResult.Status.OK, r); + } else { + return new PluginResult(PluginResult.Status.INVALID_ACTION); + } + } catch (FileNotFoundException e) { + Log.e(LOG_TAG, e.getMessage(), e); + JSONObject error = createFileUploadError(FILE_NOT_FOUND_ERR); return new PluginResult(PluginResult.Status.IO_EXCEPTION, error); } catch (IllegalArgumentException e) { - Log.e(LOG_TAG, e.getMessage(), e); - JSONObject error = createFileUploadError(INVALID_URL_ERR); + Log.e(LOG_TAG, e.getMessage(), e); + JSONObject error = createFileUploadError(INVALID_URL_ERR); return new PluginResult(PluginResult.Status.IO_EXCEPTION, error); - } catch (SSLException e) { - Log.e(LOG_TAG, e.getMessage(), e); - Log.d(LOG_TAG, "Got my ssl exception!!!"); - JSONObject error = createFileUploadError(CONNECTION_ERR); + } catch (SSLException e) { + Log.e(LOG_TAG, e.getMessage(), e); + Log.d(LOG_TAG, "Got my ssl exception!!!"); + JSONObject error = createFileUploadError(CONNECTION_ERR); return new PluginResult(PluginResult.Status.IO_EXCEPTION, error); } catch (IOException e) { - Log.e(LOG_TAG, e.getMessage(), e); - JSONObject error = createFileUploadError(CONNECTION_ERR); + Log.e(LOG_TAG, e.getMessage(), e); + JSONObject error = createFileUploadError(CONNECTION_ERR); return new PluginResult(PluginResult.Status.IO_EXCEPTION, error); - } catch (JSONException e) { - Log.e(LOG_TAG, e.getMessage(), e); - return new PluginResult(PluginResult.Status.JSON_EXCEPTION); - } - } - - // always verify the host - don't check for certificate - final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() { - public boolean verify(String hostname, SSLSession session) { - return true; + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + return new PluginResult(PluginResult.Status.JSON_EXCEPTION); } - }; + } - /** - * This function will install a trust manager that will blindly trust all SSL - * certificates. The reason this code is being added is to enable developers - * to do development using self signed SSL certificates on their web server. - * - * The standard HttpsURLConnection class will throw an exception on self - * signed certificates if this code is not run. - */ - private void trustAllHosts() { + // always verify the host - don't check for certificate + final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() { + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + + /** + * This function will install a trust manager that will blindly trust all SSL + * certificates. The reason this code is being added is to enable developers + * to do development using self signed SSL certificates on their web server. + * + * The standard HttpsURLConnection class will throw an exception on self + * signed certificates if this code is not run. + */ + private void trustAllHosts() { // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return new java.security.cert.X509Certificate[] {}; + return new java.security.cert.X509Certificate[] {}; } public void checkClientTrusted(X509Certificate[] chain, @@ -157,54 +161,54 @@ public class FileTransfer extends Plugin { // Install the all-trusting trust manager try { - // Backup the current SSL socket factory - defaultSSLSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory(); - // Install our all trusting manager - SSLContext sc = SSLContext.getInstance("TLS"); + // Backup the current SSL socket factory + defaultSSLSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory(); + // Install our all trusting manager + SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } catch (Exception e) { Log.e(LOG_TAG, e.getMessage(), e); } - } + } - /** - * Create an error object based on the passed in errorCode - * @param errorCode the error - * @return JSONObject containing the error - */ - private JSONObject createFileUploadError(int errorCode) { - JSONObject error = null; - try { - error = new JSONObject(); - error.put("code", errorCode); - } catch (JSONException e) { - Log.e(LOG_TAG, e.getMessage(), e); - } - return error; - } - - /** - * Convenience method to read a parameter from the list of JSON args. - * @param args the args passed to the Plugin - * @param position the position to retrieve the arg from - * @param defaultString the default to be used if the arg does not exist - * @return String with the retrieved value - */ - private String getArgument(JSONArray args, int position, String defaultString) { - String arg = defaultString; - if(args.length() >= position) { - arg = args.optString(position); - if (arg == null || "null".equals(arg)) { - arg = defaultString; - } - } - return arg; - } - /** - * Uploads the specified file to the server URL provided using an HTTP - * multipart request. + * Create an error object based on the passed in errorCode + * @param errorCode the error + * @return JSONObject containing the error + */ + private JSONObject createFileUploadError(int errorCode) { + JSONObject error = null; + try { + error = new JSONObject(); + error.put("code", errorCode); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return error; + } + + /** + * Convenience method to read a parameter from the list of JSON args. + * @param args the args passed to the Plugin + * @param position the position to retrieve the arg from + * @param defaultString the default to be used if the arg does not exist + * @return String with the retrieved value + */ + private String getArgument(JSONArray args, int position, String defaultString) { + String arg = defaultString; + if(args.length() >= position) { + arg = args.optString(position); + if (arg == null || "null".equals(arg)) { + arg = defaultString; + } + } + return arg; + } + + /** + * Uploads the specified file to the server URL provided using an HTTP + * multipart request. * @param file Full path of the file on the file system * @param server URL of the server to receive the file * @param fileKey Name of file request parameter @@ -213,173 +217,214 @@ public class FileTransfer extends Plugin { * @param params key:value pairs of user-defined parameters * @return FileUploadResult containing result of upload request */ - public FileUploadResult upload(String file, String server, final String fileKey, final String fileName, - final String mimeType, JSONObject params, boolean trustEveryone, boolean chunkedMode) throws IOException, SSLException { - // Create return object - FileUploadResult result = new FileUploadResult(); - - // Get a input stream of the file on the phone - InputStream fileInputStream = getPathFromUri(file); + public FileUploadResult upload(String file, String server, final String fileKey, final String fileName, + final String mimeType, JSONObject params, boolean trustEveryone, boolean chunkedMode) throws IOException, SSLException { + // Create return object + FileUploadResult result = new FileUploadResult(); - HttpURLConnection conn = null; - DataOutputStream dos = null; + // Get a input stream of the file on the phone + InputStream fileInputStream = getPathFromUri(file); - int bytesRead, bytesAvailable, bufferSize; - long totalBytes; - byte[] buffer; - int maxBufferSize = 8096; + HttpURLConnection conn = null; + DataOutputStream dos = null; - //------------------ CLIENT REQUEST - // open a URL connection to the server - URL url = new URL(server); - - // Open a HTTP connection to the URL based on protocol + int bytesRead, bytesAvailable, bufferSize; + long totalBytes; + byte[] buffer; + int maxBufferSize = 8096; + + //------------------ CLIENT REQUEST + // open a URL connection to the server + URL url = new URL(server); + + // Open a HTTP connection to the URL based on protocol if (url.getProtocol().toLowerCase().equals("https")) { - // Using standard HTTPS connection. Will not allow self signed certificate - if (!trustEveryone) { - conn = (HttpsURLConnection) url.openConnection(); - } - // Use our HTTPS connection that blindly trusts everyone. - // This should only be used in debug environments - else { - // Setup the HTTPS connection class to trust everyone - trustAllHosts(); - HttpsURLConnection https = (HttpsURLConnection) url.openConnection(); - // Save the current hostnameVerifier - defaultHostnameVerifier = https.getHostnameVerifier(); - // Setup the connection not to verify hostnames - https.setHostnameVerifier(DO_NOT_VERIFY); - conn = https; - } - } + // Using standard HTTPS connection. Will not allow self signed certificate + if (!trustEveryone) { + conn = (HttpsURLConnection) url.openConnection(); + } + // Use our HTTPS connection that blindly trusts everyone. + // This should only be used in debug environments + else { + // Setup the HTTPS connection class to trust everyone + trustAllHosts(); + HttpsURLConnection https = (HttpsURLConnection) url.openConnection(); + // Save the current hostnameVerifier + defaultHostnameVerifier = https.getHostnameVerifier(); + // Setup the connection not to verify hostnames + https.setHostnameVerifier(DO_NOT_VERIFY); + conn = https; + } + } // Return a standard HTTP connection else { - conn = (HttpURLConnection) url.openConnection(); + conn = (HttpURLConnection) url.openConnection(); } - - // Allow Inputs - conn.setDoInput(true); - - // Allow Outputs - conn.setDoOutput(true); - - // Don't use a cached copy. - conn.setUseCaches(false); - - // Use a post method. - conn.setRequestMethod("POST"); - conn.setRequestProperty("Connection", "Keep-Alive"); - conn.setRequestProperty("Content-Type", "multipart/form-data;boundary="+BOUNDRY); - // Set the cookies on the response - String cookie = CookieManager.getInstance().getCookie(server); - if (cookie != null) { - conn.setRequestProperty("Cookie", cookie); - } - - // Should set this up as an option - if (chunkedMode) { - conn.setChunkedStreamingMode(maxBufferSize); - } - - dos = new DataOutputStream( conn.getOutputStream() ); + // Allow Inputs + conn.setDoInput(true); - // Send any extra parameters - try { - for (Iterator iter = params.keys(); iter.hasNext();) { - Object key = iter.next(); - dos.writeBytes(LINE_START + BOUNDRY + LINE_END); - dos.writeBytes("Content-Disposition: form-data; name=\"" + key.toString() + "\";"); - dos.writeBytes(LINE_END + LINE_END); - dos.write(params.getString(key.toString()).getBytes()); - dos.writeBytes(LINE_END); - } - } catch (JSONException e) { - Log.e(LOG_TAG, e.getMessage(), e); - } - - dos.writeBytes(LINE_START + BOUNDRY + LINE_END); - dos.writeBytes("Content-Disposition: form-data; name=\"" + fileKey + "\";" + " filename=\"" + fileName +"\"" + LINE_END); - dos.writeBytes("Content-Type: " + mimeType + LINE_END); - dos.writeBytes(LINE_END); - - // create a buffer of maximum size - bytesAvailable = fileInputStream.available(); - bufferSize = Math.min(bytesAvailable, maxBufferSize); - buffer = new byte[bufferSize]; - - // read file and write it into form... - bytesRead = fileInputStream.read(buffer, 0, bufferSize); - totalBytes = 0; + // Allow Outputs + conn.setDoOutput(true); - while (bytesRead > 0) { - totalBytes += bytesRead; - result.setBytesSent(totalBytes); - dos.write(buffer, 0, bufferSize); - bytesAvailable = fileInputStream.available(); - bufferSize = Math.min(bytesAvailable, maxBufferSize); - bytesRead = fileInputStream.read(buffer, 0, bufferSize); - } + // Don't use a cached copy. + conn.setUseCaches(false); - // send multipart form data necesssary after file data... - dos.writeBytes(LINE_END); - dos.writeBytes(LINE_START + BOUNDRY + LINE_START + LINE_END); - - // close streams - fileInputStream.close(); - dos.flush(); - dos.close(); + // Use a post method. + conn.setRequestMethod("POST"); + conn.setRequestProperty("Connection", "Keep-Alive"); + conn.setRequestProperty("Content-Type", "multipart/form-data;boundary="+BOUNDRY); - //------------------ read the SERVER RESPONSE - StringBuffer responseString = new StringBuffer(""); - DataInputStream inStream; - try { - inStream = new DataInputStream ( conn.getInputStream() ); - } catch(FileNotFoundException e) { - throw new IOException("Received error from server"); - } - - String line; - while (( line = inStream.readLine()) != null) { - responseString.append(line); - } - Log.d(LOG_TAG, "got response from server"); - Log.d(LOG_TAG, responseString.toString()); + // Set the cookies on the response + String cookie = CookieManager.getInstance().getCookie(server); + if (cookie != null) { + conn.setRequestProperty("Cookie", cookie); + } - // send request and retrieve response + // Should set this up as an option + if (chunkedMode) { + conn.setChunkedStreamingMode(maxBufferSize); + } + + dos = new DataOutputStream( conn.getOutputStream() ); + + // Send any extra parameters + try { + for (Iterator iter = params.keys(); iter.hasNext();) { + Object key = iter.next(); + dos.writeBytes(LINE_START + BOUNDRY + LINE_END); + dos.writeBytes("Content-Disposition: form-data; name=\"" + key.toString() + "\";"); + dos.writeBytes(LINE_END + LINE_END); + dos.write(params.getString(key.toString()).getBytes()); + dos.writeBytes(LINE_END); + } + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + + dos.writeBytes(LINE_START + BOUNDRY + LINE_END); + dos.writeBytes("Content-Disposition: form-data; name=\"" + fileKey + "\";" + " filename=\"" + fileName +"\"" + LINE_END); + dos.writeBytes("Content-Type: " + mimeType + LINE_END); + dos.writeBytes(LINE_END); + + // create a buffer of maximum size + bytesAvailable = fileInputStream.available(); + bufferSize = Math.min(bytesAvailable, maxBufferSize); + buffer = new byte[bufferSize]; + + // read file and write it into form... + bytesRead = fileInputStream.read(buffer, 0, bufferSize); + totalBytes = 0; + + while (bytesRead > 0) { + totalBytes += bytesRead; + result.setBytesSent(totalBytes); + dos.write(buffer, 0, bufferSize); + bytesAvailable = fileInputStream.available(); + bufferSize = Math.min(bytesAvailable, maxBufferSize); + bytesRead = fileInputStream.read(buffer, 0, bufferSize); + } + + // send multipart form data necesssary after file data... + dos.writeBytes(LINE_END); + dos.writeBytes(LINE_START + BOUNDRY + LINE_START + LINE_END); + + // close streams + fileInputStream.close(); + dos.flush(); + dos.close(); + + //------------------ read the SERVER RESPONSE + StringBuffer responseString = new StringBuffer(""); + DataInputStream inStream; + try { + inStream = new DataInputStream ( conn.getInputStream() ); + } catch(FileNotFoundException e) { + throw new IOException("Received error from server"); + } + + String line; + while (( line = inStream.readLine()) != null) { + responseString.append(line); + } + Log.d(LOG_TAG, "got response from server"); + Log.d(LOG_TAG, responseString.toString()); + + // send request and retrieve response result.setResponseCode(conn.getResponseCode()); result.setResponse(responseString.toString()); inStream.close(); conn.disconnect(); - + // Revert back to the proper verifier and socket factories if (trustEveryone && url.getProtocol().toLowerCase().equals("https")) { - ((HttpsURLConnection)conn).setHostnameVerifier(defaultHostnameVerifier); - HttpsURLConnection.setDefaultSSLSocketFactory(defaultSSLSocketFactory); - } - - return result; - } + ((HttpsURLConnection)conn).setHostnameVerifier(defaultHostnameVerifier); + HttpsURLConnection.setDefaultSSLSocketFactory(defaultSSLSocketFactory); + } - /** + return result; + } + + /** + * Downloads a file form a given URL and saves it to the specified directory. + * + * @param server URL of the server to receive the file + * @param file Full path of the file on the file system + * @return String containing the path to the downloaded file + */ + public String download(String filePath, String sourceUrl) throws IOException { + try { + File file = new File(filePath); + file.getParentFile().mkdirs(); + + URL url = new URL(sourceUrl); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setDoOutput(true); + connection.connect(); + + Log.d(LOG_TAG, "Download file:" + url); + + InputStream inputStream = connection.getInputStream(); + byte[] buffer = new byte[1024]; + int len1 = 0; + + FileOutputStream outputStream = new FileOutputStream(file); + + while ( (len1 = inputStream.read(buffer)) > 0 ) { + outputStream.write(buffer,0, len1); + } + + outputStream.close(); + + Log.d(LOG_TAG, "Saved file: " + filePath); + } catch (IOException e) { + Log.d(LOG_TAG, e.getMessage(), e); + throw new IOException("Error while downloading"); + } + + return filePath; + } + + /** * Get an input stream based on file path or content:// uri - * + * * @param path * @return an input stream - * @throws FileNotFoundException + * @throws FileNotFoundException */ - private InputStream getPathFromUri(String path) throws FileNotFoundException { - if (path.startsWith("content:")) { - Uri uri = Uri.parse(path); - return ctx.getContentResolver().openInputStream(uri); - } - else if (path.startsWith("file://")) { - return new FileInputStream(path.substring(7)); - } - else { - return new FileInputStream(path); - } - } + private InputStream getPathFromUri(String path) throws FileNotFoundException { + if (path.startsWith("content:")) { + Uri uri = Uri.parse(path); + return ctx.getContentResolver().openInputStream(uri); + } + else if (path.startsWith("file://")) { + return new FileInputStream(path.substring(7)); + } + else { + return new FileInputStream(path); + } + } } From 188e3e7d7d428b942d93853bff4a50e9227967df Mon Sep 17 00:00:00 2001 From: macdonst Date: Mon, 28 Nov 2011 15:44:28 -0500 Subject: [PATCH 06/18] Remove WebViewReflect.java from Android While looking at issue #34 I realized that we don't need the WebViewReflect class anymore. Since we only support 2.1 or better and all the methods that WebViewReflect was protecting us from are available in the API version. --- framework/src/com/phonegap/DroidGap.java | 11 +- .../src/com/phonegap/WebViewReflect.java | 140 ------------------ 2 files changed, 2 insertions(+), 149 deletions(-) delete mode 100644 framework/src/com/phonegap/WebViewReflect.java diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 6a0a0613..26a04f56 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -19,7 +19,6 @@ package com.phonegap; import java.util.HashMap; -import java.util.Map.Entry; import java.util.ArrayList; import java.util.Stack; import java.util.regex.Pattern; @@ -30,7 +29,6 @@ import java.io.IOException; import org.json.JSONArray; import org.json.JSONException; -import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; @@ -43,12 +41,10 @@ import android.content.res.Configuration; import android.content.res.XmlResourceParser; import android.graphics.Bitmap; import android.graphics.Color; -import android.graphics.Rect; import android.media.AudioManager; import android.net.Uri; import android.net.http.SslError; import android.os.Bundle; -import android.util.Log; import android.view.Display; import android.view.KeyEvent; import android.view.Menu; @@ -275,8 +271,6 @@ public class DroidGap extends PhonegapActivity { ViewGroup.LayoutParams.FILL_PARENT, 1.0F)); - WebViewReflect.checkCompatibility(); - this.appView.setWebChromeClient(new GapClient(DroidGap.this)); this.setWebViewClient(this.appView, new GapViewClient(this)); @@ -299,10 +293,10 @@ public class DroidGap extends PhonegapActivity { settings.setDatabasePath(databasePath); // Enable DOM storage - WebViewReflect.setDomStorage(settings); + settings.setDomStorageEnabled(true); // Enable built-in geolocation - WebViewReflect.setGeolocationEnabled(settings, true); + settings.setGeolocationEnabled(true); // Add web view but make it invisible while loading URL this.appView.setVisibility(View.INVISIBLE); @@ -311,7 +305,6 @@ public class DroidGap extends PhonegapActivity { // Clear cancel flag this.cancelLoadUrl = false; - } /** diff --git a/framework/src/com/phonegap/WebViewReflect.java b/framework/src/com/phonegap/WebViewReflect.java deleted file mode 100644 index 22f57d3f..00000000 --- a/framework/src/com/phonegap/WebViewReflect.java +++ /dev/null @@ -1,140 +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 com.phonegap; - -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import android.webkit.WebSettings; - -public class WebViewReflect { - private static Method mWebSettings_setDatabaseEnabled; - private static Method mWebSettings_setDatabasePath; - private static Method mWebSettings_setDomStorageEnabled; - private static Method mWebSettings_setGeolocationEnabled; - - static - { - checkCompatibility(); - } - - private static void setDatabaseEnabled(boolean e) throws IOException { - try - { - mWebSettings_setDatabaseEnabled.invoke(e); - } - catch (InvocationTargetException ite) { - /* unpack original exception when possible */ - Throwable cause = ite.getCause(); - if (cause instanceof IOException) { - throw (IOException) cause; - } else if (cause instanceof RuntimeException) { - throw (RuntimeException) cause; - } else if (cause instanceof Error) { - throw (Error) cause; - } else { - /* unexpected checked exception; wrap and re-throw */ - throw new RuntimeException(ite); - } - } catch (IllegalAccessException ie) { - System.err.println("unexpected " + ie); - } - } - - - public static void checkCompatibility() { - try { - mWebSettings_setDatabaseEnabled = WebSettings.class.getMethod( - "setDatabaseEnabled", new Class[] { boolean.class } ); - mWebSettings_setDatabasePath = WebSettings.class.getMethod( - "setDatabasePath", new Class[] { String.class }); - mWebSettings_setDomStorageEnabled = WebSettings.class.getMethod( - "setDomStorageEnabled", new Class[] { boolean.class }); - mWebSettings_setGeolocationEnabled = WebSettings.class.getMethod( - "setGeolocationEnabled", new Class[] { boolean.class }); - /* success, this is a newer device */ - } catch (NoSuchMethodException nsme) { - /* failure, must be older device */ - } - } - - public static void setStorage(WebSettings setting, boolean enable, String path) { - if (mWebSettings_setDatabaseEnabled != null) { - /* feature is supported */ - try { - mWebSettings_setDatabaseEnabled.invoke(setting, enable); - mWebSettings_setDatabasePath.invoke(setting, path); - } catch (IllegalArgumentException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalAccessException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InvocationTargetException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } else { - /* feature not supported, do something else */ - } - } - public static void setGeolocationEnabled(WebSettings setting, boolean enable) { - if (mWebSettings_setGeolocationEnabled != null) { - /* feature is supported */ - try { - mWebSettings_setGeolocationEnabled.invoke(setting, enable); - } catch (IllegalArgumentException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalAccessException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InvocationTargetException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } else { - /* feature not supported, do something else */ - System.out.println("Native Geolocation not supported - we're ok"); - } - } - public static void setDomStorage(WebSettings setting) - { - if(mWebSettings_setDomStorageEnabled != null) - { - /* feature is supported */ - try { - mWebSettings_setDomStorageEnabled.invoke(setting, true); - } catch (IllegalArgumentException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalAccessException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InvocationTargetException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } else { - /* feature not supported, do something else */ - } - - } -} From 75963c88d5923529f42af010d76c8c21bc79b863 Mon Sep 17 00:00:00 2001 From: Alexander Keller Date: Tue, 29 Nov 2011 08:39:57 +0100 Subject: [PATCH 07/18] changes after review of macdonst, download returns FileEntry --- framework/src/com/phonegap/FileTransfer.java | 32 ++++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/framework/src/com/phonegap/FileTransfer.java b/framework/src/com/phonegap/FileTransfer.java index fabe14dd..6bd6d4ee 100644 --- a/framework/src/com/phonegap/FileTransfer.java +++ b/framework/src/com/phonegap/FileTransfer.java @@ -99,9 +99,9 @@ public class FileTransfer extends Plugin { Log.d(LOG_TAG, "****** About to return a result from upload"); return new PluginResult(PluginResult.Status.OK, r.toJSONObject()); } else if (action.equals("download")) { - String r = download(file, server); + JSONObject r = download(file, server); Log.d(LOG_TAG, "****** About to return a result from download"); - return new PluginResult(PluginResult.Status.OK, r); + return new PluginResult(PluginResult.Status.OK, r, "window.localFileSystem._castEntry"); } else { return new PluginResult(PluginResult.Status.INVALID_ACTION); } @@ -371,13 +371,16 @@ public class FileTransfer extends Plugin { * * @param server URL of the server to receive the file * @param file Full path of the file on the file system - * @return String containing the path to the downloaded file + * @return JSONObject the downloaded file */ - public String download(String filePath, String sourceUrl) throws IOException { + public JSONObject download(String filePath, String sourceUrl) throws IOException { try { File file = new File(filePath); + + // create needed directories file.getParentFile().mkdirs(); + // connect to server URL url = new URL(sourceUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); @@ -388,23 +391,32 @@ public class FileTransfer extends Plugin { InputStream inputStream = connection.getInputStream(); byte[] buffer = new byte[1024]; - int len1 = 0; + int bytesRead = 0; FileOutputStream outputStream = new FileOutputStream(file); - while ( (len1 = inputStream.read(buffer)) > 0 ) { - outputStream.write(buffer,0, len1); + // write bytes to file + while ( (bytesRead = inputStream.read(buffer)) > 0 ) { + outputStream.write(buffer,0, bytesRead); } outputStream.close(); Log.d(LOG_TAG, "Saved file: " + filePath); - } catch (IOException e) { + + // create FileEntry object + JSONObject entry = new JSONObject(); + + entry.put("isFile", file.isFile()); + entry.put("isDirectory", file.isDirectory()); + entry.put("name", file.getName()); + entry.put("fullPath", file.getAbsolutePath()); + + return entry; + } catch (Exception e) { Log.d(LOG_TAG, e.getMessage(), e); throw new IOException("Error while downloading"); } - - return filePath; } /** From 8dbb8f58b1ad55442faaa2398ac4cb2211734de7 Mon Sep 17 00:00:00 2001 From: Alexander Keller Date: Tue, 29 Nov 2011 09:38:39 +0100 Subject: [PATCH 08/18] made getEntry of FileUtils public in order to avoid duplicate code in FileTransfer --- framework/src/com/phonegap/FileTransfer.java | 9 +- framework/src/com/phonegap/FileUtils.java | 1682 +++++++++--------- 2 files changed, 843 insertions(+), 848 deletions(-) diff --git a/framework/src/com/phonegap/FileTransfer.java b/framework/src/com/phonegap/FileTransfer.java index 6bd6d4ee..d2a056a8 100644 --- a/framework/src/com/phonegap/FileTransfer.java +++ b/framework/src/com/phonegap/FileTransfer.java @@ -405,14 +405,9 @@ public class FileTransfer extends Plugin { Log.d(LOG_TAG, "Saved file: " + filePath); // create FileEntry object - JSONObject entry = new JSONObject(); + FileUtils fileUtil = new FileUtils(); - entry.put("isFile", file.isFile()); - entry.put("isDirectory", file.isDirectory()); - entry.put("name", file.getName()); - entry.put("fullPath", file.getAbsolutePath()); - - return entry; + return fileUtil.getEntry(file); } catch (Exception e) { Log.d(LOG_TAG, e.getMessage(), e); throw new IOException("Error while downloading"); diff --git a/framework/src/com/phonegap/FileUtils.java b/framework/src/com/phonegap/FileUtils.java index 00aa9ce3..0c64971d 100755 --- a/framework/src/com/phonegap/FileUtils.java +++ b/framework/src/com/phonegap/FileUtils.java @@ -50,214 +50,214 @@ import com.phonegap.file.TypeMismatchException; * Only files on the SD card can be accessed. */ public class FileUtils extends Plugin { - private static final String LOG_TAG = "FileUtils"; - private static final String _DATA = "_data"; // The column name where the file path is stored + private static final String LOG_TAG = "FileUtils"; + private static final String _DATA = "_data"; // The column name where the file path is stored - public static int NOT_FOUND_ERR = 1; - public static int SECURITY_ERR = 2; - public static int ABORT_ERR = 3; + 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 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() { - } + public static int TEMPORARY = 0; + public static int PERSISTENT = 1; + public static int RESOURCE = 2; + public static int APPLICATION = 3; - /** - * 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()*1024)) { - 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) { - notifyDelete(args.getString(0)); - 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); - } - } - - /** - * Need to check to see if we need to clean up the content store - * - * @param filePath the path to check - */ - private void notifyDelete(String filePath) { - int result = this.ctx.getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - MediaStore.Images.Media.DATA + " = ?", - new String[] {filePath}); - } + FileReader f_in; + FileWriter f_out; /** - * 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 { + * 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()*1024)) { + 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) { + notifyDelete(args.getString(0)); + 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); + } + } + + /** + * Need to check to see if we need to clean up the content store + * + * @param filePath the path to check + */ + private void notifyDelete(String filePath) { + int result = this.ctx.getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + MediaStore.Images.Media.DATA + " = ?", + new String[] {filePath}); + } + + /** + * 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); @@ -266,15 +266,15 @@ public class FileUtils extends Plugin { 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); - } + // 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()) { @@ -283,614 +283,614 @@ public class FileUtils extends Plugin { if (!fp.canRead()) { throw new IOException(); } - return getEntry(fp); - } + 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); + /** + * 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(); - } + 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 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); - } + // 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 + } - /** - * 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); + return getEntry(destFile); + } - // You can't delete the root directory. - if (atRootDirectory(filePath)) { - return false; - } + /** + * 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"); + } - 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); - } - } + // 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 (!directory.delete()) { - throw new FileExistsException("could not delete: " + directory.getName()); - } else { - return true; - } - } + // 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"); + } + } - /** - * 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(); - } + // 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 + } - /** - * 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 getEntry(destinationDir); + } - // Return the directory - return getEntry(fp); - } + /** + * 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); - /** - * 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; - } + // You can't delete the root directory. + if (atRootDirectory(filePath)) { + return false; + } - /** - * 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()); - } + return removeDirRecursively(fp); + } - /** - * 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; - } + /** + * 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); + } + } - /** - * 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()); + if (!directory.delete()) { + throw new FileExistsException("could not delete: " + directory.getName()); + } else { + return true; + } + } - return metadata; - } + /** + * 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); - /** - * 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()); + // You can't delete the root directory. + if (atRootDirectory(filePath)) { + throw new NoModificationAllowedException("You can't delete the root directory"); + } - return metadata; - } + // 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."); + } - /** - * 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 fp.delete(); + } - return fs; - } + /** + * 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"); + } + } - /** - * 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(); + // 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"); + } - 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); + File fp = createFileObject(dirPath, fileName); - return entry; - } + 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"); + } + } + } - /** - * 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)); - } + // Return the directory + return getEntry(fp); + } - /** - * 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; - } + /** + * 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 + */ + public 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) @@ -898,125 +898,125 @@ public class FileUtils extends Plugin { * @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); - } + 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:;base64, * @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[] 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); + } - byte[] base64 = Base64.encodeBase64(bos.toByteArray()); - String data = "data:" + contentType + ";base64," + new String(base64); - return data; + // 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)); - } - + 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. + * @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(); + 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 + * @throws FileNotFoundException, IOException */ private long truncateFile(String filename, long size) throws FileNotFoundException, IOException { - RandomAccessFile raf = new RandomAccessFile(filename, "rw"); + RandomAccessFile raf = new RandomAccessFile(filename, "rw"); - if (raf.length() >= size) { - FileChannel channel = raf.getChannel(); - channel.truncate(size); - return size; - } - - return raf.length(); + 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 + * @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); - } - } - + 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); + } + } + /** * Queries the media store to find out what the file path is for the Uri we supply - * + * * @param contentUri the Uri of the audio/image/video * @param ctx the current applicaiton context * @return the full path to the file From f387720e54348b71c51010e7588885b963e9544e Mon Sep 17 00:00:00 2001 From: Alexander Keller Date: Wed, 30 Nov 2011 09:15:01 +0100 Subject: [PATCH 09/18] FileTransfer returns JSONObject with code, source and target for upload and download --- framework/assets/js/filetransfer.js | 8 ++-- framework/src/com/phonegap/FileTransfer.java | 42 ++++++++++---------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/framework/assets/js/filetransfer.js b/framework/assets/js/filetransfer.js index 4c423064..e3e0d996 100644 --- a/framework/assets/js/filetransfer.js +++ b/framework/assets/js/filetransfer.js @@ -85,13 +85,13 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro /** * Downloads a file form a given URL and saves it to the specified directory. - * @param sourceUrl {String} URL of the server to receive the file - * @param targetFile {String} Full path of the file on the device + * @param source {String} URL of the server to receive the file + * @param target {String} Full path of the file on the device * @param successCallback (Function} Callback to be invoked when upload has completed * @param errorCallback {Function} Callback to be invoked upon error */ -FileTransfer.prototype.download = function(sourceUrl, targetFile, successCallback, errorCallback) { - PhoneGap.exec(successCallback, errorCallback, 'FileTransfer', 'download', [targetFile, sourceUrl]); +FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, 'FileTransfer', 'download', [source, target]); }; /** diff --git a/framework/src/com/phonegap/FileTransfer.java b/framework/src/com/phonegap/FileTransfer.java index d2a056a8..8e0e1e03 100644 --- a/framework/src/com/phonegap/FileTransfer.java +++ b/framework/src/com/phonegap/FileTransfer.java @@ -71,15 +71,15 @@ public class FileTransfer extends Plugin { */ @Override public PluginResult execute(String action, JSONArray args, String callbackId) { - String file = null; - String server = null; + String source = null; + String target = null; try { - file = args.getString(0); - server = args.getString(1); + source = args.getString(0); + target = args.getString(1); } catch (JSONException e) { - Log.d(LOG_TAG, "Missing filename or server name"); - return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing filename or server name"); + Log.d(LOG_TAG, "Missing source or target"); + return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing source or target"); } try { @@ -94,12 +94,12 @@ public class FileTransfer extends Plugin { mimeType = getArgument(args, 4, "image/jpeg"); JSONObject params = args.optJSONObject(5); boolean trustEveryone = args.optBoolean(6); - boolean chunkedMode = args.getBoolean(7); - FileUploadResult r = upload(file, server, fileKey, fileName, mimeType, params, trustEveryone, chunkedMode); + boolean chunkedMode = args.optBoolean(7); + FileUploadResult r = upload(source, target, fileKey, fileName, mimeType, params, trustEveryone, chunkedMode); Log.d(LOG_TAG, "****** About to return a result from upload"); return new PluginResult(PluginResult.Status.OK, r.toJSONObject()); } else if (action.equals("download")) { - JSONObject r = download(file, server); + JSONObject r = download(source, target); Log.d(LOG_TAG, "****** About to return a result from download"); return new PluginResult(PluginResult.Status.OK, r, "window.localFileSystem._castEntry"); } else { @@ -107,20 +107,20 @@ public class FileTransfer extends Plugin { } } catch (FileNotFoundException e) { Log.e(LOG_TAG, e.getMessage(), e); - JSONObject error = createFileUploadError(FILE_NOT_FOUND_ERR); + JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target); return new PluginResult(PluginResult.Status.IO_EXCEPTION, error); } catch (IllegalArgumentException e) { Log.e(LOG_TAG, e.getMessage(), e); - JSONObject error = createFileUploadError(INVALID_URL_ERR); + JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target); return new PluginResult(PluginResult.Status.IO_EXCEPTION, error); } catch (SSLException e) { Log.e(LOG_TAG, e.getMessage(), e); Log.d(LOG_TAG, "Got my ssl exception!!!"); - JSONObject error = createFileUploadError(CONNECTION_ERR); + JSONObject error = createFileTransferError(CONNECTION_ERR, source, target); return new PluginResult(PluginResult.Status.IO_EXCEPTION, error); } catch (IOException e) { Log.e(LOG_TAG, e.getMessage(), e); - JSONObject error = createFileUploadError(CONNECTION_ERR); + JSONObject error = createFileTransferError(CONNECTION_ERR, source, target); return new PluginResult(PluginResult.Status.IO_EXCEPTION, error); } catch (JSONException e) { Log.e(LOG_TAG, e.getMessage(), e); @@ -177,11 +177,13 @@ public class FileTransfer extends Plugin { * @param errorCode the error * @return JSONObject containing the error */ - private JSONObject createFileUploadError(int errorCode) { + private JSONObject createFileTransferError(int errorCode, String source, String target) { JSONObject error = null; try { error = new JSONObject(); error.put("code", errorCode); + error.put("source", source); + error.put("target", target); } catch (JSONException e) { Log.e(LOG_TAG, e.getMessage(), e); } @@ -369,19 +371,19 @@ public class FileTransfer extends Plugin { /** * Downloads a file form a given URL and saves it to the specified directory. * - * @param server URL of the server to receive the file - * @param file Full path of the file on the file system + * @param source URL of the server to receive the file + * @param target Full path of the file on the file system * @return JSONObject the downloaded file */ - public JSONObject download(String filePath, String sourceUrl) throws IOException { + public JSONObject download(String source, String target) throws IOException { try { - File file = new File(filePath); + File file = new File(target); // create needed directories file.getParentFile().mkdirs(); // connect to server - URL url = new URL(sourceUrl); + URL url = new URL(source); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setDoOutput(true); @@ -402,7 +404,7 @@ public class FileTransfer extends Plugin { outputStream.close(); - Log.d(LOG_TAG, "Saved file: " + filePath); + Log.d(LOG_TAG, "Saved file: " + target); // create FileEntry object FileUtils fileUtil = new FileUtils(); From 729d141c3f19ade38c5ae3618071f2b932c51125 Mon Sep 17 00:00:00 2001 From: wshen Date: Thu, 1 Dec 2011 09:42:23 -0800 Subject: [PATCH 10/18] removed FileExistsException that is not needed --- framework/src/com/phonegap/FileUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/com/phonegap/FileUtils.java b/framework/src/com/phonegap/FileUtils.java index 5f948566..33f847df 100755 --- a/framework/src/com/phonegap/FileUtils.java +++ b/framework/src/com/phonegap/FileUtils.java @@ -329,7 +329,7 @@ public class FileUtils extends Plugin { * @throws EncodingException * @throws JSONException */ - private JSONObject transferTo(String fileName, JSONObject newParent, String newName, boolean move) throws JSONException, FileExistsException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException { + private JSONObject transferTo(String fileName, JSONObject newParent, String newName, boolean move) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException { // Check for invalid file name if (newName != null && newName.contains(":")) { throw new EncodingException("Bad file name"); From 1dfc65facee1fa75601025368e0fc8399bc50aac Mon Sep 17 00:00:00 2001 From: wshen Date: Thu, 1 Dec 2011 10:04:49 -0800 Subject: [PATCH 11/18] removed FileExistsException that is not needed --- framework/src/com/phonegap/FileUtils.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/src/com/phonegap/FileUtils.java b/framework/src/com/phonegap/FileUtils.java index de20405c..9bc0d0a9 100755 --- a/framework/src/com/phonegap/FileUtils.java +++ b/framework/src/com/phonegap/FileUtils.java @@ -322,14 +322,13 @@ public class FileUtils extends Plugin { * @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 { + private JSONObject transferTo(String fileName, JSONObject newParent, String newName, boolean move) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException { // Check for invalid file name if (newName != null && newName.contains(":")) { throw new EncodingException("Bad file name"); From 4b9e50146efec340d1ed0d91d38f8d3fc2139b97 Mon Sep 17 00:00:00 2001 From: macdonst Date: Mon, 5 Dec 2011 11:27:08 -0500 Subject: [PATCH 12/18] Tagging to 1.3.0rc1 --- VERSION | 2 +- .../project/phonegap/templates/project/assets/www/index.html | 2 +- framework/assets/www/index.html | 2 +- framework/src/com/phonegap/Device.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) mode change 100755 => 100644 framework/src/com/phonegap/Device.java diff --git a/VERSION b/VERSION index 26aaba0e..2dd64f41 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.0 +1.3.0rc1 diff --git a/bin/templates/project/phonegap/templates/project/assets/www/index.html b/bin/templates/project/phonegap/templates/project/assets/www/index.html index e9e786d6..6b1f6057 100644 --- a/bin/templates/project/phonegap/templates/project/assets/www/index.html +++ b/bin/templates/project/phonegap/templates/project/assets/www/index.html @@ -5,7 +5,7 @@ PhoneGap - + diff --git a/framework/assets/www/index.html b/framework/assets/www/index.html index d75751c8..827934ea 100644 --- a/framework/assets/www/index.html +++ b/framework/assets/www/index.html @@ -1,7 +1,7 @@ - + diff --git a/framework/src/com/phonegap/Device.java b/framework/src/com/phonegap/Device.java old mode 100755 new mode 100644 index f752a8db..78ce41de --- a/framework/src/com/phonegap/Device.java +++ b/framework/src/com/phonegap/Device.java @@ -37,7 +37,7 @@ import android.telephony.TelephonyManager; public class Device extends Plugin { public static final String TAG = "Device"; - public static String phonegapVersion = "1.2.0"; // PhoneGap version + public static String phonegapVersion = "1.3.0rc1"; // PhoneGap version public static String platform = "Android"; // Device OS public static String uuid; // Device UUID From f95fdb5873166b575e5e797a34660b14eca52f3c Mon Sep 17 00:00:00 2001 From: macdonst Date: Tue, 6 Dec 2011 06:01:00 +0800 Subject: [PATCH 13/18] Fix for CB-104: Capture not returning an error code on cancel --- framework/src/com/phonegap/Capture.java | 28 ++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/framework/src/com/phonegap/Capture.java b/framework/src/com/phonegap/Capture.java index a0ccf234..a9a5cb5f 100644 --- a/framework/src/com/phonegap/Capture.java +++ b/framework/src/com/phonegap/Capture.java @@ -49,6 +49,13 @@ public class Capture extends Plugin { private static final int CAPTURE_IMAGE = 1; // Constant for capture image private static final int CAPTURE_VIDEO = 2; // Constant for capture video private static final String LOG_TAG = "Capture"; + + private static final int CAPTURE_INTERNAL_ERR = 0; + private static final int CAPTURE_APPLICATION_BUSY = 1; + private static final int CAPTURE_INVALID_ARGUMENT = 2; + private static final int CAPTURE_NO_MEDIA_FILES = 3; + private static final int CAPTURE_NOT_SUPPORTED = 20; + private String callbackId; // The ID of the callback to be invoked with our result private long limit; // the number of pics/vids/clips to take private double duration; // optional duration parameter for video recording @@ -260,7 +267,7 @@ public class Capture extends Plugin { uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values); } catch (UnsupportedOperationException ex) { LOG.d(LOG_TAG, "Can't write to internal media storage."); - this.fail("Error capturing image - no media storage found."); + this.fail(createErrorObject(CAPTURE_INTERNAL_ERR, "Error capturing image - no media storage found.")); return; } } @@ -290,7 +297,7 @@ public class Capture extends Plugin { } } catch (IOException e) { e.printStackTrace(); - this.fail("Error capturing image."); + this.fail(createErrorObject(CAPTURE_INTERNAL_ERR, "Error capturing image.")); } } else if (requestCode == CAPTURE_VIDEO) { // Get the uri of the video clip @@ -315,7 +322,7 @@ public class Capture extends Plugin { } // user canceled the action else { - this.fail("Canceled."); + this.fail(createErrorObject(CAPTURE_NO_MEDIA_FILES, "Canceled.")); } } // If something else @@ -326,7 +333,7 @@ public class Capture extends Plugin { } // something bad happened else { - this.fail("Did not complete!"); + this.fail(createErrorObject(CAPTURE_NO_MEDIA_FILES, "Did not complete!")); } } } @@ -369,13 +376,24 @@ public class Capture extends Plugin { return obj; } + + private JSONObject createErrorObject(int code, String message) { + JSONObject obj = new JSONObject(); + try { + obj.put("code", code); + obj.put("message", message); + } catch (JSONException e) { + // This will never happen + } + return obj; + } /** * Send error message to JavaScript. * * @param err */ - public void fail(String err) { + public void fail(JSONObject err) { this.error(new PluginResult(PluginResult.Status.ERROR, err), this.callbackId); } } From a640804897d5dc6b10849f45fce02cfeb31f9fbf Mon Sep 17 00:00:00 2001 From: macdonst Date: Tue, 13 Dec 2011 09:41:54 -0500 Subject: [PATCH 14/18] Tagging to 1.3.0rc2 --- VERSION | 2 +- .../project/phonegap/templates/project/assets/www/index.html | 2 +- framework/assets/www/index.html | 2 +- framework/src/com/phonegap/Device.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index 2dd64f41..3cc9007e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.3.0rc1 +1.3.0rc2 diff --git a/bin/templates/project/phonegap/templates/project/assets/www/index.html b/bin/templates/project/phonegap/templates/project/assets/www/index.html index 6b1f6057..69c10643 100644 --- a/bin/templates/project/phonegap/templates/project/assets/www/index.html +++ b/bin/templates/project/phonegap/templates/project/assets/www/index.html @@ -5,7 +5,7 @@ PhoneGap - + diff --git a/framework/assets/www/index.html b/framework/assets/www/index.html index 827934ea..7e2da415 100644 --- a/framework/assets/www/index.html +++ b/framework/assets/www/index.html @@ -1,7 +1,7 @@ - + diff --git a/framework/src/com/phonegap/Device.java b/framework/src/com/phonegap/Device.java index 78ce41de..bebbafff 100644 --- a/framework/src/com/phonegap/Device.java +++ b/framework/src/com/phonegap/Device.java @@ -37,7 +37,7 @@ import android.telephony.TelephonyManager; public class Device extends Plugin { public static final String TAG = "Device"; - public static String phonegapVersion = "1.3.0rc1"; // PhoneGap version + public static String phonegapVersion = "1.3.0rc2"; // PhoneGap version public static String platform = "Android"; // Device OS public static String uuid; // Device UUID From 1511183dfdaeb9cf90afb4d8f93a7d4a13fb33a8 Mon Sep 17 00:00:00 2001 From: macdonst Date: Fri, 16 Dec 2011 13:29:15 -0500 Subject: [PATCH 15/18] Tagging 1.3.0 --- VERSION | 2 +- .../project/phonegap/templates/project/assets/www/index.html | 2 +- framework/assets/www/index.html | 2 +- framework/src/com/phonegap/Device.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index 3cc9007e..f0bb29e7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.3.0rc2 +1.3.0 diff --git a/bin/templates/project/phonegap/templates/project/assets/www/index.html b/bin/templates/project/phonegap/templates/project/assets/www/index.html index 69c10643..a3bf1635 100644 --- a/bin/templates/project/phonegap/templates/project/assets/www/index.html +++ b/bin/templates/project/phonegap/templates/project/assets/www/index.html @@ -5,7 +5,7 @@ PhoneGap - + diff --git a/framework/assets/www/index.html b/framework/assets/www/index.html index 7e2da415..72af12a0 100644 --- a/framework/assets/www/index.html +++ b/framework/assets/www/index.html @@ -1,7 +1,7 @@ - + diff --git a/framework/src/com/phonegap/Device.java b/framework/src/com/phonegap/Device.java index bebbafff..c3aad552 100644 --- a/framework/src/com/phonegap/Device.java +++ b/framework/src/com/phonegap/Device.java @@ -37,7 +37,7 @@ import android.telephony.TelephonyManager; public class Device extends Plugin { public static final String TAG = "Device"; - public static String phonegapVersion = "1.3.0rc2"; // PhoneGap version + public static String phonegapVersion = "1.3.0"; // PhoneGap version public static String platform = "Android"; // Device OS public static String uuid; // Device UUID From 65e3a8a453540d24ad2bad006339b4eb007c2bda Mon Sep 17 00:00:00 2001 From: Libby Baldwin Date: Mon, 19 Dec 2011 18:30:54 -0800 Subject: [PATCH 16/18] add compass demo for Android --- .../templates/project/assets/www/index.html | 10 +++++++--- .../templates/project/assets/www/main.js | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/bin/templates/project/phonegap/templates/project/assets/www/index.html b/bin/templates/project/phonegap/templates/project/assets/www/index.html index a3bf1635..d89121be 100644 --- a/bin/templates/project/phonegap/templates/project/assets/www/index.html +++ b/bin/templates/project/phonegap/templates/project/assets/www/index.html @@ -4,7 +4,7 @@ PhoneGap - + @@ -29,8 +29,12 @@ Beep Vibrate Get a Picture - Get Phone's Contacts - Check Network + Get Phone's Contacts + Check Network +
+
Compass Heading:
Off
+
+ Toggle Compass diff --git a/bin/templates/project/phonegap/templates/project/assets/www/main.js b/bin/templates/project/phonegap/templates/project/assets/www/main.js index 1001aabc..f6316928 100644 --- a/bin/templates/project/phonegap/templates/project/assets/www/main.js +++ b/bin/templates/project/phonegap/templates/project/assets/www/main.js @@ -119,6 +119,25 @@ function check_network() { confirm('Connection type:\n ' + states[networkState]); } +var watchID = null; + +function updateHeading(h) { + document.getElementById('h').innerHTML = h.magneticHeading; +} + +function toggleCompass() { + if (watchID !== null) { + navigator.compass.clearWatch(watchID); + watchID = null; + updateHeading({ magneticHeading : "Off"}); + } else { + var options = { frequency: 1000 }; + watchID = navigator.compass.watchHeading(updateHeading, function(e) { + alert('Compass Error: ' + e.code); + }, options); + } +} + function init() { // the next line makes it impossible to see Contacts on the HTC Evo since it // doesn't have a scroll button From fae551f0cea22c9843911cdcc2472b4e8135c93d Mon Sep 17 00:00:00 2001 From: macdonst Date: Thu, 22 Dec 2011 09:47:40 +0800 Subject: [PATCH 17/18] Fix NullPointerException in DroidGap.onMeasure() It looks like on some devices the onMeasure() method is called before the callbackServer is instantiated. This causes a NullPointerException which kills the application. --- framework/src/com/phonegap/DroidGap.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 26a04f56..f9065676 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -1695,14 +1695,18 @@ public class DroidGap extends PhonegapActivity { // If the height as gotten bigger then we will assume the soft keyboard has // gone away. else if (height > oldHeight) { - LOG.v(TAG, "Throw hide keyboard event"); - callbackServer.sendJavascript("PhoneGap.fireDocumentEvent('hidekeyboard');"); + if (callbackServer != null) { + LOG.v(TAG, "Throw hide keyboard event"); + callbackServer.sendJavascript("PhoneGap.fireDocumentEvent('hidekeyboard');"); + } } // If the height as gotten smaller then we will assume the soft keyboard has // been displayed. else if (height < oldHeight) { - LOG.v(TAG, "Throw show keyboard event"); - callbackServer.sendJavascript("PhoneGap.fireDocumentEvent('showkeyboard');"); + if (callbackServer != null) { + LOG.v(TAG, "Throw show keyboard event"); + callbackServer.sendJavascript("PhoneGap.fireDocumentEvent('showkeyboard');"); + } } // Update the old height for the next event From c66142d6b8ae9385505a3ea5ccd9896ece0edde1 Mon Sep 17 00:00:00 2001 From: macdonst Date: Wed, 4 Jan 2012 00:47:22 +0800 Subject: [PATCH 18/18] Fixing issue with FileTransfer.upload when the passed in url contains a ? --- framework/src/com/phonegap/FileTransfer.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/framework/src/com/phonegap/FileTransfer.java b/framework/src/com/phonegap/FileTransfer.java index 8e0e1e03..0c3132ff 100644 --- a/framework/src/com/phonegap/FileTransfer.java +++ b/framework/src/com/phonegap/FileTransfer.java @@ -429,7 +429,12 @@ public class FileTransfer extends Plugin { return ctx.getContentResolver().openInputStream(uri); } else if (path.startsWith("file://")) { - return new FileInputStream(path.substring(7)); + int question = path.indexOf("?"); + if (question == -1) { + return new FileInputStream(path.substring(7)); + } else { + return new FileInputStream(path.substring(7, question)); + } } else { return new FileInputStream(path);