diff --git a/src/cheogram/java/com/cheogram/android/WebxdcPage.java b/src/cheogram/java/com/cheogram/android/WebxdcPage.java index 747021e038b900ebbb46b8a49a221569e1ac3810..292420782ea092062cfb6bc2ff8c054cd1ac9fa8 100644 --- a/src/cheogram/java/com/cheogram/android/WebxdcPage.java +++ b/src/cheogram/java/com/cheogram/android/WebxdcPage.java @@ -18,13 +18,15 @@ import android.view.LayoutInflater; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; import android.webkit.JavascriptInterface; +import android.webkit.ValueCallback; +import android.webkit.WebChromeClient; import android.webkit.WebResourceRequest; import android.webkit.WebResourceResponse; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; +import android.widget.ArrayAdapter; import android.widget.TextView; import androidx.annotation.RequiresApi; @@ -64,6 +66,7 @@ import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.ui.ConversationsActivity; +import eu.siacs.conversations.ui.XmppActivity; import eu.siacs.conversations.utils.MimeUtils; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xml.Element; @@ -77,10 +80,12 @@ public class WebxdcPage implements ConversationPage { protected String baseUrl; protected Message source; protected WebxdcUpdate lastUpdate = null; + protected WeakReference activity; - public WebxdcPage(Cid cid, Message source, XmppConnectionService xmppConnectionService) { + public WebxdcPage(final XmppActivity activity, Cid cid, Message source, XmppConnectionService xmppConnectionService) { this.xmppConnectionService = xmppConnectionService; this.source = source; + this.activity = new WeakReference(activity); File f = xmppConnectionService.getFileForCid(cid); try { if (f != null) zip = new ZipFile(xmppConnectionService.getFileForCid(cid)); @@ -252,6 +257,20 @@ public class WebxdcPage implements ConversationPage { } }); + binding.webview.setWebChromeClient(new WebChromeClient() { + @Override + public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) { + // WebxdcActivity.this.filePathCallback = filePathCallback; + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, fileChooserParams.getMode() == FileChooserParams.MODE_OPEN_MULTIPLE); + final XmppActivity activity = WebxdcPage.this.activity.get(); + if (activity != null) activity.startActivityWithCallback(Intent.createChooser(intent, "Choose a file"), filePathCallback); + return activity != null; + } + }); + // disable "safe browsing" as this has privacy issues, // eg. at least false positives are sent to the "Safe Browsing Lookup API". // as all URLs opened in the WebView are local anyway, diff --git a/src/cheogram/res/raw/webxdc.js b/src/cheogram/res/raw/webxdc.js index ef789d1bdb7c294d1d8dc6594d62d2896f89f3e3..9277a263378f38285d7136e6ec848e5c9992cfd7 100644 --- a/src/cheogram/res/raw/webxdc.js +++ b/src/cheogram/res/raw/webxdc.js @@ -36,5 +36,26 @@ window.webxdc = (() => { sendUpdate: (payload, descr) => { InternalJSApi.sendStatusUpdate(JSON.stringify(payload), descr); }, + + importFiles: (filters) => { + var element = document.createElement("input"); + element.type = "file"; + element.accept = [ + ...(filters.extensions || []), + ...(filters.mimeTypes || []), + ].join(","); + element.multiple = filters.multiple || false; + const promise = new Promise((resolve, _reject) => { + element.onchange = (_ev) => { + const files = Array.from(element.files || []); + document.body.removeChild(element); + resolve(files); + }; + }); + element.style.display = "none"; + document.body.appendChild(element); + element.click(); + return promise; + }, }; })(); diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 14d77c3907b1b03db003f3547139a698121acb0e..f31ff9c63904b83d8dc3cf741b61901a8281ef04 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -3213,7 +3213,7 @@ public class ConversationFragment extends XmppFragment if (message == null) return; Cid webxdcCid = message.getFileParams().getCids().get(0); - WebxdcPage webxdc = new WebxdcPage(webxdcCid, message, activity.xmppConnectionService); + WebxdcPage webxdc = new WebxdcPage(activity, webxdcCid, message, activity.xmppConnectionService); Conversation conversation = (Conversation) message.getConversation(); if (!conversation.switchToSession("webxdc\0" + message.getUuid())) { conversation.startWebxdc(webxdc); diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java index 72b9fb293319ac572d5b544acd7de27b96504bc3..b6a9ae30b5854423f8e85ed6eebcff4f06e31ac7 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java @@ -41,9 +41,11 @@ import android.text.Html; import android.text.InputType; import android.util.DisplayMetrics; import android.util.Log; +import android.util.Pair; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.webkit.ValueCallback; import android.widget.Button; import android.widget.CheckBox; import android.widget.ImageView; @@ -65,6 +67,7 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.PriorityQueue; import java.util.concurrent.RejectedExecutionException; import eu.siacs.conversations.Config; @@ -117,6 +120,7 @@ public abstract class XmppActivity extends ActionBarActivity { protected Toast mToast; public Runnable onOpenPGPKeyPublished = () -> Toast.makeText(XmppActivity.this, R.string.openpgp_has_been_published, Toast.LENGTH_SHORT).show(); protected ConferenceInvite mPendingConferenceInvite = null; + protected PriorityQueue>> activityCallbacks = new PriorityQueue<>((x, y) -> y.first.compareTo(x.first)); protected ServiceConnection mConnection = new ServiceConnection() { @Override @@ -854,6 +858,13 @@ public abstract class XmppActivity extends ActionBarActivity { } } + public synchronized void startActivityWithCallback(Intent intent, ValueCallback cb) { + Pair> peek = activityCallbacks.peek(); + int index = peek == null ? 1 : peek.first + 1; + activityCallbacks.add(new Pair<>(index, cb)); + startActivityForResult(intent, index); + } + protected void onActivityResult(int requestCode, int resultCode, final Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_INVITE_TO_CONVERSATION && resultCode == RESULT_OK) { @@ -865,6 +876,21 @@ public abstract class XmppActivity extends ActionBarActivity { } mPendingConferenceInvite = null; } + } else if (resultCode == RESULT_OK) { + for (Pair> cb : new ArrayList<>(activityCallbacks)) { + if (cb.first == requestCode) { + activityCallbacks.remove(cb); + ArrayList dataUris = new ArrayList<>(); + if (data.getDataString() != null) { + dataUris.add(Uri.parse(data.getDataString())); + } else if (data.getClipData() != null) { + for (int i = 0; i < data.getClipData().getItemCount(); i++) { + dataUris.add(data.getClipData().getItemAt(i).getUri()); + } + } + cb.second.onReceiveValue(dataUris.toArray(new Uri[0])); + } + } } } diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index 251a4fe5cb00dc6430f2f9ff8ed8fc40846430df..38602c030cd8a062a2d276a88eea0e9a117df0bf 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -689,7 +689,7 @@ public class MessageAdapter extends ArrayAdapter { private void displayWebxdcMessage(ViewHolder viewHolder, final Message message, final boolean darkBackground, final int type) { Cid webxdcCid = message.getFileParams().getCids().get(0); - WebxdcPage webxdc = new WebxdcPage(webxdcCid, message, activity.xmppConnectionService); + WebxdcPage webxdc = new WebxdcPage(activity, webxdcCid, message, activity.xmppConnectionService); displayTextMessage(viewHolder, message, darkBackground, type); viewHolder.image.setVisibility(View.GONE); viewHolder.audioPlayer.setVisibility(View.GONE);