Kaydet (Commit) 5ccb510e authored tarafından Tomaž Vajngerl's avatar Tomaž Vajngerl

LOAndroid3: (partially) render page with LOKitTileProvider

+ TileProvider & TileIterator interfaces
+ Clean-up obsolete mozilla stuff

Change-Id: Ief56f11bf7f8fd6da383ffc7be3461b765bf0157
üst 9948d956
...@@ -16,16 +16,10 @@ import java.nio.ByteBuffer; ...@@ -16,16 +16,10 @@ import java.nio.ByteBuffer;
import java.util.Random; import java.util.Random;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import org.libreoffice.kit.LibreOfficeKit;
import org.libreoffice.kit.Office;
import org.libreoffice.kit.Document;
public class LOKitThread extends Thread { public class LOKitThread extends Thread {
private static final String LOGTAG = "GeckoThread"; private static final String LOGTAG = "GeckoThread";
private static final int TILE_SIZE = 256; private static final int TILE_SIZE = 256;
private TileProvider mTileProvider;
public Office mOffice;
public Document mDocument;
public ConcurrentLinkedQueue<LOEvent> gEvents = new ConcurrentLinkedQueue<LOEvent>(); public ConcurrentLinkedQueue<LOEvent> gEvents = new ConcurrentLinkedQueue<LOEvent>();
private ViewportMetrics mViewportMetrics; private ViewportMetrics mViewportMetrics;
...@@ -33,55 +27,40 @@ public class LOKitThread extends Thread { ...@@ -33,55 +27,40 @@ public class LOKitThread extends Thread {
LOKitThread() { LOKitThread() {
} }
private void openDocument() { private boolean draw() throws InterruptedException {
// enable debugging messages as the first thing
LibreOfficeKit.putenv("SAL_LOG=+WARN+INFO-INFO.legacy.osl-INFO.i18nlangtag");
LibreOfficeKit.init(LibreOfficeMainActivity.mAppContext);
mOffice = new Office(LibreOfficeKit.getLibreOfficeKitHandle());
String input = "/assets/test1.odt";
mDocument = mOffice.documentLoad(input);
}
private synchronized boolean draw() throws InterruptedException {
final LibreOfficeMainActivity application = LibreOfficeMainActivity.mAppContext; final LibreOfficeMainActivity application = LibreOfficeMainActivity.mAppContext;
openDocument(); if (mTileProvider == null)
mTileProvider = new LOKitTileProvider(application.getLayerController());
long height = mDocument.getDocumentHeight();
long width = mDocument.getDocumentWidth();
Log.e(LOGTAG, "Document Size: " + width + " " + height); int pageWidth = mTileProvider.getPageWidth();
int pageHeight = mTileProvider.getPageHeight();
int pageWidth = 1024; String metadata = createJson(0, 0, pageWidth, pageHeight, pageWidth, pageHeight, 0, 0, 1.0);
int pageHeight = 1024; mViewportMetrics = new ViewportMetrics();
String metadata = createJson(0, 0, 256, 256, pageWidth, pageHeight, 0, 0, 1.0); boolean shouldContinue = application.getLayerClient().beginDrawing(pageWidth, pageHeight, TILE_SIZE, TILE_SIZE, metadata);
Rect bufferRect = application.getLayerClient().beginDrawing(256, 256, TILE_SIZE, TILE_SIZE, metadata); if (!shouldContinue) {
/*if (bufferRect == null) {
Log.e(LOGTAG, "beginDrawing - false");
return false; return false;
}*/ }
Log.e(LOGTAG, "Filling tiles..");
ByteBuffer buffer = ByteBuffer.allocateDirect(TILE_SIZE * TILE_SIZE * 4);
Log.e(LOGTAG, "PaintTile..");
mDocument.paintTile(buffer, 256, 256, 1024, 1024, 4096, 4096);
Log.e(LOGTAG, "EndPaintTile.."); Log.i(LOGTAG, "Filling tiles..");
Bitmap bitmap = Bitmap.createBitmap(TILE_SIZE, TILE_SIZE, Bitmap.Config.ARGB_8888); int x = 0;
bitmap.copyPixelsFromBuffer(buffer); int y = 0;
for (Bitmap bitmap : mTileProvider.getTileIterator()) {
application.getLayerClient().addTile(bitmap, x, y);
x += TILE_SIZE;
if (x > pageWidth) {
x = 0;
y += TILE_SIZE;
}
}
application.getLayerClient().addTile(bitmap, 0, 0); Log.i(LOGTAG, "End Draw");
Log.e(LOGTAG, "EndDrawing.."); application.getLayerClient().endDrawing(0, 0, pageWidth, pageHeight);
application.getLayerClient().endDrawing(0, 0, 256, 256);
return true; return true;
} }
...@@ -143,22 +122,20 @@ public class LOKitThread extends Thread { ...@@ -143,22 +122,20 @@ public class LOKitThread extends Thread {
try { try {
boolean drawn = false; boolean drawn = false;
while (true) { while (true) {
if (!gEvents.isEmpty()) { if (!gEvents.isEmpty()) {
processEvent(gEvents.poll()); processEvent(gEvents.poll());
} else { } else {
if (!drawn) { if (!drawn) {
drawn = draw(); drawn = draw();
} }
Thread.sleep(2000L); Thread.sleep(100L);
} }
} }
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
} }
} }
private synchronized void processEvent(LOEvent event) throws InterruptedException { private void processEvent(LOEvent event) throws InterruptedException {
switch (event.mType) { switch (event.mType) {
case LOEvent.VIEWPORT: case LOEvent.VIEWPORT:
mViewportMetrics = event.getViewport(); mViewportMetrics = event.getViewport();
......
package org.libreoffice;
import android.graphics.Bitmap;
import android.util.Log;
import org.mozilla.gecko.gfx.LayerController;
import java.nio.ByteBuffer;
import java.util.Iterator;
import org.libreoffice.kit.LibreOfficeKit;
import org.libreoffice.kit.Office;
import org.libreoffice.kit.Document;
public class LOKitTileProvider implements TileProvider {
private final LayerController mLayerController;
public static int TILE_SIZE = 256;
public final Office mOffice;
public final Document mDocument;
public LOKitTileProvider(LayerController layerController) {
this.mLayerController = layerController;
LibreOfficeKit.putenv("SAL_LOG=+WARN+INFO-INFO.legacy.osl-INFO.i18nlangtag");
LibreOfficeKit.init(LibreOfficeMainActivity.mAppContext);
mOffice = new Office(LibreOfficeKit.getLibreOfficeKitHandle());
String input = "/assets/test1.odt";
mDocument = mOffice.documentLoad(input);
}
@Override
public int getPageWidth() {
return (int) (mDocument.getDocumentWidth() / 1440.0 * LOKitShell.getDpi());
}
@Override
public int getPageHeight() {
return (int) (mDocument.getDocumentHeight() / 1440.0 * LOKitShell.getDpi());
}
public TileIterator getTileIterator() {
return new LoKitTileIterator();
}
public class LoKitTileIterator implements TileIterator, Iterator<Bitmap> {
private final double mTileWidth;
private final double mTileHeight;
private boolean mShouldContinue = true;
private double mPositionWidth = 0;
private double mPositionHeight = 0;
private double mPageWidth;
private double mPageHeight;
public LoKitTileIterator() {
mTileWidth = (TILE_SIZE / (double) LOKitShell.getDpi()) * 1440.0;
mTileHeight = (TILE_SIZE / (double) LOKitShell.getDpi()) * 1440.0;
mPageWidth = mDocument.getDocumentWidth();
mPageHeight = mDocument.getDocumentHeight();
}
@Override
public boolean hasNext() {
return mShouldContinue;
}
@Override
public Bitmap next() {
ByteBuffer buffer = ByteBuffer.allocateDirect(TILE_SIZE * TILE_SIZE * 4);
Bitmap bitmap = Bitmap.createBitmap(TILE_SIZE, TILE_SIZE, Bitmap.Config.ARGB_8888);
mDocument.paintTile(buffer, TILE_SIZE, TILE_SIZE, (int) mPositionWidth, (int) mPositionHeight, (int) mTileWidth, (int) mTileHeight);
mPositionWidth += mTileWidth;
if (mPositionWidth > mPageWidth) {
mPositionHeight += mTileHeight;
mPositionWidth = 0;
}
if (mPositionHeight > mPageHeight || mPositionHeight > 20000) {
mShouldContinue = false;
}
bitmap.copyPixelsFromBuffer(buffer);
return bitmap;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public Iterator<Bitmap> iterator() {
return this;
}
}
}
...@@ -11,8 +11,6 @@ import android.view.MotionEvent; ...@@ -11,8 +11,6 @@ import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.os.Environment;
import java.io.File;
import org.mozilla.gecko.gfx.GeckoSoftwareLayerClient; import org.mozilla.gecko.gfx.GeckoSoftwareLayerClient;
import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.gfx.LayerController;
...@@ -63,10 +61,12 @@ public class LibreOfficeMainActivity extends Activity { ...@@ -63,10 +61,12 @@ public class LibreOfficeMainActivity extends Activity {
mAppContext = this; mAppContext = this;
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - onCreate"); Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - onCreate");
setContentView(R.layout.activity_main);
// setup gecko layout // setup gecko layout
mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout); mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout);
mMainLayout = (LinearLayout) findViewById(R.id.main_layout); mMainLayout = (LinearLayout) findViewById(R.id.main_layout);
......
package org.libreoffice;
import android.graphics.Bitmap;
import org.apache.http.MethodNotSupportedException;
import org.mozilla.gecko.gfx.LayerController;
import java.util.Iterator;
import java.util.List;
public class MockTileProvider implements TileProvider {
private final LayerController layerController;
public MockTileProvider(LayerController layerController) {
this.layerController = layerController;
}
@Override
public int getPageWidth() {
return 549;
}
@Override
public int getPageHeight() {
return 630;
}
public TileIterator getTileIterator() {
return new MockTileIterator(layerController);
}
public class MockTileIterator implements TileIterator, Iterator<Bitmap> {
private final LayerController layerController;
private int tileNumber = 1;
public MockTileIterator(LayerController layerController) {
this.layerController = layerController;
}
@Override
public boolean hasNext() {
return tileNumber <= 9;
}
@Override
public Bitmap next() {
String imageName = "d" + tileNumber;
tileNumber++;
Bitmap bitmap = layerController.getDrawable(imageName);
return bitmap;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public Iterator<Bitmap> iterator() {
return this;
}
}
}
package org.libreoffice;
import android.graphics.Bitmap;
public interface TileIterator extends Iterable<Bitmap> {
}
package org.libreoffice;
import android.graphics.Bitmap;
import java.util.List;
public interface TileProvider {
int getPageWidth();
int getPageHeight();
TileIterator getTileIterator();
}
...@@ -59,15 +59,7 @@ import org.mozilla.gecko.util.FloatUtils; ...@@ -59,15 +59,7 @@ import org.mozilla.gecko.util.FloatUtils;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
//import org.mozilla.gecko.GeckoApp; public abstract class GeckoLayerClient implements GeckoEventListener {
//import org.mozilla.gecko.GeckoAppShell;
//import org.mozilla.gecko.GeckoEvent;
public abstract class GeckoLayerClient extends LayerClient implements GeckoEventListener {
public static final int LAYER_CLIENT_TYPE_NONE = 0;
public static final int LAYER_CLIENT_TYPE_SOFTWARE = 1;
public static final int LAYER_CLIENT_TYPE_GL = 2;
private static final String LOGTAG = "GeckoLayerClient"; private static final String LOGTAG = "GeckoLayerClient";
private static final long MIN_VIEWPORT_CHANGE_DELAY = 25L; private static final long MIN_VIEWPORT_CHANGE_DELAY = 25L;
private static Pattern sColorPattern; private static Pattern sColorPattern;
...@@ -88,53 +80,26 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent ...@@ -88,53 +80,26 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent
// inside a transaction, so no synchronization is needed. // inside a transaction, so no synchronization is needed.
private boolean mUpdateViewportOnEndDraw; private boolean mUpdateViewportOnEndDraw;
private String mLastCheckerboardColor; private String mLastCheckerboardColor;
/* Used by robocop for testing purposes */
private DrawListener mDrawListener; protected LayerController mLayerController;
public GeckoLayerClient(Context context) { public GeckoLayerClient(Context context) {
mScreenSize = new IntSize(0, 0); mScreenSize = new IntSize(0, 0);
} }
// Parses a color from an RGB triple of the form "rgb([0-9]+, [0-9]+, [0-9]+)". If the color
// cannot be parsed, returns white.
private static int parseColorFromGecko(String string) {
if (sColorPattern == null) {
sColorPattern = Pattern.compile("rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)");
}
Matcher matcher = sColorPattern.matcher(string);
if (!matcher.matches()) {
return Color.WHITE;
}
int r = Integer.parseInt(matcher.group(1));
int g = Integer.parseInt(matcher.group(2));
int b = Integer.parseInt(matcher.group(3));
return Color.rgb(r, g, b);
}
protected abstract boolean setupLayer(); protected abstract boolean setupLayer();
protected abstract boolean shouldDrawProceed(int tileWidth, int tileHeight);
protected abstract void updateLayerAfterDraw(Rect updatedRect); protected abstract void updateLayerAfterDraw(Rect updatedRect);
protected abstract IntSize getBufferSize(); protected abstract IntSize getBufferSize();
protected abstract IntSize getTileSize(); protected abstract IntSize getTileSize();
protected abstract void tileLayerUpdated();
public abstract Bitmap getBitmap();
public abstract int getType();
/** /**
* Attaches the root layer to the layer controller so that Gecko appears. * Attaches the root layer to the layer controller so that Gecko appears.
*/ */
@Override
public void setLayerController(LayerController layerController) { public void setLayerController(LayerController layerController) {
super.setLayerController(layerController); mLayerController = layerController;
layerController.setRoot(mTileLayer); layerController.setRoot(mTileLayer);
if (mGeckoViewport != null) { if (mGeckoViewport != null) {
...@@ -144,80 +109,25 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent ...@@ -144,80 +109,25 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent
sendResizeEventIfNecessary(); sendResizeEventIfNecessary();
} }
public Rect beginDrawing(int width, int height, int tileWidth, int tileHeight, String metadata) { public boolean beginDrawing(int width, int height, int tileWidth, int tileHeight, String metadata) {
Log.e(LOGTAG, "### beginDrawing " + width + " " + height + " " + tileWidth + " " + tileHeight); Log.e(LOGTAG, "### beginDrawing " + width + " " + height + " " + tileWidth + " " + tileHeight);
if (setupLayer()) { if (setupLayer()) {
Log.e(LOGTAG, "### Cancelling due to layer setup"); Log.e(LOGTAG, "### Cancelling due to layer setup");
return null; return false;
}
if (!shouldDrawProceed(tileWidth, tileHeight)) {
Log.e(LOGTAG, "### Cancelling draw due to shouldDrawProceed()");
return null;
} }
LayerController controller = getLayerController();
try { try {
JSONObject viewportObject = new JSONObject(metadata); JSONObject viewportObject = new JSONObject(metadata);
mNewGeckoViewport = new ViewportMetrics(viewportObject); mNewGeckoViewport = new ViewportMetrics(viewportObject);
Log.e(LOGTAG, "### beginDrawing new Gecko viewport " + mNewGeckoViewport); Log.e(LOGTAG, "### beginDrawing new Gecko viewport " + mNewGeckoViewport);
// Update the background color, if it's present.
String backgroundColorString = viewportObject.optString("backgroundColor");
if (backgroundColorString != null && !backgroundColorString.equals(mLastCheckerboardColor)) {
mLastCheckerboardColor = backgroundColorString;
controller.setCheckerboardColor(parseColorFromGecko(backgroundColorString));
}
} catch (JSONException e) { } catch (JSONException e) {
Log.e(LOGTAG, "Aborting draw, bad viewport description: " + metadata); Log.e(LOGTAG, "Aborting draw, bad viewport description: " + metadata);
return null; return false;
} }
// Make sure we don't spend time painting areas we aren't interested in. mTileLayer.beginTransaction();
// Only do this if the Gecko viewport isn't going to override our viewport. return true;
Rect bufferRect = new Rect(0, 0, width, height);
if (!mUpdateViewportOnEndDraw) {
// First, find out our ideal displayport. We do this by taking the
// clamped viewport origin and taking away the optimum viewport offset.
// This would be what we would send to Gecko if adjustViewport were
// called now.
ViewportMetrics currentMetrics = controller.getViewportMetrics();
PointF currentBestOrigin = RectUtils.getOrigin(currentMetrics.getClampedViewport());
PointF viewportOffset = currentMetrics.getOptimumViewportOffset(new IntSize(width, height));
currentBestOrigin.offset(-viewportOffset.x, -viewportOffset.y);
Rect currentRect = RectUtils.round(new RectF(currentBestOrigin.x, currentBestOrigin.y,
currentBestOrigin.x + width, currentBestOrigin.y + height));
// Second, store Gecko's displayport.
PointF currentOrigin = mNewGeckoViewport.getDisplayportOrigin();
bufferRect = RectUtils.round(new RectF(currentOrigin.x, currentOrigin.y,
currentOrigin.x + width, currentOrigin.y + height));
// Take the intersection of the two as the area we're interested in rendering.
if (!bufferRect.intersect(currentRect)) {
// If there's no intersection, we have no need to render anything,
// but make sure to update the viewport size.
beginTransaction(mTileLayer);
try {
updateViewport(true);
} finally {
endTransaction(mTileLayer);
}
return null;
}
bufferRect.offset(Math.round(-currentOrigin.x), Math.round(-currentOrigin.y));
}
beginTransaction(mTileLayer);
return bufferRect;
} }
/* /*
...@@ -225,23 +135,17 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent ...@@ -225,23 +135,17 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent
* a little more JNI magic. * a little more JNI magic.
*/ */
public void endDrawing(int x, int y, int width, int height) { public void endDrawing(int x, int y, int width, int height) {
synchronized (getLayerController()) { synchronized (mLayerController) {
try { try {
updateViewport(!mUpdateViewportOnEndDraw); updateViewport(!mUpdateViewportOnEndDraw);
mUpdateViewportOnEndDraw = false; mUpdateViewportOnEndDraw = false;
Rect rect = new Rect(x, y, x + width, y + height); Rect rect = new Rect(x, y, x + width, y + height);
updateLayerAfterDraw(rect); updateLayerAfterDraw(rect);
} finally { } finally {
endTransaction(mTileLayer); mTileLayer.endTransaction();
} }
} }
Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - endDrawing"); Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - endDrawing");
/* Used by robocop for testing purposes */
if (mDrawListener != null) {
mDrawListener.drawFinished(x, y, width, height);
}
} }
protected void updateViewport(boolean onlyUpdatePageSize) { protected void updateViewport(boolean onlyUpdatePageSize) {
...@@ -249,27 +153,25 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent ...@@ -249,27 +153,25 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent
// JS-side viewport dimensions override the java-side ones because // JS-side viewport dimensions override the java-side ones because
// java is the One True Source of this information, and allowing JS // java is the One True Source of this information, and allowing JS
// to override can lead to race conditions where this data gets clobbered. // to override can lead to race conditions where this data gets clobbered.
FloatSize viewportSize = getLayerController().getViewportSize(); FloatSize viewportSize = mLayerController.getViewportSize();
mGeckoViewport = mNewGeckoViewport; mGeckoViewport = mNewGeckoViewport;
mGeckoViewport.setSize(viewportSize); mGeckoViewport.setSize(viewportSize);
LayerController controller = getLayerController();
PointF displayportOrigin = mGeckoViewport.getDisplayportOrigin(); PointF displayportOrigin = mGeckoViewport.getDisplayportOrigin();
mTileLayer.setOrigin(PointUtils.round(displayportOrigin)); mTileLayer.setOrigin(PointUtils.round(displayportOrigin));
mTileLayer.setResolution(mGeckoViewport.getZoomFactor()); mTileLayer.setResolution(mGeckoViewport.getZoomFactor());
this.tileLayerUpdated();
Log.e(LOGTAG, "### updateViewport onlyUpdatePageSize=" + onlyUpdatePageSize + " getTileViewport " + mGeckoViewport); Log.e(LOGTAG, "### updateViewport onlyUpdatePageSize=" + onlyUpdatePageSize + " getTileViewport " + mGeckoViewport);
if (onlyUpdatePageSize) { if (onlyUpdatePageSize) {
// Don't adjust page size when zooming unless zoom levels are // Don't adjust page size when zooming unless zoom levels are
// approximately equal. // approximately equal.
if (FloatUtils.fuzzyEquals(controller.getZoomFactor(), mGeckoViewport.getZoomFactor())) { if (FloatUtils.fuzzyEquals(mLayerController.getZoomFactor(), mGeckoViewport.getZoomFactor())) {
controller.setPageSize(mGeckoViewport.getPageSize()); mLayerController.setPageSize(mGeckoViewport.getPageSize());
} }
} else { } else {
controller.setViewportMetrics(mGeckoViewport); mLayerController.setViewportMetrics(mGeckoViewport);
controller.abortPanZoomAnimation(); mLayerController.abortPanZoomAnimation();
} }
} }
...@@ -284,14 +186,15 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent ...@@ -284,14 +186,15 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent
// size is zero (which indicates that the rendering surface hasn't been // size is zero (which indicates that the rendering surface hasn't been
// allocated yet). // allocated yet).
boolean screenSizeChanged = (metrics.widthPixels != mScreenSize.width || metrics.heightPixels != mScreenSize.height); boolean screenSizeChanged = (metrics.widthPixels != mScreenSize.width || metrics.heightPixels != mScreenSize.height);
boolean viewportSizeValid = (getLayerController() != null && getLayerController().getViewportSize().isPositive()); boolean viewportSizeValid = (mLayerController != null && mLayerController.getViewportSize().isPositive());
if (!(force || (screenSizeChanged && viewportSizeValid))) { if (!(force || (screenSizeChanged && viewportSizeValid))) {
return; return;
} }
mScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels); mScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels);
IntSize bufferSize = getBufferSize(), tileSize = getTileSize(); IntSize bufferSize = getBufferSize();
IntSize tileSize = getTileSize();
Log.e(LOGTAG, "### Screen-size changed to " + mScreenSize); Log.e(LOGTAG, "### Screen-size changed to " + mScreenSize);
...@@ -301,13 +204,12 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent ...@@ -301,13 +204,12 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent
LOKitShell.sendEvent(event); LOKitShell.sendEvent(event);
} }
@Override
public void render() { public void render() {
adjustViewportWithThrottling(); adjustViewportWithThrottling();
} }
private void adjustViewportWithThrottling() { private void adjustViewportWithThrottling() {
if (!getLayerController().getRedrawHint()) if (!mLayerController.getRedrawHint())
return; return;
if (mPendingViewportAdjust) if (mPendingViewportAdjust)
...@@ -315,7 +217,7 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent ...@@ -315,7 +217,7 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent
long timeDelta = System.currentTimeMillis() - mLastViewportChangeTime; long timeDelta = System.currentTimeMillis() - mLastViewportChangeTime;
if (timeDelta < MIN_VIEWPORT_CHANGE_DELAY) { if (timeDelta < MIN_VIEWPORT_CHANGE_DELAY) {
getLayerController().getView().postDelayed( mLayerController.getView().postDelayed(
new Runnable() { new Runnable() {
public void run() { public void run() {
mPendingViewportAdjust = false; mPendingViewportAdjust = false;
...@@ -330,13 +232,12 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent ...@@ -330,13 +232,12 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent
adjustViewport(); adjustViewport();
} }
@Override
public void viewportSizeChanged() { public void viewportSizeChanged() {
mViewportSizeChanged = true; mViewportSizeChanged = true;
} }
private void adjustViewport() { private void adjustViewport() {
ViewportMetrics viewportMetrics = new ViewportMetrics(getLayerController().getViewportMetrics()); ViewportMetrics viewportMetrics = new ViewportMetrics(mLayerController.getViewportMetrics());
PointF viewportOffset = viewportMetrics.getOptimumViewportOffset(getBufferSize()); PointF viewportOffset = viewportMetrics.getOptimumViewportOffset(getBufferSize());
viewportMetrics.setViewportOffset(viewportOffset); viewportMetrics.setViewportOffset(viewportOffset);
...@@ -366,7 +267,6 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent ...@@ -366,7 +267,6 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent
} }
} }
@Override
public void geometryChanged() { public void geometryChanged() {
sendResizeEventIfNecessary(); sendResizeEventIfNecessary();
render(); render();
...@@ -381,18 +281,4 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent ...@@ -381,18 +281,4 @@ public abstract class GeckoLayerClient extends LayerClient implements GeckoEvent
private void sendResizeEventIfNecessary() { private void sendResizeEventIfNecessary() {
sendResizeEventIfNecessary(false); sendResizeEventIfNecessary(false);
} }
/**
* Used by robocop for testing purposes. Not for production use! This is called via reflection by robocop.
*/
public void setDrawListener(DrawListener listener) {
mDrawListener = listener;
}
/**
* Used by robocop for testing purposes. Not for production use! This is used via reflection by robocop.
*/
public interface DrawListener {
public void drawFinished(int x, int y, int width, int height);
}
} }
\ No newline at end of file
...@@ -41,7 +41,7 @@ package org.mozilla.gecko.gfx; ...@@ -41,7 +41,7 @@ package org.mozilla.gecko.gfx;
import org.libreoffice.LOKitShell; import org.libreoffice.LOKitShell;
import org.mozilla.gecko.gfx.CairoImage; import org.mozilla.gecko.gfx.CairoImage;
import org.mozilla.gecko.gfx.IntSize; import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.LayerClient; import org.mozilla.gecko.gfx.GeckoLayerClient;
import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.gfx.LayerController;
import org.mozilla.gecko.gfx.LayerRenderer; import org.mozilla.gecko.gfx.LayerRenderer;
import org.mozilla.gecko.gfx.MultiTileLayer; import org.mozilla.gecko.gfx.MultiTileLayer;
...@@ -77,20 +77,11 @@ public class GeckoSoftwareLayerClient extends GeckoLayerClient { ...@@ -77,20 +77,11 @@ public class GeckoSoftwareLayerClient extends GeckoLayerClient {
mFormat = CairoImage.FORMAT_ARGB32; mFormat = CairoImage.FORMAT_ARGB32;
} }
/*protected void finalize() throws Throwable {
try {
if (mBuffer != null)
LOKitShell.freeDirectBuffer(mBuffer);
mBuffer = null;
} finally {
super.finalize();
}
}*/
public void setLayerController(LayerController layerController) { public void setLayerController(LayerController layerController) {
super.setLayerController(layerController); super.setLayerController(layerController);
layerController.setRoot(mTileLayer); layerController.setRoot(mTileLayer);
if (mGeckoViewport != null) { if (mGeckoViewport != null) {
layerController.setViewportMetrics(mGeckoViewport); layerController.setViewportMetrics(mGeckoViewport);
} }
...@@ -104,7 +95,7 @@ public class GeckoSoftwareLayerClient extends GeckoLayerClient { ...@@ -104,7 +95,7 @@ public class GeckoSoftwareLayerClient extends GeckoLayerClient {
if(mTileLayer == null) if(mTileLayer == null)
mTileLayer = new MultiTileLayer(TILE_SIZE); mTileLayer = new MultiTileLayer(TILE_SIZE);
getLayerController().setRoot(mTileLayer); mLayerController.setRoot(mTileLayer);
// Force a resize event to be sent because the results of this // Force a resize event to be sent because the results of this
// are different depending on what tile system we're using // are different depending on what tile system we're using
...@@ -114,22 +105,11 @@ public class GeckoSoftwareLayerClient extends GeckoLayerClient { ...@@ -114,22 +105,11 @@ public class GeckoSoftwareLayerClient extends GeckoLayerClient {
} }
@Override @Override
protected boolean shouldDrawProceed(int tileWidth, int tileHeight) { public boolean beginDrawing(int width, int height, int tileWidth, int tileHeight, String metadata) {
// Make sure the tile-size matches. If it doesn't, we could crash trying boolean shouldContinue = super.beginDrawing(width, height, tileWidth, tileHeight, metadata);
// to access invalid memory.
if (tileWidth != TILE_SIZE.width || tileHeight != TILE_SIZE.height) {
Log.e(LOGTAG, "Aborting draw, incorrect tile size of " + tileWidth + "x" + tileHeight);
return false;
}
return true;
}
@Override
public Rect beginDrawing(int width, int height, int tileWidth, int tileHeight, String metadata) {
Rect bufferRect = super.beginDrawing(width, height, tileWidth, tileHeight, metadata);
if (bufferRect == null) { if (!shouldContinue) {
return bufferRect; return shouldContinue;
} }
// If the window size has changed, reallocate the buffer to match. // If the window size has changed, reallocate the buffer to match.
...@@ -137,7 +117,7 @@ public class GeckoSoftwareLayerClient extends GeckoLayerClient { ...@@ -137,7 +117,7 @@ public class GeckoSoftwareLayerClient extends GeckoLayerClient {
mBufferSize = new IntSize(width, height); mBufferSize = new IntSize(width, height);
} }
return bufferRect; return shouldContinue;
} }
@Override @Override
...@@ -147,76 +127,6 @@ public class GeckoSoftwareLayerClient extends GeckoLayerClient { ...@@ -147,76 +127,6 @@ public class GeckoSoftwareLayerClient extends GeckoLayerClient {
} }
} }
/*private void copyPixelsFromMultiTileLayer(Bitmap target) {
Canvas c = new Canvas(target);
ByteBuffer tileBuffer = mBuffer.slice();
int bpp = CairoUtils.bitsPerPixelForCairoFormat(mFormat) / 8;
for (int y = 0; y < mBufferSize.height; y += TILE_SIZE.height) {
for (int x = 0; x < mBufferSize.width; x += TILE_SIZE.width) {
// Calculate tile size
IntSize tileSize = new IntSize(Math.min(mBufferSize.width - x, TILE_SIZE.width),
Math.min(mBufferSize.height - y, TILE_SIZE.height));
// Create a Bitmap from this tile
Bitmap tile = Bitmap.createBitmap(tileSize.width, tileSize.height,
CairoUtils.cairoFormatTobitmapConfig(mFormat));
tile.copyPixelsFromBuffer(tileBuffer.asIntBuffer());
// Copy the tile to the master Bitmap and recycle it
c.drawBitmap(tile, x, y, null);
tile.recycle();
// Progress the buffer to the next tile
tileBuffer.position(tileSize.getArea() * bpp);
tileBuffer = tileBuffer.slice();
}
}
}*/
@Override
protected void tileLayerUpdated() {
/* No-op. */
}
@Override
public Bitmap getBitmap() {
if (mTileLayer == null)
return null;
// Begin a tile transaction, otherwise the buffer can be destroyed while
// we're reading from it.
/*beginTransaction(mTileLayer);
try {
if (mBuffer == null || mBufferSize.width <= 0 || mBufferSize.height <= 0)
return null;
try {
Bitmap b = null;
if (mTileLayer instanceof MultiTileLayer) {
b = Bitmap.createBitmap(mBufferSize.width, mBufferSize.height,CairoUtils.cairoFormatTobitmapConfig(mFormat));
copyPixelsFromMultiTileLayer(b);
} else {
Log.w(LOGTAG, "getBitmap() called on a layer (" + mTileLayer + ") we don't know how to get a bitmap from");
}
return b;
} catch (OutOfMemoryError oom) {
Log.w(LOGTAG, "Unable to create bitmap", oom);
return null;
}
} finally {
endTransaction(mTileLayer);
}*/
return null;
}
@Override
public int getType() {
return LAYER_CLIENT_TYPE_SOFTWARE;
}
@Override @Override
protected IntSize getBufferSize() { protected IntSize getBufferSize() {
return new IntSize( return new IntSize(
...@@ -235,3 +145,4 @@ public class GeckoSoftwareLayerClient extends GeckoLayerClient { ...@@ -235,3 +145,4 @@ public class GeckoSoftwareLayerClient extends GeckoLayerClient {
} }
} }
} }
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Android code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package org.mozilla.gecko.gfx;
/**
* A layer client provides tiles and manages other information used by the layer controller.
*/
public abstract class LayerClient {
private LayerController mLayerController;
public abstract void geometryChanged();
public abstract void viewportSizeChanged();
protected abstract void render();
public LayerController getLayerController() {
return mLayerController;
}
public void setLayerController(LayerController layerController) {
mLayerController = layerController;
}
/**
* A utility function for calling Layer.beginTransaction with the
* appropriate LayerView.
*/
public void beginTransaction(Layer aLayer) {
if (mLayerController != null) {
LayerView view = mLayerController.getView();
if (view != null) {
aLayer.beginTransaction(view);
return;
}
}
aLayer.beginTransaction();
}
// Included for symmetry.
public void endTransaction(Layer aLayer) {
aLayer.endTransaction();
}
}
...@@ -88,7 +88,7 @@ public class LayerController { ...@@ -88,7 +88,7 @@ public class LayerController {
private boolean mWaitForTouchListeners; private boolean mWaitForTouchListeners;
private PanZoomController mPanZoomController; private PanZoomController mPanZoomController;
private OnTouchListener mOnTouchListener; /* The touch listener. */ private OnTouchListener mOnTouchListener; /* The touch listener. */
private LayerClient mLayerClient; /* The layer client. */ private GeckoLayerClient mLayerClient; /* The layer client. */
/* The new color for the checkerboard. */ /* The new color for the checkerboard. */
private int mCheckerboardColor; private int mCheckerboardColor;
private boolean mCheckerboardShouldShowChecks; private boolean mCheckerboardShouldShowChecks;
...@@ -111,11 +111,11 @@ public class LayerController { ...@@ -111,11 +111,11 @@ public class LayerController {
mForceRedraw = true; mForceRedraw = true;
} }
public LayerClient getLayerClient() { public GeckoLayerClient getLayerClient() {
return mLayerClient; return mLayerClient;
} }
public void setLayerClient(LayerClient layerClient) { public void setLayerClient(GeckoLayerClient layerClient) {
mLayerClient = layerClient; mLayerClient = layerClient;
layerClient.setLayerController(this); layerClient.setLayerController(this);
} }
......
...@@ -88,7 +88,7 @@ abstract class Axis { ...@@ -88,7 +88,7 @@ abstract class Axis {
private float mTouchPos; /* Position of the most recent touch event on the current drag. */ private float mTouchPos; /* Position of the most recent touch event on the current drag. */
private float mLastTouchPos; /* Position of the touch event before touchPos. */ private float mLastTouchPos; /* Position of the touch event before touchPos. */
private float mVelocity; /* Velocity in this direction; pixels per animation frame. */ private float mVelocity; /* Velocity in this direction; pixels per animation frame. */
public boolean mScrollingDisabled; /* Whether movement on this axis is locked. */ private boolean mScrollingDisabled; /* Whether movement on this axis is locked. */
private boolean mDisableSnap; /* Whether overscroll snapping is disabled. */ private boolean mDisableSnap; /* Whether overscroll snapping is disabled. */
private float mDisplacement; private float mDisplacement;
...@@ -147,7 +147,7 @@ abstract class Axis { ...@@ -147,7 +147,7 @@ abstract class Axis {
} }
private Overscroll getOverscroll() { private Overscroll getOverscroll() {
boolean minus = (getOrigin() < 0.0f); boolean minus = getOrigin() < 0.0f;
boolean plus = (getViewportEnd() > getPageLength()); boolean plus = (getViewportEnd() > getPageLength());
if (minus && plus) { if (minus && plus) {
return Overscroll.BOTH; return Overscroll.BOTH;
...@@ -164,10 +164,14 @@ abstract class Axis { ...@@ -164,10 +164,14 @@ abstract class Axis {
// overscrolled on this axis, returns 0. // overscrolled on this axis, returns 0.
private float getExcess() { private float getExcess() {
switch (getOverscroll()) { switch (getOverscroll()) {
case MINUS: return -getOrigin(); case MINUS:
case PLUS: return getViewportEnd() - getPageLength(); return -getOrigin();
case BOTH: return getViewportEnd() - getPageLength() - getOrigin(); case PLUS:
default: return 0.0f; return getViewportEnd() - getPageLength();
case BOTH:
return getViewportEnd() - getPageLength() - getOrigin();
default:
return 0.0f;
} }
} }
...@@ -176,8 +180,7 @@ abstract class Axis { ...@@ -176,8 +180,7 @@ abstract class Axis {
* possible and this axis has not been scroll locked while panning. Otherwise, returns false. * possible and this axis has not been scroll locked while panning. Otherwise, returns false.
*/ */
private boolean scrollable() { private boolean scrollable() {
return getViewportLength() <= getPageLength() - MIN_SCROLLABLE_DISTANCE && return getViewportLength() <= getPageLength() - MIN_SCROLLABLE_DISTANCE && !mScrollingDisabled;
!mScrollingDisabled;
} }
/* /*
......
...@@ -38,22 +38,23 @@ ...@@ -38,22 +38,23 @@
package org.mozilla.gecko.ui; package org.mozilla.gecko.ui;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.FloatMath;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import org.json.JSONObject; import org.json.JSONObject;
import org.json.JSONException;
import org.libreoffice.LOKitShell; import org.libreoffice.LOKitShell;
import org.libreoffice.LibreOfficeMainActivity; import org.libreoffice.LibreOfficeMainActivity;
import org.mozilla.gecko.GeckoEventListener;
import org.mozilla.gecko.gfx.FloatSize; import org.mozilla.gecko.gfx.FloatSize;
import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.gfx.LayerController;
import org.mozilla.gecko.gfx.PointUtils; import org.mozilla.gecko.gfx.PointUtils;
import org.mozilla.gecko.gfx.ViewportMetrics; import org.mozilla.gecko.gfx.ViewportMetrics;
import org.mozilla.gecko.util.FloatUtils; import org.mozilla.gecko.util.FloatUtils;
import org.mozilla.gecko.GeckoEventListener;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.FloatMath;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
...@@ -65,29 +66,19 @@ import java.util.TimerTask; ...@@ -65,29 +66,19 @@ import java.util.TimerTask;
*/ */
public class PanZoomController public class PanZoomController
extends GestureDetector.SimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener
implements SimpleScaleGestureDetector.SimpleScaleGestureListener, GeckoEventListener implements SimpleScaleGestureDetector.SimpleScaleGestureListener, GeckoEventListener {
{ // The distance the user has to pan before we recognize it as such (e.g. to avoid 1-pixel pans
// between the touch-down and touch-up of a click). In units of density-independent pixels.
public static final float PAN_THRESHOLD = 1 / 16f * LOKitShell.getDpi();
private static final String LOGTAG = "GeckoPanZoomController"; private static final String LOGTAG = "GeckoPanZoomController";
private static String MESSAGE_ZOOM_RECT = "Browser:ZoomToRect";
private static String MESSAGE_ZOOM_PAGE = "Browser:ZoomToPageWidth";
// Animation stops if the velocity is below this value when overscrolled or panning. // Animation stops if the velocity is below this value when overscrolled or panning.
private static final float STOPPED_THRESHOLD = 4.0f; private static final float STOPPED_THRESHOLD = 4.0f;
// Animation stops is the velocity is below this threshold when flinging. // Animation stops is the velocity is below this threshold when flinging.
private static final float FLING_STOPPED_THRESHOLD = 0.1f; private static final float FLING_STOPPED_THRESHOLD = 0.1f;
// The distance the user has to pan before we recognize it as such (e.g. to avoid 1-pixel pans
// between the touch-down and touch-up of a click). In units of density-independent pixels.
public static final float PAN_THRESHOLD = 1/16f * LOKitShell.getDpi();
// Angle from axis within which we stay axis-locked // Angle from axis within which we stay axis-locked
private static final double AXIS_LOCK_ANGLE = Math.PI / 6.0; // 30 degrees private static final double AXIS_LOCK_ANGLE = Math.PI / 6.0; // 30 degrees
// The maximum amount we allow you to zoom into a page // The maximum amount we allow you to zoom into a page
private static final float MAX_ZOOM = 4.0f; private static final float MAX_ZOOM = 4.0f;
/* 16 precomputed frames of the _ease-out_ animation from the CSS Transitions specification. */ /* 16 precomputed frames of the _ease-out_ animation from the CSS Transitions specification. */
private static final float[] EASE_OUT_ANIMATION_FRAMES = { private static final float[] EASE_OUT_ANIMATION_FRAMES = {
0.00000f, /* 0 */ 0.00000f, /* 0 */
...@@ -107,27 +98,13 @@ public class PanZoomController ...@@ -107,27 +98,13 @@ public class PanZoomController
0.97401f, /* 14 */ 0.97401f, /* 14 */
0.99309f, /* 15 */ 0.99309f, /* 15 */
}; };
private static String MESSAGE_ZOOM_RECT = "Browser:ZoomToRect";
private enum PanZoomState { private static String MESSAGE_ZOOM_PAGE = "Browser:ZoomToPageWidth";
NOTHING, /* no touch-start events received */
FLING, /* all touches removed, but we're still scrolling page */
TOUCHING, /* one touch-start event received */
PANNING_LOCKED, /* touch-start followed by move (i.e. panning with axis lock) */
PANNING, /* panning without axis lock */
PANNING_HOLD, /* in panning, but not moving.
* similar to TOUCHING but after starting a pan */
PANNING_HOLD_LOCKED, /* like PANNING_HOLD, but axis lock still in effect */
PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
ANIMATED_ZOOM /* animated zoom to a new rect */
}
private final LayerController mController; private final LayerController mController;
private final SubdocumentScrollHelper mSubscroller; private final SubdocumentScrollHelper mSubscroller;
private final Axis mX; private final Axis mX;
private final Axis mY; private final Axis mY;
private Thread mMainThread; private Thread mMainThread;
/* The timer that handles flings or bounces. */ /* The timer that handles flings or bounces. */
private Timer mAnimationTimer; private Timer mAnimationTimer;
/* The runnable being scheduled by the animation timer. */ /* The runnable being scheduled by the animation timer. */
...@@ -146,31 +123,19 @@ public class PanZoomController ...@@ -146,31 +123,19 @@ public class PanZoomController
mY = new AxisY(mSubscroller); mY = new AxisY(mSubscroller);
mMainThread = LibreOfficeMainActivity.mAppContext.getMainLooper().getThread(); mMainThread = LibreOfficeMainActivity.mAppContext.getMainLooper().getThread();
checkMainThread();
mState = PanZoomState.NOTHING; mState = PanZoomState.NOTHING;
//GeckoAppShell.registerGeckoEventListener(MESSAGE_ZOOM_RECT, this);
//GeckoAppShell.registerGeckoEventListener(MESSAGE_ZOOM_PAGE, this);
}
// for debugging bug 713011; it can be taken out once that is resolved.
private void checkMainThread() {
if (mMainThread != Thread.currentThread()) {
// log with full stack trace
Log.e(LOGTAG, "Uh-oh, we're running on the wrong thread!", new Exception());
}
} }
public void handleMessage(String event, JSONObject message) { public void handleMessage(String event, JSONObject message) {
Log.i(LOGTAG, "Got message: " + event); Log.i(LOGTAG, "Got message: " + event);
try { try {
if (MESSAGE_ZOOM_RECT.equals(event)) { if (MESSAGE_ZOOM_RECT.equals(event)) {
float x = (float)message.getDouble("x"); float x = (float) message.getDouble("x");
float y = (float)message.getDouble("y"); float y = (float) message.getDouble("y");
final RectF zoomRect = new RectF(x, y, final RectF zoomRect = new RectF(x, y,
x + (float)message.getDouble("w"), x + (float) message.getDouble("w"),
y + (float)message.getDouble("h")); y + (float) message.getDouble("h"));
mController.post(new Runnable() { mController.post(new Runnable() {
public void run() { public void run() {
animatedZoomTo(zoomRect); animatedZoomTo(zoomRect);
...@@ -185,9 +150,9 @@ public class PanZoomController ...@@ -185,9 +150,9 @@ public class PanZoomController
float newHeight = viewableRect.height() * pageSize.width / viewableRect.width(); float newHeight = viewableRect.height() * pageSize.width / viewableRect.width();
float dh = viewableRect.height() - newHeight; // increase in the height float dh = viewableRect.height() - newHeight; // increase in the height
final RectF r = new RectF(0.0f, final RectF r = new RectF(0.0f,
y + dh/2, y + dh / 2,
pageSize.width, pageSize.width,
y + dh/2 + newHeight); y + dh / 2 + newHeight);
mController.post(new Runnable() { mController.post(new Runnable() {
public void run() { public void run() {
animatedZoomTo(r); animatedZoomTo(r);
...@@ -201,17 +166,23 @@ public class PanZoomController ...@@ -201,17 +166,23 @@ public class PanZoomController
public boolean onTouchEvent(MotionEvent event) { public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) { switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: return onTouchStart(event); case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE: return onTouchMove(event); return onTouchStart(event);
case MotionEvent.ACTION_UP: return onTouchEnd(event); case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_CANCEL: return onTouchCancel(event); return onTouchMove(event);
default: return false; case MotionEvent.ACTION_UP:
return onTouchEnd(event);
case MotionEvent.ACTION_CANCEL:
return onTouchCancel(event);
default:
return false;
} }
} }
/** This function must be called from the UI thread. */ /**
* This function must be called from the UI thread.
*/
public void abortAnimation() { public void abortAnimation() {
checkMainThread();
// this happens when gecko changes the viewport on us or if the device is rotated. // this happens when gecko changes the viewport on us or if the device is rotated.
// if that's the case, abort any animation in progress and re-zoom so that the page // if that's the case, abort any animation in progress and re-zoom so that the page
// snaps to edges. for other cases (where the user's finger(s) are down) don't do // snaps to edges. for other cases (where the user's finger(s) are down) don't do
...@@ -235,11 +206,13 @@ public class PanZoomController ...@@ -235,11 +206,13 @@ public class PanZoomController
} }
} }
/** This must be called on the UI thread. */ /**
* This must be called on the UI thread.
*/
public void pageSizeUpdated() { public void pageSizeUpdated() {
if (mState == PanZoomState.NOTHING) { if (mState == PanZoomState.NOTHING) {
ViewportMetrics validated = getValidViewportMetrics(); ViewportMetrics validated = getValidViewportMetrics();
if (! mController.getViewportMetrics().fuzzyEquals(validated)) { if (!mController.getViewportMetrics().fuzzyEquals(validated)) {
// page size changed such that we are now in overscroll. snap to the // page size changed such that we are now in overscroll. snap to the
// the nearest valid viewport // the nearest valid viewport
mController.setViewportMetrics(validated); mController.setViewportMetrics(validated);
...@@ -248,10 +221,6 @@ public class PanZoomController ...@@ -248,10 +221,6 @@ public class PanZoomController
} }
} }
/*
* Panning/scrolling
*/
private boolean onTouchStart(MotionEvent event) { private boolean onTouchStart(MotionEvent event) {
Log.d(LOGTAG, "onTouchStart in state " + mState); Log.d(LOGTAG, "onTouchStart in state " + mState);
// user is taking control of movement, so stop // user is taking control of movement, so stop
...@@ -279,6 +248,9 @@ public class PanZoomController ...@@ -279,6 +248,9 @@ public class PanZoomController
return false; return false;
} }
/*
* Panning/scrolling
*/
private boolean onTouchMove(MotionEvent event) { private boolean onTouchMove(MotionEvent event) {
Log.d(LOGTAG, "onTouchMove in state " + mState); Log.d(LOGTAG, "onTouchMove in state " + mState);
...@@ -295,8 +267,6 @@ public class PanZoomController ...@@ -295,8 +267,6 @@ public class PanZoomController
} }
cancelTouch(); cancelTouch();
startPanning(event.getX(0), event.getY(0), event.getEventTime()); startPanning(event.getX(0), event.getY(0), event.getEventTime());
//GeckoApp.mAppContext.hidePlugins(false /* don't hide layers */);
//GeckoApp.mAutoCompletePopup.hide();
track(event); track(event);
return true; return true;
...@@ -404,7 +374,7 @@ public class PanZoomController ...@@ -404,7 +374,7 @@ public class PanZoomController
} }
private void track(float x, float y, long time) { private void track(float x, float y, long time) {
float timeDelta = (float)(time - mLastEventTime); float timeDelta = (float) (time - mLastEventTime);
if (FloatUtils.fuzzyEquals(timeDelta, 0)) { if (FloatUtils.fuzzyEquals(timeDelta, 0)) {
// probably a duplicate event, ignore it. using a zero timeDelta will mess // probably a duplicate event, ignore it. using a zero timeDelta will mess
// up our velocity // up our velocity
...@@ -490,8 +460,10 @@ public class PanZoomController ...@@ -490,8 +460,10 @@ public class PanZoomController
mAnimationRunnable = runnable; mAnimationRunnable = runnable;
mAnimationTimer.scheduleAtFixedRate(new TimerTask() { mAnimationTimer.scheduleAtFixedRate(new TimerTask() {
@Override @Override
public void run() { mController.post(runnable); } public void run() {
}, 0, 1000L/60L); mController.post(runnable);
}
}, 0, 1000L / 60L);
} }
/* Stops the fling or bounce animation. */ /* Stops the fling or bounce animation. */
...@@ -504,14 +476,12 @@ public class PanZoomController ...@@ -504,14 +476,12 @@ public class PanZoomController
mAnimationRunnable.terminate(); mAnimationRunnable.terminate();
mAnimationRunnable = null; mAnimationRunnable = null;
} }
//GeckoApp.mAppContext.showPlugins();
} }
private float getVelocity() { private float getVelocity() {
float xvel = mX.getRealVelocity(); float xVelocity = mX.getRealVelocity();
float yvel = mY.getRealVelocity(); float yVelocity = mY.getRealVelocity();
return FloatMath.sqrt(xvel * xvel + yvel * yvel); return FloatMath.sqrt(xVelocity * xVelocity + yVelocity * yVelocity);
} }
private boolean stopped() { private boolean stopped() {
...@@ -526,155 +496,18 @@ public class PanZoomController ...@@ -526,155 +496,18 @@ public class PanZoomController
mX.displace(); mX.displace();
mY.displace(); mY.displace();
PointF displacement = getDisplacement(); PointF displacement = getDisplacement();
if (! mSubscroller.scrollBy(displacement)) { if (!mSubscroller.scrollBy(displacement)) {
synchronized (mController) { synchronized (mController) {
mController.scrollBy(displacement); mController.scrollBy(displacement);
} }
} }
} }
private abstract class AnimationRunnable implements Runnable {
private boolean mAnimationTerminated;
/* This should always run on the UI thread */
public final void run() {
/*
* Since the animation timer queues this runnable on the UI thread, it
* is possible that even when the animation timer is cancelled, there
* are multiple instances of this queued, so we need to have another
* mechanism to abort. This is done by using the mAnimationTerminated flag.
*/
if (mAnimationTerminated) {
return;
}
animateFrame();
}
protected abstract void animateFrame();
/* This should always run on the UI thread */
protected final void terminate() {
mAnimationTerminated = true;
}
}
/* The callback that performs the bounce animation. */
private class BounceRunnable extends AnimationRunnable {
/* The current frame of the bounce-back animation */
private int mBounceFrame;
/*
* The viewport metrics that represent the start and end of the bounce-back animation,
* respectively.
*/
private ViewportMetrics mBounceStartMetrics;
private ViewportMetrics mBounceEndMetrics;
BounceRunnable(ViewportMetrics startMetrics, ViewportMetrics endMetrics) {
mBounceStartMetrics = startMetrics;
mBounceEndMetrics = endMetrics;
}
protected void animateFrame() {
/*
* The pan/zoom controller might have signaled to us that it wants to abort the
* animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
* out.
*/
if (mState != PanZoomState.FLING) {
finishAnimation();
return;
}
/* Perform the next frame of the bounce-back animation. */
if (mBounceFrame < EASE_OUT_ANIMATION_FRAMES.length) {
advanceBounce();
return;
}
/* Finally, if there's nothing else to do, complete the animation and go to sleep. */
finishBounce();
finishAnimation();
mState = PanZoomState.NOTHING;
}
/* Performs one frame of a bounce animation. */
private void advanceBounce() {
synchronized (mController) {
float t = EASE_OUT_ANIMATION_FRAMES[mBounceFrame];
ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
mController.setViewportMetrics(newMetrics);
mController.notifyLayerClientOfGeometryChange();
mBounceFrame++;
}
}
/* Concludes a bounce animation and snaps the viewport into place. */
private void finishBounce() {
synchronized (mController) {
mController.setViewportMetrics(mBounceEndMetrics);
mController.notifyLayerClientOfGeometryChange();
mBounceFrame = -1;
}
}
}
// The callback that performs the fling animation.
private class FlingRunnable extends AnimationRunnable {
protected void animateFrame() {
/*
* The pan/zoom controller might have signaled to us that it wants to abort the
* animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
* out.
*/
if (mState != PanZoomState.FLING) {
finishAnimation();
return;
}
/* Advance flings, if necessary. */
boolean flingingX = mX.advanceFling();
boolean flingingY = mY.advanceFling();
boolean overscrolled = (mX.overscrolled() || mY.overscrolled());
/* If we're still flinging in any direction, update the origin. */
if (flingingX || flingingY) {
updatePosition();
/*
* Check to see if we're still flinging with an appreciable velocity. The threshold is
* higher in the case of overscroll, so we bounce back eagerly when overscrolling but
* coast smoothly to a stop when not. In other words, require a greater velocity to
* maintain the fling once we enter overscroll.
*/
float threshold = (overscrolled && !mSubscroller.scrolling() ? STOPPED_THRESHOLD : FLING_STOPPED_THRESHOLD);
if (getVelocity() >= threshold) {
// we're still flinging
return;
}
mX.stopFling();
mY.stopFling();
}
/* Perform a bounce-back animation if overscrolled. */
if (overscrolled) {
bounce();
} else {
finishAnimation();
mState = PanZoomState.NOTHING;
}
}
}
private void finishAnimation() { private void finishAnimation() {
checkMainThread();
Log.d(LOGTAG, "Finishing animation at " + mController.getViewportMetrics()); Log.d(LOGTAG, "Finishing animation at " + mController.getViewportMetrics());
stopAnimationTimer(); stopAnimationTimer();
// Force a viewport synchronisation // Force a viewport synchronisation
//GeckoApp.mAppContext.showPlugins();
mController.setForceRedraw(); mController.setForceRedraw();
mController.notifyLayerClientOfGeometryChange(); mController.notifyLayerClientOfGeometryChange();
} }
...@@ -726,26 +559,6 @@ public class PanZoomController ...@@ -726,26 +559,6 @@ public class PanZoomController
return viewportMetrics; return viewportMetrics;
} }
private class AxisX extends Axis {
AxisX(SubdocumentScrollHelper subscroller) { super(subscroller); }
@Override
public float getOrigin() { return mController.getOrigin().x; }
@Override
protected float getViewportLength() { return mController.getViewportSize().width; }
@Override
protected float getPageLength() { return mController.getPageSize().width; }
}
private class AxisY extends Axis {
AxisY(SubdocumentScrollHelper subscroller) { super(subscroller); }
@Override
public float getOrigin() { return mController.getOrigin().y; }
@Override
protected float getViewportLength() { return mController.getViewportSize().height; }
@Override
protected float getPageLength() { return mController.getPageSize().height; }
}
/* /*
* Zooming * Zooming
*/ */
...@@ -758,8 +571,7 @@ public class PanZoomController ...@@ -758,8 +571,7 @@ public class PanZoomController
mState = PanZoomState.PINCHING; mState = PanZoomState.PINCHING;
mLastZoomFocus = new PointF(detector.getFocusX(), detector.getFocusY()); mLastZoomFocus = new PointF(detector.getFocusX(), detector.getFocusY());
//GeckoApp.mAppContext.hidePlugins(false /* don't hide layers, only views */);
//GeckoApp.mAutoCompletePopup.hide();
cancelTouch(); cancelTouch();
return true; return true;
...@@ -785,10 +597,11 @@ public class PanZoomController ...@@ -785,10 +597,11 @@ public class PanZoomController
* factor toward 1.0. * factor toward 1.0.
*/ */
float resistance = Math.min(mX.getEdgeResistance(), mY.getEdgeResistance()); float resistance = Math.min(mX.getEdgeResistance(), mY.getEdgeResistance());
if (spanRatio > 1.0f) if (spanRatio > 1.0f) {
spanRatio = 1.0f + (spanRatio - 1.0f) * resistance; spanRatio = 1.0f + (spanRatio - 1.0f) * resistance;
else } else {
spanRatio = 1.0f - (1.0f - spanRatio) * resistance; spanRatio = 1.0f - (1.0f - spanRatio) * resistance;
}
synchronized (mController) { synchronized (mController) {
float newZoomFactor = mController.getZoomFactor() * spanRatio; float newZoomFactor = mController.getZoomFactor() * spanRatio;
...@@ -797,7 +610,7 @@ public class PanZoomController ...@@ -797,7 +610,7 @@ public class PanZoomController
// such that it asymptotically reaches MAX_ZOOM + 1.0 // such that it asymptotically reaches MAX_ZOOM + 1.0
// but never exceeds that // but never exceeds that
float excessZoom = newZoomFactor - MAX_ZOOM; float excessZoom = newZoomFactor - MAX_ZOOM;
excessZoom = 1.0f - (float)Math.exp(-excessZoom); excessZoom = 1.0f - (float) Math.exp(-excessZoom);
newZoomFactor = MAX_ZOOM + excessZoom; newZoomFactor = MAX_ZOOM + excessZoom;
} }
...@@ -832,55 +645,29 @@ public class PanZoomController ...@@ -832,55 +645,29 @@ public class PanZoomController
return (mState != PanZoomState.PINCHING && mState != PanZoomState.ANIMATED_ZOOM); return (mState != PanZoomState.PINCHING && mState != PanZoomState.ANIMATED_ZOOM);
} }
private void sendPointToGecko(String event, MotionEvent motionEvent) {
String json;
try {
PointF point = new PointF(motionEvent.getX(), motionEvent.getY());
point = mController.convertViewPointToLayerPoint(point);
if (point == null) {
return;
}
json = PointUtils.toJSON(point).toString();
} catch (Exception e) {
Log.e(LOGTAG, "Unable to convert point to JSON for " + event, e);
return;
}
//GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(event, json));
}
@Override @Override
public void onLongPress(MotionEvent motionEvent) { public void onLongPress(MotionEvent motionEvent) {
sendPointToGecko("Gesture:LongPress", motionEvent);
} }
@Override @Override
public boolean onDown(MotionEvent motionEvent) { public boolean onDown(MotionEvent motionEvent) {
sendPointToGecko("Gesture:ShowPress", motionEvent);
return false; return false;
} }
@Override @Override
public boolean onSingleTapConfirmed(MotionEvent motionEvent) { public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
//GeckoApp.mAutoCompletePopup.hide();
sendPointToGecko("Gesture:SingleTap", motionEvent);
return true; return true;
} }
@Override @Override
public boolean onDoubleTap(MotionEvent motionEvent) { public boolean onDoubleTap(MotionEvent motionEvent) {
sendPointToGecko("Gesture:DoubleTap", motionEvent);
return true; return true;
} }
public void cancelTouch() { public void cancelTouch() {
//GeckoEvent e = GeckoEvent.createBroadcastEvent("Gesture:CancelTouch", "");
//GeckoAppShell.sendEventToGecko(e);
} }
private boolean animatedZoomTo(RectF zoomToRect) { private boolean animatedZoomTo(RectF zoomToRect) {
//GeckoApp.mAutoCompletePopup.hide();
mState = PanZoomState.ANIMATED_ZOOM; mState = PanZoomState.ANIMATED_ZOOM;
final float startZoom = mController.getZoomFactor(); final float startZoom = mController.getZoomFactor();
...@@ -918,4 +705,193 @@ public class PanZoomController ...@@ -918,4 +705,193 @@ public class PanZoomController
bounce(finalMetrics); bounce(finalMetrics);
return true; return true;
} }
private enum PanZoomState {
NOTHING, /* no touch-start events received */
FLING, /* all touches removed, but we're still scrolling page */
TOUCHING, /* one touch-start event received */
PANNING_LOCKED, /* touch-start followed by move (i.e. panning with axis lock) */
PANNING, /* panning without axis lock */
PANNING_HOLD, /* in panning, but not moving.
* similar to TOUCHING but after starting a pan */
PANNING_HOLD_LOCKED, /* like PANNING_HOLD, but axis lock still in effect */
PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
ANIMATED_ZOOM /* animated zoom to a new rect */
}
private abstract class AnimationRunnable implements Runnable {
private boolean mAnimationTerminated;
/* This should always run on the UI thread */
public final void run() {
/*
* Since the animation timer queues this runnable on the UI thread, it
* is possible that even when the animation timer is cancelled, there
* are multiple instances of this queued, so we need to have another
* mechanism to abort. This is done by using the mAnimationTerminated flag.
*/
if (mAnimationTerminated) {
return;
}
animateFrame();
}
protected abstract void animateFrame();
/* This should always run on the UI thread */
protected final void terminate() {
mAnimationTerminated = true;
}
}
/* The callback that performs the bounce animation. */
private class BounceRunnable extends AnimationRunnable {
/* The current frame of the bounce-back animation */
private int mBounceFrame;
/*
* The viewport metrics that represent the start and end of the bounce-back animation,
* respectively.
*/
private ViewportMetrics mBounceStartMetrics;
private ViewportMetrics mBounceEndMetrics;
BounceRunnable(ViewportMetrics startMetrics, ViewportMetrics endMetrics) {
mBounceStartMetrics = startMetrics;
mBounceEndMetrics = endMetrics;
}
protected void animateFrame() {
/*
* The pan/zoom controller might have signaled to us that it wants to abort the
* animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
* out.
*/
if (mState != PanZoomState.FLING) {
finishAnimation();
return;
}
/* Perform the next frame of the bounce-back animation. */
if (mBounceFrame < EASE_OUT_ANIMATION_FRAMES.length) {
advanceBounce();
return;
}
/* Finally, if there's nothing else to do, complete the animation and go to sleep. */
finishBounce();
finishAnimation();
mState = PanZoomState.NOTHING;
}
/* Performs one frame of a bounce animation. */
private void advanceBounce() {
synchronized (mController) {
float t = EASE_OUT_ANIMATION_FRAMES[mBounceFrame];
ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
mController.setViewportMetrics(newMetrics);
mController.notifyLayerClientOfGeometryChange();
mBounceFrame++;
}
}
/* Concludes a bounce animation and snaps the viewport into place. */
private void finishBounce() {
synchronized (mController) {
mController.setViewportMetrics(mBounceEndMetrics);
mController.notifyLayerClientOfGeometryChange();
mBounceFrame = -1;
}
}
}
// The callback that performs the fling animation.
private class FlingRunnable extends AnimationRunnable {
protected void animateFrame() {
/*
* The pan/zoom controller might have signaled to us that it wants to abort the
* animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
* out.
*/
if (mState != PanZoomState.FLING) {
finishAnimation();
return;
}
/* Advance flings, if necessary. */
boolean flingingX = mX.advanceFling();
boolean flingingY = mY.advanceFling();
boolean overscrolled = (mX.overscrolled() || mY.overscrolled());
/* If we're still flinging in any direction, update the origin. */
if (flingingX || flingingY) {
updatePosition();
/*
* Check to see if we're still flinging with an appreciable velocity. The threshold is
* higher in the case of overscroll, so we bounce back eagerly when overscrolling but
* coast smoothly to a stop when not. In other words, require a greater velocity to
* maintain the fling once we enter overscroll.
*/
float threshold = (overscrolled && !mSubscroller.scrolling() ? STOPPED_THRESHOLD : FLING_STOPPED_THRESHOLD);
if (getVelocity() >= threshold) {
// we're still flinging
return;
}
mX.stopFling();
mY.stopFling();
}
/* Perform a bounce-back animation if overscrolled. */
if (overscrolled) {
bounce();
} else {
finishAnimation();
mState = PanZoomState.NOTHING;
}
}
}
private class AxisX extends Axis {
AxisX(SubdocumentScrollHelper subscroller) {
super(subscroller);
}
@Override
public float getOrigin() {
return mController.getOrigin().x;
}
@Override
protected float getViewportLength() {
return mController.getViewportSize().width;
}
@Override
protected float getPageLength() {
return mController.getPageSize().width;
}
}
private class AxisY extends Axis {
AxisY(SubdocumentScrollHelper subscroller) {
super(subscroller);
}
@Override
public float getOrigin() {
return mController.getOrigin().y;
}
@Override
protected float getViewportLength() {
return mController.getViewportSize().height;
}
@Override
protected float getPageLength() {
return mController.getPageSize().height;
}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment