[Libreoffice-commits] core.git: 7 commits - android/Bootstrap android/experimental android/qa desktop/Library_sofficeapp.mk desktop/source sal/android sal/inc sal/Library_sal.mk sal/Module_sal.mk sal/osl sal/Package_inc.mk vcl/android vcl/generic vcl/inc vcl/Module_vcl.mk vcl/source

Michael Meeks michael.meeks at suse.com
Thu Feb 21 12:55:09 PST 2013


 android/Bootstrap/Makefile.shared                                      |    4 
 android/Bootstrap/src/org/libreoffice/android/Bootstrap.java           |  107 +
 android/Bootstrap/version.map                                          |    2 
 android/experimental/DocumentLoader/native-code.cxx                    |    3 
 android/experimental/LibreOffice4Android/native-code.cxx               |    1 
 android/qa/desktop/Makefile                                            |   12 
 android/qa/desktop/native-code.cxx                                     |   18 
 android/qa/desktop/src/org/libreoffice/android/examples/LODesktop.java |   53 
 desktop/Library_sofficeapp.mk                                          |    9 
 desktop/source/app/app.cxx                                             |    7 
 desktop/source/app/sofficemain.cxx                                     |   32 
 sal/Library_sal.mk                                                     |    2 
 sal/Module_sal.mk                                                      |    8 
 sal/Package_inc.mk                                                     |    1 
 sal/android/android_native_app_glue.c                                  |  460 +++++
 sal/android/lo-bootstrap.c                                             |  126 +
 sal/inc/osl/detail/android-bootstrap.h                                 |    2 
 sal/inc/osl/detail/android_native_app_glue.h                           |  351 ++++
 sal/inc/sal/main.h                                                     |   20 
 sal/osl/unx/process_impl.cxx                                           |   24 
 vcl/Module_vcl.mk                                                      |    5 
 vcl/android/androidinst.cxx                                            |  786 +++++++++-
 vcl/generic/glyphs/gcach_ftyp.cxx                                      |   24 
 vcl/inc/android/androidinst.hxx                                        |   14 
 vcl/source/app/svmain.cxx                                              |    3 
 vcl/source/window/dialog.cxx                                           |   13 
 26 files changed, 2014 insertions(+), 73 deletions(-)

New commits:
commit dfd17e881b1fdfb08021dc7b098e8b54b7b83ba3
Author: Michael Meeks <michael.meeks at suse.com>
Date:   Fri Jan 18 00:17:20 2013 +0800

    remove un-conditional 20 second sleep on-start for now.
    
    Change-Id: I37ff2164b1f0fcdc4f3fad41f344b0d73105a2c1

diff --git a/sal/android/lo-bootstrap.c b/sal/android/lo-bootstrap.c
index 9aeb32b..40f565c 100644
--- a/sal/android/lo-bootstrap.c
+++ b/sal/android/lo-bootstrap.c
@@ -485,6 +485,7 @@ Java_org_libreoffice_android_Bootstrap_putenv(JNIEnv* env,
 
     putenv(s);
 
+#if 0
     {
         static int beenhere=0;
         if (!beenhere) {
@@ -493,6 +494,7 @@ Java_org_libreoffice_android_Bootstrap_putenv(JNIEnv* env,
             beenhere = 1;
         }
     }
+#endif
 
     (*env)->ReleaseStringUTFChars(env, string, s);
 }
commit 47c46638f1a6eaa6e58844d6da56ebf673791cbc
Author: Tor Lillqvist <tml at iki.fi>
Date:   Thu Feb 21 21:45:34 2013 +0200

    Need the spell library
    
    Change-Id: I381386852e20bf0424f3189099b10bb33de98bc8

diff --git a/android/experimental/LibreOffice4Android/native-code.cxx b/android/experimental/LibreOffice4Android/native-code.cxx
index 30dae38..530e6ee 100644
--- a/android/experimental/LibreOffice4Android/native-code.cxx
+++ b/android/experimental/LibreOffice4Android/native-code.cxx
@@ -114,6 +114,7 @@ lo_get_libmap(void)
         { "libsmdlo.a", smd_component_getFactory },
         { "libsmlo.a", sm_component_getFactory },
         { "libsotlo.a", sot_component_getFactory },
+        { "libspelllo.a", spell_component_getFactory },
         { "libscriptframe.a", scriptframe_component_getFactory },
         { "libstringresource.uno.a", stringresource_component_getFactory },
         { "libsvgfilterlo.a", svgfilter_component_getFactory },
commit e459f5128a8f522ed3d5cf6fa542c0d75f96c07f
Author: Tor Lillqvist <tml at iki.fi>
Date:   Thu Feb 21 21:34:30 2013 +0200

    Need the protocolhandler and spell libraries
    
    Change-Id: I88b514326be80e56053a28f4a434162fd8d4397b

diff --git a/android/experimental/DocumentLoader/native-code.cxx b/android/experimental/DocumentLoader/native-code.cxx
index 22c9a7e..985a0c0 100644
--- a/android/experimental/DocumentLoader/native-code.cxx
+++ b/android/experimental/DocumentLoader/native-code.cxx
@@ -28,6 +28,7 @@ extern "C"
     extern void * lnth_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * lotuswordpro_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * oox_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
+    extern void * protocolhandler_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * sb_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * sc_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * scd_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
@@ -75,6 +76,7 @@ lo_get_libmap(void)
         { "liblnthlo.a", lnth_component_getFactory },
         { "liblwpftlo.a", lotuswordpro_component_getFactory },
         { "libooxlo.a", oox_component_getFactory },
+        { "libprotocolhandlerlo.a", protocolhandler_component_getFactory },
         { "libscdlo.a", scd_component_getFactory },
         { "libscfiltlo.a", scfilt_component_getFactory },
         { "libsblo.a", sb_component_getFactory },
@@ -83,6 +85,7 @@ lo_get_libmap(void)
         { "libsdlo.a", sd_component_getFactory },
         { "libsmdlo.a", smd_component_getFactory },
         { "libsmlo.a", sm_component_getFactory },
+        { "libspelllo.a", spell_component_getFactory },
         { "libsvgfilterlo.a", svgfilter_component_getFactory },
         { "libswdlo.a", swd_component_getFactory },
         { "libswlo.a", sw_component_getFactory },
commit aeebcdf5bbf3fc1b91d9bc01a160f74952910fc4
Author: Tor Lillqvist <tml at iki.fi>
Date:   Thu Feb 21 21:11:47 2013 +0200

    Just link directly to the bundled static libfreetype on Android
    
    The dlsym() crack found the system (but non-public) libfreetype symbols, and
    that seems to be incompatible and caused the app to crash.
    
    Change-Id: I9b0bcac098d7cb3560640de3dfc10f51569c462d

diff --git a/vcl/generic/glyphs/gcach_ftyp.cxx b/vcl/generic/glyphs/gcach_ftyp.cxx
index de44e31..1cb2d87 100644
--- a/vcl/generic/glyphs/gcach_ftyp.cxx
+++ b/vcl/generic/glyphs/gcach_ftyp.cxx
@@ -53,6 +53,11 @@
 #include FT_TRUETYPE_TAGS_H
 #include FT_TRUETYPE_IDS_H
 
+#ifdef ANDROID
+#include FT_SIZES_H
+#include FT_SYNTHESIS_H
+#endif
+
 #ifndef FT_RENDER_MODE_MONO  // happens in the MACOSX build
     #define FT_RENDER_MODE_MONO ft_render_mode_mono
 #endif
@@ -126,8 +131,8 @@ static int nFTVERSION = 0;
 static FT_Error (*pFTNewSize)(FT_Face,FT_Size*);
 static FT_Error (*pFTActivateSize)(FT_Size);
 static FT_Error (*pFTDoneSize)(FT_Size);
-FT_Error (*pFTEmbolden)(FT_GlyphSlot);
-FT_Error (*pFTOblique)(FT_GlyphSlot);
+void (*pFTEmbolden)(FT_GlyphSlot);
+void (*pFTOblique)(FT_GlyphSlot);
 static bool bEnableSizeFT = false;
 
 struct EqStr{ bool operator()(const char* a, const char* b) const { return !strcmp(a,b); } };
@@ -481,6 +486,19 @@ FreetypeManager::FreetypeManager()
 {
     /*FT_Error rcFT =*/ FT_Init_FreeType( &aLibFT );
 
+#ifdef ANDROID
+    // For Android we use the bundled static libfreetype.a, and we
+    // want to avoid accidentally finding the FT_* symbols in the
+    // system FreeType code (which *is* present in a system library,
+    // libskia.so, but is not a public API, and in fact does crash the
+    // app if used).
+    pFTNewSize = FT_New_Size;
+    pFTActivateSize = FT_Activate_Size;
+    pFTDoneSize = FT_Done_Size;
+    pFTEmbolden = FT_GlyphSlot_Embolden;
+    pFTOblique = FT_GlyphSlot_Oblique;
+    nFTVERSION = FTVERSION;
+#else
 #ifdef RTLD_DEFAULT // true if a good dlfcn.h header was included
     // Get version of freetype library to enable workarounds.
     // Freetype <= 2.0.9 does not have FT_Library_Version().
@@ -517,7 +535,7 @@ FreetypeManager::FreetypeManager()
     // assume systems where dlsym is not possible use supplied library
     nFTVERSION = FTVERSION;
 #endif
-
+#endif
     // TODO: remove when the priorities are selected by UI
     char* pEnv;
     pEnv = ::getenv( "SAL_EMBEDDED_BITMAP_PRIORITY" );
commit 8656ff7212e319cd96d799ee0f2f8bb156ad6d71
Author: Tor Lillqvist <tml at iki.fi>
Date:   Thu Feb 21 18:09:10 2013 +0200

    Do strip the .so that will be included in the .apk
    
    There is no use for non-exported symbols and debugging information in the .so
    on the device.
    
    Debugging with ndk-gdb uses the non-stripped copy of the .so located on the
    build machine and works fine (as fine as the NDK gdb can work) even if the .so
    that is actually running on the device includes no debugging information.
    
    Change-Id: If4e77284a74427261eefac0e167ed42161c773f8

diff --git a/android/Bootstrap/Makefile.shared b/android/Bootstrap/Makefile.shared
index 1f13013..9eb1800 100644
--- a/android/Bootstrap/Makefile.shared
+++ b/android/Bootstrap/Makefile.shared
@@ -92,7 +92,7 @@ $(OBJLOCAL)/liblo-native-code.so : $(wildcard $(OUTDIR)/lib/lib*.a) native-code.
 
 $(SODEST)/liblo-native-code.so : $(OBJLOCAL)/liblo-native-code.so
 	mkdir -p $(SODEST)
-	cp -a $(OBJLOCAL)/liblo-native-code.so $(SODEST)/liblo-native-code.so
+	$(STRIP) -o $(SODEST)/liblo-native-code.so $(OBJLOCAL)/liblo-native-code.so
 
 # shrinkme $(STRIP) -o $(SODEST)/liblo-native-code.so $(OBJLOCAL)/liblo-native-code.so
 
commit 4d258787559426e1e2a0279888eb669622889899
Author: Michael Meeks <michael.meeks at suse.com>
Date:   Sat Dec 15 18:13:11 2012 +0000

    android: finally starting and rendering at least something again.
    
    Only renders on very-first-start after install (oddly).
    We initialize vcl in it's own thread to avoid problems.
    Thanks to tml for fixing a linking issue.
    Change-Id: I960d11c6098681356fea0634970545aa9af9bacb

diff --git a/android/Bootstrap/Makefile.shared b/android/Bootstrap/Makefile.shared
index 3eb28a7..1f13013 100644
--- a/android/Bootstrap/Makefile.shared
+++ b/android/Bootstrap/Makefile.shared
@@ -88,11 +88,13 @@ WHOLELIBS = \
 
 $(OBJLOCAL)/liblo-native-code.so : $(wildcard $(OUTDIR)/lib/lib*.a) native-code.cxx
 	mkdir -p $(OBJLOCAL)
-	$(CXX) -Wl,-Map,liblo-native-code.map -Wl,--gc-sections -Wl,--version-script=../../Bootstrap/version.map -Wl,--no-keep-files-mapped -Wl,--no-undefined -DANDROID -DDISABLE_DYNLOADING -shared -o $(OBJLOCAL)/liblo-native-code.so -I$(OUTDIR)/inc native-code.cxx -L$(OUTDIR)/lib $(WHOLELIBS) $(LIBS) -lgnustl_static -landroid -llog -lz
+	$(CXX) -Wl,-Map,liblo-native-code.map -Wl,--gc-sections -Wl,--version-script=../../Bootstrap/version.map -Wl,--no-keep-files-mapped -Wl,--no-undefined -DANDROID -DDISABLE_DYNLOADING -shared -o $(OBJLOCAL)/liblo-native-code.so -I$(OUTDIR)/inc native-code.cxx -L$(OUTDIR)/lib $(WHOLELIBS) $(LIBS) -lgnustl_static -landroid -ljnigraphics -llog -lz
 
 $(SODEST)/liblo-native-code.so : $(OBJLOCAL)/liblo-native-code.so
 	mkdir -p $(SODEST)
-	$(STRIP) -o $(SODEST)/liblo-native-code.so $(OBJLOCAL)/liblo-native-code.so
+	cp -a $(OBJLOCAL)/liblo-native-code.so $(SODEST)/liblo-native-code.so
+
+# shrinkme $(STRIP) -o $(SODEST)/liblo-native-code.so $(OBJLOCAL)/liblo-native-code.so
 
 link-so: $(SODEST)/liblo-native-code.so
 
diff --git a/android/Bootstrap/version.map b/android/Bootstrap/version.map
index 1b65003..e12d998 100644
--- a/android/Bootstrap/version.map
+++ b/android/Bootstrap/version.map
@@ -1,5 +1,5 @@
 dummy {
-    global:
+     global:
         Java_*;
         JNI_OnLoad;
     local:
diff --git a/android/qa/desktop/Makefile b/android/qa/desktop/Makefile
index 685e584..5ea6f35 100644
--- a/android/qa/desktop/Makefile
+++ b/android/qa/desktop/Makefile
@@ -128,6 +128,18 @@ copy-stuff:
 		mkdir -p assets/presets/$$D ; \
 		echo "content" > assets/presets/$$D/stamp; \
 	done
+# lofficerc
+	mkdir -p assets/program/
+	echo "[Bootstrap]" > assets/program/lofficerc
+	echo "Logo=1" >> assets/program/lofficerc
+	echo "NativeProgress=1" >> assets/program/lofficerc
+	echo "URE_BOOTSTRAP=file:///assets/program/fundamentalrc" >> assets/program/lofficerc
+#	echo "RTL_LOGFILE=file:///dev/log/main" >> assets/program/lofficerc
+	echo "HOME=$(APP_DATA_PATH)/files" >> assets/program/lofficerc
+	echo "OSL_SOCKET_PATH=$(APP_DATA_PATH)/files" >> assets/program/lofficerc
+# - this looks useful but breaks more than it fixes ...
+#	echo "DISABLE_EXTENSION_SYNCHRONIZATION=1" >> assets/program/lofficerc
+
 
 setup-jars:
 #
diff --git a/android/qa/desktop/native-code.cxx b/android/qa/desktop/native-code.cxx
index 22c9a7e..4f54ccd 100644
--- a/android/qa/desktop/native-code.cxx
+++ b/android/qa/desktop/native-code.cxx
@@ -13,6 +13,7 @@ extern "C"
 {
     extern void * animcore_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * avmedia_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
+    extern void * cui_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * dba_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * dbaxml_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * evtatt_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
@@ -27,6 +28,7 @@ extern "C"
     extern void * lng_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * lnth_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * lotuswordpro_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
+    extern void * protocolhandler_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * oox_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * sb_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * sc_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
@@ -36,14 +38,17 @@ extern "C"
     extern void * sdd_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * sm_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * smd_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
+    extern void * spl_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * spell_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * svgfilter_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * sw_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
+    extern void * svx_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * swd_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * t602filter_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * textfd_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * unoxml_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * unordf_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
+    extern void * uui_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * wpftdraw_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * wpftwriter_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
     extern void * xmlfd_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey );
@@ -62,6 +67,7 @@ lo_get_libmap(void)
         { "libavmedialo.a", avmedia_component_getFactory },
         { "libdbalo.a", dba_component_getFactory },
         { "libdbaxmllo.a", dbaxml_component_getFactory },
+        { "libcuilo.a", cui_component_getFactory },
         { "libevtattlo.a", evtatt_component_getFactory },
         { "libfileacc.a", fileacc_component_getFactory },
         { "libfrmlo.a", frm_component_getFactory },
@@ -75,6 +81,7 @@ lo_get_libmap(void)
         { "liblnthlo.a", lnth_component_getFactory },
         { "liblwpftlo.a", lotuswordpro_component_getFactory },
         { "libooxlo.a", oox_component_getFactory },
+        { "libprotocolhandlerlo.a", protocolhandler_component_getFactory },
         { "libscdlo.a", scd_component_getFactory },
         { "libscfiltlo.a", scfilt_component_getFactory },
         { "libsblo.a", sb_component_getFactory },
@@ -83,13 +90,16 @@ lo_get_libmap(void)
         { "libsdlo.a", sd_component_getFactory },
         { "libsmdlo.a", smd_component_getFactory },
         { "libsmlo.a", sm_component_getFactory },
+        { "libspllo.a", spl_component_getFactory },
         { "libsvgfilterlo.a", svgfilter_component_getFactory },
+        { "libsvxlo.a", svx_component_getFactory },
         { "libswdlo.a", swd_component_getFactory },
         { "libswlo.a", sw_component_getFactory },
         { "libt602filterlo.a", t602filter_component_getFactory },
         { "libtextfdlo.a", textfd_component_getFactory },
         { "libunordflo.a", unordf_component_getFactory },
         { "libunoxmllo.a", unoxml_component_getFactory },
+        { "libuuilo.a", uui_component_getFactory },
         { "libwpftdrawlo.a", wpftdraw_component_getFactory },
         { "libwpftwriterlo.a", wpftwriter_component_getFactory },
         { "libxmlfdlo.a", xmlfd_component_getFactory },
@@ -99,6 +109,14 @@ lo_get_libmap(void)
         { NULL, NULL }
     };
 
+    // We need to pull this in, too, as it isn't in any of the libs we
+    // link with -Wl,--whole-archive.
+    extern void Java_org_libreoffice_android_examples_LODesktop_spawnMain();
+    volatile void *p = (void *) Java_org_libreoffice_android_examples_LODesktop_spawnMain;
+
+    extern void Java_org_libreoffice_android_examples_LODesktop_renderVCL();
+    volatile void *q = (void *) Java_org_libreoffice_android_examples_LODesktop_renderVCL;
+
     return map;
 }
 
diff --git a/android/qa/desktop/src/org/libreoffice/android/examples/LODesktop.java b/android/qa/desktop/src/org/libreoffice/android/examples/LODesktop.java
index 5891cea..bf370e6 100644
--- a/android/qa/desktop/src/org/libreoffice/android/examples/LODesktop.java
+++ b/android/qa/desktop/src/org/libreoffice/android/examples/LODesktop.java
@@ -58,6 +58,7 @@ package org.libreoffice.android.examples;
 
 import android.app.Activity;
 import android.app.AlertDialog;
+import android.content.Context;
 import android.content.DialogInterface;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -121,6 +122,12 @@ public class LODesktop
 {
     private static final String TAG = "LODesktop";
 
+    /* implementend by desktop */
+    private static native void spawnMain();
+
+    /* implementend by vcl */
+    public static native void renderVCL(Bitmap bitmap);
+
     /**
      * This class contains the state that is initialized once and never changes
      * (not specific to a document or a view).
@@ -233,17 +240,6 @@ public class LODesktop
             bootstrapContext.mcf = bootstrapContext.componentContext.getServiceManager();
 
             Log.i(TAG, "mcf is" + (bootstrapContext.mcf!=null ? " not" : "") + " null");
-
-            Bootstrap.initVCL();
-
-            Object desktop = bootstrapContext.mcf.createInstanceWithContext
-                ("com.sun.star.frame.Desktop", bootstrapContext.componentContext);
-
-            Log.i(TAG, "desktop is" + (desktop!=null ? " not" : "") + " null");
-
-            bootstrapContext.componentLoader = (XComponentLoader) UnoRuntime.queryInterface(XComponentLoader.class, desktop);
-
-            Log.i(TAG, "componentLoader is" + (bootstrapContext.componentLoader!=null ? " not" : "") + " null");
         }
         catch (Exception e)
         {
@@ -260,9 +256,11 @@ public class LODesktop
         Log.i(TAG, "onCreate - added here\n");
 
         try {
-            String input = getIntent().getStringExtra("input");
-            if (input == null)
-                input = "/assets/test1.odt";
+            String input;
+//            input = getIntent().getStringExtra("input");
+//            if (input == null)
+//            input = "/assets/test1.odt";
+            input = "-writer";
 
             // We need to fake up an argv, and the argv[0] even needs to
             // point to some file name that we can pretend is the "program".
@@ -276,12 +274,39 @@ public class LODesktop
             if (bootstrapContext == null)
                 initBootstrapContext();
 
+            spawnMain();
         }
         catch (Exception e) {
             e.printStackTrace(System.err);
             finish();
         }
+
+        Log.i(TAG, "onCreate - set content view\n");
+        setContentView(new BitmapView(this, this));
     }
 }
 
+    class BitmapView extends android.view.View
+    {
+        LODesktop mDesktop;
+        Bitmap mBitmap;
+
+        public BitmapView(Context context, LODesktop desktop)
+        {
+            super(context);
+            mDesktop = desktop;
+            mBitmap = Bitmap.createBitmap(1000, 600, Bitmap.Config.ARGB_8888);
+        }
+
+        @Override protected void onDraw(Canvas canvas) {
+//            canvas.drawColor(0xFF1ABCDD);
+
+            mDesktop.renderVCL(mBitmap);
+            canvas.drawBitmap(mBitmap, 0, 0, null);
+
+            // re-call ourselves a bit later ...
+            invalidate();
+        }
+    }
+
 // vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/desktop/source/app/app.cxx b/desktop/source/app/app.cxx
index 1dc7552..6260a65 100644
--- a/desktop/source/app/app.cxx
+++ b/desktop/source/app/app.cxx
@@ -562,6 +562,8 @@ Desktop::~Desktop()
 
 void Desktop::Init()
 {
+    fprintf (stderr, "Desktop::Init()\n");
+
     RTL_LOGFILE_CONTEXT( aLog, "desktop (cd100003) ::Desktop::Init" );
     SetBootstrapStatus(BS_OK);
 
@@ -593,9 +595,12 @@ void Desktop::Init()
         }
     }
 
-    if ( m_aBootstrapError == BE_OK )
+    fprintf( stderr, "OfficeIPCThread %d ...\n", m_aBootstrapError == BE_OK );
+
+    if ( 1 )
     {
         const CommandLineArgs& rCmdLineArgs = GetCommandLineArgs();
+
         // start ipc thread only for non-remote offices
         RTL_LOGFILE_CONTEXT( aLog2, "desktop (cd100003) ::OfficeIPCThread::EnableOfficeIPCThread" );
         OfficeIPCThread::Status aStatus = OfficeIPCThread::EnableOfficeIPCThread();
diff --git a/desktop/source/app/sofficemain.cxx b/desktop/source/app/sofficemain.cxx
index ce226c1..3af1474 100644
--- a/desktop/source/app/sofficemain.cxx
+++ b/desktop/source/app/sofficemain.cxx
@@ -29,6 +29,12 @@
 #include <rtl/bootstrap.hxx>
 #include <tools/extendapplicationenvironment.hxx>
 
+
+#ifdef ANDROID
+#  include <jni.h>
+#  include <salhelper/thread.hxx>
+#endif
+
 int SVMain();
 
 // -=-= main() -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
@@ -70,6 +76,7 @@ extern "C" int DESKTOP_DLLPUBLIC soffice_main()
         return EXIT_SUCCESS;
     }
 #endif
+    fprintf (stderr, "enter svmain()\n");
     return SVMain();
 #if defined ANDROID
     } catch (const ::com::sun::star::uno::Exception &e) {
@@ -80,4 +87,29 @@ extern "C" int DESKTOP_DLLPUBLIC soffice_main()
 #endif
 }
 
+#ifdef ANDROID
+class MainThread : public salhelper::Thread
+{
+public:
+    MainThread() : salhelper::Thread("vcl-mainloop") { launch(); }
+    virtual void execute()
+    {
+        int nRet;
+        do {
+            nRet = soffice_main();
+            fprintf( stderr, "nRet %d\n", nRet );
+        } while (nRet == 81 || nRet == 79); // pretend to re-start.
+        exit (nRet);
+    }
+};
+
+extern "C" SAL_JNI_EXPORT void JNICALL
+Java_org_libreoffice_android_examples_LODesktop_spawnMain(JNIEnv* /* env */,
+                                                          jobject /* dummy */)
+{
+    fprintf(stderr, "Spawn main!\n");
+    new MainThread();
+}
+#endif
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/android/android_native_app_glue.c b/sal/android/android_native_app_glue.c
index 0c2dd5e..e419c3d 100644
--- a/sal/android/android_native_app_glue.c
+++ b/sal/android/android_native_app_glue.c
@@ -440,6 +440,7 @@ static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue)
 __attribute__ ((visibility("default"))) void ANativeActivity_onCreate(ANativeActivity* activity,
         void* savedState, size_t savedStateSize) {
     LOGI("Creating: %p\n", activity);
+    fprintf (stderr, "ANativeActivity_onCreate - Meeks !\n");
     activity->callbacks->onDestroy = onDestroy;
     activity->callbacks->onStart = onStart;
     activity->callbacks->onResume = onResume;
diff --git a/vcl/android/androidinst.cxx b/vcl/android/androidinst.cxx
index 916769e..7daae4e 100644
--- a/vcl/android/androidinst.cxx
+++ b/vcl/android/androidinst.cxx
@@ -32,11 +32,14 @@
 #include <jni.h>
 #include <android/log.h>
 #include <android/looper.h>
+#include <android/bitmap.h>
 #include <osl/detail/android-bootstrap.h>
 #include <osl/detail/android_native_app_glue.h>
 #include <rtl/strbuf.hxx>
 #include <basebmp/scanlineformats.hxx>
 
+static bool bHitIdle = false;
+
 class AndroidSalData : public SalGenericData
 {
 public:
@@ -260,26 +263,35 @@ void AndroidSalInstance::BlitFrameToWindow(ANativeWindow_Buffer *pOutBuffer,
     BlitFrameRegionToWindow(pOutBuffer, aDev, aWhole, 0, 0);
 }
 
-void AndroidSalInstance::RedrawWindows(ANativeWindow *pWindow)
+void AndroidSalInstance::RedrawWindows(ANativeWindow *pWindow, ANativeWindow_Buffer *pBuffer)
 {
-    if (!pWindow)
-        return;
-
     ANativeWindow_Buffer aOutBuffer;
     memset ((void *)&aOutBuffer, 0, sizeof (aOutBuffer));
 
-//    ARect aRect;
-    fprintf (stderr, "pre lock #3\n");
-    int32_t nRet = ANativeWindow_lock(pWindow, &aOutBuffer, NULL);
-    fprintf (stderr, "locked window %d returned " // rect:  %d,%d->%d,%d "
+    fprintf (stderr, "RedrawWindows\n");
+
+    int32_t nRet = 0;
+    if (pBuffer != NULL)
+        aOutBuffer = *pBuffer;
+    else
+    {
+        if (!pWindow)
+            return;
+
+        //    ARect aRect;
+        fprintf (stderr, "pre lock #3\n");
+        nRet = ANativeWindow_lock(pWindow, &aOutBuffer, NULL);
+    }
+    fprintf (stderr, "Frame count: %d locked window %d returned " // rect:  %d,%d->%d,%d "
              "buffer: %dx%d stride %d, format %d, bits %p\n",
+             (int)getFrames().size(),
              nRet, // aRect.left, aRect.top, aRect.right, aRect.bottom,
              aOutBuffer.width, aOutBuffer.height, aOutBuffer.stride,
              aOutBuffer.format, aOutBuffer.bits);
     if (aOutBuffer.bits != NULL)
     {
 
-#if 0   // pre-'clean' the buffer with cruft:
+#if 1   // pre-'clean' the buffer with cruft:
         // hard-code / guess at a format ...
         int32_t *p = (int32_t *)aOutBuffer.bits;
         for (int32_t y = 0; y < aOutBuffer.height; y++)
@@ -287,7 +299,6 @@ void AndroidSalInstance::RedrawWindows(ANativeWindow *pWindow)
             for (int32_t x = 0; x < aOutBuffer.stride; x++)
                 *p++ = (y << 24) + (x << 10) + 0xff ;
         }
-
 #endif
         int i = 0;
         std::list< SalFrame* >::const_iterator it;
@@ -328,7 +339,9 @@ void AndroidSalInstance::RedrawWindows(ANativeWindow *pWindow)
     }
     else
         fprintf (stderr, "no buffer for locked window\n");
-    ANativeWindow_unlockAndPost(pWindow);
+
+    if (pBuffer && pWindow)
+        ANativeWindow_unlockAndPost(pWindow);
 
     fprintf (stderr, "done render!\n");
     maRedrawRegion.SetEmpty();
@@ -372,7 +385,7 @@ void AndroidSalInstance::GetWorkArea( Rectangle& rRect )
 {
     if (!mpApp || !mpApp->window)
         rRect = Rectangle( Point( 0, 0 ),
-                           Size( 800, 600 ) );
+                           Size( 1280, 750 ) );
     else
         rRect = Rectangle( Point( 0, 0 ),
                            Size( ANativeWindow_getWidth( mpApp->window ),
@@ -627,6 +640,10 @@ void AndroidSalInstance::Wakeup()
 
 void AndroidSalInstance::DoReleaseYield (int nTimeoutMS)
 {
+    if (!bHitIdle)
+        fprintf( stderr, "hit idle !\n" );
+    bHitIdle = true;
+
     // Presumably this should never be called at all except in
     // NativeActivity-based apps with a GUI, like android/qa/desktop, where
     // the message pump is run here in vcl?
@@ -825,13 +842,14 @@ SalInstance *CreateSalInstance()
     fprintf (stderr, "Android: CreateSalInstance!\n");
     AndroidSalInstance* pInstance = new AndroidSalInstance( new SalYieldMutex() );
     new AndroidSalData( pInstance );
-    pInstance->AcquireYieldMutex(1);
+// FIXME: we init VCL in a different thread from where we run the mainloop [!] ...
+//    pInstance->AcquireYieldMutex(1);
     return pInstance;
 }
 
 void DestroySalInstance( SalInstance *pInst )
 {
-    pInst->ReleaseYieldMutex();
+//    pInst->ReleaseYieldMutex();
     delete pInst;
 }
 
@@ -869,5 +887,67 @@ int AndroidSalSystem::ShowNativeDialog( const rtl::OUString& rTitle,
     return 0;
 }
 
+// Render everything
+extern "C" SAL_JNI_EXPORT void JNICALL
+Java_org_libreoffice_android_examples_LODesktop_renderVCL(JNIEnv *env,
+                                                          jobject /* dummy */,
+                                                          jobject bitmap)
+{
+    if (!bHitIdle)
+        return;
+
+    AndroidBitmapInfo  info;
+    void*              pixels;
+    int                ret;
+
+    if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) {
+        fprintf(stderr, "AndroidBitmap_getInfo() failed ! error=%d", ret);
+        return;
+    }
+
+#if 0
+    if (info.format != ANDROID_BITMAP_FORMAT_RGB_565) {
+        fprintf(stderr, "Bitmap format is not RGB_565 !");
+        return;
+    }
+#endif
+
+    if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) {
+        fprintf(stderr, "AndroidBitmap_lockPixels() failed ! error=%d", ret);
+    }
+
+/*
+typedef struct ANativeWindow_Buffer {
+    // The number of pixels that are show horizontally.
+    int32_t width;
+
+    // The number of pixels that are shown vertically.
+    int32_t height;
+
+    // The number of *pixels* that a line in the buffer takes in
+    // memory.  This may be >= width.
+    int32_t stride;
+
+    // The format of the buffer.  One of WINDOW_FORMAT_*
+    int32_t format;
+
+    // The actual bits.
+    void* bits;
+
+    // Do not touch.
+    uint32_t reserved[6];
+} ANativeWindow_Buffer;
+*/
+
+    ANativeWindow_Buffer dummyOut; // look like a window for now ...
+    dummyOut.width = info.width;
+    dummyOut.height = info.height;
+    dummyOut.stride = info.stride / 4; // sigh !
+    dummyOut.format = info.format;
+    dummyOut.bits = pixels;
+    AndroidSalInstance::getInstance()->RedrawWindows (NULL, &dummyOut);
+
+    AndroidBitmap_unlockPixels(env, bitmap);
+}
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/android/androidinst.hxx b/vcl/inc/android/androidinst.hxx
index addec80..74acc80 100644
--- a/vcl/inc/android/androidinst.hxx
+++ b/vcl/inc/android/androidinst.hxx
@@ -62,7 +62,7 @@ public:
     // incoming android event handlers:
     void      onAppCmd     (struct android_app* app, int32_t cmd);
     int32_t   onInputEvent (struct android_app* app, AInputEvent* event);
-    void      RedrawWindows(ANativeWindow *pWindow);
+    void RedrawWindows(ANativeWindow *pWindow, ANativeWindow_Buffer *pBuffer = NULL);
     SalFrame *getFocusFrame() const;
 
     void      damaged(AndroidSalFrame *frame, const Rectangle &rRect);
diff --git a/vcl/source/app/svmain.cxx b/vcl/source/app/svmain.cxx
index 308bfb7..58cec57 100644
--- a/vcl/source/app/svmain.cxx
+++ b/vcl/source/app/svmain.cxx
@@ -156,10 +156,13 @@ int ImplSVMain()
 
     sal_Bool bInit = InitVCL();
 
+    fprintf (stderr, "init vcl %d\n", bInit);
+
     if( bInit )
     {
         // call application main
         pSVData->maAppData.mbInAppMain = sal_True;
+        fprintf(stderr, "call app  main from vcl!\n");
         nReturn = pSVData->mpApp->Main();
         pSVData->maAppData.mbInAppMain = sal_False;
     }
commit 52a8744afee2cd589813f0377d93f821fce7aedd
Author: Michael Meeks <michael.meeks at suse.com>
Date:   Thu Dec 13 19:33:42 2012 +0000

    Revert "Clean up remains of NativeActivity-based Android app support"
    
    This reverts commit cecc926070ee3d2ad6296fc5e0cfcde8642bb140.
    
    Conflicts:
    	sal/android/lo-bootstrap.c
    	sal/inc/osl/detail/android-bootstrap.h

diff --git a/android/Bootstrap/src/org/libreoffice/android/Bootstrap.java b/android/Bootstrap/src/org/libreoffice/android/Bootstrap.java
index aaa6e5f..3db5844 100644
--- a/android/Bootstrap/src/org/libreoffice/android/Bootstrap.java
+++ b/android/Bootstrap/src/org/libreoffice/android/Bootstrap.java
@@ -29,15 +29,27 @@
 package org.libreoffice.android;
 
 import android.app.Activity;
+import android.app.NativeActivity;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
 import android.util.Log;
 
+import fi.iki.tml.CommandLine;
+
 import java.io.File;
 import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.util.Arrays;
+import java.util.Scanner;
+
+// We extend NativeActivity so that we can get at the intent of the
+// activity and its extra parameters, that we use to tell us what
+// actual LibreOffice "program" to run. I.e. something that on desktop
+// OSes would be a program, but for Android is actually built as a
+// shared object, with a "lo_main" function.
 
-public class Bootstrap
+public class Bootstrap extends NativeActivity
 {
     private static String TAG = "lo-bootstrap";
 
@@ -50,6 +62,9 @@ public class Bootstrap
                                         String cacheDir,
                                         String apkFile);
 
+    public static native boolean setup(Object lo_main_argument,
+                                       int lo_main_delay);
+
     // Extracts files in the .apk that need to be extraced into the app's tree
     static native void extract_files();
 
@@ -95,8 +110,8 @@ public class Bootstrap
 
     static boolean setup_done = false;
 
-    // This setup() method should be called from the upper Java level of
-    // LO-based apps.
+    // This setup() method is called 1) in apps that use *this* class as their activity from onCreate(),
+    // and 2) should be called from other kinds of LO code using apps.
     public static synchronized void setup(Activity activity)
     {
         if (setup_done)
@@ -137,6 +152,92 @@ public class Bootstrap
         putenv("TMPDIR=" + activity.getCacheDir().getAbsolutePath());
     }
 
+    @Override
+    protected void onCreate(Bundle savedInstanceState)
+    {
+        setup(this);
+
+        String mainLibrary = getIntent().getStringExtra("lo-main-library");
+
+        if (mainLibrary == null)
+            mainLibrary = "libcppunittester";
+
+        mainLibrary += ".so";
+
+        Log.i(TAG, String.format("mainLibrary=%s", mainLibrary));
+
+        // Get "command line" to pass to the LO "program"
+        String cmdLine = getIntent().getStringExtra("lo-main-cmdline");
+
+        if (cmdLine == null) {
+            String indirectFile = getIntent().getStringExtra("lo-main-indirect-cmdline");
+            if (indirectFile != null) {
+                try {
+                    // Somewhat stupid but short way to read a file into a string
+                    cmdLine = new Scanner(new File(indirectFile), "UTF-8").useDelimiter("\\A").next().trim();
+                }
+                catch (java.io.FileNotFoundException e) {
+                    Log.i(TAG, String.format("Could not read %s: %s",indirectFile, e.toString()));
+                }
+            }
+
+            if (cmdLine == null)
+                cmdLine = "";
+        }
+
+        Log.i(TAG, String.format("cmdLine=%s", cmdLine));
+
+        String[] argv = CommandLine.split(cmdLine);
+
+        // Handle env var assignments in the command line.
+        while (argv.length > 0 &&
+               argv[0].matches("[A-Z_]+=.*")) {
+            putenv(argv[0]);
+            argv = Arrays.copyOfRange(argv, 1, argv.length);
+        }
+
+        // argv[0] will be replaced by android_main() in lo-bootstrap.c by the
+        // pathname of the mainLibrary.
+        String[] newargv = new String[argv.length + 1];
+        newargv[0] = "dummy-program-name";
+        System.arraycopy(argv, 0, newargv, 1, argv.length);
+        argv = newargv;
+
+        // Load the LO "program" here
+        System.loadLibrary(mainLibrary);
+
+        // Start a strace on ourself if requested.
+
+        // Note that the started strace will have its stdout and
+        // stderr connected to /dev/null, so you definitely want to
+        // specify an -o option in the lo-strace extra. Also, strace
+        // will trace only *this* thread, which is not the one that
+        // eventually will run android_main() and lo_main(), so you
+        // also want the -f option.
+        String strace_args = getIntent().getStringExtra("lo-strace");
+        if (strace_args != null)
+            system("/system/xbin/strace -p " + getpid() + " " + (strace_args != "yes" ? strace_args : "" ) + " &");
+
+        int delay = 0;
+        String sdelay = getIntent().getStringExtra("lo-main-delay");
+        if (sdelay != null)
+            delay = Integer.parseInt(sdelay);
+
+        // Tell lo-bootstrap.c the stuff it needs to know
+        if (!setup(argv, delay))
+            return;
+
+        // Finally, call our super-class, NativeActivity's onCreate(),
+        // which eventually calls the ANativeActivity_onCreate() in
+        // android_native_app_glue.c, which starts a thread in which
+        // android_main() from lo-bootstrap.c is called.
+
+        // android_main() calls the lo_main() defined in sal/main.h
+        // through the function pointer passed to setup() above, with
+        // the argc and argv also saved from the setup() call.
+        super.onCreate(savedInstanceState);
+    }
+
     // Now with static loading we always have all native code in one native
     // library which we always call liblo-native-code.so, regardless of the
     // app. The library has already been unpacked into /data/data/<app
diff --git a/desktop/Library_sofficeapp.mk b/desktop/Library_sofficeapp.mk
index 2a635b5..4687b5d 100644
--- a/desktop/Library_sofficeapp.mk
+++ b/desktop/Library_sofficeapp.mk
@@ -94,4 +94,13 @@ ifeq ($(ENABLE_TELEPATHY),TRUE)
 $(eval $(call gb_Library_use_libraries,sofficeapp,tubes))
 endif
 
+#
+# We need the lo_main symbol for our boostrap loader
+#
+ifeq ($(OS),ANDROID)
+$(eval $(call gb_Library_add_cobjects,sofficeapp,\
+    desktop/source/app/main \
+))
+endif
+
 # vim: set ts=4 sw=4 et:
diff --git a/sal/Library_sal.mk b/sal/Library_sal.mk
index 533fb6c..0bd28fa 100644
--- a/sal/Library_sal.mk
+++ b/sal/Library_sal.mk
@@ -181,7 +181,7 @@ $(eval $(call gb_Library_add_exception_objects,sal,\
 	sal/osl/unx/module \
 	sal/osl/unx/process \
 	sal/osl/unx/process_impl \
-        $(if $(filter DESKTOP,$(BUILD_TYPE)), sal/osl/unx/salinit) \
+	sal/osl/unx/salinit \
 	sal/osl/unx/uunxapi \
 ))
 $(eval $(call gb_Library_add_cobjects,sal,\
diff --git a/sal/Module_sal.mk b/sal/Module_sal.mk
index 7d20fa3..af97e48 100644
--- a/sal/Module_sal.mk
+++ b/sal/Module_sal.mk
@@ -31,8 +31,7 @@ $(eval $(call gb_Module_Module,sal))
 $(eval $(call gb_Module_add_targets,sal,\
 	CustomTarget_generated \
 	CustomTarget_sal_allheaders \
-	$(if $(filter DESKTOP,$(BUILD_TYPE)), \
-		Executable_cppunittester) \
+	Executable_cppunittester \
 	$(if $(filter $(OS),ANDROID), \
 		Library_lo-bootstrap) \
 	Library_sal \
@@ -46,14 +45,13 @@ $(eval $(call gb_Module_add_targets,sal,\
 ))
 
 ifneq (,$(filter DESKTOP,$(BUILD_TYPE)))
-
 $(eval $(call gb_Module_add_targets,sal,\
 		Executable_osl_process_child \
 ))
-
 $(eval $(call gb_Module_add_check_targets,sal,\
 		CppunitTest_sal_osl_process \
 ))
+endif
 
 $(eval $(call gb_Module_add_check_targets,sal,\
 	$(if $(filter TRUE,$(DISABLE_DYNLOADING)),,CppunitTest_Module_DLL) \
@@ -90,8 +88,6 @@ $(eval $(call gb_Module_add_check_targets,sal,\
 	CppunitTest_sal_rtl_math \
 ))
 
-endif
-
 # CppunitTest_sal_osl_pipe has circular dependency on unotest
 # $(eval $(call gb_Module_add_subsequentcheck_targets,sal,\
 	CppunitTest_sal_osl_pipe \
diff --git a/sal/Package_inc.mk b/sal/Package_inc.mk
index 15fb477..0b78afd 100644
--- a/sal/Package_inc.mk
+++ b/sal/Package_inc.mk
@@ -61,6 +61,7 @@ $(eval $(call gb_Package_add_file,sal_inc,inc/osl/thread.hxx,osl/thread.hxx))
 $(eval $(call gb_Package_add_file,sal_inc,inc/osl/time.h,osl/time.h))
 $(eval $(call gb_Package_add_file,sal_inc,inc/osl/util.h,osl/util.h))
 $(eval $(call gb_Package_add_file,sal_inc,inc/osl/detail/file.h,osl/detail/file.h))
+$(eval $(call gb_Package_add_file,sal_inc,inc/osl/detail/android_native_app_glue.h,osl/detail/android_native_app_glue.h))
 $(eval $(call gb_Package_add_file,sal_inc,inc/osl/detail/android-bootstrap.h,osl/detail/android-bootstrap.h))
 $(eval $(call gb_Package_add_file,sal_inc,inc/osl/detail/ios-bootstrap.h,osl/detail/ios-bootstrap.h))
 $(eval $(call gb_Package_add_file,sal_inc,inc/osl/detail/component-mapping.h,osl/detail/component-mapping.h))
diff --git a/sal/android/android_native_app_glue.c b/sal/android/android_native_app_glue.c
new file mode 100644
index 0000000..0c2dd5e
--- /dev/null
+++ b/sal/android/android_native_app_glue.c
@@ -0,0 +1,459 @@
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Copyright (C) 2010 The Android Open Source Project
+ *
+ *   Licensed 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
+ */
+
+#include <jni.h>
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+#include "osl/detail/android-bootstrap.h"
+#include "osl/detail/android_native_app_glue.h"
+#include <android/log.h>
+
+#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "lo-bootstrap", __VA_ARGS__))
+
+static void free_saved_state(struct android_app* android_app) {
+    pthread_mutex_lock(&android_app->mutex);
+    if (android_app->savedState != NULL) {
+        free(android_app->savedState);
+        android_app->savedState = NULL;
+        android_app->savedStateSize = 0;
+    }
+    pthread_mutex_unlock(&android_app->mutex);
+}
+
+int8_t android_app_read_cmd(struct android_app* android_app) {
+    int8_t cmd;
+    if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) {
+        switch (cmd) {
+            case APP_CMD_SAVE_STATE:
+                free_saved_state(android_app);
+                break;
+        }
+        return cmd;
+    } else {
+        LOGI("No data on command pipe!");
+    }
+    return -1;
+}
+
+static void print_cur_config(struct android_app* android_app) {
+    char lang[2], country[2];
+    AConfiguration_getLanguage(android_app->config, lang);
+    AConfiguration_getCountry(android_app->config, country);
+
+    LOGI("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
+            "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
+            "modetype=%d modenight=%d",
+            AConfiguration_getMcc(android_app->config),
+            AConfiguration_getMnc(android_app->config),
+            lang[0], lang[1], country[0], country[1],
+            AConfiguration_getOrientation(android_app->config),
+            AConfiguration_getTouchscreen(android_app->config),
+            AConfiguration_getDensity(android_app->config),
+            AConfiguration_getKeyboard(android_app->config),
+            AConfiguration_getNavigation(android_app->config),
+            AConfiguration_getKeysHidden(android_app->config),
+            AConfiguration_getNavHidden(android_app->config),
+            AConfiguration_getSdkVersion(android_app->config),
+            AConfiguration_getScreenSize(android_app->config),
+            AConfiguration_getScreenLong(android_app->config),
+            AConfiguration_getUiModeType(android_app->config),
+            AConfiguration_getUiModeNight(android_app->config));
+}
+
+void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) {
+    switch (cmd) {
+        case APP_CMD_INPUT_CHANGED:
+            LOGI("APP_CMD_INPUT_CHANGED\n");
+            pthread_mutex_lock(&android_app->mutex);
+            if (android_app->inputQueue != NULL) {
+                AInputQueue_detachLooper(android_app->inputQueue);
+            }
+            android_app->inputQueue = android_app->pendingInputQueue;
+            if (android_app->inputQueue != NULL) {
+                LOGI("Attaching input queue to looper");
+                AInputQueue_attachLooper(android_app->inputQueue,
+                        android_app->looper, LOOPER_ID_INPUT, NULL,
+                        &android_app->inputPollSource);
+            }
+            pthread_cond_broadcast(&android_app->cond);
+            pthread_mutex_unlock(&android_app->mutex);
+            break;
+
+        case APP_CMD_INIT_WINDOW:
+            LOGI("APP_CMD_INIT_WINDOW\n");
+            pthread_mutex_lock(&android_app->mutex);
+            android_app->window = android_app->pendingWindow;
+            pthread_cond_broadcast(&android_app->cond);
+            pthread_mutex_unlock(&android_app->mutex);
+            break;
+
+        case APP_CMD_TERM_WINDOW:
+            LOGI("APP_CMD_TERM_WINDOW\n");
+            pthread_cond_broadcast(&android_app->cond);
+            break;
+
+        case APP_CMD_RESUME:
+        case APP_CMD_START:
+        case APP_CMD_PAUSE:
+        case APP_CMD_STOP:
+            LOGI("activityState=%d\n", cmd);
+            pthread_mutex_lock(&android_app->mutex);
+            android_app->activityState = cmd;
+            pthread_cond_broadcast(&android_app->cond);
+            pthread_mutex_unlock(&android_app->mutex);
+            break;
+
+        case APP_CMD_CONFIG_CHANGED:
+            LOGI("APP_CMD_CONFIG_CHANGED\n");
+            AConfiguration_fromAssetManager(android_app->config,
+                    android_app->activity->assetManager);
+            print_cur_config(android_app);
+            break;
+
+        case APP_CMD_DESTROY:
+            LOGI("APP_CMD_DESTROY\n");
+            android_app->destroyRequested = 1;
+            break;
+    }
+}
+
+void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) {
+    switch (cmd) {
+        case APP_CMD_TERM_WINDOW:
+            LOGI("APP_CMD_TERM_WINDOW\n");
+            pthread_mutex_lock(&android_app->mutex);
+            android_app->window = NULL;
+            pthread_cond_broadcast(&android_app->cond);
+            pthread_mutex_unlock(&android_app->mutex);
+            break;
+        case APP_CMD_WINDOW_REDRAW_NEEDED:
+            LOGI("APP_CMD_WINDOW_REDRAW_NEEDED - post\n");
+            pthread_mutex_lock(&android_app->mutex);
+            pthread_cond_broadcast(&android_app->cond);
+            pthread_mutex_unlock(&android_app->mutex);
+	    break;
+
+        case APP_CMD_SAVE_STATE:
+            LOGI("APP_CMD_SAVE_STATE\n");
+            pthread_mutex_lock(&android_app->mutex);
+            android_app->stateSaved = 1;
+            pthread_cond_broadcast(&android_app->cond);
+            pthread_mutex_unlock(&android_app->mutex);
+            break;
+
+        case APP_CMD_RESUME:
+            free_saved_state(android_app);
+            break;
+    }
+}
+
+void app_dummy() {
+
+}
+
+static void android_app_destroy(struct android_app* android_app) {
+    LOGI("android_app_destroy!");
+    free_saved_state(android_app);
+    pthread_mutex_lock(&android_app->mutex);
+    if (android_app->inputQueue != NULL) {
+        AInputQueue_detachLooper(android_app->inputQueue);
+    }
+    AConfiguration_delete(android_app->config);
+    android_app->destroyed = 1;
+    pthread_cond_broadcast(&android_app->cond);
+    pthread_mutex_unlock(&android_app->mutex);
+    // Can't touch android_app object after this.
+}
+
+static void process_input(struct android_app* app, struct android_poll_source* source) {
+    AInputEvent* event = NULL;
+    (void) source;
+    if (AInputQueue_getEvent(app->inputQueue, &event) >= 0) {
+        LOGI("New input event: type=%d\n", AInputEvent_getType(event));
+        if (AInputQueue_preDispatchEvent(app->inputQueue, event)) {
+            return;
+        }
+        int32_t handled = 0;
+        if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event);
+        AInputQueue_finishEvent(app->inputQueue, event, handled);
+    } else {
+        LOGI("Failure reading next input event: %s\n", strerror(errno));
+    }
+}
+
+static void process_cmd(struct android_app* app, struct android_poll_source* source) {
+    int8_t cmd = android_app_read_cmd(app);
+    (void) source;
+    android_app_pre_exec_cmd(app, cmd);
+    if (app->onAppCmd != NULL) app->onAppCmd(app, cmd);
+    android_app_post_exec_cmd(app, cmd);
+}
+
+static void* android_app_entry(void* param) {
+    struct android_app* android_app = (struct android_app*)param;
+
+    android_app->config = AConfiguration_new();
+    AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager);
+
+    print_cur_config(android_app);
+
+    android_app->cmdPollSource.id = LOOPER_ID_MAIN;
+    android_app->cmdPollSource.app = android_app;
+    android_app->cmdPollSource.process = process_cmd;
+    android_app->inputPollSource.id = LOOPER_ID_INPUT;
+    android_app->inputPollSource.app = android_app;
+    android_app->inputPollSource.process = process_input;
+
+    ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
+    ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL,
+            &android_app->cmdPollSource);
+    android_app->looper = looper;
+
+    pthread_mutex_lock(&android_app->mutex);
+    android_app->running = 1;
+    pthread_cond_broadcast(&android_app->cond);
+    pthread_mutex_unlock(&android_app->mutex);
+
+    android_main(android_app);
+
+    android_app_destroy(android_app);
+    return NULL;
+}
+
+// --------------------------------------------------------------------
+// Native activity interaction (called from main thread)
+// --------------------------------------------------------------------
+
+static struct android_app* android_app_create(ANativeActivity* activity,
+        void* savedState, size_t savedStateSize) {
+    struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app));
+    memset(android_app, 0, sizeof(struct android_app));
+    android_app->activity = activity;
+
+    pthread_mutex_init(&android_app->mutex, NULL);
+    pthread_cond_init(&android_app->cond, NULL);
+
+    if (savedState != NULL) {
+        android_app->savedState = malloc(savedStateSize);
+        android_app->savedStateSize = savedStateSize;
+        memcpy(android_app->savedState, savedState, savedStateSize);
+    }
+
+    int msgpipe[2];
+    if (pipe(msgpipe)) {
+        LOGI("could not create pipe: %s", strerror(errno));
+    }
+    android_app->msgread = msgpipe[0];
+    android_app->msgwrite = msgpipe[1];
+
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+    pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
+
+    // Wait for thread to start.
+    pthread_mutex_lock(&android_app->mutex);
+    while (!android_app->running) {
+        pthread_cond_wait(&android_app->cond, &android_app->mutex);
+    }
+    pthread_mutex_unlock(&android_app->mutex);
+
+    return android_app;
+}
+
+static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) {
+    if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) {
+        LOGI("Failure writing android_app cmd: %s\n", strerror(errno));
+    }
+}
+
+static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) {
+    pthread_mutex_lock(&android_app->mutex);
+    android_app->pendingInputQueue = inputQueue;
+    android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED);
+    while (android_app->inputQueue != android_app->pendingInputQueue) {
+        pthread_cond_wait(&android_app->cond, &android_app->mutex);
+    }
+    pthread_mutex_unlock(&android_app->mutex);
+}
+
+static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) {
+    pthread_mutex_lock(&android_app->mutex);
+    if (android_app->pendingWindow != NULL) {
+        android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
+    }
+    android_app->pendingWindow = window;
+    if (window != NULL) {
+        android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW);
+    }
+    while (android_app->window != android_app->pendingWindow) {
+        pthread_cond_wait(&android_app->cond, &android_app->mutex);
+    }
+    pthread_mutex_unlock(&android_app->mutex);
+}
+
+static void android_app_window_redraw_needed(struct android_app* android_app, ANativeWindow* window) {
+    pthread_mutex_lock(&android_app->mutex);
+    if (window != NULL) {
+        android_app_write_cmd(android_app, APP_CMD_WINDOW_REDRAW_NEEDED);
+    }
+    while (android_app->window != android_app->pendingWindow) {
+        pthread_cond_wait(&android_app->cond, &android_app->mutex);
+    }
+    pthread_mutex_unlock(&android_app->mutex);
+}
+
+static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) {
+    pthread_mutex_lock(&android_app->mutex);
+    android_app_write_cmd(android_app, cmd);
+    while (android_app->activityState != cmd) {
+        pthread_cond_wait(&android_app->cond, &android_app->mutex);
+    }
+    pthread_mutex_unlock(&android_app->mutex);
+}
+
+static void android_app_free(struct android_app* android_app) {
+    pthread_mutex_lock(&android_app->mutex);
+    android_app_write_cmd(android_app, APP_CMD_DESTROY);
+    while (!android_app->destroyed) {
+        pthread_cond_wait(&android_app->cond, &android_app->mutex);
+    }
+    pthread_mutex_unlock(&android_app->mutex);
+
+    close(android_app->msgread);
+    close(android_app->msgwrite);
+    pthread_cond_destroy(&android_app->cond);
+    pthread_mutex_destroy(&android_app->mutex);
+    free(android_app);
+}
+
+static void onDestroy(ANativeActivity* activity) {
+    LOGI("Destroy: %p\n", activity);
+    android_app_free((struct android_app*)activity->instance);
+}
+
+static void onStart(ANativeActivity* activity) {
+    LOGI("Start: %p\n", activity);
+    android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START);
+}
+
+static void onResume(ANativeActivity* activity) {
+    LOGI("Resume: %p\n", activity);
+    android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME);
+}
+
+static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
+    struct android_app* android_app = (struct android_app*)activity->instance;
+    void* savedState = NULL;
+
+    LOGI("SaveInstanceState: %p\n", activity);
+    pthread_mutex_lock(&android_app->mutex);
+    android_app->stateSaved = 0;
+    android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
+    while (!android_app->stateSaved) {
+        pthread_cond_wait(&android_app->cond, &android_app->mutex);
+    }
+
+    if (android_app->savedState != NULL) {
+        savedState = android_app->savedState;
+        *outLen = android_app->savedStateSize;
+        android_app->savedState = NULL;
+        android_app->savedStateSize = 0;
+    }
+
+    pthread_mutex_unlock(&android_app->mutex);
+
+    return savedState;
+}
+
+static void onPause(ANativeActivity* activity) {
+    LOGI("Pause: %p\n", activity);
+    android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE);
+}
+
+static void onStop(ANativeActivity* activity) {
+    LOGI("Stop: %p\n", activity);
+    android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP);
+}
+
+static void onConfigurationChanged(ANativeActivity* activity) {
+    struct android_app* android_app = (struct android_app*)activity->instance;
+    LOGI("ConfigurationChanged: %p\n", activity);
+    android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED);
+}
+
+static void onLowMemory(ANativeActivity* activity) {
+    struct android_app* android_app = (struct android_app*)activity->instance;
+    LOGI("LowMemory: %p\n", activity);
+    android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY);
+}
+
+static void onWindowFocusChanged(ANativeActivity* activity, int focused) {
+    LOGI("WindowFocusChanged: %p -- %d\n", activity, focused);
+    android_app_write_cmd((struct android_app*)activity->instance,
+            focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS);
+}
+
+static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
+    LOGI("NativeWindowCreated: %p -- %p\n", activity, window);
+    android_app_set_window((struct android_app*)activity->instance, window);
+}
+
+static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
+    LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window);
+    android_app_set_window((struct android_app*)activity->instance, NULL);
+}
+
+static void onNativeWindowRedrawNeeded(ANativeActivity* activity, ANativeWindow* window) {
+    LOGI("onNativeWindowRedrawNeeded: %p -- %p\n", activity, window);
+    android_app_window_redraw_needed((struct android_app*)activity->instance, window);
+}
+
+static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
+    LOGI("InputQueueCreated: %p -- %p\n", activity, queue);
+    android_app_set_input((struct android_app*)activity->instance, queue);
+}
+
+static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
+    LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue);
+    android_app_set_input((struct android_app*)activity->instance, NULL);
+}
+
+__attribute__ ((visibility("default"))) void ANativeActivity_onCreate(ANativeActivity* activity,
+        void* savedState, size_t savedStateSize) {
+    LOGI("Creating: %p\n", activity);
+    activity->callbacks->onDestroy = onDestroy;
+    activity->callbacks->onStart = onStart;
+    activity->callbacks->onResume = onResume;
+    activity->callbacks->onSaveInstanceState = onSaveInstanceState;
+    activity->callbacks->onPause = onPause;
+    activity->callbacks->onStop = onStop;
+    activity->callbacks->onConfigurationChanged = onConfigurationChanged;
+    activity->callbacks->onLowMemory = onLowMemory;
+    activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
+    activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
+    activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
+    activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded;
+    activity->callbacks->onInputQueueCreated = onInputQueueCreated;
+    activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
+
+    activity->instance = android_app_create(activity, savedState, savedStateSize);
+}
diff --git a/sal/android/lo-bootstrap.c b/sal/android/lo-bootstrap.c
index 61f2c76..9aeb32b 100644
--- a/sal/android/lo-bootstrap.c
+++ b/sal/android/lo-bootstrap.c
@@ -30,22 +30,19 @@
  * instead of those above.
  */
 
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
 #include <sys/stat.h>
 #include <sys/time.h>
-#include <time.h>
 
-#include <dlfcn.h>
+#include <unistd.h>
 #include <fcntl.h>
-#include <pthread.h>
+#include <dlfcn.h>
 #include <sys/mman.h>
-#include <unistd.h>
 
-#include <jni.h>
 #include <zlib.h>
+#include <jni.h>
 
 #include <android/log.h>
 
@@ -53,6 +50,12 @@
 
 #include "osl/detail/android-bootstrap.h"
 
+#pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
+
+#include "android_native_app_glue.c"
+
+#pragma GCC diagnostic warning "-Wdeclaration-after-statement"
+
 #undef LOGI
 #undef LOGW
 
@@ -69,6 +72,15 @@ struct engine {
     int dummy;
 };
 
+/* These vars are valid / used only when this library is used from
+ *  NativeActivity-based apps.
+ */
+static struct android_app *app;
+static int (*lo_main)(int, const char **);
+static int lo_main_argc;
+static const char **lo_main_argv;
+static int sleep_time = 0;
+
 /* These are valid / used in all apps. */
 static const char *data_dir;
 static const char *cache_dir;
@@ -266,6 +278,26 @@ setup_assets_tree(void)
     return 1;
 }
 
+static void
+engine_handle_cmd(struct android_app* state,
+                  int32_t cmd)
+{
+    (void) state;
+
+    switch (cmd) {
+    case APP_CMD_SAVE_STATE:
+        break;
+    case APP_CMD_INIT_WINDOW:
+        break;
+    case APP_CMD_TERM_WINDOW:
+        break;
+    case APP_CMD_GAINED_FOCUS:
+        break;
+    case APP_CMD_LOST_FOCUS:
+        break;
+    }
+}
+
 /* The lo-native-code shared library is always loaded from Java, so this is
  * always called by JNI first.
  */
@@ -372,6 +404,7 @@ get_jni_string_array(JNIEnv *env,
         const char *s = (*env)->GetStringUTFChars(env, (*env)->GetObjectArrayElement(env, strv, i), NULL);
         (*argv)[i] = strdup(s);
         (*env)->ReleaseStringUTFChars(env, (*env)->GetObjectArrayElement(env, strv, i), s);
+        /* LOGI("argv[%d] = %s", i, lo_main_argv[i]); */
     }
     (*argv)[*argc] = NULL;
 
@@ -379,6 +412,26 @@ get_jni_string_array(JNIEnv *env,
 }
 
 
+// public static native boolean setup(Object lo_main_argument,
+//                                    int lo_main_delay);
+
+__attribute__ ((visibility("default")))
+jboolean
+Java_org_libreoffice_android_Bootstrap_setup__Ljava_lang_Object_2I(JNIEnv* env,
+                                                                    jobject clazz,
+                                                                    jobject lo_main_argument,
+                                                                    jint lo_main_delay)
+{
+    (void) clazz;
+
+    if (!get_jni_string_array(env, "setup: lo_main_argument", lo_main_argument, &lo_main_argc, &lo_main_argv))
+        return JNI_FALSE;
+
+    sleep_time = lo_main_delay;
+
+    return JNI_TRUE;
+}
+
 // public static native int getpid();
 
 __attribute__ ((visibility("default")))
@@ -1257,4 +1310,59 @@ lo_get_app_data_dir(void)
     return data_dir;
 }
 
+__attribute__ ((visibility("default")))
+struct android_app *
+lo_get_app(void)
+{
+    return app;
+}
+
+/* Note that android_main() is used only in NativeActivity-based apps.  Only
+ * the android/qa/sc unit test app is such, and it is unclear whether there is
+ * any reason to continue maintaining that buildable.
+ */
+__attribute__ ((visibility("default")))
+void
+android_main(struct android_app* state)
+{
+    jint nRet;
+    JNIEnv *pEnv = NULL;
+    struct engine engine;
+    Dl_info lo_main_info;
+    JavaVMAttachArgs aArgs = {
+        JNI_VERSION_1_2,
+        "LibreOfficeThread",
+        NULL
+    };
+
+    fprintf (stderr, "android_main in thread: %d\n", (int)pthread_self());
+
+    if (sleep_time != 0) {
+        LOGI("android_main: Sleeping for %d seconds, start ndk-gdb NOW if that is your intention", sleep_time);
+        sleep(sleep_time);
+    }
+
+    nRet = (*(*state->activity->vm)->AttachCurrentThreadAsDaemon)(state->activity->vm, &pEnv, &aArgs);
+    fprintf (stderr, "attach thread returned %d %p\n", nRet, pEnv);
+
+    app = state;
+
+    memset(&engine, 0, sizeof(engine));
+    state->userData = &engine;
+    state->onAppCmd = engine_handle_cmd;
+
+    /* Look up lo_main() dynamically even if it is in the same .so as this code,
+     * but that is only in the case for code built to be used in a NativeActivity-based app.
+     */
+    lo_main = dlsym(RTLD_DEFAULT, "lo_main");
+
+    if (dladdr(lo_main, &lo_main_info) != 0) {
+        lo_main_argv[0] = lo_main_info.dli_fname;
+    }
+
+    lo_main(lo_main_argc, lo_main_argv);
+    nRet = (*(*state->activity->vm)->DetachCurrentThread)(state->activity->vm);
+    fprintf (stderr, "exit android_main\n");
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/inc/osl/detail/android-bootstrap.h b/sal/inc/osl/detail/android-bootstrap.h
index 672536f..d1022f9 100644
--- a/sal/inc/osl/detail/android-bootstrap.h
+++ b/sal/inc/osl/detail/android-bootstrap.h
@@ -62,6 +62,8 @@ JavaVM *lo_get_javavm(void);
 
 const char *lo_get_app_data_dir(void);
 
+struct android_app *lo_get_app(void);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/sal/inc/osl/detail/android_native_app_glue.h b/sal/inc/osl/detail/android_native_app_glue.h
new file mode 100644
index 0000000..862ca9e
--- /dev/null
+++ b/sal/inc/osl/detail/android_native_app_glue.h
@@ -0,0 +1,351 @@
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Copyright (C) 2010 The Android Open Source Project
+ *
+ *   Licensed 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
+ */
+
+#ifndef _ANDROID_NATIVE_APP_GLUE_H
+#define _ANDROID_NATIVE_APP_GLUE_H
+#if defined(ANDROID)
+
+#include <poll.h>
+#include <pthread.h>
+#include <sched.h>
+
+#include <android/configuration.h>
+#include <android/looper.h>
+#include <android/native_activity.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The native activity interface provided by <android/native_activity.h>
+ * is based on a set of application-provided callbacks that will be called
+ * by the Activity's main thread when certain events occur.
+ *
+ * This means that each one of this callbacks _should_ _not_ block, or they
+ * risk having the system force-close the application. This programming
+ * model is direct, lightweight, but constraining.
+ *
+ * The 'threaded_native_app' static library is used to provide a different
+ * execution model where the application can implement its own main event
+ * loop in a different thread instead. Here's how it works:
+ *
+ * 1/ The application must provide a function named "android_main()" that
+ *    will be called when the activity is created, in a new thread that is
+ *    distinct from the activity's main thread.
+ *
+ * 2/ android_main() receives a pointer to a valid "android_app" structure
+ *    that contains references to other important objects, e.g. the
+ *    ANativeActivity obejct instance the application is running in.
+ *
+ * 3/ the "android_app" object holds an ALooper instance that already
+ *    listens to two important things:
+ *
+ *      - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX
+ *        declarations below.
+ *
+ *      - input events coming from the AInputQueue attached to the activity.
+ *
+ *    Each of these correspond to an ALooper identifier returned by
+ *    ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT,
+ *    respectively.
+ *
+ *    Your application can use the same ALooper to listen to additional
+ *    file-descriptors.  They can either be callback based, or with return
+ *    identifiers starting with LOOPER_ID_USER.
+ *
+ * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event,
+ *    the returned data will point to an android_poll_source structure.  You
+ *    can call the process() function on it, and fill in android_app->onAppCmd
+ *    and android_app->onInputEvent to be called for your own processing
+ *    of the event.
+ *
+ *    Alternatively, you can call the low-level functions to read and process
+ *    the data directly...  look at the process_cmd() and process_input()
+ *    implementations in the glue to see how to do this.
+ *
+ * See the sample named "native-activity" that comes with the NDK with a
+ * full usage example.  Also look at the JavaDoc of NativeActivity.
+ */
+
+struct android_app;
+
+/**
+ * Data associated with an ALooper fd that will be returned as the "outData"
+ * when that source has data ready.
+ */
+struct android_poll_source {
+    // The identifier of this source.  May be LOOPER_ID_MAIN or
+    // LOOPER_ID_INPUT.
+    int32_t id;
+
+    // The android_app this ident is associated with.
+    struct android_app* app;
+
+    // Function to call to perform the standard processing of data from
+    // this source.
+    void (*process)(struct android_app* app, struct android_poll_source* source);
+};
+
+/**
+ * This is the interface for the standard glue code of a threaded
+ * application.  In this model, the application's code is running
+ * in its own thread separate from the main thread of the process.
+ * It is not required that this thread be associated with the Java
+ * VM, although it will need to be in order to make JNI calls any
+ * Java objects.
+ */
+struct android_app {
+    // The application can place a pointer to its own state object
+    // here if it likes.
+    void* userData;
+
+    // Fill this in with the function to process main app commands (APP_CMD_*)
+    void (*onAppCmd)(struct android_app* app, int32_t cmd);
+
+    // Fill this in with the function to process input events.  At this point
+    // the event has already been pre-dispatched, and it will be finished upon
+    // return.  Return 1 if you have handled the event, 0 for any default
+    // dispatching.
+    int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event);
+
+    // The ANativeActivity object instance that this app is running in.
+    ANativeActivity* activity;
+
+    // The current configuration the app is running in.
+    AConfiguration* config;
+
+    // This is the last instance's saved state, as provided at creation time.
+    // It is NULL if there was no state.  You can use this as you need; the
+    // memory will remain around until you call android_app_exec_cmd() for
+    // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL.
+    // These variables should only be changed when processing a APP_CMD_SAVE_STATE,
+    // at which point they will be initialized to NULL and you can malloc your
+    // state and place the information here.  In that case the memory will be
+    // freed for you later.
+    void* savedState;
+    size_t savedStateSize;
+
+    // The ALooper associated with the app's thread.
+    ALooper* looper;
+
+    // When non-NULL, this is the input queue from which the app will
+    // receive user input events.
+    AInputQueue* inputQueue;
+
+    // When non-NULL, this is the window surface that the app can draw in.
+    ANativeWindow* window;
+
+    // Current content rectangle of the window; this is the area where the
+    // window's content should be placed to be seen by the user.
+    ARect contentRect;
+
+    // Current state of the app's activity.  May be either APP_CMD_START,
+    // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below.
+    int activityState;
+
+    // This is non-zero when the application's NativeActivity is being
+    // destroyed and waiting for the app thread to complete.
+    int destroyRequested;
+
+    // -------------------------------------------------
+    // Below are "private" implementation of the glue code.
+
+    pthread_mutex_t mutex;
+    pthread_cond_t cond;
+
+    int msgread;
+    int msgwrite;
+
+    pthread_t thread;
+
+    struct android_poll_source cmdPollSource;
+    struct android_poll_source inputPollSource;
+
+    int running;
+    int stateSaved;
+    int destroyed;
+    int redrawNeeded;
+    AInputQueue* pendingInputQueue;
+    ANativeWindow* pendingWindow;
+    ARect pendingContentRect;
+};
+
+enum {
+    /**
+     * Looper data ID of commands coming from the app's main thread, which
+     * is returned as an identifier from ALooper_pollOnce().  The data for this
+     * identifier is a pointer to an android_poll_source structure.
+     * These can be retrieved and processed with android_app_read_cmd()
+     * and android_app_exec_cmd().
+     */
+    LOOPER_ID_MAIN = 1,
+
+    /**
+     * Looper data ID of events coming from the AInputQueue of the
+     * application's window, which is returned as an identifier from
+     * ALooper_pollOnce().  The data for this identifier is a pointer to an
+     * android_poll_source structure.  These can be read via the inputQueue
+     * object of android_app.
+     */
+    LOOPER_ID_INPUT = 2,
+
+    /**
+     * Start of user-defined ALooper identifiers.
+     */
+    LOOPER_ID_USER = 3,
+};
+
+enum {
+    /**
+     * Command from main thread: the AInputQueue has changed.  Upon processing
+     * this command, android_app->inputQueue will be updated to the new queue
+     * (or NULL).
+     */
+    APP_CMD_INPUT_CHANGED,
+
+    /**
+     * Command from main thread: a new ANativeWindow is ready for use.  Upon
+     * receiving this command, android_app->window will contain the new window
+     * surface.
+     */
+    APP_CMD_INIT_WINDOW,
+
+    /**
+     * Command from main thread: the existing ANativeWindow needs to be
+     * terminated.  Upon receiving this command, android_app->window still
+     * contains the existing window; after calling android_app_exec_cmd
+     * it will be set to NULL.
+     */
+    APP_CMD_TERM_WINDOW,
+
+    /**
+     * Command from main thread: the current ANativeWindow has been resized.
+     * Please redraw with its new size.
+     */
+    APP_CMD_WINDOW_RESIZED,
+
+    /**
+     * Command from main thread: the system needs that the current ANativeWindow
+     * be redrawn.  You should redraw the window before handing this to
+     * android_app_exec_cmd() in order to avoid transient drawing glitches.
+     */
+    APP_CMD_WINDOW_REDRAW_NEEDED,
+
+    /**
+     * Command from main thread: the content area of the window has changed,
+     * such as from the soft input window being shown or hidden.  You can
+     * find the new content rect in android_app::contentRect.
+     */
+    APP_CMD_CONTENT_RECT_CHANGED,
+
+    /**
+     * Command from main thread: the app's activity window has gained
+     * input focus.
+     */
+    APP_CMD_GAINED_FOCUS,
+
+    /**
+     * Command from main thread: the app's activity window has lost
+     * input focus.
+     */
+    APP_CMD_LOST_FOCUS,
+
+    /**
+     * Command from main thread: the current device configuration has changed.
+     */
+    APP_CMD_CONFIG_CHANGED,
+
+    /**
+     * Command from main thread: the system is running low on memory.
+     * Try to reduce your memory use.
+     */
+    APP_CMD_LOW_MEMORY,
+
+    /**
+     * Command from main thread: the app's activity has been started.
+     */
+    APP_CMD_START,
+
+    /**
+     * Command from main thread: the app's activity has been resumed.
+     */
+    APP_CMD_RESUME,
+
+    /**
+     * Command from main thread: the app should generate a new saved state
+     * for itself, to restore from later if needed.  If you have saved state,
+     * allocate it with malloc and place it in android_app.savedState with
+     * the size in android_app.savedStateSize.  The will be freed for you
+     * later.
+     */
+    APP_CMD_SAVE_STATE,
+
+    /**
+     * Command from main thread: the app's activity has been paused.
+     */
+    APP_CMD_PAUSE,
+
+    /**
+     * Command from main thread: the app's activity has been stopped.
+     */
+    APP_CMD_STOP,
+
+    /**
+     * Command from main thread: the app's activity is being destroyed,
+     * and waiting for the app thread to clean up and exit before proceeding.
+     */
+    APP_CMD_DESTROY,
+};
+
+/**
+ * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next
+ * app command message.
+ */
+int8_t android_app_read_cmd(struct android_app* android_app);
+
+/**
+ * Call with the command returned by android_app_read_cmd() to do the
+ * initial pre-processing of the given command.  You can perform your own
+ * actions for the command after calling this function.
+ */
+void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd);
+
+/**
+ * Call with the command returned by android_app_read_cmd() to do the
+ * final post-processing of the given command.  You must have done your own
+ * actions for the command before calling this function.
+ */
+void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd);
+
+/**
+ * Dummy function you can call to ensure glue code isn't stripped.
+ */
+void app_dummy();
+
+/**
+ * This is the function that application code must implement, representing
+ * the main entry to the app.
+ */
+extern void android_main(struct android_app* app);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID
+#endif /* _ANDROID_NATIVE_APP_GLUE_H */
diff --git a/sal/inc/sal/main.h b/sal/inc/sal/main.h
index 8648e38..7cdc9bc 100644
--- a/sal/inc/sal/main.h
+++ b/sal/inc/sal/main.h
@@ -38,7 +38,25 @@ SAL_DLLPUBLIC void SAL_CALL sal_detail_deinitialize();
 
 #if defined IOS || defined ANDROID
 
