File upload.
Implement java side of file upload. Requires changes to external/
webkit to not break; requires changes to packages/apps/Browser
before it actually is used.
Fix http://b/issue?id=675743
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 9456ae1..d2861cb 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -19,17 +19,21 @@
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.AssetManager;
+import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.ParseException;
+import android.net.Uri;
import android.net.WebAddress;
import android.net.http.SslCertificate;
import android.os.Handler;
import android.os.Message;
+import android.provider.OpenableColumns;
import android.util.Log;
import android.util.TypedValue;
import junit.framework.Assert;
+import java.io.InputStream;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
@@ -463,6 +467,60 @@
}
/**
+ * Called by JNI. Given a URI, find the associated file and return its size
+ * @param uri A String representing the URI of the desired file.
+ * @return int The size of the given file.
+ */
+ private int getFileSize(String uri) {
+ int size = 0;
+ Cursor cursor = mContext.getContentResolver().query(Uri.parse(uri),
+ new String[] { OpenableColumns.SIZE },
+ null,
+ null,
+ null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToNext()) {
+ size = cursor.getInt(0);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ return size;
+ }
+
+ /**
+ * Called by JNI. Given a URI, a buffer, and an offset into the buffer,
+ * copy the resource into buffer.
+ * @param uri A String representing the URI of the desired file.
+ * @param buffer The byte array to copy the data into.
+ * @param offset The offet into buffer to place the data.
+ * @return int The size of the given file, or zero if it fails.
+ */
+ private int getFile(String uri, byte[] buffer, int offset) {
+ int size = 0;
+ try {
+ InputStream stream = mContext.getContentResolver()
+ .openInputStream(Uri.parse(uri));
+ size = stream.available();
+ if (buffer != null && buffer.length - offset >= size) {
+ stream.read(buffer, offset, size);
+ } else {
+ size = 0;
+ }
+ stream.close();
+ } catch (java.io.FileNotFoundException e) {
+ Log.e(LOGTAG, "FileNotFoundException:" + e);
+ size = 0;
+ } catch (java.io.IOException e2) {
+ Log.e(LOGTAG, "IOException: " + e2);
+ size = 0;
+ }
+ return size;
+ }
+
+ /**
* Start loading a resource.
* @param loaderHandle The native ResourceLoader that is the target of the
* data.
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 8d55247..bde18600 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -107,6 +107,7 @@
private static final int GEOLOCATION_PERMISSIONS_HIDE_PROMPT = 131;
private static final int RECEIVED_TOUCH_ICON_URL = 132;
private static final int GET_VISITED_HISTORY = 133;
+ private static final int OPEN_FILE_CHOOSER = 134;
// Message triggered by the client to resume execution
private static final int NOTIFY = 200;
@@ -662,6 +663,12 @@
mWebChromeClient.getVisitedHistory((ValueCallback<String[]>)msg.obj);
}
break;
+
+ case OPEN_FILE_CHOOSER:
+ if (mWebChromeClient != null) {
+ mWebChromeClient.openFileChooser((UploadFile) msg.obj);
+ }
+ break;
}
}
@@ -1348,4 +1355,40 @@
msg.obj = callback;
sendMessage(msg);
}
+
+ private class UploadFile implements ValueCallback<Uri> {
+ private Uri mValue;
+ public void onReceiveValue(Uri value) {
+ mValue = value;
+ synchronized (CallbackProxy.this) {
+ CallbackProxy.this.notify();
+ }
+ }
+ public Uri getResult() {
+ return mValue;
+ }
+ }
+
+ /**
+ * Called by WebViewCore to open a file chooser.
+ */
+ /* package */ Uri openFileChooser() {
+ if (mWebChromeClient == null) {
+ return null;
+ }
+ Message myMessage = obtainMessage(OPEN_FILE_CHOOSER);
+ UploadFile uploadFile = new UploadFile();
+ myMessage.obj = uploadFile;
+ synchronized (this) {
+ sendMessage(myMessage);
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ Log.e(LOGTAG,
+ "Caught exception while waiting for openFileChooser");
+ Log.e(LOGTAG, Log.getStackTraceString(e));
+ }
+ }
+ return uploadFile.getResult();
+ }
}
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 7f5b862..ae4f7c2 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -17,6 +17,7 @@
package android.webkit;
import android.graphics.Bitmap;
+import android.net.Uri;
import android.os.Message;
import android.view.View;
@@ -302,4 +303,13 @@
public void getVisitedHistory(ValueCallback<String[]> callback) {
}
+ /**
+ * Tell the client to open a file chooser.
+ * @param uploadFile A ValueCallback to set the URI of the file to upload.
+ * onReceiveValue must be called to wake up the thread.
+ * @hide
+ */
+ public void openFileChooser(ValueCallback<Uri> uploadFile) {
+ uploadFile.onReceiveValue(null);
+ }
}
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index dbc42ca..3fe6961 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.Intent;
+import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.DrawFilter;
import android.graphics.Paint;
@@ -26,11 +27,13 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.provider.Browser;
+import android.provider.OpenableColumns;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.KeyEvent;
@@ -273,6 +276,39 @@
mCallbackProxy.onJsAlert(url, message);
}
+
+ /**
+ * Called by JNI. Open a file chooser to upload a file.
+ * @return String version of the URI plus the name of the file.
+ * FIXME: Just return the URI here, and in FileSystem::pathGetFileName, call
+ * into Java to get the filename.
+ */
+ private String openFileChooser() {
+ Uri uri = mCallbackProxy.openFileChooser();
+ if (uri == null) return "";
+ // Find out the name, and append it to the URI.
+ // Webkit will treat the name as the filename, and
+ // the URI as the path. The URI will be used
+ // in BrowserFrame to get the actual data.
+ Cursor cursor = mContext.getContentResolver().query(
+ uri,
+ new String[] { OpenableColumns.DISPLAY_NAME },
+ null,
+ null,
+ null);
+ String name = "";
+ if (cursor != null) {
+ try {
+ if (cursor.moveToNext()) {
+ name = cursor.getString(0);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ return uri.toString() + "/" + name;
+ }
+
/**
* Notify the browser that the origin has exceeded it's database quota.
* @param url The URL that caused the overflow.