summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Meeks <michael.meeks@collabora.com>2019-12-18 17:10:04 +0000
committerJan Holesovsky <kendy@collabora.com>2019-12-19 01:19:08 +0100
commit76114dd957e8f121969dd206213b3a31a4de7afc (patch)
treeece7592e340426de0aab35a3bc4fa241688babb7
parente07dca49eb0a271ea800462ae08f8e18f46b699e (diff)
android: improved native copy/paste.
don't do copy/paste in the JS if we can avoid it. support text & html for cut / copy and share code. inject our own origin cookie to allow local short-circuiting. Change-Id: I3187104e9602e86b50cf52d45a9277db44ca8b3b Reviewed-on: https://gerrit.libreoffice.org/85455 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com> Reviewed-by: Jan Holesovsky <kendy@collabora.com>
-rw-r--r--android/lib/src/main/cpp/androidapp.cpp42
-rw-r--r--android/lib/src/main/java/org/libreoffice/androidlib/LOActivity.java124
-rw-r--r--loleaflet/src/control/Control.JSDialogBuilder.js2
-rw-r--r--loleaflet/src/map/Map.js2
4 files changed, 126 insertions, 44 deletions
diff --git a/android/lib/src/main/cpp/androidapp.cpp b/android/lib/src/main/cpp/androidapp.cpp
index d964d3579..577df5a90 100644
--- a/android/lib/src/main/cpp/androidapp.cpp
+++ b/android/lib/src/main/cpp/androidapp.cpp
@@ -333,11 +333,47 @@ Java_org_libreoffice_androidlib_LOActivity_saveAs(JNIEnv *env, jobject instance,
env->ReleaseStringUTFChars(format_, format);
}
+static jstring tojstringAndFree(JNIEnv *env, char *str)
+{
+ if (!str)
+ return env->NewStringUTF("");
+ jstring ret = env->NewStringUTF(str);
+ free(str);
+ return ret;
+}
+
extern "C"
-JNIEXPORT jstring JNICALL
-Java_org_libreoffice_androidlib_LOActivity_getTextSelection(JNIEnv *env, jobject instance) {
+JNIEXPORT jobjectArray JNICALL
+Java_org_libreoffice_androidlib_LOActivity_getClipboardContent(JNIEnv *env, jobject instance)
+{
+ const char *mimeTypes[] = { "text/plain;charset=utf-8", "text/html", nullptr };
+ size_t outCount = 0;
+ char **outMimeTypes = nullptr;
+ size_t *outSizes = nullptr;
+ char **outStreams = nullptr;
+
+ jobjectArray values = (jobjectArray)env->NewObjectArray(2,env->FindClass("java/lang/String"),env->NewStringUTF(""));
+
+ if (getLOKDocument()->getClipboard(mimeTypes,
+ &outCount, &outMimeTypes,
+ &outSizes, &outStreams))
+ {
+ if (outCount != 2)
+ LOG_DBG("clipboard fetch produced wrong results");
+ else
+ {
+ env->SetObjectArrayElement(values,0,tojstringAndFree(env, outStreams[0]));
+ env->SetObjectArrayElement(values,1,tojstringAndFree(env, outStreams[1]));
+ free (outMimeTypes[0]);
+ free (outMimeTypes[1]);
+ free (outMimeTypes);
+ free (outStreams);
+ }
+ }
+ else
+ LOG_DBG("failed to fetch mime-types");
- return env->NewStringUTF(getLOKDocument()->getTextSelection("text/plain;charset=utf-8"));
+ return values;
}
extern "C"
diff --git a/android/lib/src/main/java/org/libreoffice/androidlib/LOActivity.java b/android/lib/src/main/java/org/libreoffice/androidlib/LOActivity.java
index e1efc01f9..713032122 100644
--- a/android/lib/src/main/java/org/libreoffice/androidlib/LOActivity.java
+++ b/android/lib/src/main/java/org/libreoffice/androidlib/LOActivity.java
@@ -78,6 +78,9 @@ public class LOActivity extends AppCompatActivity {
private int providerId;
+ /// Unique number identifying this app + document.
+ private long loadDocumentMillis = 0;
+
@Nullable
private URI documentUri;
@@ -478,6 +481,8 @@ public class LOActivity extends AppCompatActivity {
mWebView.loadUrl(finalUrlToLoad);
documentLoaded = true;
+
+ loadDocumentMillis = android.os.SystemClock.uptimeMillis();
}
static {
@@ -564,46 +569,13 @@ public class LOActivity extends AppCompatActivity {
case "uno":
switch (messageAndParam[1]) {
case ".uno:Paste":
- clipData = clipboardManager.getPrimaryClip();
- if (clipData != null) {
- if (clipData.getDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
- final ClipData.Item clipItem = clipData.getItemAt(0);
- nativeHandler.post(new Runnable() {
- @Override
- public void run() {
- LOActivity.this.paste("text/plain;charset=utf-16", clipItem.getText().toString());
- }
- });
- }
- return false;
- }
+ return performPaste();
+ case ".uno:Copy":
+ case ".uno:Cut":
+ populateClipboard();
break;
- case ".uno:Copy": {
- nativeHandler.post(new Runnable() {
- @Override
- public void run() {
- String tempSelectedText = LOActivity.this.getTextSelection();
- if (!tempSelectedText.equals("")) {
- clipData = ClipData.newPlainText(ClipDescription.MIMETYPE_TEXT_PLAIN, tempSelectedText);
- clipboardManager.setPrimaryClip(clipData);
- }
- }
- });
+ default:
break;
- }
- case ".uno:Cut": {
- nativeHandler.post(new Runnable() {
- @Override
- public void run() {
- String tempSelectedText = LOActivity.this.getTextSelection();
- if (!tempSelectedText.equals("")) {
- clipData = ClipData.newPlainText(ClipDescription.MIMETYPE_TEXT_PLAIN, tempSelectedText);
- clipboardManager.setPrimaryClip(clipData);
- }
- }
- });
- break;
- }
}
break;
case "DIM_SCREEN": {
@@ -671,9 +643,83 @@ public class LOActivity extends AppCompatActivity {
public native void saveAs(String fileUri, String format);
- public native String getTextSelection();
+ public native String[] getClipboardContent();
public native void paste(String mimeType, String data);
+
+ /// Returns a magic that specifies this application - and this document.
+ private final String getClipboardMagic() {
+ return "lool-clip-magic-4a22437e49a8-" + Long.toString(loadDocumentMillis);
+ }
+
+ /// Needs to be executed after the .uno:Copy / Paste has executed
+ public final void populateClipboard()
+ {
+ /// FIXME: in theory we can do better with URIs to temporary files and so on...
+ nativeHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // text[0], html[1]
+ String[] clipStrings = LOActivity.this.getClipboardContent();
+ if (clipStrings.length < 2 ||
+ (clipStrings[0].length() == 0 && clipStrings[1].length() == 0))
+ Log.e(TAG, "no clipboard to copy");
+ else
+ {
+ String text = clipStrings[0];
+ String html = clipStrings[1];
+
+ int idx = html.indexOf("<meta name=\"generator\" content=\"");
+ if (idx < 0)
+ idx = html.indexOf("<meta http-equiv=\"content-type\" content=\"text/html;");
+ if (idx >= 0) { // inject our magic
+ StringBuffer newHtml = new StringBuffer(html);
+ newHtml.insert(idx, "<meta name=\"origin\" content=\"" + getClipboardMagic() + "\"/>\n");
+ html = newHtml.toString();
+ }
+
+ if (text == null || text.length() == 0)
+ Log.i(TAG, "set text to clipoard with: text '" + text + "' and html '" + html + "'");
+ clipData = ClipData.newHtmlText(ClipDescription.MIMETYPE_TEXT_HTML, text, html);
+ clipboardManager.setPrimaryClip(clipData);
+ }
+ }
+ });
+ }
+
+ /// Do the paste, and return true if we should short-circuit the paste locally
+ private final boolean performPaste()
+ {
+ clipData = clipboardManager.getPrimaryClip();
+ ClipDescription clipDesc = clipData != null ? clipData.getDescription() : null;
+ if (clipDesc != null) {
+ if (clipDesc.hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML)) {
+ final String html = clipData.getItemAt(0).getHtmlText();
+ if (html.contains(getClipboardMagic())) {
+ Log.i(TAG, "clipboard comes from us: short circuit it " + html);
+ return true;
+ } else {
+ Log.i(TAG, "foreign html '" + html + "'");
+ nativeHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ LOActivity.this.paste("text/html", html);
+ }
+ });
+ }
+ }
+ else if (clipDesc.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
+ final ClipData.Item clipItem = clipData.getItemAt(0);
+ nativeHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ LOActivity.this.paste("text/plain;charset=utf-16", clipItem.getText().toString());
+ }
+ });
+ }
+ }
+ return false;
+ }
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/loleaflet/src/control/Control.JSDialogBuilder.js b/loleaflet/src/control/Control.JSDialogBuilder.js
index 617a8d9d0..9d91807cd 100644
--- a/loleaflet/src/control/Control.JSDialogBuilder.js
+++ b/loleaflet/src/control/Control.JSDialogBuilder.js
@@ -1444,7 +1444,7 @@ L.Control.JSDialogBuilder = L.Control.extend({
// before close the wizard then execute the action
if (data.executionType === 'action') {
builder.map.menubar._executeAction(undefined, data.id);
- } else if (!builder.map._clip.filterExecCopyPaste(data.command)) {
+ } else if (!builder.map._clip || !builder.map._clip.filterExecCopyPaste(data.command)) {
builder.map.sendUnoCommand(data.command)
}
});
diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 58d9cf849..778013556 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -58,7 +58,7 @@ L.Map = L.Evented.extend({
this.options.documentContainer = L.DomUtil.get(this.options.documentContainer);
}
- if (!window.ThisIsTheiOSApp)
+ if (!window.ThisIsTheiOSApp && !window.ThisIsTheAndroidApp)
this._clip = L.clipboard(this);
this._initContainer(id);
this._initLayout();