1 package org
.libreoffice
;
3 import android
.graphics
.Bitmap
;
4 import android
.graphics
.PointF
;
5 import android
.graphics
.RectF
;
6 import android
.view
.KeyEvent
;
8 import org
.libreoffice
.canvas
.SelectionHandle
;
9 import org
.mozilla
.gecko
.gfx
.CairoImage
;
10 import org
.mozilla
.gecko
.gfx
.ComposedTileLayer
;
11 import org
.mozilla
.gecko
.gfx
.GeckoLayerClient
;
12 import org
.mozilla
.gecko
.gfx
.ImmutableViewportMetrics
;
13 import org
.mozilla
.gecko
.gfx
.SubTile
;
15 import java
.util
.ArrayList
;
16 import java
.util
.List
;
17 import java
.util
.concurrent
.LinkedBlockingQueue
;
20 * Thread that communicates with LibreOffice through LibreOfficeKit JNI interface. The thread
21 * consumes events from other threads (mainly the UI thread) and acts accordingly.
23 public class LOKitThread
extends Thread
{
24 private static final String LOGTAG
= LOKitThread
.class.getSimpleName();
26 private LinkedBlockingQueue
<LOEvent
> mEventQueue
= new LinkedBlockingQueue
<LOEvent
>();
28 private LibreOfficeMainActivity mApplication
;
29 private TileProvider mTileProvider
;
30 private InvalidationHandler mInvalidationHandler
;
31 private ImmutableViewportMetrics mViewportMetrics
;
32 private GeckoLayerClient mLayerClient
;
34 public LOKitThread() {
35 mInvalidationHandler
= null;
36 TileProviderFactory
.initialize();
40 * Starting point of the thread. Processes events that gather in the queue.
47 event
= mEventQueue
.take();
49 } catch (InterruptedException exception
) {
50 throw new RuntimeException(exception
);
56 * Viewport changed, Recheck if tiles need to be added / removed.
58 private void tileReevaluationRequest(ComposedTileLayer composedTileLayer
) {
59 if (mTileProvider
== null) {
62 List
<SubTile
> tiles
= new ArrayList
<SubTile
>();
64 mLayerClient
.beginDrawing();
65 composedTileLayer
.addNewTiles(tiles
);
66 mLayerClient
.endDrawing();
68 for (SubTile tile
: tiles
) {
69 TileIdentifier tileId
= tile
.id
;
70 CairoImage image
= mTileProvider
.createTile(tileId
.x
, tileId
.y
, tileId
.size
, tileId
.zoom
);
71 mLayerClient
.beginDrawing();
75 mLayerClient
.endDrawing();
76 mLayerClient
.forceRender();
79 mLayerClient
.beginDrawing();
80 composedTileLayer
.markTiles();
81 composedTileLayer
.clearMarkedTiles();
82 mLayerClient
.endDrawing();
83 mLayerClient
.forceRender();
87 * Invalidate tiles that intersect the input rect.
89 private void tileInvalidation(RectF rect
) {
90 if (mLayerClient
== null || mTileProvider
== null) {
94 mLayerClient
.beginDrawing();
96 List
<SubTile
> tiles
= new ArrayList
<SubTile
>();
97 mLayerClient
.invalidateTiles(tiles
, rect
);
99 for (SubTile tile
: tiles
) {
100 CairoImage image
= mTileProvider
.createTile(tile
.id
.x
, tile
.id
.y
, tile
.id
.size
, tile
.id
.zoom
);
101 tile
.setImage(image
);
104 mLayerClient
.endDrawing();
105 mLayerClient
.forceRender();
109 * Handle the geometry change + draw.
111 private void redraw() {
112 if (mLayerClient
== null || mTileProvider
== null) {
113 // called too early...
117 mLayerClient
.setPageRect(0, 0, mTileProvider
.getPageWidth(), mTileProvider
.getPageHeight());
118 mViewportMetrics
= mLayerClient
.getViewportMetrics();
119 mLayerClient
.setViewportMetrics(mViewportMetrics
);
121 zoomAndRepositionTheDocument();
123 mLayerClient
.forceRedraw();
124 mLayerClient
.forceRender();
128 * Reposition the view (zoom and position) when the document is firstly shown. This is document type dependent.
130 private void zoomAndRepositionTheDocument() {
131 if (mTileProvider
.isSpreadsheet()) {
132 // Don't do anything for spreadsheets - show at 100%
133 } else if (mTileProvider
.isTextDocument()) {
134 // Always zoom text document to the beginning of the document and centered by width
135 float centerY
= mViewportMetrics
.getCssViewport().centerY();
136 mLayerClient
.zoomTo(new RectF(0, centerY
, mTileProvider
.getPageWidth(), centerY
));
138 // Other documents - always show the whole document on the screen,
139 // regardless of document shape and orientation.
140 if (mViewportMetrics
.getViewport().width() < mViewportMetrics
.getViewport().height()) {
141 mLayerClient
.zoomTo(mTileProvider
.getPageWidth(), 0);
143 mLayerClient
.zoomTo(0, mTileProvider
.getPageHeight());
149 * Invalidate everything + handle the geometry change
151 private void refresh() {
152 mLayerClient
.clearAndResetlayers();
157 * Change part of the document.
159 private void changePart(int partIndex
) {
160 LOKitShell
.showProgressSpinner();
161 mTileProvider
.changePart(partIndex
);
162 mViewportMetrics
= mLayerClient
.getViewportMetrics();
163 mLayerClient
.setViewportMetrics(mViewportMetrics
.scaleTo(0.9f
, new PointF()));
165 LOKitShell
.hideProgressSpinner();
169 * Handle load document event.
170 * @param filename - filename where the document is located
172 private void loadDocument(String filename
) {
173 if (mApplication
== null) {
174 mApplication
= LibreOfficeMainActivity
.mAppContext
;
177 mLayerClient
= LibreOfficeMainActivity
.getLayerClient();
179 mInvalidationHandler
= new InvalidationHandler(LibreOfficeMainActivity
.mAppContext
);
180 mTileProvider
= TileProviderFactory
.create(mLayerClient
, mInvalidationHandler
, filename
);
182 if (mTileProvider
.isReady()) {
183 LOKitShell
.showProgressSpinner();
185 LOKitShell
.hideProgressSpinner();
192 * Close the currently loaded document.
194 public void closeDocument() {
195 if (mTileProvider
!= null) {
196 mTileProvider
.close();
197 mTileProvider
= null;
202 * Process the input event.
204 private void processEvent(LOEvent event
) {
205 switch (event
.mType
) {
207 loadDocument(event
.mString
);
212 case LOEvent
.SIZE_CHANGED
:
215 case LOEvent
.CHANGE_PART
:
216 changePart(event
.mPartIndex
);
218 case LOEvent
.TILE_INVALIDATION
:
219 tileInvalidation(event
.mInvalidationRect
);
221 case LOEvent
.THUMBNAIL
:
222 createThumbnail(event
.mTask
);
225 touch(event
.mTouchType
, event
.mDocumentCoordinate
);
227 case LOEvent
.KEY_EVENT
:
228 keyEvent(event
.mKeyEvent
);
230 case LOEvent
.TILE_REEVALUATION_REQUEST
:
231 tileReevaluationRequest(event
.mComposedTileLayer
);
233 case LOEvent
.CHANGE_HANDLE_POSITION
:
234 changeHandlePosition(event
.mHandleType
, event
.mDocumentCoordinate
);
236 case LOEvent
.SWIPE_LEFT
:
239 case LOEvent
.SWIPE_RIGHT
:
242 case LOEvent
.NAVIGATION_CLICK
:
243 mInvalidationHandler
.changeStateTo(InvalidationHandler
.OverlayState
.NONE
);
245 case LOEvent
.UNO_COMMAND
:
246 mTileProvider
.postUnoCommand(event
.mString
, event
.mValue
);
252 * Request a change of the handle position.
254 private void changeHandlePosition(SelectionHandle
.HandleType handleType
, PointF documentCoordinate
) {
255 if (handleType
== SelectionHandle
.HandleType
.MIDDLE
) {
256 mTileProvider
.setTextSelectionReset(documentCoordinate
);
257 } else if (handleType
== SelectionHandle
.HandleType
.START
) {
258 mTileProvider
.setTextSelectionStart(documentCoordinate
);
259 } else if (handleType
== SelectionHandle
.HandleType
.END
) {
260 mTileProvider
.setTextSelectionEnd(documentCoordinate
);
265 * Processes key events.
267 private void keyEvent(KeyEvent keyEvent
) {
268 if (!LOKitShell
.isEditingEnabled()) {
271 if (mTileProvider
== null) {
274 mInvalidationHandler
.keyEvent();
275 mTileProvider
.sendKeyEvent(keyEvent
);
279 * Process swipe left event.
281 private void onSwipeLeft() {
282 mTileProvider
.onSwipeLeft();
286 * Process swipe right event.
288 private void onSwipeRight() {
289 mTileProvider
.onSwipeRight();
293 * Processes touch events.
295 private void touch(String touchType
, PointF documentCoordinate
) {
296 if (mTileProvider
== null) {
300 // to handle hyperlinks, enable single tap even in the Viewer
301 boolean editing
= LOKitShell
.isEditingEnabled();
302 float zoomFactor
= mViewportMetrics
.getZoomFactor();
304 if (touchType
.equals("LongPress") && editing
) {
305 mInvalidationHandler
.changeStateTo(InvalidationHandler
.OverlayState
.TRANSITION
);
306 mTileProvider
.mouseButtonDown(documentCoordinate
, 1, zoomFactor
);
307 mTileProvider
.mouseButtonUp(documentCoordinate
, 1, zoomFactor
);
308 mTileProvider
.mouseButtonDown(documentCoordinate
, 2, zoomFactor
);
309 mTileProvider
.mouseButtonUp(documentCoordinate
, 2, zoomFactor
);
310 } else if (touchType
.equals("SingleTap")) {
311 mInvalidationHandler
.changeStateTo(InvalidationHandler
.OverlayState
.TRANSITION
);
312 mTileProvider
.mouseButtonDown(documentCoordinate
, 1, zoomFactor
);
313 mTileProvider
.mouseButtonUp(documentCoordinate
, 1, zoomFactor
);
314 } else if (touchType
.equals("GraphicSelectionStart") && editing
) {
315 mTileProvider
.setGraphicSelectionStart(documentCoordinate
);
316 } else if (touchType
.equals("GraphicSelectionEnd") && editing
) {
317 mTileProvider
.setGraphicSelectionEnd(documentCoordinate
);
322 * Create thumbnail for the requested document task.
324 private void createThumbnail(final ThumbnailCreator
.ThumbnailCreationTask task
) {
325 final Bitmap bitmap
= task
.getThumbnail(mTileProvider
);
326 task
.applyBitmap(bitmap
);
332 public void queueEvent(LOEvent event
) {
333 mEventQueue
.add(event
);
337 * Clear all events in the queue (used when document is closed).
339 public void clearQueue() {
344 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */