Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / android / source / src / java / org / libreoffice / LOKitThread.java
blob7b2e9b27f04a60e3c95db78d4a72038473cf9f2e
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();
39 /**
40 * Starting point of the thread. Processes events that gather in the queue.
42 @Override
43 public void run() {
44 while (true) {
45 LOEvent event;
46 try {
47 event = mEventQueue.take();
48 processEvent(event);
49 } catch (InterruptedException exception) {
50 throw new RuntimeException(exception);
55 /**
56 * Viewport changed, Recheck if tiles need to be added / removed.
58 private void tileReevaluationRequest(ComposedTileLayer composedTileLayer) {
59 if (mTileProvider == null) {
60 return;
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();
72 if (image != null) {
73 tile.setImage(image);
75 mLayerClient.endDrawing();
76 mLayerClient.forceRender();
79 mLayerClient.beginDrawing();
80 composedTileLayer.markTiles();
81 composedTileLayer.clearMarkedTiles();
82 mLayerClient.endDrawing();
83 mLayerClient.forceRender();
86 /**
87 * Invalidate tiles that intersect the input rect.
89 private void tileInvalidation(RectF rect) {
90 if (mLayerClient == null || mTileProvider == null) {
91 return;
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);
102 tile.invalidate();
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...
114 return;
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));
137 } else {
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);
142 } else {
143 mLayerClient.zoomTo(0, mTileProvider.getPageHeight());
149 * Invalidate everything + handle the geometry change
151 private void refresh() {
152 mLayerClient.clearAndResetlayers();
153 redraw();
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()));
164 refresh();
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();
184 refresh();
185 LOKitShell.hideProgressSpinner();
186 } else {
187 closeDocument();
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) {
206 case LOEvent.LOAD:
207 loadDocument(event.mString);
208 break;
209 case LOEvent.CLOSE:
210 closeDocument();
211 break;
212 case LOEvent.SIZE_CHANGED:
213 redraw();
214 break;
215 case LOEvent.CHANGE_PART:
216 changePart(event.mPartIndex);
217 break;
218 case LOEvent.TILE_INVALIDATION:
219 tileInvalidation(event.mInvalidationRect);
220 break;
221 case LOEvent.THUMBNAIL:
222 createThumbnail(event.mTask);
223 break;
224 case LOEvent.TOUCH:
225 touch(event.mTouchType, event.mDocumentCoordinate);
226 break;
227 case LOEvent.KEY_EVENT:
228 keyEvent(event.mKeyEvent);
229 break;
230 case LOEvent.TILE_REEVALUATION_REQUEST:
231 tileReevaluationRequest(event.mComposedTileLayer);
232 break;
233 case LOEvent.CHANGE_HANDLE_POSITION:
234 changeHandlePosition(event.mHandleType, event.mDocumentCoordinate);
235 break;
236 case LOEvent.SWIPE_LEFT:
237 onSwipeLeft();
238 break;
239 case LOEvent.SWIPE_RIGHT:
240 onSwipeRight();
241 break;
242 case LOEvent.NAVIGATION_CLICK:
243 mInvalidationHandler.changeStateTo(InvalidationHandler.OverlayState.NONE);
244 break;
245 case LOEvent.UNO_COMMAND:
246 mTileProvider.postUnoCommand(event.mString, event.mValue);
247 break;
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()) {
269 return;
271 if (mTileProvider == null) {
272 return;
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) {
297 return;
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);
330 * Queue an event.
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() {
340 mEventQueue.clear();
344 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */