9package org.libreoffice;
11import android.content.Context;
12import android.graphics.Bitmap;
13import android.graphics.PointF;
14import android.os.Build;
15import android.print.PrintAttributes;
16import android.print.PrintDocumentAdapter;
17import android.print.PrintManager;
18import android.util.Log;
19import android.view.KeyEvent;
20import android.widget.Toast;
22import org.json.JSONException;
23import org.json.JSONObject;
33import java.nio.ByteBuffer;
38class LOKitTileProvider
implements TileProvider {
39 private static final String LOGTAG = LOKitTileProvider.class.getSimpleName();
40 private static final int TILE_SIZE = 256;
41 private final float mTileWidth;
42 private final float mTileHeight;
44 private Office mOffice;
46 private final boolean mIsReady;
47 private final LibreOfficeMainActivity mContext;
49 private final float mDPI;
50 private float mWidthTwip;
51 private float mHeightTwip;
53 private final Document.MessageCallback mMessageCallback;
55 private final long objectCreationTime =
System.currentTimeMillis();
62 LOKitTileProvider(LibreOfficeMainActivity context, InvalidationHandler messageCallback, String input) {
64 mMessageCallback = messageCallback;
66 LibreOfficeKit.putenv(
"SAL_LOG=+WARN+INFO");
67 LibreOfficeKit.init(mContext);
69 mOffice =
new Office(LibreOfficeKit.getLibreOfficeKitHandle());
70 mOffice.setMessageCallback(messageCallback);
71 mOffice.setOptionalFeatures(
Document.LOK_FEATURE_DOCUMENT_PASSWORD);
72 mContext.setTileProvider(
this);
75 Log.i(LOGTAG,
"====> Loading file '" + input +
"'");
77 File fileToBeEncoded =
new File(input);
78 String encodedFileName = android.net.Uri.encode(fileToBeEncoded.getName());
80 mDocument = mOffice.documentLoad(
81 (
new File(fileToBeEncoded.getParent(),encodedFileName)).getPath()
84 if (mDocument ==
null && !mContext.isPasswordProtected()) {
85 Log.i(LOGTAG,
"====> mOffice.documentLoad() returned null, trying to restart 'Office' and loading again");
87 Log.i(LOGTAG,
"====> mOffice.destroy() done");
88 ByteBuffer handle = LibreOfficeKit.getLibreOfficeKitHandle();
89 Log.i(LOGTAG,
"====> getLibreOfficeKitHandle() = " + handle);
90 mOffice =
new Office(handle);
91 Log.i(LOGTAG,
"====> new Office created");
92 mOffice.setMessageCallback(messageCallback);
93 mOffice.setOptionalFeatures(
Document.LOK_FEATURE_DOCUMENT_PASSWORD);
94 Log.i(LOGTAG,
"====> setup Lokit callback and optional features (password support)");
95 mDocument = mOffice.documentLoad(
96 (
new File(fileToBeEncoded.getParent(),encodedFileName)).getPath()
100 Log.i(LOGTAG,
"====> mDocument = " + mDocument);
102 mDPI = LOKitShell.getDpi(mContext);
106 if (mDocument !=
null)
107 mDocument.initializeForRendering();
109 if (checkDocument()) {
120 private void postLoad() {
121 mDocument.setMessageCallback(mMessageCallback);
125 if (mDocument.getDocumentType() ==
Document.DOCTYPE_TEXT) {
126 mContext.disableNavigationDrawer();
127 mContext.getToolbarController().hideItem(
R.id.action_parts);
131 if (mDocument.getDocumentType() ==
Document.DOCTYPE_SPREADSHEET) {
132 mContext.initializeCalcHeaders();
135 mDocument.setPart(0);
137 setupDocumentFonts();
139 LOKitShell.getMainHandler().post(
new Runnable() {
142 mContext.getDocumentPartViewListAdapter().notifyDataSetChanged();
147 public void addPart(){
148 int parts = mDocument.getParts();
149 if(mDocument.getDocumentType() ==
Document.DOCTYPE_SPREADSHEET){
151 JSONObject jsonObject =
new JSONObject();
152 JSONObject
values =
new JSONObject();
153 JSONObject values2 =
new JSONObject();
154 values.put(
"type",
"long");
156 values2.put(
"type",
"string");
157 values2.put(
"value",
"");
158 jsonObject.put(
"Name", values2);
159 jsonObject.put(
"Index", values);
160 LOKitShell.sendEvent(
new LOEvent(
LOEvent.UNO_COMMAND,
".uno:Insert", jsonObject.toString()));
161 }
catch (JSONException e) {
164 }
else if (mDocument.getDocumentType() ==
Document.DOCTYPE_PRESENTATION){
165 LOKitShell.sendEvent(
new LOEvent(
LOEvent.UNO_COMMAND,
".uno:InsertPage"));
168 String partName = mDocument.getPartName(parts);
169 if (partName.isEmpty()) {
170 partName = getGenericPartName(parts);
172 mDocument.setPart(parts);
174 final DocumentPartView partView =
new DocumentPartView(parts, partName);
175 mContext.getDocumentPartView().add(partView);
178 public void resetParts(){
179 mContext.getDocumentPartView().clear();
180 if (mDocument.getDocumentType() !=
Document.DOCTYPE_TEXT) {
181 int parts = mDocument.getParts();
182 for (
int i = 0;
i < parts;
i++) {
183 String partName = mDocument.getPartName(i);
185 if (partName.isEmpty()) {
186 partName = getGenericPartName(i);
188 Log.i(LOGTAG,
"resetParts: " + partName);
189 mDocument.setPart(i);
191 final DocumentPartView partView =
new DocumentPartView(i, partName);
192 mContext.getDocumentPartView().add(partView);
197 public void renamePart(String partName) {
199 for(
int i=0;
i<mDocument.getParts();
i++){
200 if(mContext.getDocumentPartView().get(i).partName.equals(partName)){
202 Toast.makeText(mContext, mContext.getString(
R.string.name_already_used), Toast.LENGTH_SHORT).show();
206 JSONObject parameter =
new JSONObject();
207 JSONObject
name =
new JSONObject();
208 name.put(
"type",
"string");
209 name.put(
"value", partName);
210 parameter.put(
"Name", name);
211 if(isPresentation()){
212 LOKitShell.sendEvent(
new LOEvent(
LOEvent.UNO_COMMAND_NOTIFY,
".uno:RenamePage", parameter.toString(),
true));
214 JSONObject
index =
new JSONObject();
215 index.put(
"type",
"long");
216 index.put(
"value", getCurrentPartNumber()+1);
217 parameter.put(
"Index", index);
218 LOKitShell.sendEvent(
new LOEvent(
LOEvent.UNO_COMMAND_NOTIFY,
".uno:Name", parameter.toString(),
true));
220 }
catch (JSONException e){
225 public void removePart() {
227 if (!isSpreadsheet() && !isPresentation()) {
232 if(isPresentation()){
233 LOKitShell.sendEvent(
new LOEvent(
LOEvent.UNO_COMMAND_NOTIFY,
".uno:DeletePage",
true));
237 if(getPartsCount() < 2){
241 JSONObject parameter =
new JSONObject();
242 JSONObject
index =
new JSONObject();
243 index.put(
"type",
"long");
244 index.put(
"value", getCurrentPartNumber()+1);
245 parameter.put(
"Index", index);
246 LOKitShell.sendEvent(
new LOEvent(
LOEvent.UNO_COMMAND_NOTIFY,
".uno:Remove", parameter.toString(),
true));
247 }
catch (JSONException e){
253 public boolean saveDocumentAs(
final String filePath, String format,
boolean takeOwnership) {
256 options =
"TakeOwnership";
260 Log.d(
"saveFilePathURL", newFilePath);
261 LOKitShell.showProgressSpinner(mContext);
262 mDocument.saveAs(newFilePath, format, options);
264 if (!mOffice.getError().isEmpty()){
266 Log.e(
"Save Error", mOffice.getError());
267 if (format.equals(
"svg")) {
269 Log.d(LOGTAG,
"Error in creating temp slideshow svg file");
270 }
else if(format.equals(
"pdf")){
271 Log.d(LOGTAG,
"Error in creating pdf file");
273 LOKitShell.getMainHandler().post(
new Runnable() {
277 mContext.showCustomStatusMessage(mContext.getString(
R.string.unable_to_save));
283 if (format.equals(
"svg")) {
285 LOKitShell.getMainHandler().post(
new Runnable() {
288 mContext.startPresentation(newFilePath);
291 }
else if (takeOwnership) {
295 LOKitShell.hideProgressSpinner(mContext);
300 public boolean saveDocumentAs(
final String filePath,
boolean takeOwnership) {
301 final int docType = mDocument.getDocumentType();
302 if (docType ==
Document.DOCTYPE_TEXT)
303 return saveDocumentAs(filePath,
"odt", takeOwnership);
304 else if (docType ==
Document.DOCTYPE_SPREADSHEET)
305 return saveDocumentAs(filePath,
"ods", takeOwnership);
306 else if (docType ==
Document.DOCTYPE_PRESENTATION)
307 return saveDocumentAs(filePath,
"odp", takeOwnership);
308 else if (docType ==
Document.DOCTYPE_DRAWING)
309 return saveDocumentAs(filePath,
"odg", takeOwnership);
311 Log.w(LOGTAG,
"Cannot determine file format from document. Not saving.");
315 public void printDocument() {
316 if (Build.VERSION.SDK_INT < 19) {
317 mContext.showCustomStatusMessage(mContext.getString(
R.string.printing_not_supported));
321 String mInputFileName = (
new File(mInputFile)).getName();
322 String file = mInputFileName.substring(0,(mInputFileName.length()-3))+
"pdf";
323 String cacheFile = mContext.getExternalCacheDir().getAbsolutePath() +
"/" + file;
324 mDocument.saveAs(
"file://"+cacheFile,
"pdf",
"");
326 PrintManager printManager = (PrintManager) mContext.getSystemService(Context.PRINT_SERVICE);
327 PrintDocumentAdapter printAdapter =
new PDFDocumentAdapter(mContext, cacheFile);
328 printManager.print(
"Document", printAdapter,
new PrintAttributes.Builder().build());
330 }
catch (Exception e) {
335 public void saveDocument(){
336 mContext.saveDocument();
339 private void setupDocumentFonts() {
340 String values = mDocument.getCommandValues(
".uno:CharFontName");
341 if (values ==
null ||
values.isEmpty())
344 mContext.getFontController().parseJson(values);
345 mContext.getFontController().setupFontViews();
348 private String getGenericPartName(
int i) {
349 if (mDocument ==
null) {
352 switch (mDocument.getDocumentType()) {
355 return mContext.getString(
R.string.page) +
" " + (
i + 1);
357 return mContext.getString(
R.string.sheet) +
" " + (
i + 1);
359 return mContext.getString(
R.string.slide) +
" " + (
i + 1);
362 return mContext.getString(
R.string.part) +
" " + (
i + 1);
367 return input / 1440.0f * dpi;
370 private static float pixelToTwip(
float input,
float dpi) {
371 return (input / dpi) * 1440.0f;
379 public int getPartsCount() {
380 return mDocument.getParts();
386 public String getPartPageRectangles() {
387 return mDocument.getPartPageRectangles();
393 public String getCalcHeaders() {
396 long nWidth = mDocument.getDocumentWidth();
397 long nHeight = mDocument.getDocumentHeight();
398 return mDocument.getCommandValues(
".uno:ViewRowColumnHeaders?x=" + nX +
"&y=" + nY
399 +
"&width=" + nWidth +
"&height=" + nHeight);
406 public void onSwipeLeft() {
407 if (mDocument.getDocumentType() ==
Document.DOCTYPE_PRESENTATION &&
408 getCurrentPartNumber() < getPartsCount()-1) {
409 LOKitShell.sendChangePartEvent(getCurrentPartNumber()+1);
417 public void onSwipeRight() {
418 if (mDocument.getDocumentType() ==
Document.DOCTYPE_PRESENTATION &&
419 getCurrentPartNumber() > 0) {
420 LOKitShell.sendChangePartEvent(getCurrentPartNumber()-1);
424 private boolean checkDocument() {
428 if (mDocument ==
null || !mOffice.getError().isEmpty()) {
429 error =
"Cannot open " + mInputFile +
": " + mOffice.getError();
432 ret = resetDocumentSize();
434 error =
"Document returned an invalid size or the document is empty.";
438 if (!ret && !mContext.isPasswordProtected()) {
439 final String message = error;
440 LOKitShell.getMainHandler().post(
new Runnable() {
443 mContext.showAlertDialog(message);
446 }
else if (!ret && mContext.isPasswordProtected()) {
453 private boolean resetDocumentSize() {
454 mWidthTwip = mDocument.getDocumentWidth();
455 mHeightTwip = mDocument.getDocumentHeight();
457 if (mWidthTwip == 0 || mHeightTwip == 0) {
458 Log.e(LOGTAG,
"Document size zero - last error: " + mOffice.getError());
461 Log.i(LOGTAG,
"Reset document size: " + mDocument.getDocumentWidth() +
" x " + mDocument.getDocumentHeight());
468 public void setDocumentSize(
int pageWidth,
int pageHeight){
469 mWidthTwip = pageWidth;
470 mHeightTwip = pageHeight;
477 public int getPageWidth() {
485 public int getPageHeight() {
493 public boolean isReady() {
501 public CairoImage createTile(
float x,
float y, IntSize tileSize,
float zoom) {
502 ByteBuffer buffer = DirectBufferAllocator.guardedAllocate(tileSize.width * tileSize.height * 4);
506 CairoImage image =
new BufferedCairoImage(buffer, tileSize.width, tileSize.height, CairoImage.FORMAT_ARGB32);
507 rerenderTile(image, x, y, tileSize, zoom);
515 public void rerenderTile(CairoImage image,
float x,
float y, IntSize tileSize,
float zoom) {
516 if (mDocument !=
null && image.getBuffer() !=
null) {
519 float twipWidth = mTileWidth / zoom;
520 float twipHeight = mTileHeight / zoom;
521 long start =
System.currentTimeMillis() - objectCreationTime;
524 mDocument.paintTile(image.getBuffer(), tileSize.width, tileSize.height, (
int) twipX, (
int) twipY, (
int) twipWidth, (
int) twipHeight);
526 long stop =
System.currentTimeMillis() - objectCreationTime;
529 if (mDocument ==
null) {
530 Log.e(LOGTAG,
"Document is null!!");
539 public Bitmap thumbnail(
int size) {
540 int widthPixel = getPageWidth();
541 int heightPixel = getPageHeight();
543 if (widthPixel > heightPixel) {
544 double ratio = heightPixel / (double) widthPixel;
546 heightPixel = (
int) (widthPixel * ratio);
548 double ratio = widthPixel / (double) heightPixel;
550 widthPixel = (
int) (heightPixel * ratio);
553 Log.w(LOGTAG,
"Thumbnail size: " + getPageWidth() +
" " + getPageHeight() +
" " + widthPixel +
" " + heightPixel);
555 ByteBuffer buffer = ByteBuffer.allocateDirect(widthPixel * heightPixel * 4);
556 if (mDocument !=
null)
557 mDocument.paintTile(buffer, widthPixel, heightPixel, 0, 0, (
int) mWidthTwip, (
int) mHeightTwip);
561 bitmap =
Bitmap.createBitmap(widthPixel, heightPixel,
Bitmap.Config.ARGB_8888);
562 bitmap.copyPixelsFromBuffer(buffer);
563 }
catch (IllegalArgumentException e) {
564 Log.e(LOGTAG,
"width (" + widthPixel +
") and height (" + heightPixel +
") must not be 0! (ToDo: likely timing issue)");
566 if (bitmap ==
null) {
567 Log.w(LOGTAG,
"Thumbnail not created!");
576 public void close() {
577 Log.i(LOGTAG,
"Document destroyed: " + mInputFile);
578 if (mDocument !=
null) {
588 public boolean isDrawing() {
589 return mDocument !=
null && mDocument.getDocumentType() ==
Document.DOCTYPE_DRAWING;
596 public boolean isTextDocument() {
597 return mDocument !=
null && mDocument.getDocumentType() ==
Document.DOCTYPE_TEXT;
604 public boolean isSpreadsheet() {
605 return mDocument !=
null && mDocument.getDocumentType() ==
Document.DOCTYPE_SPREADSHEET;
612 public boolean isPresentation(){
613 return mDocument !=
null && mDocument.getDocumentType() ==
Document.DOCTYPE_PRESENTATION;
619 private int getCharCode(KeyEvent keyEvent) {
620 switch (keyEvent.getKeyCode())
622 case KeyEvent.KEYCODE_DEL:
623 case KeyEvent.KEYCODE_ENTER:
626 return keyEvent.getUnicodeChar();
633 private int getKeyCode(KeyEvent keyEvent) {
634 switch (keyEvent.getKeyCode()) {
635 case KeyEvent.KEYCODE_DEL:
637 case KeyEvent.KEYCODE_ENTER:
647 public void sendKeyEvent(KeyEvent keyEvent) {
648 switch (keyEvent.getAction()) {
649 case KeyEvent.ACTION_MULTIPLE:
650 String keyString = keyEvent.getCharacters();
651 for (
int i = 0;
i < keyString.length();
i++) {
652 int codePoint = keyString.codePointAt(i);
653 mDocument.postKeyEvent(
Document.KEY_EVENT_PRESS, codePoint, getKeyCode(keyEvent));
656 case KeyEvent.ACTION_DOWN:
657 mDocument.postKeyEvent(
Document.KEY_EVENT_PRESS, getCharCode(keyEvent), getKeyCode(keyEvent));
659 case KeyEvent.ACTION_UP:
660 mDocument.postKeyEvent(
Document.KEY_EVENT_RELEASE, getCharCode(keyEvent), getKeyCode(keyEvent));
665 private void mouseButton(
int type, PointF inDocument,
int numberOfClicks,
float zoomFactor) {
669 mDocument.setClientZoom(TILE_SIZE, TILE_SIZE, (
int) (mTileWidth / zoomFactor), (
int) (mTileHeight / zoomFactor));
670 mDocument.postMouseEvent(type, x, y, numberOfClicks,
Document.MOUSE_BUTTON_LEFT,
Document.KEYBOARD_MODIFIER_NONE);
677 public void mouseButtonDown(PointF documentCoordinate,
int numberOfClicks,
float zoomFactor) {
678 mouseButton(
Document.MOUSE_EVENT_BUTTON_DOWN, documentCoordinate, numberOfClicks, zoomFactor);
685 public void mouseButtonUp(PointF documentCoordinate,
int numberOfClicks,
float zoomFactor) {
686 mouseButton(
Document.MOUSE_EVENT_BUTTON_UP, documentCoordinate, numberOfClicks, zoomFactor);
694 public void postUnoCommand(String command, String arguments) {
695 postUnoCommand(command, arguments,
false);
704 public void postUnoCommand(String command, String arguments,
boolean notifyWhenFinished) {
705 mDocument.postUnoCommand(command, arguments, notifyWhenFinished);
708 private void setTextSelection(
int type, PointF documentCoordinate) {
711 mDocument.setTextSelection(type, x, y);
718 public void setTextSelectionStart(PointF documentCoordinate) {
719 setTextSelection(
Document.SET_TEXT_SELECTION_START, documentCoordinate);
726 public void setTextSelectionEnd(PointF documentCoordinate) {
727 setTextSelection(
Document.SET_TEXT_SELECTION_END, documentCoordinate);
734 public void setTextSelectionReset(PointF documentCoordinate) {
735 setTextSelection(
Document.SET_TEXT_SELECTION_RESET, documentCoordinate);
744 return mDocument.getTextSelection(mimeType);
754 public boolean paste(String mimeType, String data) {
755 return mDocument.paste(mimeType, data);
763 public void setGraphicSelectionStart(PointF documentCoordinate) {
764 setGraphicSelection(
Document.SET_GRAPHIC_SELECTION_START, documentCoordinate);
771 public void setGraphicSelectionEnd(PointF documentCoordinate) {
772 setGraphicSelection(
Document.SET_GRAPHIC_SELECTION_END, documentCoordinate);
775 private void setGraphicSelection(
int type, PointF documentCoordinate) {
778 LibreOfficeMainActivity.setDocumentChanged(
true);
779 mDocument.setGraphicSelection(type, x, y);
783 protected void finalize() throws Throwable {
792 public void changePart(
int partIndex) {
793 if (mDocument ==
null)
796 mDocument.setPart(partIndex);
804 public int getCurrentPartNumber() {
805 if (mDocument ==
null)
808 return mDocument.getPart();
811 public void setDocumentPassword(String url, String password) {
812 mOffice.setDocumentPassword(url, password);
815 public Document.MessageCallback getMessageCallback() {
816 return mMessageCallback;
A Cairo image that simply saves a buffer of pixel data.
def run(arg=None, arg2=-1)
OString OOO_DLLPUBLIC_TEST getTextSelection(const css::uno::Reference< css::datatransfer::XTransferable > &xTransferable, OString mimeType)
std::vector< char * > values
const wchar_t *typedef int(__stdcall *DllNativeUnregProc)(int
float pixelToTwip(float fInput, float zoom)
float twipToPixel(float fInput, float zoom)