1 /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 */ 9 package org.libreoffice; 10 11 import android.content.Context; 12 import android.graphics.Bitmap; 13 import android.graphics.PointF; 14 import android.os.Build; 15 import android.print.PrintAttributes; 16 import android.print.PrintDocumentAdapter; 17 import android.print.PrintManager; 18 import android.util.Log; 19 import android.view.KeyEvent; 20 import android.widget.Toast; 21 22 import org.json.JSONException; 23 import org.json.JSONObject; 24 import org.libreoffice.kit.DirectBufferAllocator; 25 import org.libreoffice.kit.Document; 26 import org.libreoffice.kit.LibreOfficeKit; 27 import org.libreoffice.kit.Office; 28 import org.mozilla.gecko.gfx.BufferedCairoImage; 29 import org.mozilla.gecko.gfx.CairoImage; 30 import org.mozilla.gecko.gfx.IntSize; 31 32 import java.io.File; 33 import java.nio.ByteBuffer; 34 35 /** 36 * LOKit implementation of TileProvider. 37 */ 38 class 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; 43 private String mInputFile; 44 private Office mOffice; 45 private Document mDocument; 46 private final boolean mIsReady; 47 private final LibreOfficeMainActivity mContext; 48 49 private final float mDPI; 50 private float mWidthTwip; 51 private float mHeightTwip; 52 53 private final Document.MessageCallback mMessageCallback; 54 55 private final long objectCreationTime = System.currentTimeMillis(); 56 57 /** 58 * Initialize LOKit and load the document. 59 * @param messageCallback - callback for messages retrieved from LOKit 60 * @param input - input path of the document 61 */ LOKitTileProvider(LibreOfficeMainActivity context, InvalidationHandler messageCallback, String input)62 LOKitTileProvider(LibreOfficeMainActivity context, InvalidationHandler messageCallback, String input) { 63 mContext = context; 64 mMessageCallback = messageCallback; 65 66 LibreOfficeKit.putenv("SAL_LOG=+WARN+INFO"); 67 LibreOfficeKit.init(mContext); 68 69 mOffice = new Office(LibreOfficeKit.getLibreOfficeKitHandle()); 70 mOffice.setMessageCallback(messageCallback); 71 mOffice.setOptionalFeatures(Document.LOK_FEATURE_DOCUMENT_PASSWORD); 72 mContext.setTileProvider(this); 73 mInputFile = input; 74 75 Log.i(LOGTAG, "====> Loading file '" + input + "'"); 76 77 File fileToBeEncoded = new File(input); 78 String encodedFileName = android.net.Uri.encode(fileToBeEncoded.getName()); 79 80 mDocument = mOffice.documentLoad( 81 (new File(fileToBeEncoded.getParent(),encodedFileName)).getPath() 82 ); 83 84 if (mDocument == null && !mContext.isPasswordProtected()) { 85 Log.i(LOGTAG, "====> mOffice.documentLoad() returned null, trying to restart 'Office' and loading again"); 86 mOffice.destroy(); 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() 97 ); 98 } 99 100 Log.i(LOGTAG, "====> mDocument = " + mDocument); 101 102 mDPI = LOKitShell.getDpi(mContext); 103 mTileWidth = pixelToTwip(TILE_SIZE, mDPI); 104 mTileHeight = pixelToTwip(TILE_SIZE, mDPI); 105 106 if (mDocument != null) 107 mDocument.initializeForRendering(); 108 109 if (checkDocument()) { 110 postLoad(); 111 mIsReady = true; 112 } else { 113 mIsReady = false; 114 } 115 } 116 117 /** 118 * Triggered after the document is loaded. 119 */ postLoad()120 private void postLoad() { 121 mDocument.setMessageCallback(mMessageCallback); 122 123 int parts = mDocument.getParts(); 124 Log.i(LOGTAG, "Document parts: " + parts); 125 mContext.getDocumentPartView().clear(); 126 127 // Writer documents always have one part, so hide the navigation drawer. 128 if (mDocument.getDocumentType() != Document.DOCTYPE_TEXT) { 129 for (int i = 0; i < parts; i++) { 130 String partName = mDocument.getPartName(i); 131 if (partName.isEmpty()) { 132 partName = getGenericPartName(i); 133 }else if (partName.startsWith("Slide") || partName.startsWith("Sheet") || partName.startsWith("Part")) { 134 partName = getGenericPartName(i); 135 } 136 Log.i(LOGTAG, "Document part " + i + " name:'" + partName + "'"); 137 138 mDocument.setPart(i); 139 resetDocumentSize(); 140 final DocumentPartView partView = new DocumentPartView(i, partName); 141 mContext.getDocumentPartView().add(partView); 142 } 143 } else { 144 mContext.disableNavigationDrawer(); 145 mContext.getToolbarController().hideItem(R.id.action_parts); 146 } 147 148 // Enable headers for Calc documents 149 if (mDocument.getDocumentType() == Document.DOCTYPE_SPREADSHEET) { 150 mContext.initializeCalcHeaders(); 151 } 152 153 mDocument.setPart(0); 154 155 setupDocumentFonts(); 156 157 LOKitShell.getMainHandler().post(new Runnable() { 158 @Override 159 public void run() { 160 mContext.getDocumentPartViewListAdapter().notifyDataSetChanged(); 161 } 162 }); 163 } 164 addPart()165 public void addPart(){ 166 int parts = mDocument.getParts(); 167 if(mDocument.getDocumentType() == Document.DOCTYPE_SPREADSHEET){ 168 try{ 169 JSONObject jsonObject = new JSONObject(); 170 JSONObject values = new JSONObject(); 171 JSONObject values2 = new JSONObject(); 172 values.put("type", "long"); 173 values.put("value", 0); //add to the last 174 values2.put("type", "string"); 175 values2.put("value", ""); 176 jsonObject.put("Name", values2); 177 jsonObject.put("Index", values); 178 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Insert", jsonObject.toString())); 179 }catch (JSONException e) { 180 e.printStackTrace(); 181 } 182 } else if (mDocument.getDocumentType() == Document.DOCTYPE_PRESENTATION){ 183 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:InsertPage")); 184 } 185 186 String partName = mDocument.getPartName(parts); 187 if (partName.isEmpty()) { 188 partName = getGenericPartName(parts); 189 } 190 mDocument.setPart(parts); 191 resetDocumentSize(); 192 final DocumentPartView partView = new DocumentPartView(parts, partName); 193 mContext.getDocumentPartView().add(partView); 194 } 195 resetParts()196 public void resetParts(){ 197 int parts = mDocument.getParts(); 198 mContext.getDocumentPartView().clear(); 199 if (mDocument.getDocumentType() != Document.DOCTYPE_TEXT) { 200 for (int i = 0; i < parts; i++) { 201 String partName = mDocument.getPartName(i); 202 203 if (partName.isEmpty()) { 204 partName = getGenericPartName(i); 205 } 206 Log.i(LOGTAG, "resetParts: " + partName); 207 mDocument.setPart(i); 208 resetDocumentSize(); 209 final DocumentPartView partView = new DocumentPartView(i, partName); 210 mContext.getDocumentPartView().add(partView); 211 } 212 } 213 } public void renamePart(String partName) { 214 try{ 215 for(int i=0; i<mDocument.getParts(); i++){ 216 if(mContext.getDocumentPartView().get(i).partName.equals(partName)){ 217 //part name must be unique 218 Toast.makeText(mContext, mContext.getString(R.string.name_already_used), Toast.LENGTH_SHORT).show(); 219 return; 220 } 221 } 222 JSONObject parameter = new JSONObject(); 223 JSONObject name = new JSONObject(); 224 name.put("type", "string"); 225 name.put("value", partName); 226 parameter.put("Name", name); 227 if(isPresentation()){ 228 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND_NOTIFY, ".uno:RenamePage", parameter.toString(),true)); 229 }else { 230 JSONObject index = new JSONObject(); 231 index.put("type","long"); 232 index.put("value", getCurrentPartNumber()+1); 233 parameter.put("Index", index); 234 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND_NOTIFY, ".uno:Name", parameter.toString(),true)); 235 } 236 }catch (JSONException e){ 237 e.printStackTrace(); 238 } 239 } 240 removePart()241 public void removePart() { 242 try{ 243 if (!isSpreadsheet() && !isPresentation()) { 244 //document must be spreadsheet or presentation 245 return; 246 } 247 248 if(isPresentation()){ 249 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND_NOTIFY, ".uno:DeletePage", true)); 250 return; 251 } 252 253 if(getPartsCount() < 2){ 254 return; 255 } 256 257 JSONObject parameter = new JSONObject(); 258 JSONObject index = new JSONObject(); 259 index.put("type","long"); 260 index.put("value", getCurrentPartNumber()+1); 261 parameter.put("Index", index); 262 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND_NOTIFY, ".uno:Remove", parameter.toString(),true)); 263 }catch (JSONException e){ 264 e.printStackTrace(); 265 } 266 } 267 268 @Override saveDocumentAs(final String filePath, String format, boolean takeOwnership)269 public boolean saveDocumentAs(final String filePath, String format, boolean takeOwnership) { 270 String options = ""; 271 if (takeOwnership) { 272 options = "TakeOwnership"; 273 } 274 275 final String newFilePath = "file://" + filePath; 276 Log.d("saveFilePathURL", newFilePath); 277 LOKitShell.showProgressSpinner(mContext); 278 mDocument.saveAs(newFilePath, format, options); 279 final boolean ok; 280 if (!mOffice.getError().isEmpty()){ 281 ok = true; 282 Log.e("Save Error", mOffice.getError()); 283 if (format.equals("svg")) { 284 // error in creating temp slideshow svg file 285 Log.d(LOGTAG, "Error in creating temp slideshow svg file"); 286 } else if(format.equals("pdf")){ 287 Log.d(LOGTAG, "Error in creating pdf file"); 288 } else { 289 LOKitShell.getMainHandler().post(new Runnable() { 290 @Override 291 public void run() { 292 // There was some error 293 mContext.showCustomStatusMessage(mContext.getString(R.string.unable_to_save)); 294 } 295 }); 296 } 297 } else { 298 ok = false; 299 if (format.equals("svg")) { 300 // successfully created temp slideshow svg file 301 LOKitShell.getMainHandler().post(new Runnable() { 302 @Override 303 public void run() { 304 mContext.startPresentation(newFilePath); 305 } 306 }); 307 } else if (takeOwnership) { 308 mInputFile = filePath; 309 } 310 } 311 LOKitShell.hideProgressSpinner(mContext); 312 return ok; 313 } 314 315 @Override saveDocumentAs(final String filePath, boolean takeOwnership)316 public boolean saveDocumentAs(final String filePath, boolean takeOwnership) { 317 final int docType = mDocument.getDocumentType(); 318 if (docType == Document.DOCTYPE_TEXT) 319 return saveDocumentAs(filePath, "odt", takeOwnership); 320 else if (docType == Document.DOCTYPE_SPREADSHEET) 321 return saveDocumentAs(filePath, "ods", takeOwnership); 322 else if (docType == Document.DOCTYPE_PRESENTATION) 323 return saveDocumentAs(filePath, "odp", takeOwnership); 324 else if (docType == Document.DOCTYPE_DRAWING) 325 return saveDocumentAs(filePath, "odg", takeOwnership); 326 327 Log.w(LOGTAG, "Cannot determine file format from document. Not saving."); 328 return false; 329 } 330 printDocument()331 public void printDocument() { 332 if (Build.VERSION.SDK_INT < 19) { 333 mContext.showCustomStatusMessage(mContext.getString(R.string.printing_not_supported)); 334 return; 335 } 336 337 String mInputFileName = (new File(mInputFile)).getName(); 338 String file = mInputFileName.substring(0,(mInputFileName.length()-3))+"pdf"; 339 String cacheFile = mContext.getExternalCacheDir().getAbsolutePath() + "/" + file; 340 mDocument.saveAs("file://"+cacheFile,"pdf",""); 341 try { 342 PrintManager printManager = (PrintManager) mContext.getSystemService(Context.PRINT_SERVICE); 343 PrintDocumentAdapter printAdapter = new PDFDocumentAdapter(mContext, cacheFile); 344 printManager.print("Document", printAdapter, new PrintAttributes.Builder().build()); 345 346 } catch (Exception e) { 347 e.printStackTrace(); 348 } 349 } 350 saveDocument()351 public void saveDocument(){ 352 mContext.saveDocument(); 353 } 354 setupDocumentFonts()355 private void setupDocumentFonts() { 356 String values = mDocument.getCommandValues(".uno:CharFontName"); 357 if (values == null || values.isEmpty()) 358 return; 359 360 mContext.getFontController().parseJson(values); 361 mContext.getFontController().setupFontViews(); 362 } 363 getGenericPartName(int i)364 private String getGenericPartName(int i) { 365 if (mDocument == null) { 366 return ""; 367 } 368 switch (mDocument.getDocumentType()) { 369 case Document.DOCTYPE_DRAWING: 370 case Document.DOCTYPE_TEXT: 371 return mContext.getString(R.string.page) + " " + (i + 1); 372 case Document.DOCTYPE_SPREADSHEET: 373 return mContext.getString(R.string.sheet) + " " + (i + 1); 374 case Document.DOCTYPE_PRESENTATION: 375 return mContext.getString(R.string.slide) + " " + (i + 1); 376 case Document.DOCTYPE_OTHER: 377 default: 378 return mContext.getString(R.string.part) + " " + (i + 1); 379 } 380 } 381 twipToPixel(float input, float dpi)382 static float twipToPixel(float input, float dpi) { 383 return input / 1440.0f * dpi; 384 } 385 pixelToTwip(float input, float dpi)386 private static float pixelToTwip(float input, float dpi) { 387 return (input / dpi) * 1440.0f; 388 } 389 390 391 /** 392 * @see TileProvider#getPartsCount() 393 */ 394 @Override getPartsCount()395 public int getPartsCount() { 396 return mDocument.getParts(); 397 } 398 399 /** 400 * Wrapper for getPartPageRectangles() JNI function. 401 */ getPartPageRectangles()402 public String getPartPageRectangles() { 403 return mDocument.getPartPageRectangles(); 404 } 405 406 /** 407 * Fetch Calc header information. 408 */ getCalcHeaders()409 public String getCalcHeaders() { 410 long nX = 0; 411 long nY = 0; 412 long nWidth = mDocument.getDocumentWidth(); 413 long nHeight = mDocument.getDocumentHeight(); 414 return mDocument.getCommandValues(".uno:ViewRowColumnHeaders?x=" + nX + "&y=" + nY 415 + "&width=" + nWidth + "&height=" + nHeight); 416 } 417 418 /** 419 * @see TileProvider#onSwipeLeft() 420 */ 421 @Override onSwipeLeft()422 public void onSwipeLeft() { 423 if (mDocument.getDocumentType() == Document.DOCTYPE_PRESENTATION && 424 getCurrentPartNumber() < getPartsCount()-1) { 425 LOKitShell.sendChangePartEvent(getCurrentPartNumber()+1); 426 } 427 } 428 429 /** 430 * @see TileProvider#onSwipeRight() 431 */ 432 @Override onSwipeRight()433 public void onSwipeRight() { 434 if (mDocument.getDocumentType() == Document.DOCTYPE_PRESENTATION && 435 getCurrentPartNumber() > 0) { 436 LOKitShell.sendChangePartEvent(getCurrentPartNumber()-1); 437 } 438 } 439 checkDocument()440 private boolean checkDocument() { 441 String error = null; 442 boolean ret; 443 444 if (mDocument == null || !mOffice.getError().isEmpty()) { 445 error = "Cannot open " + mInputFile + ": " + mOffice.getError(); 446 ret = false; 447 } else { 448 ret = resetDocumentSize(); 449 if (!ret) { 450 error = "Document returned an invalid size or the document is empty."; 451 } 452 } 453 454 if (!ret && !mContext.isPasswordProtected()) { 455 final String message = error; 456 LOKitShell.getMainHandler().post(new Runnable() { 457 @Override 458 public void run() { 459 mContext.showAlertDialog(message); 460 } 461 }); 462 } else if (!ret && mContext.isPasswordProtected()) { 463 mContext.finish(); 464 } 465 466 return ret; 467 } 468 resetDocumentSize()469 private boolean resetDocumentSize() { 470 mWidthTwip = mDocument.getDocumentWidth(); 471 mHeightTwip = mDocument.getDocumentHeight(); 472 473 if (mWidthTwip == 0 || mHeightTwip == 0) { 474 Log.e(LOGTAG, "Document size zero - last error: " + mOffice.getError()); 475 return false; 476 } else { 477 Log.i(LOGTAG, "Reset document size: " + mDocument.getDocumentWidth() + " x " + mDocument.getDocumentHeight()); 478 } 479 480 return true; 481 } 482 483 @Override setDocumentSize(int pageWidth, int pageHeight)484 public void setDocumentSize(int pageWidth, int pageHeight){ 485 mWidthTwip = pageWidth; 486 mHeightTwip = pageHeight; 487 } 488 489 /** 490 * @see TileProvider#getPageWidth() 491 */ 492 @Override getPageWidth()493 public int getPageWidth() { 494 return (int) twipToPixel(mWidthTwip, mDPI); 495 } 496 497 /** 498 * @see TileProvider#getPageHeight() 499 */ 500 @Override getPageHeight()501 public int getPageHeight() { 502 return (int) twipToPixel(mHeightTwip, mDPI); 503 } 504 505 /** 506 * @see TileProvider#isReady() 507 */ 508 @Override isReady()509 public boolean isReady() { 510 return mIsReady; 511 } 512 513 /** 514 * @see TileProvider#createTile(float, float, org.mozilla.gecko.gfx.IntSize, float) 515 */ 516 @Override createTile(float x, float y, IntSize tileSize, float zoom)517 public CairoImage createTile(float x, float y, IntSize tileSize, float zoom) { 518 ByteBuffer buffer = DirectBufferAllocator.guardedAllocate(tileSize.width * tileSize.height * 4); 519 if (buffer == null) 520 return null; 521 522 CairoImage image = new BufferedCairoImage(buffer, tileSize.width, tileSize.height, CairoImage.FORMAT_ARGB32); 523 rerenderTile(image, x, y, tileSize, zoom); 524 return image; 525 } 526 527 /** 528 * @see TileProvider#rerenderTile(org.mozilla.gecko.gfx.CairoImage, float, float, org.mozilla.gecko.gfx.IntSize, float) 529 */ 530 @Override rerenderTile(CairoImage image, float x, float y, IntSize tileSize, float zoom)531 public void rerenderTile(CairoImage image, float x, float y, IntSize tileSize, float zoom) { 532 if (mDocument != null && image.getBuffer() != null) { 533 float twipX = pixelToTwip(x, mDPI) / zoom; 534 float twipY = pixelToTwip(y, mDPI) / zoom; 535 float twipWidth = mTileWidth / zoom; 536 float twipHeight = mTileHeight / zoom; 537 long start = System.currentTimeMillis() - objectCreationTime; 538 539 //Log.i(LOGTAG, "paintTile >> @" + start + " (" + tileSize.width + " " + tileSize.height + " " + (int) twipX + " " + (int) twipY + " " + (int) twipWidth + " " + (int) twipHeight + ")"); 540 mDocument.paintTile(image.getBuffer(), tileSize.width, tileSize.height, (int) twipX, (int) twipY, (int) twipWidth, (int) twipHeight); 541 542 long stop = System.currentTimeMillis() - objectCreationTime; 543 //Log.i(LOGTAG, "paintTile << @" + stop + " elapsed: " + (stop - start)); 544 } else { 545 if (mDocument == null) { 546 Log.e(LOGTAG, "Document is null!!"); 547 } 548 } 549 } 550 551 /** 552 * @see TileProvider#thumbnail(int) 553 */ 554 @Override thumbnail(int size)555 public Bitmap thumbnail(int size) { 556 int widthPixel = getPageWidth(); 557 int heightPixel = getPageHeight(); 558 559 if (widthPixel > heightPixel) { 560 double ratio = heightPixel / (double) widthPixel; 561 widthPixel = size; 562 heightPixel = (int) (widthPixel * ratio); 563 } else { 564 double ratio = widthPixel / (double) heightPixel; 565 heightPixel = size; 566 widthPixel = (int) (heightPixel * ratio); 567 } 568 569 Log.w(LOGTAG, "Thumbnail size: " + getPageWidth() + " " + getPageHeight() + " " + widthPixel + " " + heightPixel); 570 571 ByteBuffer buffer = ByteBuffer.allocateDirect(widthPixel * heightPixel * 4); 572 if (mDocument != null) 573 mDocument.paintTile(buffer, widthPixel, heightPixel, 0, 0, (int) mWidthTwip, (int) mHeightTwip); 574 575 Bitmap bitmap = null; 576 try { 577 bitmap = Bitmap.createBitmap(widthPixel, heightPixel, Bitmap.Config.ARGB_8888); 578 bitmap.copyPixelsFromBuffer(buffer); 579 } catch (IllegalArgumentException e) { 580 Log.e(LOGTAG, "width (" + widthPixel + ") and height (" + heightPixel + ") must not be 0! (ToDo: likely timing issue)"); 581 } 582 if (bitmap == null) { 583 Log.w(LOGTAG, "Thumbnail not created!"); 584 } 585 return bitmap; 586 } 587 588 /** 589 * @see TileProvider#close() 590 */ 591 @Override close()592 public void close() { 593 Log.i(LOGTAG, "Document destroyed: " + mInputFile); 594 if (mDocument != null) { 595 mDocument.destroy(); 596 mDocument = null; 597 } 598 } 599 600 /** 601 * @see TileProvider#isDrawing() 602 */ 603 @Override isDrawing()604 public boolean isDrawing() { 605 return mDocument != null && mDocument.getDocumentType() == Document.DOCTYPE_DRAWING; 606 } 607 608 /** 609 * @see TileProvider#isTextDocument() 610 */ 611 @Override isTextDocument()612 public boolean isTextDocument() { 613 return mDocument != null && mDocument.getDocumentType() == Document.DOCTYPE_TEXT; 614 } 615 616 /** 617 * @see TileProvider#isSpreadsheet() 618 */ 619 @Override isSpreadsheet()620 public boolean isSpreadsheet() { 621 return mDocument != null && mDocument.getDocumentType() == Document.DOCTYPE_SPREADSHEET; 622 } 623 624 /** 625 * @see TileProvider#isPresentation() 626 */ 627 @Override isPresentation()628 public boolean isPresentation(){ 629 return mDocument != null && mDocument.getDocumentType() == Document.DOCTYPE_PRESENTATION; 630 } 631 632 /** 633 * Returns the Unicode character generated by this event or 0. 634 */ getCharCode(KeyEvent keyEvent)635 private int getCharCode(KeyEvent keyEvent) { 636 switch (keyEvent.getKeyCode()) 637 { 638 case KeyEvent.KEYCODE_DEL: 639 case KeyEvent.KEYCODE_ENTER: 640 return 0; 641 } 642 return keyEvent.getUnicodeChar(); 643 } 644 645 /** 646 * Returns the integer code representing the key of the event (non-zero for 647 * control keys). 648 */ getKeyCode(KeyEvent keyEvent)649 private int getKeyCode(KeyEvent keyEvent) { 650 switch (keyEvent.getKeyCode()) { 651 case KeyEvent.KEYCODE_DEL: 652 return com.sun.star.awt.Key.BACKSPACE; 653 case KeyEvent.KEYCODE_ENTER: 654 return com.sun.star.awt.Key.RETURN; 655 } 656 return 0; 657 } 658 659 /** 660 * @see TileProvider#sendKeyEvent(android.view.KeyEvent) 661 */ 662 @Override sendKeyEvent(KeyEvent keyEvent)663 public void sendKeyEvent(KeyEvent keyEvent) { 664 switch (keyEvent.getAction()) { 665 case KeyEvent.ACTION_MULTIPLE: 666 String keyString = keyEvent.getCharacters(); 667 for (int i = 0; i < keyString.length(); i++) { 668 int codePoint = keyString.codePointAt(i); 669 mDocument.postKeyEvent(Document.KEY_EVENT_PRESS, codePoint, getKeyCode(keyEvent)); 670 } 671 break; 672 case KeyEvent.ACTION_DOWN: 673 mDocument.postKeyEvent(Document.KEY_EVENT_PRESS, getCharCode(keyEvent), getKeyCode(keyEvent)); 674 break; 675 case KeyEvent.ACTION_UP: 676 mDocument.postKeyEvent(Document.KEY_EVENT_RELEASE, getCharCode(keyEvent), getKeyCode(keyEvent)); 677 break; 678 } 679 } 680 mouseButton(int type, PointF inDocument, int numberOfClicks, float zoomFactor)681 private void mouseButton(int type, PointF inDocument, int numberOfClicks, float zoomFactor) { 682 int x = (int) pixelToTwip(inDocument.x, mDPI); 683 int y = (int) pixelToTwip(inDocument.y, mDPI); 684 685 mDocument.setClientZoom(TILE_SIZE, TILE_SIZE, (int) (mTileWidth / zoomFactor), (int) (mTileHeight / zoomFactor)); 686 mDocument.postMouseEvent(type, x, y, numberOfClicks, Document.MOUSE_BUTTON_LEFT, Document.KEYBOARD_MODIFIER_NONE); 687 } 688 689 /** 690 * @see TileProvider#mouseButtonDown(android.graphics.PointF, int, float) 691 */ 692 @Override mouseButtonDown(PointF documentCoordinate, int numberOfClicks, float zoomFactor)693 public void mouseButtonDown(PointF documentCoordinate, int numberOfClicks, float zoomFactor) { 694 mouseButton(Document.MOUSE_EVENT_BUTTON_DOWN, documentCoordinate, numberOfClicks, zoomFactor); 695 } 696 697 /** 698 * @see TileProvider#mouseButtonUp(android.graphics.PointF, int, float) 699 */ 700 @Override mouseButtonUp(PointF documentCoordinate, int numberOfClicks, float zoomFactor)701 public void mouseButtonUp(PointF documentCoordinate, int numberOfClicks, float zoomFactor) { 702 mouseButton(Document.MOUSE_EVENT_BUTTON_UP, documentCoordinate, numberOfClicks, zoomFactor); 703 } 704 705 /** 706 * @param command UNO command string 707 * @param arguments Arguments to UNO command 708 */ 709 @Override postUnoCommand(String command, String arguments)710 public void postUnoCommand(String command, String arguments) { 711 postUnoCommand(command, arguments, false); 712 } 713 714 /** 715 * @param command 716 * @param arguments 717 * @param notifyWhenFinished 718 */ 719 @Override postUnoCommand(String command, String arguments, boolean notifyWhenFinished)720 public void postUnoCommand(String command, String arguments, boolean notifyWhenFinished) { 721 mDocument.postUnoCommand(command, arguments, notifyWhenFinished); 722 } 723 setTextSelection(int type, PointF documentCoordinate)724 private void setTextSelection(int type, PointF documentCoordinate) { 725 int x = (int) pixelToTwip(documentCoordinate.x, mDPI); 726 int y = (int) pixelToTwip(documentCoordinate.y, mDPI); 727 mDocument.setTextSelection(type, x, y); 728 } 729 730 /** 731 * @see TileProvider#setTextSelectionStart(android.graphics.PointF) 732 */ 733 @Override setTextSelectionStart(PointF documentCoordinate)734 public void setTextSelectionStart(PointF documentCoordinate) { 735 setTextSelection(Document.SET_TEXT_SELECTION_START, documentCoordinate); 736 } 737 738 /** 739 * @see TileProvider#setTextSelectionEnd(android.graphics.PointF) 740 */ 741 @Override setTextSelectionEnd(PointF documentCoordinate)742 public void setTextSelectionEnd(PointF documentCoordinate) { 743 setTextSelection(Document.SET_TEXT_SELECTION_END, documentCoordinate); 744 } 745 746 /** 747 * @see TileProvider#setTextSelectionReset(android.graphics.PointF) 748 */ 749 @Override setTextSelectionReset(PointF documentCoordinate)750 public void setTextSelectionReset(PointF documentCoordinate) { 751 setTextSelection(Document.SET_TEXT_SELECTION_RESET, documentCoordinate); 752 } 753 754 /** 755 * @param mimeType 756 * @return 757 */ 758 @Override getTextSelection(String mimeType)759 public String getTextSelection(String mimeType) { 760 return mDocument.getTextSelection(mimeType); 761 } 762 763 /** 764 * paste 765 * @param mimeType 766 * @param data 767 * @return 768 */ 769 @Override paste(String mimeType, String data)770 public boolean paste(String mimeType, String data) { 771 return mDocument.paste(mimeType, data); 772 } 773 774 775 /** 776 * @see org.libreoffice.TileProvider#setGraphicSelectionStart(android.graphics.PointF) 777 */ 778 @Override setGraphicSelectionStart(PointF documentCoordinate)779 public void setGraphicSelectionStart(PointF documentCoordinate) { 780 setGraphicSelection(Document.SET_GRAPHIC_SELECTION_START, documentCoordinate); 781 } 782 783 /** 784 * @see org.libreoffice.TileProvider#setGraphicSelectionEnd(android.graphics.PointF) 785 */ 786 @Override setGraphicSelectionEnd(PointF documentCoordinate)787 public void setGraphicSelectionEnd(PointF documentCoordinate) { 788 setGraphicSelection(Document.SET_GRAPHIC_SELECTION_END, documentCoordinate); 789 } 790 setGraphicSelection(int type, PointF documentCoordinate)791 private void setGraphicSelection(int type, PointF documentCoordinate) { 792 int x = (int) pixelToTwip(documentCoordinate.x, mDPI); 793 int y = (int) pixelToTwip(documentCoordinate.y, mDPI); 794 LibreOfficeMainActivity.setDocumentChanged(true); 795 mDocument.setGraphicSelection(type, x, y); 796 } 797 798 @Override finalize()799 protected void finalize() throws Throwable { 800 close(); 801 super.finalize(); 802 } 803 804 /** 805 * @see TileProvider#changePart(int) 806 */ 807 @Override changePart(int partIndex)808 public void changePart(int partIndex) { 809 if (mDocument == null) 810 return; 811 812 mDocument.setPart(partIndex); 813 resetDocumentSize(); 814 } 815 816 /** 817 * @see TileProvider#getCurrentPartNumber() 818 */ 819 @Override getCurrentPartNumber()820 public int getCurrentPartNumber() { 821 if (mDocument == null) 822 return 0; 823 824 return mDocument.getPart(); 825 } 826 setDocumentPassword(String url, String password)827 public void setDocumentPassword(String url, String password) { 828 mOffice.setDocumentPassword(url, password); 829 } 830 getMessageCallback()831 public Document.MessageCallback getMessageCallback() { 832 return mMessageCallback; 833 } 834 } 835 836 // vim:set shiftwidth=4 softtabstop=4 expandtab: 837