-#error No code that includes this should be built for iOS or Android
+#ifdef __cplusplus
+extern "C" SAL_DLLPUBLIC_EXPORT void lo_main(int argc, char **argv);
+#endif
+
+#define SAL_MAIN_WITH_ARGS_IMPL \
+SAL_DLLPUBLIC_EXPORT void lo_main(int argc, char **argv) \
+{ \
+    sal_detail_initialize(argc, argv); \
+    sal_main_with_args(argc, argv); \
+    sal_detail_deinitialize(); \
+}
+
+#define SAL_MAIN_IMPL \
+SAL_DLLPUBLIC_EXPORT void lo_main(int argc, char **argv) \
+{ \
+    sal_detail_initialize(argc, argv); \
+    sal_main(); \
+    sal_detail_deinitialize(); \
+}
 
 #else
 
diff --git a/sal/osl/unx/process_impl.cxx b/sal/osl/unx/process_impl.cxx
index 723d10b..4a10af5 100644
--- a/sal/osl/unx/process_impl.cxx
+++ b/sal/osl/unx/process_impl.cxx
@@ -104,10 +104,26 @@ oslProcessError SAL_CALL osl_bootstrap_getExecutableFile_Impl (
 {
     oslProcessError result = osl_Process_E_NotFound;
 
-#ifdef ANDROID
-    /* Now with just a single DSO, this one from lo-bootstrap.c is as good as
-     * any */
-    void * addr = dlsym (RTLD_DEFAULT, "JNI_OnLoad");
+#if defined(ANDROID) && !defined(DISABLE_DYNLOADING)
+    /* On Android we in theory want the address of the "lo_main()"
+     * function, as that is what corresponds to "main()" in
+     * LibreOffice programs on normal desktop OSes.
+     *
+     * But that is true only for apps with a "native activity", using
+     * <sal/main.h> and the org.libreoffice.android.Bootstrap
+     * mechanism. For more normal (?) Android apps that just use
+     * LibreOffice libraries (components) where the main program is in
+     * Java, that just use LibreOffice libraries, there is no
+     * lo_main(). (Note that we don't know for sure yet how
+     * complicated it might be to write such Android apps...)
+     *
+     * Maybe best to just pick some function in liblo-bootstrap.so
+     * which also such Java apps *must* load as the very first
+     * LibreOffice native library. We store all LibreOffice native
+     * shared libraries an app uses in the same folder anyway, so it
+     * doesn't really matter.
+     */
+    void * addr = (void *) &lo_dlopen;
 #else
     /* Determine address of "main()" function. */
     void * addr = dlsym (RTLD_DEFAULT, "main");
diff --git a/vcl/Module_vcl.mk b/vcl/Module_vcl.mk
index 116e815..f340800d 100644
--- a/vcl/Module_vcl.mk
+++ b/vcl/Module_vcl.mk
@@ -24,9 +24,8 @@ $(eval $(call gb_Module_add_targets,vcl,\
     CustomTarget_afm_hash \
     Library_vcl \
     Package_inc \
-    $(if $(filter DESKTOP,$(BUILD_TYPE)), \
-		StaticLibrary_vclmain \
-		Executable_ui-previewer) \
+    StaticLibrary_vclmain \
+	Executable_ui-previewer \
     UI_vcl \
 ))
 
diff --git a/vcl/android/androidinst.cxx b/vcl/android/androidinst.cxx
index fc1c3f9..916769e 100644
--- a/vcl/android/androidinst.cxx
+++ b/vcl/android/androidinst.cxx
@@ -31,7 +31,9 @@
 #include <generic/gendata.hxx>
 #include <jni.h>
 #include <android/log.h>
+#include <android/looper.h>
 #include <osl/detail/android-bootstrap.h>
+#include <osl/detail/android_native_app_glue.h>
 #include <rtl/strbuf.hxx>
 #include <basebmp/scanlineformats.hxx>
 
@@ -43,6 +45,531 @@ public:
     virtual bool ErrorTrapPop( bool ) { return false; }
 };
 
+static rtl::OString MotionEdgeFlagsToString(int32_t nFlags)
+{
+    rtl::OStringBuffer aStr;
+    if (nFlags == AMOTION_EVENT_EDGE_FLAG_NONE)
+        aStr.append ("no-edge");
+    if (nFlags & AMOTION_EVENT_EDGE_FLAG_TOP)
+        aStr.append (":top");
+    if (nFlags & AMOTION_EVENT_EDGE_FLAG_BOTTOM)
+        aStr.append (":bottom");
+    if (nFlags & AMOTION_EVENT_EDGE_FLAG_LEFT)
+        aStr.append (":left");
+    if (nFlags & AMOTION_EVENT_EDGE_FLAG_RIGHT)
+        aStr.append (":right");
+    return aStr.makeStringAndClear();
+}
+
+static rtl::OString KeyMetaStateToString(int32_t nFlags)
+{
+    rtl::OStringBuffer aStr;
+    if (nFlags == AMETA_NONE)
+        aStr.append ("no-meta");
+    if (nFlags & AMETA_ALT_ON)
+        aStr.append (":alt");
+    if (nFlags & AMETA_SHIFT_ON)
+        aStr.append (":shift");
+    if (nFlags & AMETA_SYM_ON)
+        aStr.append (":sym");
+    return aStr.makeStringAndClear();
+}
+
+static sal_uInt16 KeyMetaStateToCode(AInputEvent *event)
+{
+    sal_uInt16 nCode = 0;
+    int32_t nFlags = AKeyEvent_getMetaState(event);
+    if (nFlags & AMETA_SHIFT_ON)
+        nCode |= KEY_SHIFT;
+    if (nFlags & AMETA_SYM_ON)
+        nCode |= KEY_MOD1;
+    if (nFlags & AMETA_ALT_ON)
+        nCode |= KEY_MOD2;
+    return nCode;
+}
+
+static sal_uInt16 KeyToCode(AInputEvent *event)
+{
+    sal_uInt16 nCode = 0;
+    switch (AKeyEvent_getKeyCode(event)) {
+#define MAP(a,b)                                 \
+    case AKEYCODE_##a: nCode = KEY_##b; break
+#define MAP_SAME(a) MAP(a,a)
+
+    MAP_SAME(HOME);
+    MAP_SAME(0); MAP_SAME(1); MAP_SAME(2); MAP_SAME(3); MAP_SAME(4);
+    MAP_SAME(5); MAP_SAME(6); MAP_SAME(7); MAP_SAME(8); MAP_SAME(9);
+
+    MAP_SAME(A); MAP_SAME(B); MAP_SAME(C); MAP_SAME(D);
+    MAP_SAME(E); MAP_SAME(F); MAP_SAME(G); MAP_SAME(H);
+    MAP_SAME(I); MAP_SAME(J); MAP_SAME(K); MAP_SAME(L);
+    MAP_SAME(M); MAP_SAME(N); MAP_SAME(O); MAP_SAME(P);
+    MAP_SAME(Q); MAP_SAME(R); MAP_SAME(S); MAP_SAME(T);
+    MAP_SAME(U); MAP_SAME(V); MAP_SAME(W); MAP_SAME(X);
+    MAP_SAME(Y); MAP_SAME(Z);
+
+    MAP_SAME(TAB); MAP_SAME(SPACE); MAP_SAME(COMMA);
+
+    MAP(ENTER,RETURN);
+    MAP(PAGE_UP, PAGEUP);
+    MAP(PAGE_DOWN, PAGEDOWN);
+    MAP(DEL, DELETE);
+    MAP(PERIOD, POINT);
+
+    MAP(DPAD_UP, UP); MAP(DPAD_DOWN, DOWN);
+    MAP(DPAD_LEFT, LEFT); MAP(DPAD_RIGHT, RIGHT);
+
+    case AKEYCODE_BACK: // escape ?
+    case AKEYCODE_UNKNOWN:
+    case AKEYCODE_SOFT_LEFT:
+    case AKEYCODE_SOFT_RIGHT:
+    case AKEYCODE_CALL:
+    case AKEYCODE_ENDCALL:
+    case AKEYCODE_STAR:
+    case AKEYCODE_POUND:
+    case AKEYCODE_VOLUME_UP:
+    case AKEYCODE_VOLUME_DOWN:
+    case AKEYCODE_DPAD_CENTER:
+    case AKEYCODE_POWER:
+    case AKEYCODE_CAMERA:
+    case AKEYCODE_CLEAR:
+    case AKEYCODE_ALT_LEFT:
+    case AKEYCODE_ALT_RIGHT:
+    case AKEYCODE_SHIFT_LEFT:
+    case AKEYCODE_SHIFT_RIGHT:
+    case AKEYCODE_SYM:
+    case AKEYCODE_EXPLORER:
+    case AKEYCODE_ENVELOPE:
+    case AKEYCODE_GRAVE:
+    case AKEYCODE_MINUS:
+    case AKEYCODE_EQUALS:
+    case AKEYCODE_LEFT_BRACKET:
+    case AKEYCODE_RIGHT_BRACKET:
+    case AKEYCODE_BACKSLASH:
+    case AKEYCODE_SEMICOLON:
+    case AKEYCODE_APOSTROPHE:
+    case AKEYCODE_SLASH:
+    case AKEYCODE_AT:
+    case AKEYCODE_NUM:
+    case AKEYCODE_HEADSETHOOK:
+    case AKEYCODE_FOCUS: // not widget, but camera focus
+    case AKEYCODE_PLUS:
+    case AKEYCODE_MENU:
+    case AKEYCODE_NOTIFICATION:
+    case AKEYCODE_SEARCH:
+    case AKEYCODE_MEDIA_PLAY_PAUSE:
+    case AKEYCODE_MEDIA_STOP:
+    case AKEYCODE_MEDIA_NEXT:
+    case AKEYCODE_MEDIA_PREVIOUS:
+    case AKEYCODE_MEDIA_REWIND:
+    case AKEYCODE_MEDIA_FAST_FORWARD:
+    case AKEYCODE_MUTE:
+    case AKEYCODE_PICTSYMBOLS:
+    case AKEYCODE_SWITCH_CHARSET:
+    case AKEYCODE_BUTTON_A:
+    case AKEYCODE_BUTTON_B:
+    case AKEYCODE_BUTTON_C:
+    case AKEYCODE_BUTTON_X:
+    case AKEYCODE_BUTTON_Y:
+    case AKEYCODE_BUTTON_Z:
+    case AKEYCODE_BUTTON_L1:
+    case AKEYCODE_BUTTON_R1:
+    case AKEYCODE_BUTTON_L2:
+    case AKEYCODE_BUTTON_R2:
+    case AKEYCODE_BUTTON_THUMBL:
+    case AKEYCODE_BUTTON_THUMBR:
+    case AKEYCODE_BUTTON_START:
+    case AKEYCODE_BUTTON_SELECT:
+    case AKEYCODE_BUTTON_MODE:
+        fprintf (stderr, "un-mapped keycode %d\n", nCode);
+        nCode = 0;
+        break;
+#undef MAP_SAME
+#undef MAP
+    }
+    fprintf (stderr, "mapped %d -> %d\n", AKeyEvent_getKeyCode(event), nCode);
+    return nCode;
+}
+
+static void BlitFrameRegionToWindow(ANativeWindow_Buffer *pOutBuffer,
+                                    const basebmp::BitmapDeviceSharedPtr& aDev,
+                                    const ARect &rSrcRect,
+                                    int nDestX, int nDestY)
+{
+    fprintf (stderr, "Blit frame src %d,%d->%d,%d to position %d, %d\n",
+             rSrcRect.left, rSrcRect.top, rSrcRect.right, rSrcRect.bottom,
+             nDestX, nDestY);
+
+    basebmp::RawMemorySharedArray aSrcData = aDev->getBuffer();
+    basegfx::B2IVector aDevSize = aDev->getSize();
+    sal_Int32 nStride = aDev->getScanlineStride();
+    unsigned char *pSrc = aSrcData.get();
+
+    // FIXME: do some cropping goodness on aSrcRect to ensure no overflows etc.
+    ARect aSrcRect = rSrcRect;
+
+    // FIXME: by default we have WINDOW_FORMAT_RGB_565 = 4 ...
+    for (unsigned int y = 0; y < (unsigned int)(aSrcRect.bottom - aSrcRect.top); y++)
+    {
+        unsigned char *sp = ( pSrc + nStride * (aSrcRect.top + y) +
+                              aSrcRect.left * 3 /* src pixel size */ );
+
+        switch (pOutBuffer->format) {
+        case WINDOW_FORMAT_RGBA_8888:
+        case WINDOW_FORMAT_RGBX_8888:
+        {
+            unsigned char *dp = ( (unsigned char *)pOutBuffer->bits +
+                                  pOutBuffer->stride * 4 * (y + nDestY) +
+                                  nDestX * 4 /* dest pixel size */ );
+            for (unsigned int x = 0; x < (unsigned int)(aSrcRect.right - aSrcRect.left); x++)
+            {
+                dp[x*4 + 0] = sp[x*3 + 2]; // R
+                dp[x*4 + 1] = sp[x*3 + 1]; // G
+                dp[x*4 + 2] = sp[x*3 + 0]; // B
+                dp[x*4 + 3] = 255; // A
+            }
+            break;
+        }
+        case WINDOW_FORMAT_RGB_565:
+        {
+            unsigned char *dp = ( (unsigned char *)pOutBuffer->bits +
+                                  pOutBuffer->stride * 2 * (y + nDestY) +
+                                  nDestX * 2 /* dest pixel size */ );
+            for (unsigned int x = 0; x < (unsigned int)(aSrcRect.right - aSrcRect.left); x++)
+            {
+                unsigned char b = sp[x*3 + 0]; // B
+                unsigned char g = sp[x*3 + 1]; // G
+                unsigned char r = sp[x*3 + 2]; // R
+                dp[x*2 + 0] = (r & 0xf8) | (g >> 5);
+                dp[x*2 + 1] = ((g & 0x1c) << 5) | ((b & 0xf8) >> 3);
+            }
+            break;
+        }
+        default:
+            fprintf (stderr, "unknown pixel format %d !\n", pOutBuffer->format);
+            break;
+        }
+    }
+}
+
+void AndroidSalInstance::BlitFrameToWindow(ANativeWindow_Buffer *pOutBuffer,
+                                           const basebmp::BitmapDeviceSharedPtr& aDev)
+{
+    basegfx::B2IVector aDevSize = aDev->getSize();
+    ARect aWhole = { 0, 0, aDevSize.getX(), aDevSize.getY() };
+    BlitFrameRegionToWindow(pOutBuffer, aDev, aWhole, 0, 0);
+}
+
+void AndroidSalInstance::RedrawWindows(ANativeWindow *pWindow)
+{
+    if (!pWindow)
+        return;
+
+    ANativeWindow_Buffer aOutBuffer;
+    memset ((void *)&aOutBuffer, 0, sizeof (aOutBuffer));
+
+//    ARect aRect;
+    fprintf (stderr, "pre lock #3\n");
+    int32_t nRet = ANativeWindow_lock(pWindow, &aOutBuffer, NULL);
+    fprintf (stderr, "locked window %d returned " // rect:  %d,%d->%d,%d "
+             "buffer: %dx%d stride %d, format %d, bits %p\n",
+             nRet, // aRect.left, aRect.top, aRect.right, aRect.bottom,
+             aOutBuffer.width, aOutBuffer.height, aOutBuffer.stride,
+             aOutBuffer.format, aOutBuffer.bits);
+    if (aOutBuffer.bits != NULL)
+    {
+
+#if 0   // pre-'clean' the buffer with cruft:
+        // hard-code / guess at a format ...
+        int32_t *p = (int32_t *)aOutBuffer.bits;
+        for (int32_t y = 0; y < aOutBuffer.height; y++)
+        {
+            for (int32_t x = 0; x < aOutBuffer.stride; x++)
+                *p++ = (y << 24) + (x << 10) + 0xff ;
+        }
+
+#endif
+        int i = 0;
+        std::list< SalFrame* >::const_iterator it;
+        for ( it = getFrames().begin(); it != getFrames().end(); i++, it++ )
+        {
+            SvpSalFrame *pFrame = static_cast<SvpSalFrame *>(*it);
+
+            if (pFrame->IsVisible())
+            {
+                fprintf( stderr, "render visible frame %d\n", i );
+#ifndef REGION_RE_RENDER
+                BlitFrameToWindow (&aOutBuffer, pFrame->getDevice());
+#else
+                // Sadly it seems that due to double buffering, we don't
+                // get back in our buffer what we had there last time - so we cannot
+                // do incremental rendering. Presumably this will require us to
+                // render to a bitmap, and keep that updated instead in future.
+
+                // Intersect re-rendering region with this frame
+                Region aClipped( maRedrawRegion );
+                basegfx::B2IVector aDevSize = pFrame->getDevice()->getSize();
+                aClipped.Intersect( Rectangle( 0, 0, aDevSize.getX(), aDevSize.getY() ) );
+
+                Rectangle aSubRect;
+                RegionHandle aHdl = aClipped.BeginEnumRects();
+                while( aClipped.GetNextEnumRect( aHdl, aSubRect ) )
+                {
+                    ARect aASubRect = { aSubRect.Left(), aSubRect.Top(),
+                                        aSubRect.Right(), aSubRect.Bottom() };
+                    BlitFrameRegionToWindow(&aOutBuffer, pFrame->getDevice(),
+                                            aASubRect,
+                                            aSubRect.Left(), aSubRect.Top());
+                }
+                aClipped.EndEnumRects( aHdl );
+#endif
+            }
+        }
+    }
+    else
+        fprintf (stderr, "no buffer for locked window\n");
+    ANativeWindow_unlockAndPost(pWindow);
+
+    fprintf (stderr, "done render!\n");
+    maRedrawRegion.SetEmpty();
+    mbQueueReDraw = false;
+}
+
+void AndroidSalInstance::damaged(AndroidSalFrame */* frame */, const Rectangle &rRect)
+{
+    // FIXME: translate rRect to the frame's offset ...
+    maRedrawRegion.Union( rRect );
+    mbQueueReDraw = true;
+}
+
+static const char *app_cmd_name(int cmd)
+{
+    switch (cmd) {
+    case APP_CMD_INPUT_CHANGED: return "INPUT_CHANGED";
+    case APP_CMD_INIT_WINDOW: return "INIT_WINDOW";
+    case APP_CMD_TERM_WINDOW: return "TERM_WINDOW";
+    case APP_CMD_WINDOW_RESIZED: return "WINDOW_RESIZED";
+    case APP_CMD_WINDOW_REDRAW_NEEDED: return "WINDOW_REDRAW_NEEDED";
+    case APP_CMD_CONTENT_RECT_CHANGED: return "CONTENT_RECT_CHANGED";
+    case APP_CMD_GAINED_FOCUS: return "GAINED_FOCUS";
+    case APP_CMD_LOST_FOCUS: return "LOST_FOCUS";
+    case APP_CMD_CONFIG_CHANGED: return "CONFIG_CHANGED";
+    case APP_CMD_LOW_MEMORY: return "LOW_MEMORY";
+    case APP_CMD_START: return "START";
+    case APP_CMD_RESUME: return "RESUME";
+    case APP_CMD_SAVE_STATE: return "SAVE_STATE";
+    case APP_CMD_PAUSE: return "PAUSE";
+    case APP_CMD_STOP: return "STOP";
+    case APP_CMD_DESTROY: return "DESTROY";
+    default:
+        static char buf[10];
+        sprintf(buf, "%d", cmd);
+        return buf;
+    }
+}
+
+void AndroidSalInstance::GetWorkArea( Rectangle& rRect )
+{
+    if (!mpApp || !mpApp->window)
+        rRect = Rectangle( Point( 0, 0 ),
+                           Size( 800, 600 ) );
+    else
+        rRect = Rectangle( Point( 0, 0 ),
+                           Size( ANativeWindow_getWidth( mpApp->window ),
+                                 ANativeWindow_getHeight( mpApp->window ) ) );
+}
+
+void AndroidSalInstance::onAppCmd (struct android_app* app, int32_t cmd)
+{
+        fprintf (stderr, "app cmd for app %p: %s\n", app, app_cmd_name(cmd));
+        ANativeWindow *pWindow = mpApp->window;
+        switch (cmd) {
+        case APP_CMD_INIT_WINDOW:
+        {
+            ARect aRect = { 0, 0, 0, 0 };
+            aRect.right = ANativeWindow_getWidth(pWindow);
+            aRect.bottom = ANativeWindow_getHeight(pWindow);
+            int nRet = ANativeWindow_setBuffersGeometry(
+                                pWindow, ANativeWindow_getWidth(pWindow),
+                                ANativeWindow_getHeight(pWindow),
+                                WINDOW_FORMAT_RGBA_8888);
+            fprintf (stderr, "we have an app window ! %p %dx%x (%d) set %d\n",
+                     pWindow, aRect.right, aRect.bottom,
+                     ANativeWindow_getFormat(pWindow), nRet);
+            maRedrawRegion = Region( Rectangle( 0, 0, ANativeWindow_getWidth(pWindow),
+                                                ANativeWindow_getHeight(pWindow) ) );
+            mbQueueReDraw = true;
+            break;
+        }
+        case APP_CMD_WINDOW_RESIZED:
+        {
+            ARect aRect = { 0, 0, 0, 0 };
+            aRect.right = ANativeWindow_getWidth(pWindow);
+            aRect.bottom = ANativeWindow_getHeight(pWindow);
+            fprintf (stderr, "app window resized to ! %p %dx%x (%d)\n",
+                     pWindow, aRect.right, aRect.bottom,
+                     ANativeWindow_getFormat(pWindow));
+            break;
+        }
+
+        case APP_CMD_WINDOW_REDRAW_NEEDED:
+        {
+            fprintf (stderr, "redraw needed\n");
+            maRedrawRegion = Region( Rectangle( 0, 0, ANativeWindow_getWidth(pWindow),
+                                                ANativeWindow_getHeight(pWindow) ) );
+            mbQueueReDraw = true;
+            break;
+        }
+
+        case APP_CMD_CONTENT_RECT_CHANGED:
+        {
+            ARect aRect = mpApp->contentRect;
+            fprintf (stderr, "content rect changed [ k/b popped up etc. ] %d,%d->%d,%d\n",
+                     aRect.left, aRect.top, aRect.right, aRect.bottom);
+            break;
+        }
+        default:
+            fprintf (stderr, "unhandled app cmd %d\n", cmd);
+            break;
+        }
+}
+
+/*
+ * Try too hard to get a frame, in the absence of anything better to do
+ */
+SalFrame *AndroidSalInstance::getFocusFrame() const
+{
+    SalFrame *pFocus = SvpSalFrame::GetFocusFrame();
+    if (!pFocus) {
+        fprintf (stderr, "no focus frame, re-focusing first visible frame\n");
+        const std::list< SalFrame* >& rFrames( getFrames() );
+        for( std::list< SalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it )
+        {
+            SvpSalFrame *pFrame = const_cast<SvpSalFrame*>(static_cast<const SvpSalFrame*>(*it));
+            if( pFrame->IsVisible() )
+            {
+                pFrame->GetFocus();
+                pFocus = pFrame;
+                break;
+            }
+        }
+    }
+    return pFocus;
+}
+
+int32_t AndroidSalInstance::onInputEvent (struct android_app* app, AInputEvent* event)
+{
+    bool bHandled = false;
+    fprintf (stderr, "input event for app %p, event %p type %d source %d device id %d\n",
+             app, event,
+             AInputEvent_getType(event),
+             AInputEvent_getSource(event),
+             AInputEvent_getDeviceId(event));
+
+    switch (AInputEvent_getType(event))
+    {
+    case AINPUT_EVENT_TYPE_KEY:
+    {
+        int32_t nAction = AKeyEvent_getAction(event);
+        fprintf (stderr, "key event keycode %d '%s' %s flags (0x%x) 0x%x\n",
+                 AKeyEvent_getKeyCode(event),
+                 nAction == AKEY_EVENT_ACTION_DOWN ? "down" :
+                 nAction == AKEY_EVENT_ACTION_UP ? "up" : "multiple",
+                 KeyMetaStateToString(AKeyEvent_getMetaState(event)).getStr(),
+                 AKeyEvent_getMetaState (event),
+                 AKeyEvent_getFlags (event));
+
+        // FIXME: the whole SALEVENT_KEYMODCHANGE stuff is going to be interesting
+        // can we really emit that ? no input method madness required though.
+        sal_uInt16 nEvent;
+        SalKeyEvent aEvent;
+        int64_t nNsTime = AKeyEvent_getEventTime(event);
+
+        // FIXME: really we need a Java wrapper app as Mozilla has that does
+        // key event translation for us, and provides -much- cleaner events.
+        nEvent = (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_UP ?
+                  SALEVENT_KEYUP : SALEVENT_KEYINPUT);
+        sal_uInt16 nCode = KeyToCode(event);
+        sal_uInt16 nMetaState = KeyMetaStateToCode(event);
+        if (nCode >= KEY_0 && nCode <= KEY_9)
+            aEvent.mnCharCode = '0' + nCode - KEY_0;
+        else if (nCode >= KEY_A && nCode <= KEY_Z)
+            aEvent.mnCharCode = (nMetaState & KEY_SHIFT ? 'A' : 'a') + nCode - KEY_A;
+        else if (nCode == KEY_SPACE)
+            aEvent.mnCharCode = ' ';
+        else if (nCode == KEY_COMMA)
+            aEvent.mnCharCode = ',';
+        else if (nCode == KEY_POINT)
+            aEvent.mnCharCode = '.';
+        else
+            aEvent.mnCharCode = 0;
+        aEvent.mnTime = nNsTime / (1000 * 1000);
+        aEvent.mnCode = nMetaState | nCode;
+        aEvent.mnRepeat = AKeyEvent_getRepeatCount(event);
+
+        SalFrame *pFocus = getFocusFrame();
+        if (pFocus)
+            bHandled = pFocus->CallCallback( nEvent, &aEvent );
+        else
+            fprintf (stderr, "no focused frame to emit event on\n");
+
+        fprintf( stderr, "bHandled == %s\n", bHandled? "true": "false" );
+        break;
+    }
+    case AINPUT_EVENT_TYPE_MOTION:
+    {
+        size_t nPoints = AMotionEvent_getPointerCount(event);
+        fprintf (stderr, "motion event %d %g,%g %d points: %s\n",
+                 AMotionEvent_getAction(event),
+                 AMotionEvent_getXOffset(event),
+                 AMotionEvent_getYOffset(event),
+                 (int)nPoints,
+                 MotionEdgeFlagsToString(AMotionEvent_getEdgeFlags(event)).getStr());
+        for (size_t i = 0; i < nPoints; i++)
+            fprintf(stderr, "\t%d: %g,%g - pressure %g\n",
+                    (int)i,
+                    AMotionEvent_getX(event, i),
+                    AMotionEvent_getY(event, i),
+                    AMotionEvent_getPressure(event, i));
+
+        SalMouseEvent aMouseEvent;
+        sal_uInt16 nEvent = 0;
+
+        // FIXME: all this filing the nEvent and aMouseEvent has to be cleaned up
+        nEvent = AMotionEvent_getAction(event)? SALEVENT_MOUSEBUTTONUP: SALEVENT_MOUSEBUTTONDOWN;
+
+        if (nPoints > 0)
+        {
+            aMouseEvent.mnX = AMotionEvent_getX(event, 0);
+            aMouseEvent.mnY = AMotionEvent_getY(event, 0);
+        } else {
+            aMouseEvent.mnX = AMotionEvent_getXOffset(event);
+            aMouseEvent.mnY = AMotionEvent_getYOffset(event);
+        }
+
+        int64_t nNsTime = AMotionEvent_getEventTime(event);
+        aMouseEvent.mnTime = nNsTime / (1000 * 1000);
+        aMouseEvent.mnCode = 0; // FIXME
+        aMouseEvent.mnButton = MOUSE_LEFT; // FIXME
+
+        SalFrame *pFocus = getFocusFrame();
+        if (pFocus)
+            bHandled = pFocus->CallCallback( nEvent, &aMouseEvent );
+        else
+            fprintf (stderr, "no focused frame to emit event on\n");
+
+        fprintf( stderr, "bHandled == %s\n", bHandled? "true": "false" );
+
+        break;
+    }
+    default:
+        fprintf (stderr, "unknown input event type %p %d\n",
+                 event, AInputEvent_getType(event));
+        break;
+    }
+    return bHandled ? 1 : 0;
+}
+
 AndroidSalInstance *AndroidSalInstance::getInstance()
 {
     if (!ImplGetSVData())
@@ -53,11 +580,35 @@ AndroidSalInstance *AndroidSalInstance::getInstance()
     return static_cast<AndroidSalInstance *>(pData->m_pInstance);
 }
 
+extern "C" {
+    void onAppCmd_cb (struct android_app *app, int32_t cmd)
+    {
+        AndroidSalInstance::getInstance()->onAppCmd(app, cmd);
+    }
+
+    int32_t onInputEvent_cb (struct android_app *app, AInputEvent *event)
+    {

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list