LibreOffice Module android (master) 1
LOKitThread.java
Go to the documentation of this file.
1package org.libreoffice;
2
3import android.graphics.Bitmap;
4import android.graphics.PointF;
5import android.graphics.RectF;
6import android.util.Log;
7import android.view.KeyEvent;
8
16
17import java.util.ArrayList;
18import java.util.List;
19import java.util.concurrent.LinkedBlockingQueue;
20
21/*
22 * Thread that communicates with LibreOffice through LibreOfficeKit JNI interface. The thread
23 * consumes events from other threads (mainly the UI thread) and acts accordingly.
24 */
25class LOKitThread extends Thread {
26 private static final String LOGTAG = LOKitThread.class.getSimpleName();
27
28 private final LinkedBlockingQueue<LOEvent> mEventQueue = new LinkedBlockingQueue<LOEvent>();
29
30 private TileProvider mTileProvider;
31 private InvalidationHandler mInvalidationHandler;
32 private ImmutableViewportMetrics mViewportMetrics;
33 private GeckoLayerClient mLayerClient;
34 private final LibreOfficeMainActivity mContext;
35
36 LOKitThread(LibreOfficeMainActivity context) {
37 mContext = context;
38 mInvalidationHandler = null;
39 TileProviderFactory.initialize();
40 }
41
45 @Override
46 public void run() {
47 while (true) {
48 LOEvent event;
49 try {
50 event = mEventQueue.take();
51 processEvent(event);
52 } catch (InterruptedException exception) {
53 throw new RuntimeException(exception);
54 }
55 }
56 }
57
61 private void tileReevaluationRequest(ComposedTileLayer composedTileLayer) {
62 if (mTileProvider == null) {
63 return;
64 }
65 List<SubTile> tiles = new ArrayList<SubTile>();
66
67 mLayerClient.beginDrawing();
68 composedTileLayer.addNewTiles(tiles);
69 mLayerClient.endDrawing();
70
71 for (SubTile tile : tiles) {
72 TileIdentifier tileId = tile.id;
73 CairoImage image = mTileProvider.createTile(tileId.x, tileId.y, tileId.size, tileId.zoom);
74 mLayerClient.beginDrawing();
75 if (image != null) {
76 tile.setImage(image);
77 }
78 mLayerClient.endDrawing();
79 mLayerClient.forceRender();
80 }
81
82 mLayerClient.beginDrawing();
83 composedTileLayer.markTiles();
84 composedTileLayer.clearMarkedTiles();
85 mLayerClient.endDrawing();
86 mLayerClient.forceRender();
87 }
88
92 private void tileInvalidation(RectF rect) {
93 if (mLayerClient == null || mTileProvider == null) {
94 return;
95 }
96
97 mLayerClient.beginDrawing();
98
99 List<SubTile> tiles = new ArrayList<SubTile>();
100 mLayerClient.invalidateTiles(tiles, rect);
101
102 for (SubTile tile : tiles) {
103 CairoImage image = mTileProvider.createTile(tile.id.x, tile.id.y, tile.id.size, tile.id.zoom);
104 tile.setImage(image);
105 tile.invalidate();
106 }
107 mLayerClient.endDrawing();
108 mLayerClient.forceRender();
109 }
110
114 private void redraw(boolean resetZoomAndPosition) {
115 if (mLayerClient == null || mTileProvider == null) {
116 // called too early...
117 return;
118 }
119
120 mLayerClient.setPageRect(0, 0, mTileProvider.getPageWidth(), mTileProvider.getPageHeight());
121 mViewportMetrics = mLayerClient.getViewportMetrics();
122 mLayerClient.setViewportMetrics(mViewportMetrics);
123
124 if (resetZoomAndPosition) {
125 zoomAndRepositionTheDocument();
126 }
127
128 mLayerClient.forceRedraw();
129 mLayerClient.forceRender();
130 }
131
135 private void zoomAndRepositionTheDocument() {
136 if (mTileProvider.isSpreadsheet()) {
137 // Don't do anything for spreadsheets - show at 100%
138 } else if (mTileProvider.isTextDocument()) {
139 // Always zoom text document to the beginning of the document and centered by width
140 float centerY = mViewportMetrics.getCssViewport().centerY();
141 mLayerClient.zoomTo(new RectF(0, centerY, mTileProvider.getPageWidth(), centerY));
142 } else {
143 // Other documents - always show the whole document on the screen,
144 // regardless of document shape and orientation.
145 if (mViewportMetrics.getViewport().width() < mViewportMetrics.getViewport().height()) {
146 mLayerClient.zoomTo(mTileProvider.getPageWidth(), 0);
147 } else {
148 mLayerClient.zoomTo(0, mTileProvider.getPageHeight());
149 }
150 }
151 }
152
156 private void refresh(boolean resetZoomAndPosition) {
157 mLayerClient.clearAndResetlayers();
158 redraw(resetZoomAndPosition);
159 updatePartPageRectangles();
160 if (mTileProvider != null && mTileProvider.isSpreadsheet()) {
161 updateCalcHeaders();
162 }
163 }
164
169 private void updatePartPageRectangles() {
170 if (mTileProvider == null) {
171 Log.d(LOGTAG, "mTileProvider==null when calling updatePartPageRectangles");
172 return;
173 }
174 String partPageRectString = ((LOKitTileProvider) mTileProvider).getPartPageRectangles();
175 List<RectF> partPageRectangles = mInvalidationHandler.convertPayloadToRectangles(partPageRectString);
176 mContext.getDocumentOverlay().setPartPageRectangles(partPageRectangles);
177 }
178
179 private void updatePageSize(int pageWidth, int pageHeight){
180 mTileProvider.setDocumentSize(pageWidth, pageHeight);
181 redraw(true);
182 }
183
184 private void updateZoomConstraints() {
185 if (mTileProvider == null) return;
186 mLayerClient = mContext.getLayerClient();
187 // Set default zoom to the page width and min zoom so that the whole page is visible
188 final float pageHeightZoom = mLayerClient.getViewportMetrics().getHeight() / mTileProvider.getPageHeight();
189 final float pageWidthZoom = mLayerClient.getViewportMetrics().getWidth() / mTileProvider.getPageWidth();
190 final float minZoom = Math.min(pageWidthZoom, pageHeightZoom);
191 mLayerClient.setZoomConstraints(new ZoomConstraints(pageWidthZoom, minZoom, 0f));
192 }
193
197 private void changePart(int partIndex) {
198 LOKitShell.showProgressSpinner(mContext);
199 mTileProvider.changePart(partIndex);
200 mViewportMetrics = mLayerClient.getViewportMetrics();
201 // mLayerClient.setViewportMetrics(mViewportMetrics.scaleTo(0.9f, new PointF()));
202 refresh(true);
203 LOKitShell.hideProgressSpinner(mContext);
204 }
205
211 private boolean loadDocument(String filePath) {
212 mLayerClient = mContext.getLayerClient();
213
214 mInvalidationHandler = new InvalidationHandler(mContext);
215 mTileProvider = TileProviderFactory.create(mContext, mInvalidationHandler, filePath);
216
217 if (mTileProvider.isReady()) {
218 LOKitShell.showProgressSpinner(mContext);
219 updateZoomConstraints();
220 refresh(true);
221 LOKitShell.hideProgressSpinner(mContext);
222 return true;
223 } else {
224 closeDocument();
225 return false;
226 }
227 }
228
234 private void loadNewDocument(String filePath, String fileType) {
235 boolean ok = loadDocument(fileType);
236 if (ok) {
237 mTileProvider.saveDocumentAs(filePath, true);
238 }
239 }
240
244 private void saveDocumentAs(String filePath, String fileType, boolean bTakeOwnership) {
245 if (mTileProvider == null) {
246 Log.e(LOGTAG, "Error in saving, Tile Provider instance is null");
247 } else {
248 mTileProvider.saveDocumentAs(filePath, fileType, bTakeOwnership);
249 }
250 }
251
255 private void closeDocument() {
256 if (mTileProvider != null) {
257 mTileProvider.close();
258 mTileProvider = null;
259 }
260 }
261
265 private void processEvent(LOEvent event) {
266 switch (event.mType) {
267 case LOEvent.LOAD:
268 loadDocument(event.filePath);
269 break;
270 case LOEvent.LOAD_NEW:
271 loadNewDocument(event.filePath, event.fileType);
272 break;
273 case LOEvent.SAVE_AS:
274 saveDocumentAs(event.filePath, event.fileType, true);
275 break;
276 case LOEvent.SAVE_COPY_AS:
277 saveDocumentAs(event.filePath, event.fileType, false);
278 break;
279 case LOEvent.CLOSE:
280 closeDocument();
281 break;
282 case LOEvent.SIZE_CHANGED:
283 redraw(false);
284 break;
285 case LOEvent.CHANGE_PART:
286 changePart(event.mPartIndex);
287 break;
288 case LOEvent.TILE_INVALIDATION:
289 tileInvalidation(event.mInvalidationRect);
290 break;
291 case LOEvent.THUMBNAIL:
292 createThumbnail(event.mTask);
293 break;
294 case LOEvent.TOUCH:
295 touch(event.mTouchType, event.mDocumentCoordinate);
296 break;
297 case LOEvent.KEY_EVENT:
298 keyEvent(event.mKeyEvent);
299 break;
300 case LOEvent.TILE_REEVALUATION_REQUEST:
301 tileReevaluationRequest(event.mComposedTileLayer);
302 break;
303 case LOEvent.CHANGE_HANDLE_POSITION:
304 changeHandlePosition(event.mHandleType, event.mDocumentCoordinate);
305 break;
306 case LOEvent.SWIPE_LEFT:
307 if (null != mTileProvider) onSwipeLeft();
308 break;
309 case LOEvent.SWIPE_RIGHT:
310 if (null != mTileProvider) onSwipeRight();
311 break;
312 case LOEvent.NAVIGATION_CLICK:
313 mInvalidationHandler.changeStateTo(InvalidationHandler.OverlayState.NONE);
314 break;
315 case LOEvent.UNO_COMMAND:
316 if (null == mTileProvider)
317 Log.e(LOGTAG, "no mTileProvider when trying to process "+event.mValue+" from UNO_COMMAND "+event.mString);
318 else
319 mTileProvider.postUnoCommand(event.mString, event.mValue);
320 break;
321 case LOEvent.UPDATE_PART_PAGE_RECT:
322 updatePartPageRectangles();
323 break;
324 case LOEvent.UPDATE_ZOOM_CONSTRAINTS:
325 updateZoomConstraints();
326 break;
327 case LOEvent.UPDATE_CALC_HEADERS:
328 updateCalcHeaders();
329 break;
330 case LOEvent.UNO_COMMAND_NOTIFY:
331 if (null == mTileProvider)
332 Log.e(LOGTAG, "no mTileProvider when trying to process "+event.mValue+" from UNO_COMMAND "+event.mString);
333 else
334 mTileProvider.postUnoCommand(event.mString, event.mValue, event.mNotify);
335 break;
336 case LOEvent.REFRESH:
337 refresh(false);
338 break;
339 case LOEvent.PAGE_SIZE_CHANGED:
340 updatePageSize(event.mPageWidth, event.mPageHeight);
341 break;
342 }
343 }
344
345 private void updateCalcHeaders() {
346 if (null == mTileProvider) return;
347 LOKitTileProvider tileProvider = (LOKitTileProvider)mTileProvider;
348 String values = tileProvider.getCalcHeaders();
349 mContext.getCalcHeadersController().setHeaders(values);
350 }
351
355 private void changeHandlePosition(SelectionHandle.HandleType handleType, PointF documentCoordinate) {
356 switch (handleType) {
357 case MIDDLE:
358 mTileProvider.setTextSelectionReset(documentCoordinate);
359 break;
360 case START:
361 mTileProvider.setTextSelectionStart(documentCoordinate);
362 break;
363 case END:
364 mTileProvider.setTextSelectionEnd(documentCoordinate);
365 break;
366 }
367 }
368
372 private void keyEvent(KeyEvent keyEvent) {
373 if (!LOKitShell.isEditingEnabled()) {
374 return;
375 }
376 if (mTileProvider == null) {
377 return;
378 }
379 mInvalidationHandler.keyEvent();
380 mTileProvider.sendKeyEvent(keyEvent);
381 }
382
386 private void onSwipeLeft() {
387 mTileProvider.onSwipeLeft();
388 }
389
393 private void onSwipeRight() {
394 mTileProvider.onSwipeRight();
395 }
396
400 private void touch(String touchType, PointF documentCoordinate) {
401 if (mTileProvider == null || mViewportMetrics == null) {
402 return;
403 }
404
405 // to handle hyperlinks, enable single tap even in the Viewer
406 boolean editing = LOKitShell.isEditingEnabled();
407 float zoomFactor = mViewportMetrics.getZoomFactor();
408
409 if (touchType.equals("LongPress")) {
410 mInvalidationHandler.changeStateTo(InvalidationHandler.OverlayState.TRANSITION);
411 mTileProvider.mouseButtonDown(documentCoordinate, 1, zoomFactor);
412 mTileProvider.mouseButtonUp(documentCoordinate, 1, zoomFactor);
413 mTileProvider.mouseButtonDown(documentCoordinate, 2, zoomFactor);
414 mTileProvider.mouseButtonUp(documentCoordinate, 2, zoomFactor);
415 } else if (touchType.equals("SingleTap")) {
416 mInvalidationHandler.changeStateTo(InvalidationHandler.OverlayState.TRANSITION);
417 mTileProvider.mouseButtonDown(documentCoordinate, 1, zoomFactor);
418 mTileProvider.mouseButtonUp(documentCoordinate, 1, zoomFactor);
419 } else if (touchType.equals("GraphicSelectionStart") && editing) {
420 mTileProvider.setGraphicSelectionStart(documentCoordinate);
421 } else if (touchType.equals("GraphicSelectionEnd") && editing) {
422 mTileProvider.setGraphicSelectionEnd(documentCoordinate);
423 }
424 }
425
429 private void createThumbnail(final ThumbnailCreator.ThumbnailCreationTask task) {
430 final Bitmap bitmap = task.getThumbnail(mTileProvider);
431 task.applyBitmap(bitmap);
432 }
433
437 public void queueEvent(LOEvent event) {
438 mEventQueue.add(event);
439 }
440
444 public void clearQueue() {
445 mEventQueue.clear();
446 }
447}
448
449/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define LOGTAG
Selection handle is a common class for "start", "middle" and "end" types of selection handles.
ImmutableViewportMetrics are used to store the viewport metrics in way that we can access a version o...
def run(arg=None, arg2=-1)
MIDDLE
double touch(double S, double vol, double rd, double rf, double tau, double B1, double B2, types::ForDom fd, types::BarrierKIO kio, types::BarrierActive bcont, types::Greeks greek)
END
START
std::vector< char * > values