[Libreoffice-commits] online.git: 2 commits - android/app .gitignore loleaflet/Makefile.am wsd/LOOLWSD.cpp

Libreoffice Gerrit user logerrit at kemper.freedesktop.org
Wed Feb 13 14:19:01 UTC 2019


 .gitignore                                                             |    1 
 android/app/src/main/cpp/CMakeLists.txt.in                             |    2 
 android/app/src/main/cpp/androidapp.c                                  |   14 
 android/app/src/main/cpp/androidapp.cpp                                |  274 ++++++++++
 android/app/src/main/java/org/libreoffice/androidapp/MainActivity.java |   12 
 loleaflet/Makefile.am                                                  |    7 
 wsd/LOOLWSD.cpp                                                        |    2 
 7 files changed, 293 insertions(+), 19 deletions(-)

New commits:
commit dc37e633efd7bf61072f7c3804d23928249f97e2
Author:     Jan Holesovsky <kendy at collabora.com>
AuthorDate: Wed Feb 13 15:09:37 2019 +0100
Commit:     Jan Holesovsky <kendy at collabora.com>
CommitDate: Wed Feb 13 15:17:50 2019 +0100

    android: JNI for the LOOLWSD initialization.
    
    For the moment it crashes, so the actual call is commented out.
    
    Change-Id: Ie4acb2a91f280dcc08951dcce417b8d4552cbfe7

diff --git a/android/app/src/main/cpp/CMakeLists.txt.in b/android/app/src/main/cpp/CMakeLists.txt.in
index c6b293da9..8a1c4a925 100644
--- a/android/app/src/main/cpp/CMakeLists.txt.in
+++ b/android/app/src/main/cpp/CMakeLists.txt.in
@@ -1,7 +1,7 @@
 cmake_minimum_required(VERSION 3.4.1)
 
 add_library(androidapp SHARED
-            androidapp.c
+            androidapp.cpp
             ../../../../../common/FileUtil.cpp
             ../../../../../common/Log.cpp
             ../../../../../common/MessageQueue.cpp
diff --git a/android/app/src/main/cpp/androidapp.c b/android/app/src/main/cpp/androidapp.c
deleted file mode 100644
index 532a9e59b..000000000
--- a/android/app/src/main/cpp/androidapp.c
+++ /dev/null
@@ -1,14 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
-/*
- * This file is part of the LibreOffice project.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-#include "androidapp.h"
-
-int loolwsd_server_socket_fd = -1;
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/android/app/src/main/cpp/androidapp.cpp b/android/app/src/main/cpp/androidapp.cpp
new file mode 100644
index 000000000..25dfc63c7
--- /dev/null
+++ b/android/app/src/main/cpp/androidapp.cpp
@@ -0,0 +1,274 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <config.h>
+#include <jni.h>
+#include <android/log.h>
+
+#include <thread>
+
+#include <FakeSocket.hpp>
+#include <Log.hpp>
+#include <LOOLWSD.hpp>
+#include <Protocol.hpp>
+#include <Util.hpp>
+
+const int SHOW_JS_MAXLEN = 70;
+
+int loolwsd_server_socket_fd = -1;
+
+static std::string fileURL;
+static LOOLWSD *loolwsd = nullptr;
+static int fakeClientFd;
+static int closeNotificationPipeForForwardingThread[2];
+
+#if 0
+static void send2JS_ready_callback(GObject      *source_object,
+                                   GAsyncResult *res,
+                                   gpointer      user_data)
+{
+    free(user_data);
+}
+
+static void send2JS(const std::vector<char>& buffer)
+{
+    LOG_TRC_NOFILE("Send to JS: " << LOOLProtocol::getAbbreviatedMessage(buffer.data(), buffer.size()));
+
+    std::string js;
+
+    // Check if the message is binary. We say that any message that isn't just a single line is
+    // "binary" even if that strictly speaking isn't the case; for instance the commandvalues:
+    // message has a long bunch of non-binary JSON on multiple lines. But _onMessage() in Socket.js
+    // handles it fine even if such a message, too, comes in as an ArrayBuffer. (Look for the
+    // "textMsg = String.fromCharCode.apply(null, imgBytes);".)
+
+    const char *newline = (const char *)memchr(buffer.data(), '\n', buffer.size());
+    if (newline != nullptr)
+    {
+        // The data needs to be an ArrayBuffer
+        js = "window.TheFakeWebSocket.onmessage({'data': Base64ToArrayBuffer('";
+        gchar *base64 = g_base64_encode((const guchar*)buffer.data(), buffer.size());
+        js = js + std::string(base64);
+        g_free(base64);
+        js = js + "')});";
+    }
+    else
+    {
+        const unsigned char *ubufp = (const unsigned char *)buffer.data();
+        std::vector<char> data;
+        for (int i = 0; i < buffer.size(); i++)
+        {
+            if (ubufp[i] < ' ' || ubufp[i] == '\'' || ubufp[i] == '\\')
+            {
+                data.push_back('\\');
+                data.push_back('x');
+                data.push_back("0123456789abcdef"[(ubufp[i] >> 4) & 0x0F]);
+                data.push_back("0123456789abcdef"[ubufp[i] & 0x0F]);
+            }
+            else
+            {
+                data.push_back(ubufp[i]);
+            }
+        }
+        data.push_back(0);
+
+        js = "window.TheFakeWebSocket.onmessage({'data': '";
+        js = js + std::string(buffer.data(), buffer.size());
+        js = js + "'});";
+    }
+
+    std::string subjs = js.substr(0, std::min(std::string::size_type(SHOW_JS_MAXLEN), js.length()));
+    if (js.length() > SHOW_JS_MAXLEN)
+        subjs += "...";
+
+    LOG_TRC_NOFILE( "Evaluating JavaScript: " << subjs);
+
+    char *jscopy = strdup(js.c_str());
+    g_idle_add([](gpointer data)
+               {
+                   char *jscopy = (char*) data;
+                   webkit_web_view_run_javascript(webView, jscopy, nullptr, send2JS_ready_callback, jscopy);
+                   return FALSE;
+               }, jscopy);
+}
+
+static char *js_result_as_gstring(WebKitJavascriptResult *js_result)
+{
+#if WEBKIT_CHECK_VERSION(2,22,0) // unclear when this API changed ...
+    JSCValue *value = webkit_javascript_result_get_js_value(js_result);
+    if (jsc_value_is_string(value))
+        return jsc_value_to_string(value);
+    else
+        return nullptr;
+#else // older Webkits
+    JSValueRef value = webkit_javascript_result_get_value(js_result);
+    JSContextRef ctx = webkit_javascript_result_get_global_context(js_result);
+    if (JSValueIsString(ctx, value))
+    {
+        const JSStringRef js_str = JSValueToStringCopy(ctx, value, nullptr);
+        size_t gstring_max = JSStringGetMaximumUTF8CStringSize(js_str);
+        char *gstring = (char *)g_malloc(gstring_max);
+        if (gstring)
+            JSStringGetUTF8CString(js_str, gstring, gstring_max);
+        else
+            LOG_TRC_NOFILE("No string");
+        JSStringRelease(js_str);
+        return gstring;
+    }
+    else
+        LOG_TRC_NOFILE("Unexpected object type " << JSValueGetType(ctx, value));
+    return nullptr;
+#endif
+}
+
+// TODO handle the message from JS here
+static void handle_lool_message(WebKitUserContentManager *manager,
+                                WebKitJavascriptResult   *js_result,
+                                gpointer                  user_data)
+{
+    gchar *string_value = js_result_as_gstring(js_result);
+
+    if (string_value)
+    {
+        LOG_TRC_NOFILE("From JS: lool: " << string_value);
+
+        if (strcmp(string_value, "HULLO") == 0)
+        {
+            // Now we know that the JS has started completely
+
+            // Contact the permanently (during app lifetime) listening LOOLWSD server
+            // "public" socket
+            assert(loolwsd_server_socket_fd != -1);
+            int rc = fakeSocketConnect(fakeClientFd, loolwsd_server_socket_fd);
+            assert(rc != -1);
+
+            // Create a socket pair to notify the below thread when the document has been closed
+            fakeSocketPipe2(closeNotificationPipeForForwardingThread);
+
+            // Start another thread to read responses and forward them to the JavaScript
+            std::thread([]
+                        {
+                            Util::setThreadName("app2js");
+                            while (true)
+                            {
+                               struct pollfd pollfd[2];
+                               pollfd[0].fd = fakeClientFd;
+                               pollfd[0].events = POLLIN;
+                               pollfd[1].fd = closeNotificationPipeForForwardingThread[1];
+                               pollfd[1].events = POLLIN;
+                               if (fakeSocketPoll(pollfd, 2, -1) > 0)
+                               {
+                                   if (pollfd[1].revents == POLLIN)
+                                   {
+                                       // The code below handling the "BYE" fake Websocket
+                                       // message has closed the other end of the
+                                       // closeNotificationPipeForForwardingThread. Let's close
+                                       // the other end too just for cleanliness, even if a
+                                       // FakeSocket as such is not a system resource so nothing
+                                       // is saved by closing it.
+                                       fakeSocketClose(closeNotificationPipeForForwardingThread[1]);
+
+                                       // Close our end of the fake socket connection to the
+                                       // ClientSession thread, so that it terminates
+                                       fakeSocketClose(fakeClientFd);
+
+                                       return;
+                                   }
+                                   if (pollfd[0].revents == POLLIN)
+                                   {
+                                       int n = fakeSocketAvailableDataLength(fakeClientFd);
+                                       if (n == 0)
+                                           return;
+                                       std::vector<char> buf(n);
+                                       n = fakeSocketRead(fakeClientFd, buf.data(), n);
+                                       send2JS(buf);
+                                   }
+                               }
+                               else
+                                   break;
+                           }
+                           assert(false);
+                        }).detach();
+
+            // First we simply send it the URL. This corresponds to the GET request with Upgrade to
+            // WebSocket.
+            LOG_TRC_NOFILE("Actually sending to Online:" << fileURL);
+
+            // Must do this in a thread, too, so that we can return to the GTK+ main loop
+            std::thread([]
+                        {
+                            struct pollfd pollfd;
+                            pollfd.fd = fakeClientFd;
+                            pollfd.events = POLLOUT;
+                            fakeSocketPoll(&pollfd, 1, -1);
+                            fakeSocketWrite(fakeClientFd, fileURL.c_str(), fileURL.size());
+                        }).detach();
+        }
+        else if (strcmp(string_value, "BYE") == 0)
+        {
+            LOG_TRC_NOFILE("Document window terminating on JavaScript side. Closing our end of the socket.");
+
+            // Close one end of the socket pair, that will wake up the forwarding thread above
+            fakeSocketClose(closeNotificationPipeForForwardingThread[0]);
+
+            // ???
+        }
+        else
+        {
+            // As above
+            char *string_copy = strdup(string_value);
+            std::thread([=]
+                        {
+                            struct pollfd pollfd;
+                            pollfd.fd = fakeClientFd;
+                            pollfd.events = POLLOUT;
+                            fakeSocketPoll(&pollfd, 1, -1);
+                            fakeSocketWrite(fakeClientFd, string_copy, strlen(string_copy));
+                            free(string_copy);
+                        }).detach();
+        }
+        g_free(string_value);
+    }
+    else
+        LOG_TRC_NOFILE("From JS: lool: some object");
+}
+#endif
+
+/// Create the LOOLWSD instance.
+extern "C" JNIEXPORT void JNICALL
+Java_org_libreoffice_androidapp_MainActivity_createLOOLWSD(JNIEnv*, jobject)
+{
+    Log::initialize("Mobile", "trace", false, false, {});
+    Util::setThreadName("main");
+
+    fakeSocketSetLoggingCallback([](const std::string& line)
+                                 {
+                                     LOG_TRC_NOFILE(line);
+                                 });
+
+    std::thread([]
+                {
+                    assert(loolwsd == nullptr);
+                    char *argv[2];
+                    argv[0] = strdup("mobile");
+                    argv[1] = nullptr;
+                    Util::setThreadName("app");
+                    while (true)
+                    {
+                        loolwsd = new LOOLWSD();
+                        loolwsd->run(1, argv);
+                        delete loolwsd;
+                        LOG_TRC("One run of LOOLWSD completed");
+                    }
+                }).detach();
+
+    fakeClientFd = fakeSocketSocket();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/android/app/src/main/cpp/androidapp.h b/android/app/src/main/cpp/androidapp.hpp
similarity index 100%
rename from android/app/src/main/cpp/androidapp.h
rename to android/app/src/main/cpp/androidapp.hpp
diff --git a/android/app/src/main/java/org/libreoffice/androidapp/MainActivity.java b/android/app/src/main/java/org/libreoffice/androidapp/MainActivity.java
index 9388372b4..5a692cb62 100644
--- a/android/app/src/main/java/org/libreoffice/androidapp/MainActivity.java
+++ b/android/app/src/main/java/org/libreoffice/androidapp/MainActivity.java
@@ -25,6 +25,8 @@ public class MainActivity extends AppCompatActivity {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
 
+        //createLOOLWSD();
+
         final WebView browser = findViewById(R.id.browser);
         browser.setWebViewClient(new WebViewClient());
 
@@ -32,7 +34,10 @@ public class MainActivity extends AppCompatActivity {
         browserSettings.setJavaScriptEnabled(true);
         browser.addJavascriptInterface(new JavaScriptInterface(), "MainHandler");
 
-        browser.loadUrl("file:///android_asset/dist/loleaflet.html");
+        browser.loadUrl("file:///android_asset/dist/loleaflet.html?file_path=" +
+                "file:///android_asset/dist/hello-world.odt" + // TODO the real URL here
+                "&closebutton=1&permission=edit" +
+                "&debug=true"); // TODO remove later?
 
         Button jsButton = findViewById(R.id.js_button);
         jsButton.setOnClickListener(new View.OnClickListener() {
@@ -43,6 +48,11 @@ public class MainActivity extends AppCompatActivity {
                                     }
         );
     }
+
+    static {
+        System.loadLibrary("androidapp");
+    }
+    public native void createLOOLWSD();
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 5dfec75a7..1f7ab0556 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -141,7 +141,7 @@ using Poco::Net::PartHandler;
 #elif GTKAPP
 #include "gtk.hpp"
 #elif defined(__ANDROID__)
-#include "androidapp.h"
+#include "androidapp.hpp"
 #endif
 #endif
 
commit b7513394bd6ab87be91a306b4e081a446e90a4bc
Author:     Jan Holesovsky <kendy at collabora.com>
AuthorDate: Wed Feb 13 14:56:05 2019 +0100
Commit:     Jan Holesovsky <kendy at collabora.com>
CommitDate: Wed Feb 13 14:56:05 2019 +0100

    android: Copy the test documents to the assets too.
    
    Change-Id: Iaaf8d70b84adf7945461a50c4771dc984ffa08ff

diff --git a/.gitignore b/.gitignore
index aa2c717fb..6f4dddc9d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -93,6 +93,7 @@ ios/Mobile/Assets.xcassets/AppIcon.appiconset
 
 # android stuff
 /android/app/src/main/assets/dist
+/android/app/src/main/assets/hello-world.od*
 /android/app/src/main/cpp/CMakeLists.txt
 
 # backup and temporary editor files: the only convenience rules allowed here.
diff --git a/loleaflet/Makefile.am b/loleaflet/Makefile.am
index dcdcf31fc..d5b54001a 100644
--- a/loleaflet/Makefile.am
+++ b/loleaflet/Makefile.am
@@ -164,8 +164,11 @@ build-loleaflet: | $(LOLEAFLET_L10N_DST) \
 	$(builddir)/dist/loleaflet.html
 	@echo "build loleaflet completed"
 if ENABLE_ANDROIDAPP
-	@rm -rf $(srcdir)/../android/app/src/main/assets/dist
-	@cp -a $(builddir)/dist $(srcdir)/../android/app/src/main/assets/
+	@rm -rf $(abs_top_srcdir)/android/app/src/main/assets/dist
+	@cp -a $(builddir)/dist $(abs_top_srcdir)/android/app/src/main/assets/
+	@cp -a $(abs_top_srcdir)/test/data/hello.odt $(abs_top_srcdir)/android/app/src/main/assets/hello-world.odt
+	@cp -a $(abs_top_srcdir)/test/data/hello.ods $(abs_top_srcdir)/android/app/src/main/assets/hello-world.ods
+	@cp -a $(abs_top_srcdir)/test/data/hello.odp $(abs_top_srcdir)/android/app/src/main/assets/hello-world.odp
 	@echo
 	@echo "Copied JS, HTML and CSS to the Android project (android/app/src/main/assets/dist)."
 	@echo


More information about the Libreoffice-commits mailing list