summaryrefslogtreecommitdiffstats
path: root/android/source/src/java/org/libreoffice/LOKitTileProvider.java
diff options
context:
space:
mode:
Diffstat (limited to 'android/source/src/java/org/libreoffice/LOKitTileProvider.java')
-rw-r--r--android/source/src/java/org/libreoffice/LOKitTileProvider.java510
1 files changed, 510 insertions, 0 deletions
diff --git a/android/source/src/java/org/libreoffice/LOKitTileProvider.java b/android/source/src/java/org/libreoffice/LOKitTileProvider.java
new file mode 100644
index 000000000000..00af8d51b1d5
--- /dev/null
+++ b/android/source/src/java/org/libreoffice/LOKitTileProvider.java
@@ -0,0 +1,510 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ */
+package org.libreoffice;
+
+import android.graphics.Bitmap;
+import android.graphics.PointF;
+import android.util.Log;
+import android.view.KeyEvent;
+
+import org.libreoffice.kit.DirectBufferAllocator;
+import org.libreoffice.kit.Document;
+import org.libreoffice.kit.LibreOfficeKit;
+import org.libreoffice.kit.Office;
+import org.mozilla.gecko.gfx.BufferedCairoImage;
+import org.mozilla.gecko.gfx.CairoImage;
+import org.mozilla.gecko.gfx.GeckoLayerClient;
+import org.mozilla.gecko.gfx.IntSize;
+
+import java.nio.ByteBuffer;
+
+/**
+ * LOKit implementation of TileProvider.
+ */
+public class LOKitTileProvider implements TileProvider {
+ private static final String LOGTAG = LOKitTileProvider.class.getSimpleName();
+ private static int TILE_SIZE = 256;
+ private final GeckoLayerClient mLayerClient;
+ private final float mTileWidth;
+ private final float mTileHeight;
+ private final String mInputFile;
+ private Office mOffice;
+ private Document mDocument;
+ private boolean mIsReady = false;
+
+ private float mDPI;
+ private float mWidthTwip;
+ private float mHeightTwip;
+
+ private Document.MessageCallback mMessageCallback;
+
+ private long objectCreationTime = System.currentTimeMillis();
+
+ /**
+ * Initialize LOKit and load the document.
+ * @param layerClient - layerclient implementation
+ * @param messageCallback - callback for messages retrieved from LOKit
+ * @param input - input path of the document
+ */
+ public LOKitTileProvider(GeckoLayerClient layerClient, Document.MessageCallback messageCallback, String input) {
+ mLayerClient = layerClient;
+ mMessageCallback = messageCallback;
+ mDPI = (float) LOKitShell.getDpi();
+ mTileWidth = pixelToTwip(TILE_SIZE, mDPI);
+ mTileHeight = pixelToTwip(TILE_SIZE, mDPI);
+
+ LibreOfficeKit.putenv("SAL_LOG=+WARN+INFO");
+ LibreOfficeKit.init(LibreOfficeMainActivity.mAppContext);
+
+ mOffice = new Office(LibreOfficeKit.getLibreOfficeKitHandle());
+
+ mInputFile = input;
+
+ Log.i(LOGTAG, "====> Loading file '" + input + "'");
+ mDocument = mOffice.documentLoad(input);
+
+ if (mDocument == null) {
+ Log.i(LOGTAG, "====> mOffice.documentLoad() returned null, trying to restart 'Office' and loading again");
+ mOffice.destroy();
+ Log.i(LOGTAG, "====> mOffice.destroy() done");
+ ByteBuffer handle = LibreOfficeKit.getLibreOfficeKitHandle();
+ Log.i(LOGTAG, "====> getLibreOfficeKitHandle() = " + handle);
+ mOffice = new Office(handle);
+ Log.i(LOGTAG, "====> new Office created");
+ mDocument = mOffice.documentLoad(input);
+ }
+
+ Log.i(LOGTAG, "====> mDocument = " + mDocument);
+
+ if (mDocument != null)
+ mDocument.initializeForRendering();
+
+ if (checkDocument()) {
+ postLoad();
+ mIsReady = true;
+ } else {
+ mIsReady = false;
+ }
+ }
+
+ /**
+ * Triggered after the document is loaded.
+ */
+ private void postLoad() {
+ mDocument.setMessageCallback(mMessageCallback);
+
+ int parts = mDocument.getParts();
+ Log.i(LOGTAG, "Document parts: " + parts);
+
+ LibreOfficeMainActivity.mAppContext.getDocumentPartView().clear();
+
+ // Writer documents always have one part, so hide the navigation drawer.
+ if (mDocument.getDocumentType() != Document.DOCTYPE_TEXT) {
+ for (int i = 0; i < parts; i++) {
+ String partName = mDocument.getPartName(i);
+ if (partName.isEmpty()) {
+ partName = getGenericPartName(i);
+ }
+ Log.i(LOGTAG, "Document part " + i + " name:'" + partName + "'");
+
+ mDocument.setPart(i);
+ resetDocumentSize();
+ final DocumentPartView partView = new DocumentPartView(i, partName);
+ LibreOfficeMainActivity.mAppContext.getDocumentPartView().add(partView);
+ }
+ } else {
+ LibreOfficeMainActivity.mAppContext.disableNavigationDrawer();
+ }
+
+ mDocument.setPart(0);
+
+ LOKitShell.getMainHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ LibreOfficeMainActivity.mAppContext.getDocumentPartViewListAdapter().notifyDataSetChanged();
+ }
+ });
+ }
+
+ private String getGenericPartName(int i) {
+ if (mDocument == null) {
+ return "";
+ }
+ switch (mDocument.getDocumentType()) {
+ case Document.DOCTYPE_DRAWING:
+ case Document.DOCTYPE_TEXT:
+ return "Page " + (i + 1);
+ case Document.DOCTYPE_SPREADSHEET:
+ return "Sheet " + (i + 1);
+ case Document.DOCTYPE_PRESENTATION:
+ return "Slide " + (i + 1);
+ case Document.DOCTYPE_OTHER:
+ default:
+ return "Part " + (i + 1);
+ }
+ }
+
+ public static float twipToPixel(float input, float dpi) {
+ return input / 1440.0f * dpi;
+ }
+
+ public static float pixelToTwip(float input, float dpi) {
+ return (input / dpi) * 1440.0f;
+ }
+
+
+ /**
+ * @see TileProvider#getPartsCount()
+ */
+ @Override
+ public int getPartsCount() {
+ return mDocument.getParts();
+ }
+
+ /**
+ * @see TileProvider#onSwipeLeft()
+ */
+ @Override
+ public void onSwipeLeft() {
+ if (mDocument.getDocumentType() == Document.DOCTYPE_PRESENTATION &&
+ getCurrentPartNumber() < getPartsCount()-1) {
+ LOKitShell.sendChangePartEvent(getCurrentPartNumber()+1);
+ }
+ }
+
+ /**
+ * @see TileProvider#onSwipeRight()
+ */
+ @Override
+ public void onSwipeRight() {
+ if (mDocument.getDocumentType() == Document.DOCTYPE_PRESENTATION &&
+ getCurrentPartNumber() > 0) {
+ LOKitShell.sendChangePartEvent(getCurrentPartNumber()-1);
+ }
+ }
+
+ private boolean checkDocument() {
+ String error = null;
+ boolean ret;
+
+ if (mDocument == null || !mOffice.getError().isEmpty()) {
+ error = "Cannot open " + mInputFile + ": " + mOffice.getError();
+ ret = false;
+ } else {
+ ret = resetDocumentSize();
+ if (!ret) {
+ error = "Document returned an invalid size or the document is empty.";
+ }
+ }
+
+ if (!ret) {
+ final String message = error;
+ LOKitShell.getMainHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ LibreOfficeMainActivity.mAppContext.showAlertDialog(message);
+ }
+ });
+ }
+
+ return ret;
+ }
+
+ private boolean resetDocumentSize() {
+ mWidthTwip = mDocument.getDocumentWidth();
+ mHeightTwip = mDocument.getDocumentHeight();
+
+ if (mWidthTwip == 0 || mHeightTwip == 0) {
+ Log.e(LOGTAG, "Document size zero - last error: " + mOffice.getError());
+ return false;
+ } else {
+ Log.i(LOGTAG, "Reset document size: " + mDocument.getDocumentWidth() + " x " + mDocument.getDocumentHeight());
+ }
+
+ return true;
+ }
+
+ /**
+ * @see TileProvider#getPageWidth()
+ */
+ @Override
+ public int getPageWidth() {
+ return (int) twipToPixel(mWidthTwip, mDPI);
+ }
+
+ /**
+ * @see TileProvider#getPageHeight()
+ */
+ @Override
+ public int getPageHeight() {
+ return (int) twipToPixel(mHeightTwip, mDPI);
+ }
+
+ /**
+ * @see TileProvider#isReady()
+ */
+ @Override
+ public boolean isReady() {
+ return mIsReady;
+ }
+
+ /**
+ * @see TileProvider#createTile(float, float, org.mozilla.gecko.gfx.IntSize, float)
+ */
+ @Override
+ public CairoImage createTile(float x, float y, IntSize tileSize, float zoom) {
+ ByteBuffer buffer = DirectBufferAllocator.guardedAllocate(tileSize.width * tileSize.height * 4);
+ if (buffer == null)
+ return null;
+
+ CairoImage image = new BufferedCairoImage(buffer, tileSize.width, tileSize.height, CairoImage.FORMAT_ARGB32);
+ rerenderTile(image, x, y, tileSize, zoom);
+ return image;
+ }
+
+ /**
+ * @see TileProvider#rerenderTile(org.mozilla.gecko.gfx.CairoImage, float, float, org.mozilla.gecko.gfx.IntSize, float)
+ */
+ @Override
+ public void rerenderTile(CairoImage image, float x, float y, IntSize tileSize, float zoom) {
+ if (mDocument != null && image.getBuffer() != null) {
+ float twipX = pixelToTwip(x, mDPI) / zoom;
+ float twipY = pixelToTwip(y, mDPI) / zoom;
+ float twipWidth = mTileWidth / zoom;
+ float twipHeight = mTileHeight / zoom;
+ long start = System.currentTimeMillis() - objectCreationTime;
+
+ //Log.i(LOGTAG, "paintTile >> @" + start + " (" + tileSize.width + " " + tileSize.height + " " + (int) twipX + " " + (int) twipY + " " + (int) twipWidth + " " + (int) twipHeight + ")");
+ mDocument.paintTile(image.getBuffer(), tileSize.width, tileSize.height, (int) twipX, (int) twipY, (int) twipWidth, (int) twipHeight);
+
+ long stop = System.currentTimeMillis() - objectCreationTime;
+ //Log.i(LOGTAG, "paintTile << @" + stop + " elapsed: " + (stop - start));
+ } else {
+ if (mDocument == null) {
+ Log.e(LOGTAG, "Document is null!!");
+ }
+ }
+ }
+
+ /**
+ * @see TileProvider#thumbnail(int)
+ */
+ @Override
+ public Bitmap thumbnail(int size) {
+ int widthPixel = getPageWidth();
+ int heightPixel = getPageHeight();
+
+ if (widthPixel > heightPixel) {
+ double ratio = heightPixel / (double) widthPixel;
+ widthPixel = size;
+ heightPixel = (int) (widthPixel * ratio);
+ } else {
+ double ratio = widthPixel / (double) heightPixel;
+ heightPixel = size;
+ widthPixel = (int) (heightPixel * ratio);
+ }
+
+ Log.w(LOGTAG, "Thumbnail size: " + getPageWidth() + " " + getPageHeight() + " " + widthPixel + " " + heightPixel);
+
+ ByteBuffer buffer = ByteBuffer.allocateDirect(widthPixel * heightPixel * 4);
+ if (mDocument != null)
+ mDocument.paintTile(buffer, widthPixel, heightPixel, 0, 0, (int) mWidthTwip, (int) mHeightTwip);
+
+ Bitmap bitmap = Bitmap.createBitmap(widthPixel, heightPixel, Bitmap.Config.ARGB_8888);
+ bitmap.copyPixelsFromBuffer(buffer);
+ if (bitmap == null) {
+ Log.w(LOGTAG, "Thumbnail not created!");
+ }
+ return bitmap;
+ }
+
+ /**
+ * @see TileProvider#close()
+ */
+ @Override
+ public void close() {
+ Log.i(LOGTAG, "Document destroyed: " + mInputFile);
+ if (mDocument != null) {
+ mDocument.destroy();
+ mDocument = null;
+ }
+ }
+
+ /**
+ * @see TileProvider#isTextDocument()
+ */
+ @Override
+ public boolean isTextDocument() {
+ return mDocument != null && mDocument.getDocumentType() == Document.DOCTYPE_TEXT;
+ }
+
+ /**
+ * @see TileProvider#isSpreadsheet()
+ */
+ @Override
+ public boolean isSpreadsheet() {
+ return mDocument != null && mDocument.getDocumentType() == Document.DOCTYPE_SPREADSHEET;
+ }
+
+ /**
+ * Returns the Unicode character generated by this event or 0.
+ */
+ private int getCharCode(KeyEvent keyEvent) {
+ switch (keyEvent.getKeyCode())
+ {
+ case KeyEvent.KEYCODE_DEL:
+ case KeyEvent.KEYCODE_ENTER:
+ return 0;
+ }
+ return keyEvent.getUnicodeChar();
+ }
+
+ /**
+ * Returns the integer code representing the key of the event (non-zero for
+ * control keys).
+ */
+ private int getKeyCode(KeyEvent keyEvent) {
+ switch (keyEvent.getKeyCode()) {
+ case KeyEvent.KEYCODE_DEL:
+ return com.sun.star.awt.Key.BACKSPACE;
+ case KeyEvent.KEYCODE_ENTER:
+ return com.sun.star.awt.Key.RETURN;
+ }
+ return 0;
+ }
+
+ /**
+ * @see TileProvider#sendKeyEvent(android.view.KeyEvent)
+ */
+ @Override
+ public void sendKeyEvent(KeyEvent keyEvent) {
+ if (keyEvent.getAction() == KeyEvent.ACTION_MULTIPLE) {
+ String keyString = keyEvent.getCharacters();
+ for (int i = 0; i < keyString.length(); i++) {
+ int codePoint = keyString.codePointAt(i);
+ mDocument.postKeyEvent(Office.KEY_PRESS, codePoint, getKeyCode(keyEvent));
+ }
+ } else if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
+ mDocument.postKeyEvent(Office.KEY_PRESS, getCharCode(keyEvent), getKeyCode(keyEvent));
+ } else if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
+ mDocument.postKeyEvent(Office.KEY_RELEASE, getCharCode(keyEvent), getKeyCode(keyEvent));
+ }
+ }
+
+ private void mouseButton(int type, PointF inDocument, int numberOfClicks) {
+ int x = (int) pixelToTwip(inDocument.x, mDPI);
+ int y = (int) pixelToTwip(inDocument.y, mDPI);
+
+ mDocument.postMouseEvent(type, x, y, numberOfClicks);
+ }
+
+ /**
+ * @see TileProvider#mouseButtonDown(android.graphics.PointF, int)
+ */
+ @Override
+ public void mouseButtonDown(PointF documentCoordinate, int numberOfClicks) {
+ mouseButton(Document.MOUSE_BUTTON_DOWN, documentCoordinate, numberOfClicks);
+ }
+
+ /**
+ * @see TileProvider#mouseButtonUp(android.graphics.PointF, int)
+ */
+ @Override
+ public void mouseButtonUp(PointF documentCoordinate, int numberOfClicks) {
+ mouseButton(Document.MOUSE_BUTTON_UP, documentCoordinate, numberOfClicks);
+ }
+
+ @Override
+ public void postUnoCommand(String command) {
+ mDocument.postUnoCommand(command);
+ }
+
+ private void setTextSelection(int type, PointF documentCoordinate) {
+ int x = (int) pixelToTwip(documentCoordinate.x, mDPI);
+ int y = (int) pixelToTwip(documentCoordinate.y, mDPI);
+ mDocument.setTextSelection(type, x, y);
+ }
+
+ /**
+ * @see TileProvider#setTextSelectionStart(android.graphics.PointF)
+ */
+ @Override
+ public void setTextSelectionStart(PointF documentCoordinate) {
+ setTextSelection(Document.SET_TEXT_SELECTION_START, documentCoordinate);
+ }
+
+ /**
+ * @see TileProvider#setTextSelectionEnd(android.graphics.PointF)
+ */
+ @Override
+ public void setTextSelectionEnd(PointF documentCoordinate) {
+ setTextSelection(Document.SET_TEXT_SELECTION_END, documentCoordinate);
+ }
+
+ /**
+ * @see TileProvider#setTextSelectionReset(android.graphics.PointF)
+ */
+ @Override
+ public void setTextSelectionReset(PointF documentCoordinate) {
+ setTextSelection(Document.SET_TEXT_SELECTION_RESET, documentCoordinate);
+ }
+
+ /**
+ * @see org.libreoffice.TileProvider#setGraphicSelectionStart(android.graphics.PointF)
+ */
+ @Override
+ public void setGraphicSelectionStart(PointF documentCoordinate) {
+ setGraphicSelection(Document.SET_GRAPHIC_SELECTION_START, documentCoordinate);
+ }
+
+ /**
+ * @see org.libreoffice.TileProvider#setGraphicSelectionEnd(android.graphics.PointF)
+ */
+ @Override
+ public void setGraphicSelectionEnd(PointF documentCoordinate) {
+ setGraphicSelection(Document.SET_GRAPHIC_SELECTION_END, documentCoordinate);
+ }
+
+ private void setGraphicSelection(int type, PointF documentCoordinate) {
+ int x = (int) pixelToTwip(documentCoordinate.x, mDPI);
+ int y = (int) pixelToTwip(documentCoordinate.y, mDPI);
+ mDocument.setGraphicSelection(type, x, y);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ close();
+ super.finalize();
+ }
+
+ /**
+ * @see TileProvider#changePart(int)
+ */
+ @Override
+ public void changePart(int partIndex) {
+ if (mDocument == null)
+ return;
+
+ mDocument.setPart(partIndex);
+ resetDocumentSize();
+ }
+
+ /**
+ * @see TileProvider#getCurrentPartNumber()
+ */
+ @Override
+ public int getCurrentPartNumber() {
+ if (mDocument == null)
+ return 0;
+
+ return mDocument.getPart();
+ }
+}
+
+// vim:set shiftwidth=4 softtabstop=4 expandtab: