7 Contains common code for all projects on Android to bootstrap LibreOffice. In
8 addition it is a home to LibreOfficeKit (LOK - see libreofficekit/README) JNI
11 stuff in source directory
12 *************************
14 LibreOffice Android application - the code is based on Fennec (Firefox for Android).
15 It uses OpenGL ES 2 for rendering of the document tiles which are gathered from
16 LibreOffice using LOK. The application contains the LibreOffice core in one shared
17 library: liblo-native-code.so, which is bundled together with the application.
19 Architecture and Threading
20 **************************
22 The application implements editing support using 4 threads:
23 1. The Android UI thread, we can't perform anything here that would take a considerable
25 2. An OpenGL thread which contains the OpenGL context and is responsible for drawing
26 all layers (including tiles) to the screen.
27 3. A thread (LOKitThread), that performs LibreOfficeKit calls, which may take more time
28 to complete. In addition it also receives events from the soffice thread (see below)
29 when the callback emits an event. Events are stored in a blocking queue (thread
30 processes events in FCFS order, goes to sleep when no more event is available and
31 awakens when there are events in queue again).
32 4. A native thread created by LibreOfficeKit (we call it the soffice thread), where
33 LibreOffice itself runs. It receives calls from LOKitThread, and may emit callback
39 LOKitThread (org.libreoffice.LOKitThread) communicates with LO via JNI (this can
40 be done only for one thread) and processes events (defined in org.libreoffice.LOEvent)
46 LibreOfficeMainActivity (org.libreoffice.LibreOfficeMainActivity) is the entry point
47 of the application - everything starts up and tears down from here (onCreate, onResume,
48 onPause, onStart, onStop, onDestroy).
53 From here on one of the most interesting pieces are the classes around document view,
54 which includes listening to touch events, recalculating the viewport, tiled handling
55 and rendering the layers to the document.
57 Viewport - the viewport is the currently visible part of the document. It is defined
58 by view rectangle and zoom.
60 Layers - document view is rendered using many layers. Such layers are: document
61 background, scroll handles, and also the document tiles.
66 - LayerView (org.mozilla.gecko.gfx.LayerView) is the document view of the application.
67 It uses the SurfaceView (android.view.SurfaceView) as the main surface to draw on
70 - GLController (org.mozilla.gecko.gfx.GLController) - holder of the OpenGL context.
72 - RenderControllerThread (org.mozilla.gecko.gfx.RenderControllerThread) executes the
73 rendering requests through LayerRenderer.
75 - LayerRenderer (org.mozilla.gecko.gfx.LayerRenderer) renders all the layers.
77 - GeckoLayerClient (org.mozilla.gecko.gfx.GeckoLayerClient) is the middle man of the
78 application, which connects all the bits together. It is the document view layer
79 holder so the any management (including tiled rendering) usually go through this
80 class. It listens to draw requests and viewport changes from PanZoomController
83 Touch events, scrolling and zooming
84 -----------------------------------
86 The main class that handles the touch event, scrolling and zooming is JavaPanZoomController
87 org.mozilla.gecko.gfx.JavaPanZoomController (implementation of PanZoomController interface).
88 When the user performs a touch action, the document view needs to change, which means the
89 viewport changes. JavaPanZoomController changes the viewport and signals the change through
90 PanZoomTarget (org.mozilla.gecko.gfx.PanZoomTarget).
95 Tiled rendering is a technique that splits the document to bitmaps of same size (typically
96 256x256) which are fetched on demand.
98 In the application the ComposedTileLayer (org.mozilla.gecko.gfx.ComposedTileLayer) is the
99 layer responsible for tracking and managing the tiles. Tiles are in this case also layers
100 (sub layers?) implemented in SubTile (org.mozilla.gecko.gfx.SubTile), where each one is
101 responsible for one tile bitmap (actually OpenGL texture once it has been uploaded).
103 When the viewport changes, the request for tile rechecking is send to LOKitThread (see
104 LOKitThread#tileReevaluationRequest), where the tiles are rechecked, add and removed if
107 CompositeTileLayer is actually an abstract class, which has two implementations. One is
108 DynamicTileLayer (org.mozilla.gecko.gfx.DynamicTileLayer), which is used for main tile
109 view of the document, and FixedZoomTileLayer (org.mozilla.gecko.gfx.FixedZoomTileLayer),
110 which just renders the tiles at a fixed zoom level. This is then used as a background
111 low resolution layer.
116 Tile can change in LibreOffice when user changes the content (adds, removes text or changes
117 the properties). In this case, an invalidation rectangle is signaled from LibreOffice, which
118 includes a rectangle that needs to be invalidated. In this case LOKitThread gets this request
119 via callback, and rechecks all tiles if they need to be invalidated. For more details see
120 LOKitThread#tileInvalidation).
125 For editing there are 2 coarse tasks that the LibreOffice app must do:
126 1. send input events to LibreOffice core (keyboard, touch and mouse)
127 2. listen to messages (provided via callback) from LibreOffice core and react accordingly
129 In most cases when an input event happens and is send to the LO core, then a message from
130 LO core follows. For example: when the user writes to the keyboard, key event is sent and
131 a invalidation request from LO core follows. When user touches an image, a mouse event is
132 sent, and a "new graphic selection" message from LO core follows.
134 All keyboard and touch events are send to LOKitThread as LOEvents. In LOKitThread they are
135 processed and send to LibreOffice core. The touch events originate in JavaPanZoomController,
136 the keyboard events in LOKitInputConnectionHandler (org.libreoffice.LOKitInputConnectionHandler),
137 however there are other parts too - depending on the need.
139 InvalidationHandler (org.libreoffice.InvalidationHandler) is the class that is responsible
140 to process messages from LibreOffice core and to track the state.
145 Overlay elements like cursor and selections aren't drawn by the LO core, instead the core
146 only provides data (cursor position, selection rectangles) and the app needs to draw them.
147 DocumentOverlay (org.libreoffice.overlay.DocumentOverlay) and DocumentOverlayView
148 (org.libreoffice.overlay.DocumentOverlayView) are the classes that provide the overlay over
149 the document, where selections and the cursor is drawn.
155 App uses material design icons available at [1].
158 [1] - https://www.google.com/design/icons/
160 Emulator and debugging notes
161 ****************************
163 For instructions on how to build for Android, see README.cross.
165 * Getting something running
167 Attach your device, so 'adb devices' shows it. Then run:
173 and if all goes well, you should have some nice debug output to enjoy when you
178 Create an AVD in the android UI, don't even try to get the data partition size
179 right in the GUI, that is doomed to producing an AVD that doesn't work.
180 Instead start it from the console:
182 LD_LIBRARY_PATH=$(pwd)/lib emulator-arm -avd <Name> -partition-size 500
184 where <Name> is the literal name of the AVD that you entered.
186 [ In order to have proper acceleration, you need the 32-bit libGL.so:
188 sudo zypper in Mesa-libGL-devel-32bit
190 and run emulator-arm after the installation. ]
192 Then you can run ant/adb as described above.
194 After a while of this loop you might find that you have lost a lot of
195 space on your emulator's or device's /data volume. You can do:
197 adb shell stop; adb shell start
202 First of all, you need to configure the build with --enable-debug or
203 --enable-dbgutil. You may want to provide --enable-selective-debuginfo too,
204 like --enable-selective-debuginfo="sw/" or so, in order to fit into the memory
207 Building with all symbols is also possible but the linking is currently
208 slow (around 10 to 15 minutes) and you need lots of memory (around 16GB + some
213 Direct support for using ndk-gdb has been removed from the build system. It is
214 recommended that you give the lldb debugger a try that has the benefit of being
215 nicely integrated into Android Studio (see below for instructions).
216 If you nevertheless want to continue using ndk-gdb, use the following steps
217 that are described in more detail here: https://stackoverflow.com/a/10539883
219 - add android:debuggable="true" to AndroidManifest.xml
220 - push gdbserver to device, launch and attach to application
221 - forward debugging port from host to device
222 - launch matching gdb on host and run following setup commands:
223 - set solib-search-path obj/local/<appAbi>
224 - file obj/local/<appAbi>/liblo-native-code.so
225 - target remote :<portused>
227 Pretty printers aren't loaded automatically due to the single shared
228 object, but you can still load them manually. E.g. to have a pretty-printer for
229 rtl::OString, you need:
231 (gdb) python sys.path.insert(0, "/master/solenv/gdb")
232 (gdb) source /master/instdir/program/libuno_sal.so.3-gdb.py
234 * Using Android Studio (and thus lldb)
236 Note that lldb might not yield the same results as ndk-gdb. If you suspect a
237 problem with lldb, you can try to manually use ndk-gdb as described above.
238 Using lldb from within Android Studio is more comfortable though and works like this:
240 - open android/source/build.gradle in Android Studio via File|New → Import Project
241 - make sure you select the right build variant (strippedUIDebug is what you want)
242 - use Run|Edit Configurations to create a new configuration of type "Android Native"
243 - on tab "General" pick module "source"
244 - on tab "Native Debugger" add android/source/obj/local/<hostarch> to
245 the Symbol directories
247 Then you can select your new configuration and use Run | Debug to launch it.
248 Note that lldb doesn't initially stop execution, so if you want to add
249 breakpoints using lldb prompt, you manually have to pause execution, then you
250 can switch to the lldb tab and add your breakpoints. However making use of the
251 editor just using File|Open .. to open the desired file in Android Studio and
252 then toggling the breakpoint by clicking on the margin is more comfortable.
254 * Debugging the Java part
256 Open android/source/build.gradle in Android studio via File|New → Import
257 Project and you can use Android Studio's debugging interface.
258 Just make sure you pick the correct build variant (strippedUIDebug)
260 The alternative is to use the jdb command-line debugger. Steps to use it:
262 1) Find out the JDWP ID of a debuggable application:
266 From the list of currently active JDWP processes, the last number is the just
267 started debuggable application.
269 2) Forward the remote JDWP port/process ID to a local port:
271 adb forward tcp:7777 jdwp:31739
273 3) Connect to the running application:
275 jdb -sourcepath src/java/ -attach localhost:7777
277 Assuming that you're already in the LOAndroid3 directory in your shell.
279 * Debugging the missing services
281 Android library only include essential services that are compiled for
282 LibreOffice in order to reduce the size of the apk. When developing,
283 some services might become useful and we should add those services
284 to the combined library.
286 In order to identify missing services, we need to be able to receive
287 SAL_INFO from cppuhelper/source/shlib.cxx in logcat and therefore identify
288 what services are missing. To do so, you may want add the following
289 when configuring the build.
291 --enable-selective-debuginfo="cppuhelper/ sal/"
293 Which services are combined in the android lib is determined by
295 solenv/bin/native-code.py
297 * Common Errors / Gotchas
299 lo_dlneeds: Could not read ELF header of /data/data/org.libreoffice...libfoo.so
300 This (most likely) means that the install quietly failed, and that
301 the file is truncated; check it out with adb shell ls -l /data/data/....
305 All Android apps are basically Java programs. They run "in" a Dalvik
306 (or on Android 5 or newer - ART) virtual machine. Yes, you can also
307 have apps where all *your* code is native code, written in a compiled
308 language like C or C++. But also such apps are actually started
309 by system-provided Java bootstrapping code (NativeActivity) running
312 Such a native app (or actually, "activity") is not built as a
313 executable program, but as a shared object. The Java NativeActivity
314 bootstrapper loads that shared object with dlopen.
316 Anyway, our current "experimental" apps are not based on NativeActivity.
317 They have normal Java code for the activity, and just call out to a single,
318 app-specific native library (called liblo-native-code.so) to do all the