android/Bootstrap/src/org/libreoffice/kit/DirectBufferAllocator.java | 2 android/Bootstrap/src/org/libreoffice/kit/Document.java | 24 android/Bootstrap/src/org/libreoffice/kit/Office.java | 5 android/experimental/LOAndroid3/src/java/org/libreoffice/LOAbout.java | 25 android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java | 38 - android/experimental/LOAndroid3/src/java/org/libreoffice/LOEventFactory.java | 44 - android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java | 52 + android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java | 152 +++-- android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java | 191 +++++- android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java | 69 +- android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java | 14 android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java | 31 - android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ZoomConstraints.java | 3 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ComposedTileLayer.java | 188 +++--- android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java | 2 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java | 1 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/FloatSize.java | 3 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java | 252 ++------ android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/InputConnectionHandler.java | 2 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java | 289 +++++----- android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java | 212 ------- android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerView.java | 143 +++- android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/PanZoomController.java | 10 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/PanZoomTarget.java | 2 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/RenderControllerThread.java | 37 - android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ScreenshotLayer.java | 200 ------ android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/SimpleScaleGestureDetector.java | 1 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/SubTile.java | 238 ++++++++ android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/TouchEventHandler.java | 58 -- android/experimental/LOAndroid3/src/java/org/mozilla/gecko/util/FloatUtils.java | 2 solenv/bin/native-code.py | 9 31 files changed, 1128 insertions(+), 1171 deletions(-)
New commits: commit 2f7ea81c59d214f17492af2afc114bd5a2a529ea Author: Jan Holesovsky <ke...@collabora.com> Date: Sun Feb 22 01:15:41 2015 +0100 Backported Tomaž's rendering improvements from the tiled-editing branch. Much improves the rendering experience, and decreases the amount of oom's too. It is up to the 'android: support selection invalidation, show selection handles' commit (including). Change-Id: Ife926aabbb83128f221f500eab0e6acdbe589306 diff --git a/android/Bootstrap/src/org/libreoffice/kit/DirectBufferAllocator.java b/android/Bootstrap/src/org/libreoffice/kit/DirectBufferAllocator.java index 670e397..de90303 100644 --- a/android/Bootstrap/src/org/libreoffice/kit/DirectBufferAllocator.java +++ b/android/Bootstrap/src/org/libreoffice/kit/DirectBufferAllocator.java @@ -11,8 +11,6 @@ package org.libreoffice.kit; // https://code.google.com/p/android/issues/detail?id=16941 // -import android.util.Log; - import java.nio.ByteBuffer; public final class DirectBufferAllocator { diff --git a/android/Bootstrap/src/org/libreoffice/kit/Document.java b/android/Bootstrap/src/org/libreoffice/kit/Document.java index 3cd9b0e..aee11f7 100644 --- a/android/Bootstrap/src/org/libreoffice/kit/Document.java +++ b/android/Bootstrap/src/org/libreoffice/kit/Document.java @@ -9,8 +9,6 @@ package org.libreoffice.kit; -import android.util.Log; - import java.nio.ByteBuffer; public class Document { @@ -26,10 +24,29 @@ public class Document { public static final int DOCTYPE_DRAWING = 3; public static final int DOCTYPE_OTHER = 4; + /** + * Mouse event types + */ public static final int MOUSE_BUTTON_DOWN = 0; public static final int MOUSE_BUTTON_UP = 1; public static final int MOUSE_MOVE = 2; + /** + * Callback message types + */ + public static final int CALLBACK_INVALIDATE_TILES = 0; + public static final int CALLBACK_INVALIDATE_VISIBLE_CURSOR = 1; + public static final int CALLBACK_INVALIDATE_TEXT_SELECTION = 2; + public static final int CALLBACK_INVALIDATE_TEXT_SELECTION_START = 3; + public static final int CALLBACK_INVALIDATE_TEXT_SELECTION_END = 4; + + /** + * Text selection types + */ + public static final int TEXT_SELECTION_START = 0; + public static final int TEXT_SELECTION_END = 1; + public static final int TEXT_SELECTION_RESET = 2; + private final ByteBuffer handle; private MessageCallback messageCallback = null; @@ -94,8 +111,9 @@ public class Document { * @param type - mouse event type * @param x - x coordinate * @param y - y coordinate + * @param count - number of events */ - public native void postMouseEvent(int type, int x, int y); + public native void postMouseEvent(int type, int x, int y, int count); /** * Callback to retrieve messages from LOK diff --git a/android/Bootstrap/src/org/libreoffice/kit/Office.java b/android/Bootstrap/src/org/libreoffice/kit/Office.java index 25861c9..8bd780b 100644 --- a/android/Bootstrap/src/org/libreoffice/kit/Office.java +++ b/android/Bootstrap/src/org/libreoffice/kit/Office.java @@ -37,9 +37,10 @@ public class Office { /** * Post a key event to LibreOffice. * @param type - type of key event - * @param code - key event code + * @param charCode - the Unicode character generated by this event or 0. + * @param keyCode - the integer code representing the key of the event (non-zero for control keys). */ - public native void postKeyEvent(int type, int code); + public native void postKeyEvent(int type, int charCode, int keyCode); public native void destroy(); public native void destroyAndExit(); diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOAbout.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOAbout.java index 13c436f..47ecddf 100644 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOAbout.java +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOAbout.java @@ -2,27 +2,14 @@ package org.libreoffice; import android.app.Activity; import android.app.AlertDialog; -import android.content.DialogInterface; import android.content.ComponentName; -import android.os.Bundle; -import android.os.Handler; -import android.support.v4.widget.DrawerLayout; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ListView; -import android.widget.RelativeLayout; -import android.widget.TextView; +import android.content.DialogInterface; import android.content.Intent; -import android.net.Uri; import android.content.pm.PackageManager.NameNotFoundException; +import android.net.Uri; +import android.view.View; +import android.widget.TextView; -import java.util.ArrayList; -import java.util.List; import java.io.File; public abstract class LOAbout extends Activity { @@ -44,8 +31,8 @@ public abstract class LOAbout extends Activity { i.setComponent(componentName); startActivity(i); } else { - LOKitShell.sendEvent(LOEventFactory.close()); - LOKitShell.sendEvent(LOEventFactory.load(input)); + LOKitShell.sendCloseEvent(); + LOKitShell.sendLoadEvent(input); } } diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java index 809988d..807d0d0 100644 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java @@ -1,5 +1,7 @@ package org.libreoffice; +import android.graphics.PointF; +import android.graphics.RectF; import android.view.KeyEvent; import android.view.MotionEvent; @@ -14,11 +16,11 @@ public class LOEvent implements Comparable<LOEvent> { public static final int LOAD = 4; public static final int CLOSE = 5; public static final int REDRAW = 6; - public static final int TILE_REQUEST = 7; + public static final int TILE_REEVALUATION_REQUEST = 7; public static final int THUMBNAIL = 8; - public static final int TILE_RERENDER = 9; + public static final int TILE_INVALIDATION = 9; public static final int TOUCH = 10; - public static final int KEY_PRESS = 11; + public static final int KEY_EVENT = 11; public final int mType; public int mPriority = 0; @@ -27,13 +29,13 @@ public class LOEvent implements Comparable<LOEvent> { public String mTypeString; public int mPartIndex; public String mFilename; - public TileIdentifier mTileId; public ComposedTileLayer mComposedTileLayer; - public boolean mForceRedraw; - public SubTile mTile; public String mTouchType; public MotionEvent mMotionEvent; + public PointF mDocumentTouchCoordinate; + public String mKeyEventType; public KeyEvent mKeyEvent; + public RectF mInvalidationRect; public LOEvent(int type) { mType = type; @@ -44,12 +46,10 @@ public class LOEvent implements Comparable<LOEvent> { mTypeString = "Size Changed: " + widthPixels + " " + heightPixels; } - public LOEvent(int type, ComposedTileLayer composedTileLayer, TileIdentifier tileId, boolean forceRedraw) { + public LOEvent(int type, ComposedTileLayer composedTileLayer) { mType = type; - mTypeString = "Tile Request"; + mTypeString = "Tile Reevaluation"; mComposedTileLayer = composedTileLayer; - mTileId = tileId; - mForceRedraw = forceRedraw; } public LOEvent(int type, String filename) { @@ -72,27 +72,29 @@ public class LOEvent implements Comparable<LOEvent> { public LOEvent(int type, ThumbnailCreator.ThumbnailCreationTask task) { mType = type; mTask = task; + mTypeString = "Thumbnail"; } - public LOEvent(int type, ComposedTileLayer composedTileLayer, SubTile tile) { - mType = type; - mTypeString = "Tile Rerender"; - mComposedTileLayer = composedTileLayer; - mTile = tile; - } - - public LOEvent(int type, String touchType, MotionEvent motionEvent) { + public LOEvent(int type, String touchType, MotionEvent motionEvent, PointF documentTouchCoordinate) { mType = type; mTypeString = "Touch"; mTouchType = touchType; mMotionEvent = motionEvent; + mDocumentTouchCoordinate = documentTouchCoordinate; } public LOEvent(int type, KeyEvent keyEvent) { mType = type; + mTypeString = "Key Event"; mKeyEvent = keyEvent; } + public LOEvent(int type, RectF rect) { + mType = type; + mTypeString = "Tile Invalidation"; + mInvalidationRect = rect; + } + public String getTypeString() { if (mTypeString == null) { return "Event type: " + mType; diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEventFactory.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEventFactory.java deleted file mode 100644 index b866850..0000000 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEventFactory.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.libreoffice; - -import org.mozilla.gecko.gfx.ComposedTileLayer; -import org.mozilla.gecko.gfx.IntSize; -import org.mozilla.gecko.gfx.SubTile; - - -public class LOEventFactory { - public static LOEvent sizeChanged(int widthPixels, int heightPixels) { - return new LOEvent(LOEvent.SIZE_CHANGED, widthPixels, heightPixels); - } - - public static LOEvent tileSize(IntSize tileSize) { - return new LOEvent(LOEvent.TILE_SIZE, tileSize); - } - - public static LOEvent changePart(int part) { - return new LOEvent(LOEvent.CHANGE_PART, part); - } - - public static LOEvent load(String inputFile) { - return new LOEvent(LOEvent.LOAD, inputFile); - } - - public static LOEvent close() { - return new LOEvent(LOEvent.CLOSE); - } - - public static LOEvent redraw() { - return new LOEvent(LOEvent.REDRAW); - } - - public static LOEvent tileRequest(ComposedTileLayer composedTileLayer, TileIdentifier tileID, boolean forceRedraw) { - return new LOEvent(LOEvent.TILE_REQUEST, composedTileLayer, tileID, forceRedraw); - } - - public static LOEvent thumbnail(ThumbnailCreator.ThumbnailCreationTask task) { - return new LOEvent(LOEvent.THUMBNAIL, task); - } - - public static LOEvent tileRerender(ComposedTileLayer composedTileLayer, SubTile tile) { - return new LOEvent(LOEvent.TILE_RERENDER, composedTileLayer, tile); - } -} diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java index 49da6b4..2037e9d 100644 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java @@ -3,11 +3,17 @@ package org.libreoffice; import android.app.ActivityManager; import android.content.Context; +import android.graphics.PointF; +import android.graphics.RectF; import android.os.Handler; import android.util.DisplayMetrics; import android.view.KeyEvent; import android.view.MotionEvent; +import org.mozilla.gecko.gfx.ComposedTileLayer; +import org.mozilla.gecko.gfx.LayerView; +import org.mozilla.gecko.gfx.SubTile; + public class LOKitShell { private static final String LOGTAG = LOKitShell.class.getSimpleName(); @@ -54,6 +60,14 @@ public class LOKitShell { return metrics; } + public static boolean isEditingEnabled() { + return false; + } + + public static LayerView getLayerView() { + return LibreOfficeMainActivity.mAppContext.getLayerClient().getView(); + } + // EVENTS /** @@ -72,18 +86,42 @@ public class LOKitShell { /** * Send touch event to LOKitThread. */ - public static void sentTouchEvent(String touchType, MotionEvent motionEvent) { - LOKitShell.sendEvent(new LOEvent(LOEvent.TOUCH, "SingleTap", motionEvent)); + public static void sentTouchEvent(String touchType, MotionEvent motionEvent, PointF pointF) { + LOKitShell.sendEvent(new LOEvent(LOEvent.TOUCH, touchType, motionEvent, pointF)); } /** - * Send key press event to LOKitThread. + * Send key event to LOKitThread. */ - public static void sendKeyPressEvent(KeyEvent event) { - LOKitShell.sendEvent(new LOEvent(LOEvent.KEY_PRESS, event)); + public static void sendKeyEvent(KeyEvent event) { + LOKitShell.sendEvent(new LOEvent(LOEvent.KEY_EVENT, event)); } - public static boolean isEditingEnabled() { - return false; + public static void sendSizeChangedEvent(int width, int height) { + LOKitShell.sendEvent(new LOEvent(LOEvent.SIZE_CHANGED, width, height)); + } + + public static void sendChangePartEvent(int part) { + LOKitShell.sendEvent(new LOEvent(LOEvent.CHANGE_PART, part)); + } + + public static void sendLoadEvent(String inputFile) { + LOKitShell.sendEvent(new LOEvent(LOEvent.LOAD, inputFile)); + } + + public static void sendCloseEvent() { + LOKitShell.sendEvent(new LOEvent(LOEvent.CLOSE)); + } + + public static void sendRedrawEvent() { + LOKitShell.sendEvent(new LOEvent(LOEvent.REDRAW)); + } + + public static void sendTileReevaluationRequest(ComposedTileLayer composedTileLayer) { + LOKitShell.sendEvent(new LOEvent(LOEvent.TILE_REEVALUATION_REQUEST, composedTileLayer)); + } + + public static void sendTileInvalidationRequest(RectF rect) { + LOKitShell.sendEvent(new LOEvent(LOEvent.TILE_INVALIDATION, rect)); } } diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java index afc9028..9ec76be 100644 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java @@ -4,6 +4,7 @@ import android.graphics.Bitmap; import android.graphics.PointF; import android.graphics.RectF; import android.util.Log; +import android.view.KeyEvent; import android.view.MotionEvent; import org.mozilla.gecko.gfx.CairoImage; @@ -12,12 +13,15 @@ import org.mozilla.gecko.gfx.GeckoLayerClient; import org.mozilla.gecko.gfx.ImmutableViewportMetrics; import org.mozilla.gecko.gfx.SubTile; -import java.util.concurrent.PriorityBlockingQueue; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; -public class LOKitThread extends Thread implements TileProvider.TileInvalidationCallback { +public class LOKitThread extends Thread { private static final String LOGTAG = LOKitThread.class.getSimpleName(); - private PriorityBlockingQueue<LOEvent> mEventQueue = new PriorityBlockingQueue<LOEvent>(); + private LinkedBlockingQueue<LOEvent> mEventQueue = new LinkedBlockingQueue<LOEvent>(); + private LibreOfficeMainActivity mApplication; private TileProvider mTileProvider; private ImmutableViewportMetrics mViewportMetrics; @@ -27,35 +31,66 @@ public class LOKitThread extends Thread implements TileProvider.TileInvalidation TileProviderFactory.initialize(); } - private void tileRequest(ComposedTileLayer composedTileLayer, TileIdentifier tileId, boolean forceRedraw) { - if (mTileProvider == null) + @Override + public void run() { + while (true) { + LOEvent event; + try { + event = mEventQueue.take(); + processEvent(event); + } catch (InterruptedException exception) { + throw new RuntimeException(exception); + } + } + } + + /* Viewport changed, recheck if tiles need to be added / removed */ + private void tileReevaluationRequest(ComposedTileLayer composedTileLayer) { + if (mTileProvider == null) { return; + } + List<SubTile> tiles = new ArrayList<SubTile>(); + + mLayerClient.beginDrawing(); + composedTileLayer.addNewTiles(tiles); + mLayerClient.endDrawing(); - if (composedTileLayer.isStillValid(tileId)) { + for (SubTile tile : tiles) { + TileIdentifier tileId = tile.id; CairoImage image = mTileProvider.createTile(tileId.x, tileId.y, tileId.size, tileId.zoom); + mLayerClient.beginDrawing(); if (image != null) { - mLayerClient.beginDrawing(); - SubTile tile = new SubTile(image, tileId); - composedTileLayer.addTile(tile); - mLayerClient.endDrawing(mViewportMetrics); - mLayerClient.forceRender(); + tile.setImage(image); } - } else { - composedTileLayer.cleanupInvalidTile(tileId); + mLayerClient.endDrawing(); + mLayerClient.forceRender(); } + + mLayerClient.beginDrawing(); + composedTileLayer.markTiles(); + composedTileLayer.clearMarkedTiles(); + mLayerClient.endDrawing(); + mLayerClient.forceRender(); } - private void tileRerender(ComposedTileLayer composedTileLayer, SubTile tile) { - if (mTileProvider == null) + /* Invalidate tiles that intersect the input rect */ + private void tileInvalidation(RectF rect) { + if (mLayerClient == null || mTileProvider == null) { return; + } - if (composedTileLayer.isStillValid(tile.id) && !tile.markedForRemoval) { - mLayerClient.beginDrawing(); - mTileProvider.rerenderTile(tile.getImage(), tile.id.x, tile.id.y, tile.id.size, tile.id.zoom); + mLayerClient.beginDrawing(); + + List<SubTile> tiles = new ArrayList<SubTile>(); + mLayerClient.invalidateTiles(tiles, rect); + + for (SubTile tile : tiles) { + CairoImage image = mTileProvider.createTile(tile.id.x, tile.id.y, tile.id.size, tile.id.zoom); + tile.setImage(image); tile.invalidate(); - mLayerClient.endDrawing(mViewportMetrics); - mLayerClient.forceRender(); } + mLayerClient.endDrawing(); + mLayerClient.forceRender(); } /** Handle the geometry change + draw. */ @@ -72,6 +107,7 @@ public class LOKitThread extends Thread implements TileProvider.TileInvalidation zoomAndRepositionTheDocument(); mLayerClient.forceRedraw(); + mLayerClient.forceRender(); } private void zoomAndRepositionTheDocument() { @@ -118,7 +154,6 @@ public class LOKitThread extends Thread implements TileProvider.TileInvalidation if (mTileProvider.isReady()) { LOKitShell.showProgressSpinner(); - mTileProvider.registerInvalidationCallback(this); refresh(); LOKitShell.hideProgressSpinner(); } else { @@ -133,15 +168,6 @@ public class LOKitThread extends Thread implements TileProvider.TileInvalidation } } - public void run() { - try { - while (true) { - processEvent(mEventQueue.take()); - } - } catch (InterruptedException ex) { - } - } - private void processEvent(LOEvent event) { switch (event.mType) { case LOEvent.LOAD: @@ -157,32 +183,54 @@ public class LOKitThread extends Thread implements TileProvider.TileInvalidation case LOEvent.CHANGE_PART: changePart(event.mPartIndex); break; - case LOEvent.TILE_REQUEST: - tileRequest(event.mComposedTileLayer, event.mTileId, event.mForceRedraw); - break; - case LOEvent.TILE_RERENDER: - tileRerender(event.mComposedTileLayer, event.mTile); + case LOEvent.TILE_INVALIDATION: + tileInvalidation(event.mInvalidationRect); break; case LOEvent.THUMBNAIL: createThumbnail(event.mTask); break; case LOEvent.TOUCH: - if (!LOKitShell.isEditingEnabled()) { - return; - } - touch(event.mTouchType, event.mMotionEvent); + touch(event.mTouchType, event.mMotionEvent, event.mDocumentTouchCoordinate); + break; + case LOEvent.KEY_EVENT: + keyEvent(event.mKeyEvent); break; - case LOEvent.KEY_PRESS: - if (!LOKitShell.isEditingEnabled()) { - return; - } - mTileProvider.keyPress(event.mKeyEvent); + case LOEvent.TILE_REEVALUATION_REQUEST: + tileReevaluationRequest(event.mComposedTileLayer); break; } } - private void touch(String touchType, MotionEvent motionEvent) { - LibreOfficeMainActivity.mAppContext.showSoftKeyboard(); + /** + * Processes key events. + */ + private void keyEvent(KeyEvent keyEvent) { + if (!LOKitShell.isEditingEnabled()) { + return; + } + if (mTileProvider == null) { + return; + } + mTileProvider.sendKeyEvent(keyEvent); + } + + /** + * Processes touch events. + */ + private void touch(String touchType, MotionEvent motionEvent, PointF mDocumentTouchCoordinate) { + if (!LOKitShell.isEditingEnabled()) { + return; + } + if (mTileProvider == null) { + return; + } + if (touchType.equals("LongPress")) { + LibreOfficeMainActivity.mAppContext.hideSoftKeyboard(); + mTileProvider.mouseButtonDown(mDocumentTouchCoordinate, 2); + } else { // "SingleTap" + LibreOfficeMainActivity.mAppContext.showSoftKeyboard(); + mTileProvider.mouseButtonDown(mDocumentTouchCoordinate, 1); + } } private void createThumbnail(final ThumbnailCreator.ThumbnailCreationTask task) { @@ -197,18 +245,6 @@ public class LOKitThread extends Thread implements TileProvider.TileInvalidation public void clearQueue() { mEventQueue.clear(); } - - @Override - public void invalidate(RectF rect) { - if (!LOKitShell.isEditingEnabled()) { - return; - } - - Log.i(LOGTAG, "Invalidate request: " + rect); - - mLayerClient = mApplication.getLayerClient(); - mLayerClient.invalidateTiles(rect); - } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java index 2caf748..679f8b4 100644 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java @@ -1,6 +1,7 @@ package org.libreoffice; import android.graphics.Bitmap; +import android.graphics.PointF; import android.graphics.RectF; import android.util.Log; import android.view.KeyEvent; @@ -9,6 +10,8 @@ import org.libreoffice.kit.DirectBufferAllocator; import org.libreoffice.kit.Document; import org.libreoffice.kit.LibreOfficeKit; import org.libreoffice.kit.Office; +//import org.mozilla.gecko.TextSelection; +//import org.mozilla.gecko.TextSelectionHandle; import org.mozilla.gecko.gfx.BufferedCairoImage; import org.mozilla.gecko.gfx.CairoImage; import org.mozilla.gecko.gfx.GeckoLayerClient; @@ -29,7 +32,6 @@ public class LOKitTileProvider implements TileProvider, Document.MessageCallback private Office mOffice; private Document mDocument; private boolean mIsReady = false; - private TileInvalidationCallback tileInvalidationCallback = null; private float mDPI; private float mWidthTwip; @@ -72,8 +74,7 @@ public class LOKitTileProvider implements TileProvider, Document.MessageCallback if (checkDocument()) { postLoad(); mIsReady = true; - } - else { + } else { mIsReady = false; } } @@ -277,18 +278,63 @@ public class LOKitTileProvider implements TileProvider, Document.MessageCallback } /** - * Register the tile invalidation callback. + * 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; + } + @Override - public void registerInvalidationCallback(TileInvalidationCallback tileInvalidationCallback) { - this.tileInvalidationCallback = tileInvalidationCallback; + 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); + mOffice.postKeyEvent(Office.KEY_PRESS, codePoint, getKeyCode(keyEvent)); + } + } else if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) { + mOffice.postKeyEvent(Office.KEY_PRESS, getCharCode(keyEvent), getKeyCode(keyEvent)); + } else if (keyEvent.getAction() == KeyEvent.ACTION_UP) { + mOffice.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); } @Override - public void keyPress(KeyEvent keyEvent) { - mOffice.postKeyEvent(Office.KEY_PRESS, keyEvent.getKeyCode()); + public void mouseButtonDown(PointF documentCoordinate, int numberOfClicks) { + mouseButton(Document.MOUSE_BUTTON_DOWN, documentCoordinate, numberOfClicks); } + @Override + public void mouseButtonUp(PointF documentCoordinate, int numberOfClicks) { + mouseButton(Document.MOUSE_BUTTON_UP, documentCoordinate, numberOfClicks); + } @Override protected void finalize() throws Throwable { @@ -313,33 +359,126 @@ public class LOKitTileProvider implements TileProvider, Document.MessageCallback return mDocument.getPart(); } + private RectF convertCallbackMessageStringToRectF(String text) { + if (text.equals("EMPTY")) { + return null; + } + + String[] coordinates = text.split(","); + + if (coordinates.length != 4) { + return null; + } + int width = Integer.decode(coordinates[0].trim()); + int height = Integer.decode(coordinates[1].trim()); + int x = Integer.decode(coordinates[2].trim()); + int y = Integer.decode(coordinates[3].trim()); + RectF rect = new RectF( + twipToPixel(x, mDPI), + twipToPixel(y, mDPI), + twipToPixel(x + width, mDPI), + twipToPixel(y + height, mDPI) + ); + return rect; + } + /** * Process the retrieved messages from LOK */ @Override public void messageRetrieved(int signalNumber, String payload) { + if (!LOKitShell.isEditingEnabled()) { + return; + } + switch (signalNumber) { - case 0: - if (!payload.equals("EMPTY")) { - String[] coordinates = payload.split(","); - - if (coordinates.length == 4) { - int width = Integer.decode(coordinates[0].trim()); - int height = Integer.decode(coordinates[1].trim()); - int x = Integer.decode(coordinates[2].trim()); - int y = Integer.decode(coordinates[3].trim()); - RectF rect = new RectF( - twipToPixel(x, mDPI), - twipToPixel(y, mDPI), - twipToPixel(x + width, mDPI), - twipToPixel(y + height, mDPI) - ); - tileInvalidationCallback.invalidate(rect); - } - } + case Document.CALLBACK_INVALIDATE_TILES: + invalidateTiles(payload); + break; + case Document.CALLBACK_INVALIDATE_VISIBLE_CURSOR: + invalidateCursor(payload); + break; + case Document.CALLBACK_INVALIDATE_TEXT_SELECTION: + Log.i(LOGTAG, "Selection: " + payload); + invalidateSelection(payload); + break; + case Document.CALLBACK_INVALIDATE_TEXT_SELECTION_START: + Log.i(LOGTAG, "Selection start: " + payload); + invalidateSelectionStart(payload); + break; + case Document.CALLBACK_INVALIDATE_TEXT_SELECTION_END: + Log.i(LOGTAG, "Selection end: " + payload); + invalidateSelectionEnd(payload); break; } } + + private void invalidateCursor(String payload) { + /* + RectF rect = convertCallbackMessageStringToRectF(payload); + if (rect != null) { + RectF underSelection = new RectF(rect.centerX(), rect.bottom, rect.centerX(), rect.bottom); + TextSelection textSelection = LibreOfficeMainActivity.mAppContext.getTextSelection(); + textSelection.positionHandle(TextSelectionHandle.HandleType.MIDDLE, underSelection); + textSelection.showHandle(TextSelectionHandle.HandleType.MIDDLE); + + TextCursorLayer textCursorLayer = LibreOfficeMainActivity.mAppContext.getTextCursorLayer(); + textCursorLayer.positionCursor(rect); + textCursorLayer.showCursor(); + } + */ + } + + private void invalidateTiles(String payload) { + RectF rect = convertCallbackMessageStringToRectF(payload); + if (rect != null) { + LOKitShell.sendTileInvalidationRequest(rect); + } + } + + private void invalidateSelectionStart(String payload) { + /* + RectF rect = convertCallbackMessageStringToRectF(payload); + if (rect != null) { + RectF underSelection = new RectF(rect.centerX(), rect.bottom, rect.centerX(), rect.bottom); + TextSelection textSelection = LibreOfficeMainActivity.mAppContext.getTextSelection(); + textSelection.positionHandle(TextSelectionHandle.HandleType.START, underSelection); + textSelection.showHandle(TextSelectionHandle.HandleType.START); + + textSelection.hideHandle(TextSelectionHandle.HandleType.MIDDLE); + + TextCursorLayer textCursorLayer = LibreOfficeMainActivity.mAppContext.getTextCursorLayer(); + textCursorLayer.hideCursor(); + } + */ + } + + private void invalidateSelectionEnd(String payload) { + /* + RectF rect = convertCallbackMessageStringToRectF(payload); + if (rect != null) { + RectF underSelection = new RectF(rect.centerX(), rect.bottom, rect.centerX(), rect.bottom); + TextSelection textSelection = LibreOfficeMainActivity.mAppContext.getTextSelection(); + textSelection.positionHandle(TextSelectionHandle.HandleType.END, underSelection); + textSelection.showHandle(TextSelectionHandle.HandleType.END); + + textSelection.hideHandle(TextSelectionHandle.HandleType.MIDDLE); + + TextCursorLayer textCursorLayer = LibreOfficeMainActivity.mAppContext.getTextCursorLayer(); + textCursorLayer.hideCursor(); + } + */ + } + + private void invalidateSelection(String payload) { + /* + if (payload.isEmpty()) { + TextSelection textSelection = LibreOfficeMainActivity.mAppContext.getTextSelection(); + textSelection.hideHandle(TextSelectionHandle.HandleType.START); + textSelection.hideHandle(TextSelectionHandle.HandleType.END); + } + */ + } } // vim:set shiftwidth=4 softtabstop=4 expandtab: diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java index 7fa45f5..13225c3 100644 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java @@ -1,6 +1,5 @@ package org.libreoffice; -import android.app.Activity; import android.app.AlertDialog; import android.content.ContentResolver; import android.content.Context; @@ -8,10 +7,7 @@ import android.content.DialogInterface; import android.os.Bundle; import android.os.Handler; import android.support.v4.widget.DrawerLayout; -import android.util.DisplayMetrics; import android.util.Log; -import android.view.KeyEvent; -import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -19,7 +15,6 @@ import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.ListView; import android.widget.RelativeLayout; -import android.widget.TextView; import org.mozilla.gecko.ZoomConstraints; import org.mozilla.gecko.gfx.GeckoLayerClient; @@ -28,7 +23,6 @@ import org.mozilla.gecko.gfx.LayerView; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -53,6 +47,8 @@ public class LibreOfficeMainActivity extends LOAbout { private List<DocumentPartView> mDocumentPartView = new ArrayList<DocumentPartView>(); private DocumentPartViewListAdapter mDocumentPartViewListAdapter; private String mInputFile; + //private TextSelection mTextSelection; + //private TextCursorLayer mTextCursorLayer; private File mTempFile = null; public LibreOfficeMainActivity() { @@ -102,8 +98,6 @@ public class LibreOfficeMainActivity extends LOAbout { mMainHandler = new Handler(); - LayoutInflater.from(this).setFactory(ViewFactory.getInstance()); - if (getIntent().getData() != null) { if (getIntent().getData().getScheme().equals(ContentResolver.SCHEME_CONTENT)) { if (copyFileToTemp() && mTempFile != null) { @@ -141,10 +135,16 @@ public class LibreOfficeMainActivity extends LOAbout { sLOKitThread.clearQueue(); } + /* + mTextSelection = new TextSelection(mAppContext); + mTextCursorLayer = new TextCursorLayer(mAppContext); + */ + mLayerClient = new GeckoLayerClient(this); mLayerClient.setZoomConstraints(new ZoomConstraints(true)); LayerView layerView = (LayerView) findViewById(R.id.layer_view); mLayerClient.setView(layerView); + layerView.setInputConnectionHandler(new LOKitInputConnectionHandler()); mLayerClient.notifyReady(); } @@ -193,13 +193,14 @@ public class LibreOfficeMainActivity extends LOAbout { protected void onStart() { Log.i(LOGTAG, "onStart.."); super.onStart(); - LOKitShell.sendEvent(LOEventFactory.load(mInputFile)); + LOKitShell.sendLoadEvent(mInputFile); } @Override protected void onStop() { Log.i(LOGTAG, "onStop.."); - LOKitShell.sendEvent(LOEventFactory.close()); + hideSoftKeyboardDirect(); + LOKitShell.sendCloseEvent(); super.onStop(); } @@ -243,8 +244,6 @@ public class LibreOfficeMainActivity extends LOAbout { * Force the request on main thread. */ public void showSoftKeyboard() { - Log.i(LOGTAG, "SoftKeyboard show request.."); - LOKitShell.getMainHandler().post(new Runnable() { @Override public void run() { @@ -258,6 +257,30 @@ public class LibreOfficeMainActivity extends LOAbout { }); } + /** + * Hides software keyboard on UI thread. + */ + public void hideSoftKeyboard() { + LOKitShell.getMainHandler().post(new Runnable() { + @Override + public void run() { + hideSoftKeyboardDirect(); + } + }); + } + + /** + * Hides software keyboard. + */ + private void hideSoftKeyboardDirect() { + LayerView layerView = (LayerView) findViewById(R.id.layer_view); + + if (getCurrentFocus() != null) { + InputMethodManager inputMethodManager = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); + } + } + public void showProgressSpinner() { findViewById(R.id.loadingPanel).setVisibility(View.VISIBLE); } @@ -282,24 +305,24 @@ public class LibreOfficeMainActivity extends LOAbout { alertDialog.show(); } + /* + public TextSelection getTextSelection() { + return mTextSelection; + } + + public TextCursorLayer getTextCursorLayer() { + return mTextCursorLayer; + } + */ + private class DocumentPartClickListener implements android.widget.AdapterView.OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { DocumentPartView partView = mDocumentPartViewListAdapter.getItem(position); - LOKitShell.sendEvent(LOEventFactory.changePart(partView.partIndex)); + LOKitShell.sendChangePartEvent(partView.partIndex); mDrawerLayout.closeDrawer(mDrawerList); } } - - - /** - * Listen to key presses and send event to LOK. - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - LOKitShell.sendKeyPressEvent(event); - return super.onKeyDown(keyCode, event); - } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java index 1f15870..fe934a1 100644 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java @@ -1,6 +1,7 @@ package org.libreoffice; import android.graphics.Bitmap; +import android.graphics.PointF; import android.view.KeyEvent; import org.mozilla.gecko.gfx.BufferedCairoImage; @@ -23,7 +24,7 @@ public class MockTileProvider implements TileProvider { LibreOfficeMainActivity.mAppContext.mMainHandler.post(new Runnable() { @Override public void run() { - LibreOfficeMainActivity.mAppContext.getDocumentPartViewListAdapter().add(partView); + LibreOfficeMainActivity.mAppContext.getDocumentPartViewListAdapter().add(partView); } }); } @@ -89,11 +90,18 @@ public class MockTileProvider implements TileProvider { } @Override - public void registerInvalidationCallback(TileInvalidationCallback tileInvalidationCallback) { + public void sendKeyEvent(KeyEvent keyEvent) { + + } + + @Override + public void mouseButtonDown(PointF documentCoordinate, int numberOfClicks) { + } @Override - public void keyPress(KeyEvent keyEvent) { + public void mouseButtonUp(PointF documentCoordinate, int numberOfClicks) { + } @Override diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java index 48bca1b..ea868bd 100644 --- a/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java +++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java @@ -2,6 +2,7 @@ package org.libreoffice; import android.graphics.Bitmap; +import android.graphics.PointF; import android.graphics.RectF; import android.view.KeyEvent; @@ -30,12 +31,14 @@ public interface TileProvider { /** * Change the document part to the one specified by the partIndex input parameter. + * * @param partIndex - part index to change to */ void changePart(int partIndex); /** * Get the current document part number. + * * @return */ int getCurrentPartNumber(); @@ -58,27 +61,25 @@ public interface TileProvider { boolean isSpreadsheet(); /** - * Register a callback that is invoked when a tile invalidation is - * required. + * Trigger a key event. * - * @param tileInvalidationCallback - the tile invalidation callback + * @param keyEvent - contains information about key event */ - void registerInvalidationCallback(TileProvider.TileInvalidationCallback tileInvalidationCallback); + void sendKeyEvent(KeyEvent keyEvent); /** - * Trigger a key press. - * @param keyEvent - contains the + * Trigger a mouse button down event. + * + * @param documentCoordinate - coordinate relative to the document where the mouse button should be triggered + * @param numberOfClicks - number of clicks (1 - single click, 2 - double click) */ - void keyPress(KeyEvent keyEvent); + void mouseButtonDown(PointF documentCoordinate, int numberOfClicks); /** - * Callback to retrieve invalidation calls + * Trigger a mouse button up event. + * + * @param documentCoordinate - coordinate relative to the document where the mouse button should be triggered + * @param numberOfClicks - number of clicks (1 - single click, 2 - double click) */ - public interface TileInvalidationCallback { - /** - * Invoked when a region is invalidated. - * @param rect area in pixels which was invalidated and needs to be redrawn - */ - void invalidate(RectF rect); - } + void mouseButtonUp(PointF documentCoordinate, int numberOfClicks); } diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ZoomConstraints.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ZoomConstraints.java index 4ac49f7..eba0dfe 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ZoomConstraints.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/ZoomConstraints.java @@ -5,9 +5,6 @@ package org.mozilla.gecko; -import org.json.JSONException; -import org.json.JSONObject; - public final class ZoomConstraints { private final boolean mAllowZoom; private final boolean mAllowDoubleTapZoom; diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ComposedTileLayer.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ComposedTileLayer.java index ca25438..e11c91c 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ComposedTileLayer.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/ComposedTileLayer.java @@ -1,6 +1,5 @@ package org.mozilla.gecko.gfx; -import android.app.ActivityManager; import android.content.ComponentCallbacks2; import android.content.Context; import android.content.res.Configuration; @@ -8,136 +7,164 @@ import android.graphics.RectF; import android.graphics.Region; import android.util.Log; -import org.libreoffice.LOEvent; -import org.libreoffice.LOEventFactory; import org.libreoffice.LOKitShell; import org.libreoffice.TileIdentifier; import org.mozilla.gecko.util.FloatUtils; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; public abstract class ComposedTileLayer extends Layer implements ComponentCallbacks2 { private static final String LOGTAG = ComposedTileLayer.class.getSimpleName(); - protected final List<SubTile> tiles = new CopyOnWriteArrayList<SubTile>(); + protected final List<SubTile> tiles = new ArrayList<SubTile>(); protected final IntSize tileSize; + private final ReadWriteLock tilesReadWriteLock = new ReentrantReadWriteLock(); + private final Lock tilesReadLock = tilesReadWriteLock.readLock(); + private final Lock tilesWriteLock = tilesReadWriteLock.writeLock(); + protected RectF currentViewport = new RectF(); - protected float currentZoom; + protected float currentZoom = 1.0f; + protected RectF currentPageRect = new RectF(); + + private long reevaluationNanoTime = 0; public ComposedTileLayer(Context context) { context.registerComponentCallbacks(this); this.tileSize = new IntSize(256, 256); } + protected static RectF roundToTileSize(RectF input, IntSize tileSize) { + float minX = ((int) (input.left / tileSize.width)) * tileSize.width; + float minY = ((int) (input.top / tileSize.height)) * tileSize.height; + float maxX = ((int) (input.right / tileSize.width) + 1) * tileSize.width; + float maxY = ((int) (input.bottom / tileSize.height) + 1) * tileSize.height; + return new RectF(minX, minY, maxX, maxY); + } + + protected static RectF inflate(RectF rect, IntSize inflateSize) { + RectF newRect = new RectF(rect); + newRect.left -= inflateSize.width; + newRect.left = newRect.left < 0.0f ? 0.0f : newRect.left; + + newRect.top -= inflateSize.height; + newRect.top = newRect.top < 0.0f ? 0.0f : newRect.top; + + newRect.right += inflateSize.width; + newRect.bottom += inflateSize.height; + + return newRect; + } + + protected static RectF normalizeRect(RectF rect, float sourceFactor, float targetFactor) { + RectF normalizedRect = new RectF( + (rect.left / sourceFactor) * targetFactor, + (rect.top / sourceFactor) * targetFactor, + (rect.right / sourceFactor) * targetFactor, + (rect.bottom / sourceFactor) * targetFactor); + + return normalizedRect; + } + public void invalidate() { + tilesReadLock.lock(); for (SubTile tile : tiles) { tile.invalidate(); } + tilesReadLock.unlock(); } @Override public void beginTransaction() { super.beginTransaction(); + tilesReadLock.lock(); for (SubTile tile : tiles) { tile.beginTransaction(); } + tilesReadLock.unlock(); } @Override public void endTransaction() { + tilesReadLock.lock(); for (SubTile tile : tiles) { tile.endTransaction(); } + tilesReadLock.unlock(); super.endTransaction(); } @Override public void draw(RenderContext context) { + tilesReadLock.lock(); for (SubTile tile : tiles) { if (RectF.intersects(tile.getBounds(context), context.viewport)) { tile.draw(context); } } + tilesReadLock.unlock(); } @Override protected void performUpdates(RenderContext context) { super.performUpdates(context); + tilesReadLock.lock(); for (SubTile tile : tiles) { tile.beginTransaction(); tile.refreshTileMetrics(); tile.endTransaction(); tile.performUpdates(context); } + tilesReadLock.unlock(); } @Override public Region getValidRegion(RenderContext context) { Region validRegion = new Region(); + tilesReadLock.lock(); for (SubTile tile : tiles) { validRegion.op(tile.getValidRegion(context), Region.Op.UNION); } - + tilesReadLock.unlock(); return validRegion; } @Override public void setResolution(float newResolution) { super.setResolution(newResolution); + tilesReadLock.lock(); for (SubTile tile : tiles) { tile.setResolution(newResolution); } - } - - protected RectF roundToTileSize(RectF input, IntSize tileSize) { - float minX = ((int) (input.left / tileSize.width)) * tileSize.width; - float minY = ((int) (input.top / tileSize.height)) * tileSize.height; - float maxX = ((int) (input.right / tileSize.width) + 1) * tileSize.width; - float maxY = ((int) (input.bottom / tileSize.height) + 1) * tileSize.height; - return new RectF(minX, minY, maxX, maxY); - } - - protected RectF inflate(RectF rect, IntSize inflateSize) { - RectF newRect = new RectF(rect); - newRect.left -= inflateSize.width; - newRect.left = newRect.left < 0.0f ? 0.0f : newRect.left; - - newRect.top -= inflateSize.height; - newRect.top = newRect.top < 0.0f ? 0.0f : newRect.top; - - newRect.right += inflateSize.width; - newRect.bottom += inflateSize.height; - - return newRect; - } - - protected RectF normalizeRect(RectF rect, float sourceFactor, float targetFactor) { - RectF normalizedRect = new RectF( - (rect.left / sourceFactor) * targetFactor, - (rect.top / sourceFactor) * targetFactor, - (rect.right / sourceFactor) * targetFactor, - (rect.bottom / sourceFactor) * targetFactor); - - return normalizedRect; + tilesReadLock.unlock(); } public void reevaluateTiles(ImmutableViewportMetrics viewportMetrics, DisplayPortMetrics mDisplayPort) { RectF newViewPort = getViewPort(viewportMetrics); float newZoom = getZoom(viewportMetrics); - if (!currentViewport.equals(newViewPort) || currentZoom != newZoom) { - currentViewport = newViewPort; - currentZoom = newZoom; - RectF pageRect = viewportMetrics.getPageRect(); + if (currentViewport.equals(newViewPort) && FloatUtils.fuzzyEquals(currentZoom, newZoom)) { + return; + } - clearMarkedTiles(); - addNewTiles(pageRect); - markTiles(); + long currentReevaluationNanoTime = System.nanoTime(); + if ((currentReevaluationNanoTime - reevaluationNanoTime) < 25 * 1000000) { + return; } + + reevaluationNanoTime = currentReevaluationNanoTime; + + currentViewport = newViewPort; + currentZoom = newZoom; + currentPageRect = viewportMetrics.getPageRect(); + + LOKitShell.sendTileReevaluationRequest(this); } protected abstract RectF getViewPort(ImmutableViewportMetrics viewportMetrics); @@ -146,43 +173,53 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba protected abstract int getTilePriority(); - private void addNewTiles(RectF pageRect) { + private boolean containsTilesMatching(float x, float y, float currentZoom) { + tilesReadLock.lock(); + try { + for (SubTile tile : tiles) { + if (tile.id.x == x && tile.id.y == y && tile.id.zoom == currentZoom) { + return true; + } + } + return false; + } finally { + tilesReadLock.unlock(); + } + } + + public void addNewTiles(List<SubTile> newTiles) { for (float y = currentViewport.top; y < currentViewport.bottom; y += tileSize.height) { - if (y > pageRect.height()) { + if (y > currentPageRect.height()) { continue; } for (float x = currentViewport.left; x < currentViewport.right; x += tileSize.width) { - if (x > pageRect.width()) { + if (x > currentPageRect.width()) { continue; } - boolean contains = false; - for (SubTile tile : tiles) { - if (tile.id.x == x && tile.id.y == y && tile.id.zoom == currentZoom) { - contains = true; - } - } - if (!contains) { + if (!containsTilesMatching(x, y, currentZoom)) { TileIdentifier tileId = new TileIdentifier((int) x, (int) y, currentZoom, tileSize); - LOEvent event = LOEventFactory.tileRequest(this, tileId, true); - event.mPriority = getTilePriority(); - LOKitShell.sendEvent(event); + SubTile tile = createNewTile(tileId); + newTiles.add(tile); } } } } - private void clearMarkedTiles() { - List<SubTile> tilesToRemove = new ArrayList<SubTile>(); - for (SubTile tile : tiles) { + public void clearMarkedTiles() { + tilesWriteLock.lock(); + Iterator<SubTile> iterator = tiles.iterator(); + while (iterator.hasNext()) { + SubTile tile = iterator.next(); if (tile.markedForRemoval) { tile.destroy(); - tilesToRemove.add(tile); + iterator.remove(); } } - tiles.removeAll(tilesToRemove); + tilesWriteLock.unlock(); } - private void markTiles() { + public void markTiles() { + tilesReadLock.lock(); for (SubTile tile : tiles) { if (FloatUtils.fuzzyEquals(tile.id.zoom, currentZoom)) { RectF tileRect = tile.id.getRectF(); @@ -193,16 +230,23 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba tile.markForRemoval(); } } + tilesReadLock.unlock(); } public void clearAndReset() { + tilesWriteLock.lock(); tiles.clear(); + tilesWriteLock.unlock(); currentViewport = new RectF(); } - public void addTile(SubTile tile) { + private SubTile createNewTile(TileIdentifier tileId) { + SubTile tile = new SubTile(tileId); tile.beginTransaction(); + tilesWriteLock.lock(); tiles.add(tile); + tilesWriteLock.unlock(); + return tile; } public boolean isStillValid(TileIdentifier tileId) { @@ -212,14 +256,15 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba /** * Invalidate tiles which intersect the input rect */ - public void invalidateTiles(RectF cssRect) { + public void invalidateTiles(List<SubTile> tilesToInvalidate, RectF cssRect) { RectF zoomedRect = RectUtils.scale(cssRect, currentZoom); - + tilesReadLock.lock(); for (SubTile tile : tiles) { - if (RectF.intersects(zoomedRect, tile.id.getRectF())) { - LOKitShell.sendEvent(LOEventFactory.tileRerender(this, tile)); + if (!tile.markedForRemoval && RectF.intersects(zoomedRect, tile.id.getRectF())) { + tilesToInvalidate.add(tile); } } + tilesReadLock.unlock(); } @Override @@ -239,7 +284,4 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba Log.i(LOGTAG, "Trimming memory - TRIM_MEMORY_RUNNING_LOW"); } } - - public void cleanupInvalidTile(TileIdentifier tileId) { - } } diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java index 789d5b6..f860cd9 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java @@ -17,7 +17,7 @@ import org.mozilla.gecko.util.FloatUtils; import java.util.Map; final class DisplayPortCalculator { - private static final String LOGTAG = "GeckoDisplayPortCalculator"; + private static final String LOGTAG = DisplayPortCalculator.class.getSimpleName(); private static final PointF ZERO_VELOCITY = new PointF(0, 0); // Keep this in sync with the TILEDLAYERBUFFER_TILE_SIZE defined in gfx/layers/TiledLayerBuffer.h diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java index ab9a637..f622c44 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java @@ -6,6 +6,7 @@ package org.mozilla.gecko.gfx; import android.graphics.RectF; + import org.mozilla.gecko.util.FloatUtils; /* diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/FloatSize.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/FloatSize.java index dd9c1b3..7b18373 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/FloatSize.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/FloatSize.java @@ -5,10 +5,9 @@ package org.mozilla.gecko.gfx; -import org.mozilla.gecko.util.FloatUtils; - import org.json.JSONException; import org.json.JSONObject; +import org.mozilla.gecko.util.FloatUtils; public class FloatSize { public final float width, height; diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java index 9ec1c98..40fb8bb 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java @@ -6,56 +6,27 @@ package org.mozilla.gecko.gfx; import android.content.Context; -import android.graphics.Color; import android.graphics.PointF; import android.graphics.RectF; import android.util.DisplayMetrics; -import android.util.Log; -import org.libreoffice.LOEvent; -import org.libreoffice.LOEventFactory; import org.libreoffice.LOKitShell; import org.mozilla.gecko.ZoomConstraints; -import org.mozilla.gecko.util.FloatUtils; -public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener { +import java.util.List; + +public class GeckoLayerClient implements PanZoomTarget { private static final String LOGTAG = GeckoLayerClient.class.getSimpleName(); private LayerRenderer mLayerRenderer; - private boolean mLayerRendererInitialized; private Context mContext; private IntSize mScreenSize; - private IntSize mWindowSize; private DisplayPortMetrics mDisplayPort; - private DisplayPortMetrics mReturnDisplayPort; - - private boolean mRecordDrawTimes; - private final DrawTimingQueue mDrawTimingQueue; private ComposedTileLayer mLowResLayer; private ComposedTileLayer mRootLayer; - /* The Gecko viewport as per the UI thread. Must be touched only on the UI thread. - * If any events being sent to Gecko that are relative to the Gecko viewport position, - * they must (a) be relative to this viewport, and (b) be sent on the UI thread to - * avoid races. As long as these two conditions are satisfied, and the events being - * sent to Gecko are processed in FIFO order, the events will properly be relative - * to the Gecko viewport position. Note that if Gecko updates its viewport independently, - * we get notified synchronously and also update this on the UI thread. - */ - private ImmutableViewportMetrics mGeckoViewport; - - /* - * The viewport metrics being used to draw the current frame. This is only - * accessed by the compositor thread, and so needs no synchronisation. - */ - private ImmutableViewportMetrics mFrameMetrics; - - private ImmutableViewportMetrics mNewGeckoViewport; - - private boolean mPendingViewportAdjust; - private boolean mForceRedraw; /* The current viewport metrics. @@ -74,12 +45,9 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener { private ZoomConstraints mZoomConstraints; - private boolean mGeckoIsReady; - - /* The new color for the checkerboard. */ - private int mCheckerboardColor; + private boolean mIsReady; - private final PanZoomController mPanZoomController; + private PanZoomController mPanZoomController; private LayerView mView; public GeckoLayerClient(Context context) { @@ -87,35 +55,52 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener { // to before being read mContext = context; mScreenSize = new IntSize(0, 0); - mWindowSize = new IntSize(0, 0); mDisplayPort = new DisplayPortMetrics(); - mRecordDrawTimes = false; - mDrawTimingQueue = new DrawTimingQueue(); mForceRedraw = true; DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); mViewportMetrics = new ImmutableViewportMetrics(displayMetrics); mZoomConstraints = new ZoomConstraints(false); - - mPanZoomController = PanZoomController.Factory.create(this); } - public void setView(LayerView v) { - mView = v; + public void setView(LayerView view) { + mView = view; + mPanZoomController = PanZoomController.Factory.create(this, view); mView.connect(this); } public void notifyReady() { - mGeckoIsReady = true; + mIsReady = true; mRootLayer = new DynamicTileLayer(mContext); mLowResLayer = new FixedZoomTileLayer(mContext); mLayerRenderer = new LayerRenderer(mView); - mView.setListener(this); mView.setLayerRenderer(mLayerRenderer); - sendResizeEventIfNecessary(true); + + sendResizeEventIfNecessary(); + mView.requestRender(); + } + + public void destroy() { + mPanZoomController.destroy(); + } + + public Context getContext() { + return mContext; + } + + Layer getRoot() { + return mIsReady ? mRootLayer : null; + } + + Layer getLowResLayer() { + return mIsReady ? mLowResLayer : null; + } + + public LayerView getView() { + return mView; } /** @@ -131,22 +116,9 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener { if (!mPanZoomController.getRedrawHint()) { return false; } - return DisplayPortCalculator.aboutToCheckerboard(mViewportMetrics, mPanZoomController.getVelocityVector(), getDisplayPort()); } - Layer getRoot() { - return mGeckoIsReady ? mRootLayer : null; - } - - Layer getLowResLayer() { - return mGeckoIsReady ? mLowResLayer : null; - } - - public LayerView getView() { - return mView; - } - /** * The view calls this function to indicate that the viewport changed size. It must hold the * monitor while calling it. @@ -157,7 +129,7 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener { */ void setViewportSize(FloatSize size) { mViewportMetrics = mViewportMetrics.setViewportSize(size.width, size.height); - sendResizeEventIfNecessary(true); + sendResizeEventIfNecessary(); } public PanZoomController getPanZoomController() { @@ -165,26 +137,17 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener { } /* Informs Gecko that the screen size has changed. */ - private void sendResizeEventIfNecessary(boolean force) { + private void sendResizeEventIfNecessary() { DisplayMetrics metrics = LOKitShell.getDisplayMetrics(); - IntSize newScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels); - // Return immediately if the screen size hasn't changed or the viewport - // size is zero (which indicates that the rendering surface hasn't been - // allocated yet). - boolean screenSizeChanged = !mScreenSize.equals(newScreenSize); - - if (!force && !screenSizeChanged) { + if (mScreenSize.equals(newScreenSize)) { return; } - Log.d(LOGTAG, "Screen-size changed to " + mScreenSize + " - > " + newScreenSize); - mScreenSize = newScreenSize; - LOEvent event = LOEventFactory.sizeChanged(metrics.widthPixels, metrics.heightPixels); - LOKitShell.sendEvent(event); + LOKitShell.sendSizeChangedEvent(mScreenSize.width, mScreenSize.height); } /** @@ -220,11 +183,6 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener { } mDisplayPort = displayPort; - mGeckoViewport = clampedMetrics; - - if (mRecordDrawTimes) { - mDrawTimingQueue.add(displayPort); - } reevaluateTiles(); } @@ -272,94 +230,34 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener { mRootLayer.beginTransaction(); } - public void endDrawing(ImmutableViewportMetrics viewportMetrics) { - synchronized (this) { - try { - mNewGeckoViewport = viewportMetrics; - updateViewport(); - //mLowResLayer.invalidate(); - //mRootLayer.invalidate(); - } finally { - mLowResLayer.endTransaction(); - mRootLayer.endTransaction(); - } - } - } - - protected void updateViewport() { - // save and restore the viewport size stored in java; never let the - // JS-side viewport dimensions override the java-side ones because - // java is the One True Source of this information, and allowing JS - // to override can lead to race conditions where this data gets clobbered. - FloatSize viewportSize = mViewportMetrics.getSize(); - mGeckoViewport = mNewGeckoViewport.setViewportSize(viewportSize.width, viewportSize.height); - - RectF position = mGeckoViewport.getViewport(); - mRootLayer.setPosition(RectUtils.round(position)); - mRootLayer.setResolution(mGeckoViewport.zoomFactor); - - mLowResLayer.setPosition(RectUtils.round(position)); - mLowResLayer.setResolution(mGeckoViewport.zoomFactor); - - // Don't adjust page size when zooming unless zoom levels are - // approximately equal. - if (FloatUtils.fuzzyEquals(getViewportMetrics().zoomFactor, mGeckoViewport.zoomFactor)) { - setPageRect(mGeckoViewport.getPageRect(), mGeckoViewport.getCssPageRect()); - } + public void endDrawing() { + mLowResLayer.endTransaction(); + mRootLayer.endTransaction(); } public void geometryChanged() { - sendResizeEventIfNecessary(false); + sendResizeEventIfNecessary(); if (getRedrawHint()) { adjustViewport(null); } } - /** Implementation of LayerView.Listener */ - @Override - public void renderRequested() { - } - - /** Implementation of LayerView.Listener */ - @Override - public void compositionPauseRequested() { - } - - /** Implementation of LayerView.Listener */ - @Override - public void compositionResumeRequested(int width, int height) { - } - - /** Implementation of LayerView.Listener */ - @Override - public void surfaceChanged(int width, int height) { - setViewportSize(new FloatSize(width, height)); - - // We need to make this call even when the compositor isn't currently - // paused (e.g. during an orientation change), to make the compositor - // aware of the changed surface. - compositionResumeRequested(width, height); - renderRequested(); - } - - /** Implementation of LayerView.Listener */ - @Override - public void compositorCreated() { - } - /** Implementation of PanZoomTarget */ + @Override public ImmutableViewportMetrics getViewportMetrics() { return mViewportMetrics; } /** Implementation of PanZoomTarget */ + @Override public ZoomConstraints getZoomConstraints() { return mZoomConstraints; } /** Implementation of PanZoomTarget */ + @Override public void setAnimationTarget(ImmutableViewportMetrics viewport) { - if (mGeckoIsReady) { + if (mIsReady) { // We know what the final viewport of the animation is going to be, so // immediately request a draw of that area by setting the display port // accordingly. This way we should have the content pre-rendered by the @@ -372,69 +270,52 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener { /** Implementation of PanZoomTarget * You must hold the monitor while calling this. */ + @Override public void setViewportMetrics(ImmutableViewportMetrics viewport) { mViewportMetrics = viewport; mView.requestRender(); - if (mGeckoIsReady) { + if (mIsReady) { geometryChanged(); } } /** Implementation of PanZoomTarget */ + @Override public void forceRedraw() { mForceRedraw = true; - if (mGeckoIsReady) { + if (mIsReady) { geometryChanged(); } } /** Implementation of PanZoomTarget */ + @Override public boolean post(Runnable action) { return mView.post(action); } /** Implementation of PanZoomTarget */ + @Override public Object getLock() { return this; } - /** Implementation of PanZoomTarget - * Converts a point from layer view coordinates to layer coordinates. In other words, given a - * point measured in pixels from the top left corner of the layer view, returns the point in - * pixels measured from the last scroll position we sent to Gecko, in CSS pixels. Assuming the - * events being sent to Gecko are processed in FIFO order, this calculation should always be - * correct. - */ public PointF convertViewPointToLayerPoint(PointF viewPoint) { - if (!mGeckoIsReady) { - return null; - } - ImmutableViewportMetrics viewportMetrics = mViewportMetrics; PointF origin = viewportMetrics.getOrigin(); float zoom = viewportMetrics.zoomFactor; - ImmutableViewportMetrics geckoViewport = mGeckoViewport; - PointF geckoOrigin = geckoViewport.getOrigin(); - float geckoZoom = geckoViewport.zoomFactor; - - // viewPoint + origin gives the coordinate in device pixels from the top-left corner of the page. - // Divided by zoom, this gives us the coordinate in CSS pixels from the top-left corner of the page. - // geckoOrigin / geckoZoom is where Gecko thinks it is (scrollTo position) in CSS pixels from - // the top-left corner of the page. Subtracting the two gives us the offset of the viewPoint from - // the current Gecko coordinate in CSS pixels. + PointF layerPoint = new PointF( - ((viewPoint.x + origin.x) / zoom) - (geckoOrigin.x / geckoZoom), - ((viewPoint.y + origin.y) / zoom) - (geckoOrigin.y / geckoZoom)); + ((viewPoint.x + origin.x) / zoom), + ((viewPoint.y + origin.y) / zoom)); return layerPoint; } - public void destroy() { - mPanZoomController.destroy(); - } - - public Context getContext() { - return mContext; + /** Implementation of PanZoomTarget */ + @Override + public boolean isFullScreen() { + return false; } public void zoomTo(RectF rect) { @@ -448,18 +329,7 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener { } public void forceRender() { - post(new Runnable() { - public void run() { - mView.requestRender(); - } - }); - } - - private class AdjustRunnable implements Runnable { - public void run() { - mPendingViewportAdjust = false; - adjustViewport(null); - } + mView.requestRender(); } /* Root Layer Access */ @@ -473,8 +343,8 @@ public class GeckoLayerClient implements PanZoomTarget, LayerView.Listener { mRootLayer.clearAndReset(); } - public void invalidateTiles(RectF rect) { - mLowResLayer.invalidateTiles(rect); - mRootLayer.invalidateTiles(rect); + public void invalidateTiles(List<SubTile> tilesToInvalidate, RectF rect) { + mLowResLayer.invalidateTiles(tilesToInvalidate, rect); + mRootLayer.invalidateTiles(tilesToInvalidate, rect); } } \ No newline at end of file diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/InputConnectionHandler.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/InputConnectionHandler.java index 6fef53f..d460c19 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/InputConnectionHandler.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/InputConnectionHandler.java @@ -1,8 +1,8 @@ package org.mozilla.gecko.gfx; +import android.view.KeyEvent; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; -import android.view.KeyEvent; public interface InputConnectionHandler { diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java index 9440091..af3f11c 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java @@ -7,12 +7,14 @@ package org.mozilla.gecko.gfx; import android.graphics.PointF; import android.graphics.RectF; +import android.os.Build; import android.util.FloatMath; import android.util.Log; import android.view.GestureDetector; +import android.view.InputDevice; import android.view.MotionEvent; +import android.view.View; -import org.libreoffice.LOEventFactory; import org.libreoffice.LOKitShell; import org.libreoffice.LibreOfficeMainActivity; import org.mozilla.gecko.ZoomConstraints; @@ -75,7 +77,7 @@ public class JavaPanZoomController private final SubdocumentScrollHelper mSubscroller; private final Axis mX; private final Axis mY; - + private final TouchEventHandler mTouchEventHandler; private Thread mMainThread; /* The timer that handles flings or bounces. */ @@ -91,11 +93,12 @@ public class JavaPanZoomController /* Whether or not to wait for a double-tap before dispatching a single-tap */ private boolean mWaitForDoubleTap; - public JavaPanZoomController(PanZoomTarget target) { + public JavaPanZoomController(PanZoomTarget target, View view) { mTarget = target; mSubscroller = new SubdocumentScrollHelper(); mX = new AxisX(mSubscroller); mY = new AxisY(mSubscroller); + mTouchEventHandler = new TouchEventHandler(view.getContext(), view, this); mMainThread = LibreOfficeMainActivity.mAppContext.getMainLooper().getThread(); checkMainThread(); @@ -105,6 +108,7 @@ public class JavaPanZoomController public void destroy() { mSubscroller.destroy(); + mTouchEventHandler.destroy(); } private final static float easeOut(float t) { @@ -132,15 +136,40 @@ public class JavaPanZoomController } } + /** This function MUST be called on the UI thread */ + public boolean onMotionEvent(MotionEvent event) { + if (Build.VERSION.SDK_INT <= 11) { + return false; + } + + switch (event.getSource() & InputDevice.SOURCE_CLASS_MASK) { + case InputDevice.SOURCE_CLASS_POINTER: + switch (event.getAction() & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_SCROLL: return handlePointerScroll(event); + } + break; + } + return false; + } + + /** This function MUST be called on the UI thread */ public boolean onTouchEvent(MotionEvent event) { + return mTouchEventHandler.handleEvent(event); + } + + boolean handleEvent(MotionEvent event) { switch (event.getAction() & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_DOWN: return onTouchStart(event); - case MotionEvent.ACTION_MOVE: return onTouchMove(event); - case MotionEvent.ACTION_UP: return onTouchEnd(event); - case MotionEvent.ACTION_CANCEL: return onTouchCancel(event); - case MotionEvent.ACTION_SCROLL: return onScroll(event); - default: return false; + case MotionEvent.ACTION_DOWN: return handleTouchStart(event); + case MotionEvent.ACTION_MOVE: return handleTouchMove(event); + case MotionEvent.ACTION_UP: return handleTouchEnd(event); + case MotionEvent.ACTION_CANCEL: return handleTouchCancel(event); } + return false; + } + + /** This function MUST be called on the UI thread */ + public void notifyDefaultActionPrevented(boolean prevented) { + mTouchEventHandler.handleEventListenerAction(!prevented); } /** This function must be called from the UI thread. */ @@ -151,24 +180,24 @@ public class JavaPanZoomController // snaps to edges. for other cases (where the user's finger(s) are down) don't do // anything special. switch (mState) { - case FLING: - mX.stopFling(); - mY.stopFling(); - // fall through - case BOUNCE: - case ANIMATED_ZOOM: - // the zoom that's in progress likely makes no sense any more (such as if - // the screen orientation changed) so abort it - setState(PanZoomState.NOTHING); - // fall through - case NOTHING: - // Don't do animations here; they're distracting and can cause flashes on page - // transitions. - synchronized (mTarget.getLock()) { - mTarget.setViewportMetrics(getValidViewportMetrics()); - mTarget.forceRedraw(); - } - break; + case FLING: + mX.stopFling(); + mY.stopFling(); + // fall through + case BOUNCE: + case ANIMATED_ZOOM: + // the zoom that's in progress likely makes no sense any more (such as if + // the screen orientation changed) so abort it + setState(PanZoomState.NOTHING); + // fall through + case NOTHING: + // Don't do animations here; they're distracting and can cause flashes on page + // transitions. + synchronized (mTarget.getLock()) { + mTarget.setViewportMetrics(getValidViewportMetrics()); + mTarget.forceRedraw(); + } + break; } } @@ -213,122 +242,123 @@ public class JavaPanZoomController * Panning/scrolling */ - private boolean onTouchStart(MotionEvent event) { + private boolean handleTouchStart(MotionEvent event) { // user is taking control of movement, so stop // any auto-movement we have going stopAnimationTimer(); switch (mState) { - case ANIMATED_ZOOM: - // We just interrupted a double-tap animation, so force a redraw in - // case this touchstart is just a tap that doesn't end up triggering - // a redraw - mTarget.forceRedraw(); - // fall through - case FLING: - case BOUNCE: - case NOTHING: - case WAITING_LISTENERS: - startTouch(event.getX(0), event.getY(0), event.getEventTime()); - return false; - case TOUCHING: - case PANNING: - case PANNING_LOCKED: - case PANNING_HOLD: - case PANNING_HOLD_LOCKED: - case PINCHING: - Log.e(LOGTAG, "Received impossible touch down while in " + mState); - return false; + case ANIMATED_ZOOM: + // We just interrupted a double-tap animation, so force a redraw in + // case this touchstart is just a tap that doesn't end up triggering + // a redraw + mTarget.forceRedraw(); + // fall through + case FLING: + case BOUNCE: + case NOTHING: + case WAITING_LISTENERS: + startTouch(event.getX(0), event.getY(0), event.getEventTime()); + return false; + case TOUCHING: + case PANNING: + case PANNING_LOCKED: + case PANNING_HOLD: + case PANNING_HOLD_LOCKED: + case PINCHING: + Log.e(LOGTAG, "Received impossible touch down while in " + mState); + return false; } - Log.e(LOGTAG, "Unhandled case " + mState + " in onTouchStart"); + Log.e(LOGTAG, "Unhandled case " + mState + " in handleTouchStart"); return false; } - private boolean onTouchMove(MotionEvent event) { + private boolean handleTouchMove(MotionEvent event) { switch (mState) { - case FLING: - case BOUNCE: - case WAITING_LISTENERS: - // should never happen - Log.e(LOGTAG, "Received impossible touch move while in " + mState); - // fall through - case ANIMATED_ZOOM: - case NOTHING: - // may happen if user double-taps and drags without lifting after the - // second tap. ignore the move if this happens. - return false; + case FLING: + case BOUNCE: + case WAITING_LISTENERS: + // should never happen + Log.e(LOGTAG, "Received impossible touch move while in " + mState); + // fall through + case ANIMATED_ZOOM: + case NOTHING: + // may happen if user double-taps and drags without lifting after the + // second tap. ignore the move if this happens. + return false; - case TOUCHING: - if (panDistance(event) < PAN_THRESHOLD) { - return false; - } - cancelTouch(); - startPanning(event.getX(0), event.getY(0), event.getEventTime()); - track(event); - return true; + case TOUCHING: + // Don't allow panning if there is an element in full-screen mode. See bug 775511. + if (mTarget.isFullScreen() || panDistance(event) < PAN_THRESHOLD) { + return false; + } + cancelTouch(); + startPanning(event.getX(0), event.getY(0), event.getEventTime()); + track(event); + return true; - case PANNING_HOLD_LOCKED: - setState(PanZoomState.PANNING_LOCKED); - // fall through - case PANNING_LOCKED: - track(event); - return true; + case PANNING_HOLD_LOCKED: + setState(PanZoomState.PANNING_LOCKED); + // fall through + case PANNING_LOCKED: + track(event); + return true; - case PANNING_HOLD: - setState(PanZoomState.PANNING); - // fall through - case PANNING: - track(event); - return true; + case PANNING_HOLD: + setState(PanZoomState.PANNING); + // fall through + case PANNING: + track(event); + return true; - case PINCHING: - // scale gesture listener will handle this - return false; + case PINCHING: + // scale gesture listener will handle this + return false; } - Log.e(LOGTAG, "Unhandled case " + mState + " in onTouchMove"); + Log.e(LOGTAG, "Unhandled case " + mState + " in handleTouchMove"); return false; } - private boolean onTouchEnd(MotionEvent event) { + private boolean handleTouchEnd(MotionEvent event) { switch (mState) { - case FLING: - case BOUNCE: - case WAITING_LISTENERS: - // should never happen - Log.e(LOGTAG, "Received impossible touch end while in " + mState); - // fall through - case ANIMATED_ZOOM: - case NOTHING: - // may happen if user double-taps and drags without lifting after the - // second tap. ignore if this happens. - return false; + case FLING: + case BOUNCE: + case WAITING_LISTENERS: + // should never happen + Log.e(LOGTAG, "Received impossible touch end while in " + mState); + // fall through + case ANIMATED_ZOOM: + case NOTHING: + // may happen if user double-taps and drags without lifting after the + // second tap. ignore if this happens. + return false; - case TOUCHING: - // the switch into TOUCHING might have happened while the page was - // snapping back after overscroll. we need to finish the snap if that - // was the case - bounce(); - return false; + case TOUCHING: + // the switch into TOUCHING might have happened while the page was + // snapping back after overscroll. we need to finish the snap if that + // was the case + bounce(); + return false; - case PANNING: - case PANNING_LOCKED: - case PANNING_HOLD: - case PANNING_HOLD_LOCKED: - setState(PanZoomState.FLING); - fling(); - return true; + case PANNING: + case PANNING_LOCKED: + case PANNING_HOLD: + case PANNING_HOLD_LOCKED: + setState(PanZoomState.FLING); + fling(); + return true; - case PINCHING: - setState(PanZoomState.NOTHING); - return true; + case PINCHING: + setState(PanZoomState.NOTHING); + return true; } - Log.e(LOGTAG, "Unhandled case " + mState + " in onTouchEnd"); + Log.e(LOGTAG, "Unhandled case " + mState + " in handleTouchEnd"); return false; } - private boolean onTouchCancel(MotionEvent event) { + private boolean handleTouchCancel(MotionEvent event) { cancelTouch(); if (mState == PanZoomState.WAITING_LISTENERS) { @@ -346,7 +376,7 @@ public class JavaPanZoomController return false; } - private boolean onScroll(MotionEvent event) { + private boolean handlePointerScroll(MotionEvent event) { if (mState == PanZoomState.NOTHING || mState == PanZoomState.FLING) { float scrollX = event.getAxisValue(MotionEvent.AXIS_HSCROLL); float scrollY = event.getAxisValue(MotionEvent.AXIS_VSCROLL); @@ -415,8 +445,8 @@ public class JavaPanZoomController for (int i = 0; i < event.getHistorySize(); i++) { track(event.getHistoricalX(0, i), - event.getHistoricalY(0, i), - event.getHistoricalEventTime(i)); + event.getHistoricalY(0, i), + event.getHistoricalEventTime(i)); } track(event.getX(0), event.getY(0), event.getEventTime()); @@ -792,6 +822,9 @@ public class JavaPanZoomController @Override public boolean onScale(SimpleScaleGestureDetector detector) { + if (mTarget.isFullScreen()) + return false; + if (mState != PanZoomState.PINCHING) return false; @@ -835,7 +868,7 @@ public class JavaPanZoomController } scrollBy(mLastZoomFocus.x - detector.getFocusX(), - mLastZoomFocus.y - detector.getFocusY()); + mLastZoomFocus.y - detector.getFocusY()); PointF focus = new PointF(detector.getFocusX(), detector.getFocusY()); scaleWithFocus(newZoomFactor, focus); } @@ -868,7 +901,6 @@ public class JavaPanZoomController mTarget.setViewportMetrics(viewportMetrics); } - @Override public boolean getRedrawHint() { switch (mState) { case PINCHING: @@ -901,9 +933,15 @@ public class JavaPanZoomController mWaitForDoubleTap = false; } + private PointF getMotionInDocumentCoordinates(MotionEvent motionEvent) { + RectF viewport = getValidViewportMetrics().getViewport(); + PointF viewPoint = new PointF(motionEvent.getX(0), motionEvent.getY(0)); + return mTarget.convertViewPointToLayerPoint(viewPoint); + } + @Override public void onLongPress(MotionEvent motionEvent) { - LOKitShell.sentTouchEvent("LongPress", motionEvent); + LOKitShell.sentTouchEvent("LongPress", motionEvent, getMotionInDocumentCoordinates(motionEvent)); } @Override @@ -911,7 +949,7 @@ public class JavaPanZoomController // When double-tapping is allowed, we have to wait to see if this is // going to be a double-tap. if (!mWaitForDoubleTap) { - LOKitShell.sentTouchEvent("SingleTap", motionEvent); + LOKitShell.sentTouchEvent("SingleTap", motionEvent, getMotionInDocumentCoordinates(motionEvent)); } // return false because we still want to get the ACTION_UP event that triggers this return false; @@ -921,14 +959,14 @@ public class JavaPanZoomController public boolean onSingleTapConfirmed(MotionEvent motionEvent) { // In cases where we don't wait for double-tap, we handle this in onSingleTapUp. if (mWaitForDoubleTap) { - LOKitShell.sentTouchEvent("SingleTap", motionEvent); + LOKitShell.sentTouchEvent("SingleTap", motionEvent, getMotionInDocumentCoordinates(motionEvent)); } return true; } @Override public boolean onDoubleTap(MotionEvent motionEvent) { - LOKitShell.sentTouchEvent("DoubleTap", motionEvent); + LOKitShell.sentTouchEvent("DoubleTap", motionEvent, getMotionInDocumentCoordinates(motionEvent)); return true; } @@ -984,19 +1022,16 @@ public class JavaPanZoomController } /** This function must be called from the UI thread. */ - @Override public void abortPanning() { checkMainThread(); bounce(); } - @Override public void setOverScrollMode(int overscrollMode) { mX.setOverScrollMode(overscrollMode); mY.setOverScrollMode(overscrollMode); } - @Override public int getOverScrollMode() { return mX.getOverScrollMode(); } diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java index 6fe97b8..b1aea36 100644 --- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java +++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/LayerRenderer.java @@ -5,15 +5,10 @@ package org.mozilla.gecko.gfx; -import android.content.Context; -import android.content.SharedPreferences; -import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; -import android.graphics.Region; -import android.graphics.RegionIterator; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.os.SystemClock; @@ -36,7 +31,6 @@ import javax.microedition.khronos.opengles.GL10; */ public class LayerRenderer implements GLSurfaceView.Renderer { private static final String LOGTAG = "GeckoLayerRenderer"; - private static final String PROFTAG = "GeckoLayerRendererProf"; /* * The amount of time a frame is allowed to take to render before we declare it a dropped @@ -44,14 +38,9 @@ public class LayerRenderer implements GLSurfaceView.Renderer { */ private static final int MAX_FRAME_TIME = 16; /* 1000 ms / 60 FPS */ - private static final int FRAME_RATE_METER_WIDTH = 128; - private static final int FRAME_RATE_METER_HEIGHT = 32; - private final LayerView mView; private final SingleTileLayer mBackgroundLayer; - private final ScreenshotLayer mScreenshotLayer; private final NinePatchTileLayer mShadowLayer; - private TextLayer mFrameRateLayer; private final ScrollbarLayer mHorizScrollLayer; private final ScrollbarLayer mVertScrollLayer; private final FadeRunnable mFadeRunnable; @@ -63,16 +52,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer { private CopyOnWriteArrayList<Layer> mExtraLayers = new CopyOnWriteArrayList<Layer>(); - // Dropped frames display - private int[] mFrameTimings; - private int mCurrentFrame, mFrameTimingsSum, mDroppedFrames; - - // Render profiling output - private int mFramesRendered; - private float mCompleteFramesRendered; - private boolean mProfileRender; - private long mProfileOutputTime; - /* Used by robocop for testing purposes */ private IntBuffer mPixelBuffer; @@ -126,44 +105,12 @@ public class LayerRenderer implements GLSurfaceView.Renderer { " gl_FragColor = texture2D(sTexture, vTexCoord);\n" + "}\n"; - public void setCheckerboardBitmap(Bitmap bitmap, float pageWidth, float pageHeight) { - mScreenshotLayer.setBitmap(bitmap); - mScreenshotLayer.beginTransaction(); - try { - mScreenshotLayer.setPosition(new Rect(0, 0, Math.round(pageWidth), - Math.round(pageHeight))); - mScreenshotLayer.invalidate(); - } finally { - mScreenshotLayer.endTransaction(); - } - } - - public void updateCheckerboardBitmap(Bitmap bitmap, float x, float y, - float width, float height, - float pageWidth, float pageHeight) { - mScreenshotLayer.updateBitmap(bitmap, x, y, width, height); - mScreenshotLayer.beginTransaction(); - try { - mScreenshotLayer.setPosition(new Rect(0, 0, Math.round(pageWidth), - Math.round(pageHeight))); - mScreenshotLayer.invalidate(); - } finally { - mScreenshotLayer.endTransaction(); - } - } - - public void resetCheckerboard() { - mScreenshotLayer.reset(); - } - public LayerRenderer(LayerView view) { mView = view; CairoImage backgroundImage = new BufferedCairoImage(view.getBackgroundPattern()); mBackgroundLayer = new SingleTileLayer(true, backgroundImage); - mScreenshotLayer = ScreenshotLayer.create(); - CairoImage shadowImage = new BufferedCairoImage(view.getShadowPattern()); mShadowLayer = new NinePatchTileLayer(shadowImage); @@ -171,9 +118,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer { mVertScrollLayer = ScrollbarLayer.create(this, true); mFadeRunnable = new FadeRunnable(); - mFrameTimings = new int[60]; - mCurrentFrame = mFrameTimingsSum = mDroppedFrames = 0; - // Initialize the FloatBuffer that will be used to store all vertices and texture // coordinates in draw() commands. mCoordByteBuffer = DirectBufferAllocator.allocate(COORD_BUFFER_SIZE * 4); @@ -196,18 +140,13 @@ public class LayerRenderer implements GLSurfaceView.Renderer { DirectBufferAllocator.free(mCoordByteBuffer); mCoordByteBuffer = null; mCoordBuffer = null; - mScreenshotLayer.destroy(); mBackgroundLayer.destroy(); mShadowLayer.destroy(); mHorizScrollLayer.destroy(); mVertScrollLayer.destroy(); - if (mFrameRateLayer != null) { - mFrameRateLayer.destroy(); - } } public void onSurfaceCreated(GL10 gl, EGLConfig config) { - checkMonitoringEnabled(); createDefaultProgram(); activateDefaultProgram(); } @@ -292,27 +231,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer { } } - private void printCheckerboardStats() { - Log.d(PROFTAG, "Frames rendered over last 1000ms: " + mCompleteFramesRendered + "/" + mFramesRendered); - mFramesRendered = 0; - mCompleteFramesRendered = 0; - } - - /** Used by robocop for testing purposes. Not for production use! */ - IntBuffer getPixels() { - IntBuffer pixelBuffer = IntBuffer.allocate(mView.getWidth() * mView.getHeight()); - synchronized (pixelBuffer) { - mPixelBuffer = pixelBuffer; - mView.requestRender(); - try { - pixelBuffer.wait(); - } catch (InterruptedException ie) { - } - mPixelBuffer = null; - } - return pixelBuffer; - } - private RenderContext createScreenContext(ImmutableViewportMetrics metrics) { RectF viewport = new RectF(0.0f, 0.0f, metrics.getWidth(), metrics.getHeight()); RectF pageRect = new RectF(metrics.getPageRect()); @@ -333,64 +251,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer { public void onSurfaceChanged(GL10 gl, final int width, final int height) { GLES20.glViewport(0, 0, width, height); - - if (mFrameRateLayer != null) { - moveFrameRateLayer(width, height); - } - - /* TODO: Throw away tile images? */ - } - - private void updateDroppedFrames(long frameStartTime) { - int frameElapsedTime = (int)(SystemClock.uptimeMillis() - frameStartTime); - - /* Update the running statistics. */ - mFrameTimingsSum -= mFrameTimings[mCurrentFrame]; - mFrameTimingsSum += frameElapsedTime; ... etc. - the rest is truncated
_______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits