Bug 1416310 - 3. Merge GeckoLayerClient into other classes; r=rbarker

Move the GeckoLayerClient JNI callbacks to LayerSession.Compositor. Move
the rest of the viewport code to LayerView. Finally, move the input
event synthesis code to NativePanZoomController.

MozReview-Commit-ID: 1FEAM43KcwL

--HG--
extra : rebase_source : af2ded170a79f13afbb1f690ae466e77c7145ff6
This commit is contained in:
Jim Chen 2017-11-20 17:17:02 -05:00
parent 75a417ee33
commit 85057e2e9a
4 changed files with 302 additions and 40 deletions

View File

@ -48,13 +48,13 @@ public class DynamicToolbarAnimator {
private final Set<PinReason> mPinFlags = Collections.synchronizedSet(EnumSet.noneOf(PinReason.class));
private final GeckoLayerClient mTarget;
private final LayerView mTarget;
private LayerSession.Compositor mCompositor;
private final List<MetricsListener> mListeners;
private ToolbarChromeProxy mToolbarChromeProxy;
private int mMaxToolbarHeight;
public DynamicToolbarAnimator(GeckoLayerClient aTarget) {
public DynamicToolbarAnimator(final LayerView aTarget) {
mTarget = aTarget;
mListeners = new ArrayList<MetricsListener>();
}
@ -154,8 +154,8 @@ public class DynamicToolbarAnimator {
/* package-private */ IntSize getViewportSize() {
ThreadUtils.assertOnUiThread();
int viewWidth = mTarget.getView().getWidth();
int viewHeight = mTarget.getView().getHeight();
int viewWidth = mTarget.getWidth();
int viewHeight = mTarget.getHeight();
if ((mToolbarChromeProxy != null) && mToolbarChromeProxy.isToolbarChromeVisible()) {
viewHeight -= mMaxToolbarHeight;
}
@ -163,8 +163,7 @@ public class DynamicToolbarAnimator {
}
public PointF getVisibleEndOfLayerView() {
return new PointF(mTarget.getView().getWidth(),
mTarget.getView().getHeight());
return new PointF(mTarget.getWidth(), mTarget.getHeight());
}
/* package-private */ void updateCompositor() {

View File

@ -31,6 +31,8 @@ public class LayerSession {
protected class Compositor extends JNIObject {
public LayerView layerView;
private volatile boolean mContentDocumentIsDisplayed;
public boolean isReady() {
return LayerSession.this.isCompositorReady();
}
@ -55,8 +57,7 @@ public class LayerSession {
@Override protected native void disposeNative();
@WrapForJNI(calledFrom = "any", dispatchTo = "gecko")
public native void attachToJava(GeckoLayerClient layerClient,
NativePanZoomController npzc);
public native void attachToJava(NativePanZoomController npzc);
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
public native void onBoundsChanged(int left, int top, int width, int height);
@ -127,6 +128,28 @@ public class LayerSession {
@WrapForJNI(calledFrom = "ui", dispatchTo = "current")
public native void sendToolbarPixelsToCompositor(final int width, final int height,
final int[] pixels);
@WrapForJNI(calledFrom = "gecko")
private void contentDocumentChanged() {
mContentDocumentIsDisplayed = false;
}
@WrapForJNI(calledFrom = "gecko")
private boolean isContentDocumentDisplayed() {
return mContentDocumentIsDisplayed;
}
// The compositor invokes this function just before compositing a frame where the
// document is different from the document composited on the last frame. In these
// cases, the viewport information we have in Java is no longer valid and needs to
// be replaced with the new viewport information provided.
@WrapForJNI(calledFrom = "ui")
public void updateRootFrameMetrics(float scrollX, float scrollY, float zoom) {
mContentDocumentIsDisplayed = true;
if (layerView != null) {
layerView.onMetricsChanged(scrollX, scrollY, zoom);
}
}
}
protected final Compositor mCompositor = new Compositor();

View File

@ -49,12 +49,12 @@ public class LayerView extends FrameLayout {
private static AccessibilityManager sAccessibilityManager;
private GeckoLayerClient mLayerClient;
private PanZoomController mPanZoomController;
private DynamicToolbarAnimator mToolbarAnimator;
private FullScreenState mFullScreenState;
private Listener mListener;
// Accessed on UI thread.
private ImmutableViewportMetrics mViewportMetrics;
/* This should only be modified on the Java UI thread. */
private final Overscroll mOverscroll;
@ -117,13 +117,13 @@ public class LayerView extends FrameLayout {
case STATIC_TOOLBAR_READY:
// Hide toolbar and send TOOLBAR_HIDDEN message to compositor
mToolbarAnimator.onToggleChrome(false);
mListener.surfaceChanged();
adjustViewportSize();
postCompositorMessage(TOOLBAR_HIDDEN);
break;
case TOOLBAR_SHOW:
// Show toolbar.
mToolbarAnimator.onToggleChrome(true);
mListener.surfaceChanged();
adjustViewportSize();
postCompositorMessage(TOOLBAR_VISIBLE);
break;
case FIRST_PAINT:
@ -166,13 +166,14 @@ public class LayerView extends FrameLayout {
}
public void initializeView() {
mLayerClient = new GeckoLayerClient(this);
mToolbarAnimator = new DynamicToolbarAnimator(this);
mPanZoomController = PanZoomController.Factory.create(this);
if (mOverscroll != null) {
mLayerClient.setOverscrollHandler(mOverscroll);
mPanZoomController.setOverscrollHandler(mOverscroll);
}
mPanZoomController = mLayerClient.getPanZoomController();
mToolbarAnimator = mLayerClient.getDynamicToolbarAnimator();
mViewportMetrics = new ImmutableViewportMetrics()
.setViewportSize(getWidth(), getHeight());
}
/**
@ -195,9 +196,9 @@ public class LayerView extends FrameLayout {
(int)event.getToolMinor() / 2);
}
public void destroy() {
if (mLayerClient != null) {
mLayerClient.destroy();
protected void destroy() {
if (mPanZoomController != null) {
mPanZoomController.destroy();
}
}
@ -206,7 +207,7 @@ public class LayerView extends FrameLayout {
super.dispatchDraw(canvas);
// We must have a layer client to get valid viewport metrics
if (mLayerClient != null && mOverscroll != null) {
if (mOverscroll != null) {
mOverscroll.draw(canvas, getViewportMetrics());
}
}
@ -273,14 +274,11 @@ public class LayerView extends FrameLayout {
return false;
}
// Don't expose GeckoLayerClient to things outside this package; only expose it as an Object
GeckoLayerClient getLayerClient() { return mLayerClient; }
public PanZoomController getPanZoomController() { return mPanZoomController; }
public DynamicToolbarAnimator getDynamicToolbarAnimator() { return mToolbarAnimator; }
public ImmutableViewportMetrics getViewportMetrics() {
return mLayerClient.getViewportMetrics();
return mViewportMetrics;
}
public void setSurfaceBackgroundColor(int newColor) {
@ -317,28 +315,23 @@ public class LayerView extends FrameLayout {
}
}
public void setListener(Listener listener) {
mListener = listener;
}
Listener getListener() {
return mListener;
}
protected void attachCompositor(final LayerSession session) {
mCompositor = session.mCompositor;
mCompositor.layerView = this;
// Reset the content-document-is-displayed flag.
mCompositor.updateRootFrameMetrics(/* scroolX */ 0, /* scrollY */ 0,
/* zoom */ 1.0f);
mToolbarAnimator.notifyCompositorCreated(mCompositor);
final NativePanZoomController npzc = (NativePanZoomController) mPanZoomController;
if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
mCompositor.attachToJava(mLayerClient, npzc);
mCompositor.attachToJava(npzc);
} else {
GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
mCompositor, "attachToJava",
GeckoLayerClient.class, mLayerClient,
NativePanZoomController.class, npzc);
}
}
@ -348,18 +341,31 @@ public class LayerView extends FrameLayout {
return isCompositorReady() ? mCompositor : null;
}
/* package */ void onSizeChanged(int width, int height) {
if (mListener != null) {
mListener.surfaceChanged();
private void adjustViewportSize() {
final IntSize viewportSize = mToolbarAnimator.getViewportSize();
if (mViewportMetrics.viewportRectWidth == viewportSize.width &&
mViewportMetrics.viewportRectHeight == viewportSize.height) {
return;
}
mViewportMetrics = mViewportMetrics.setViewportSize(viewportSize.width,
viewportSize.height);
}
/* package */ void onSizeChanged(int width, int height) {
adjustViewportSize();
if (mOverscroll != null) {
mOverscroll.setSize(width, height);
}
}
public interface Listener {
void surfaceChanged();
/* package */ void onMetricsChanged(final float scrollX, final float scrollY,
final float zoom) {
mViewportMetrics = mViewportMetrics.setViewportOrigin(scrollX, scrollY)
.setZoomFactor(zoom);
mToolbarAnimator.onMetricsChanged(mViewportMetrics);
}
@RobocopTarget
@ -410,7 +416,7 @@ public class LayerView extends FrameLayout {
}
public float getZoomFactor() {
return getLayerClient().getViewportMetrics().zoomFactor;
return getViewportMetrics().zoomFactor;
}
public void setFullScreenState(FullScreenState state) {

View File

@ -13,13 +13,18 @@ import org.mozilla.gecko.util.ThreadUtils;
import org.json.JSONObject;
import android.graphics.PointF;
import android.os.SystemClock;
import android.util.Log;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.InputDevice;
import java.util.ArrayList;
class NativePanZoomController extends JNIObject implements PanZoomController {
private static final String LOGTAG = "GeckoNPZC";
private final float MAX_SCROLL;
private final LayerView mView;
@ -29,6 +34,8 @@ class NativePanZoomController extends JNIObject implements PanZoomController {
private float mPointerScrollFactor;
private long mLastDownTime;
private SynthesizedEventState mPointerState;
@WrapForJNI(calledFrom = "ui")
private native boolean handleMotionEvent(
int action, int actionIndex, long time, int metaState,
@ -256,4 +263,231 @@ class NativePanZoomController extends JNIObject implements PanZoomController {
private void onSelectionDragState(boolean state) {
mView.getDynamicToolbarAnimator().setPinned(state, PinReason.CARET_DRAG);
}
private static class PointerInfo {
// We reserve one pointer ID for the mouse, so that tests don't have
// to worry about tracking pointer IDs if they just want to test mouse
// event synthesization. If somebody tries to use this ID for a
// synthesized touch event we'll throw an exception.
public static final int RESERVED_MOUSE_POINTER_ID = 100000;
public int pointerId;
public int source;
public int screenX;
public int screenY;
public double pressure;
public int orientation;
public MotionEvent.PointerCoords getCoords() {
MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
coords.orientation = orientation;
coords.pressure = (float)pressure;
coords.x = screenX;
coords.y = screenY;
return coords;
}
}
private static class SynthesizedEventState {
public final ArrayList<PointerInfo> pointers;
public long downTime;
SynthesizedEventState() {
pointers = new ArrayList<PointerInfo>();
}
int getPointerIndex(int pointerId) {
for (int i = 0; i < pointers.size(); i++) {
if (pointers.get(i).pointerId == pointerId) {
return i;
}
}
return -1;
}
int addPointer(int pointerId, int source) {
PointerInfo info = new PointerInfo();
info.pointerId = pointerId;
info.source = source;
pointers.add(info);
return pointers.size() - 1;
}
int getPointerCount(int source) {
int count = 0;
for (int i = 0; i < pointers.size(); i++) {
if (pointers.get(i).source == source) {
count++;
}
}
return count;
}
MotionEvent.PointerProperties[] getPointerProperties(int source) {
MotionEvent.PointerProperties[] props =
new MotionEvent.PointerProperties[getPointerCount(source)];
int index = 0;
for (int i = 0; i < pointers.size(); i++) {
if (pointers.get(i).source == source) {
MotionEvent.PointerProperties p = new MotionEvent.PointerProperties();
p.id = pointers.get(i).pointerId;
switch (source) {
case InputDevice.SOURCE_TOUCHSCREEN:
p.toolType = MotionEvent.TOOL_TYPE_FINGER;
break;
case InputDevice.SOURCE_MOUSE:
p.toolType = MotionEvent.TOOL_TYPE_MOUSE;
break;
}
props[index++] = p;
}
}
return props;
}
MotionEvent.PointerCoords[] getPointerCoords(int source) {
MotionEvent.PointerCoords[] coords =
new MotionEvent.PointerCoords[getPointerCount(source)];
int index = 0;
for (int i = 0; i < pointers.size(); i++) {
if (pointers.get(i).source == source) {
coords[index++] = pointers.get(i).getCoords();
}
}
return coords;
}
}
private void synthesizeNativePointer(int source, int pointerId, int eventType,
int screenX, int screenY, double pressure,
int orientation)
{
final View view = mView;
final int[] origin = new int[2];
view.getLocationOnScreen(origin);
screenX -= origin[0];
screenY -= origin[1];
if (mPointerState == null) {
mPointerState = new SynthesizedEventState();
}
// Find the pointer if it already exists
int pointerIndex = mPointerState.getPointerIndex(pointerId);
// Event-specific handling
switch (eventType) {
case MotionEvent.ACTION_POINTER_UP:
if (pointerIndex < 0) {
Log.w(LOGTAG, "Pointer-up for invalid pointer");
return;
}
if (mPointerState.pointers.size() == 1) {
// Last pointer is going up
eventType = MotionEvent.ACTION_UP;
}
break;
case MotionEvent.ACTION_CANCEL:
if (pointerIndex < 0) {
Log.w(LOGTAG, "Pointer-cancel for invalid pointer");
return;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
if (pointerIndex < 0) {
// Adding a new pointer
pointerIndex = mPointerState.addPointer(pointerId, source);
if (pointerIndex == 0) {
// first pointer
eventType = MotionEvent.ACTION_DOWN;
mPointerState.downTime = SystemClock.uptimeMillis();
}
} else {
// We're moving an existing pointer
eventType = MotionEvent.ACTION_MOVE;
}
break;
case MotionEvent.ACTION_HOVER_MOVE:
if (pointerIndex < 0) {
// Mouse-move a pointer without it going "down". However
// in order to send the right MotionEvent without a lot of
// duplicated code, we add the pointer to mPointerState,
// and then remove it at the bottom of this function.
pointerIndex = mPointerState.addPointer(pointerId, source);
} else {
// We're moving an existing mouse pointer that went down.
eventType = MotionEvent.ACTION_MOVE;
}
break;
}
// Update the pointer with the new info
PointerInfo info = mPointerState.pointers.get(pointerIndex);
info.screenX = screenX;
info.screenY = screenY;
info.pressure = pressure;
info.orientation = orientation;
// Dispatch the event
int action = 0;
if (eventType == MotionEvent.ACTION_POINTER_DOWN ||
eventType == MotionEvent.ACTION_POINTER_UP) {
// for pointer-down and pointer-up events we need to add the
// index of the relevant pointer.
action = (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
action &= MotionEvent.ACTION_POINTER_INDEX_MASK;
}
action |= (eventType & MotionEvent.ACTION_MASK);
boolean isButtonDown = (source == InputDevice.SOURCE_MOUSE) &&
(eventType == MotionEvent.ACTION_DOWN ||
eventType == MotionEvent.ACTION_MOVE);
final MotionEvent event = MotionEvent.obtain(
/*downTime*/ mPointerState.downTime,
/*eventTime*/ SystemClock.uptimeMillis(),
/*action*/ action,
/*pointerCount*/ mPointerState.getPointerCount(source),
/*pointerProperties*/ mPointerState.getPointerProperties(source),
/*pointerCoords*/ mPointerState.getPointerCoords(source),
/*metaState*/ 0,
/*buttonState*/ (isButtonDown ? MotionEvent.BUTTON_PRIMARY : 0),
/*xPrecision*/ 0,
/*yPrecision*/ 0,
/*deviceId*/ 0,
/*edgeFlags*/ 0,
/*source*/ source,
/*flags*/ 0);
view.post(new Runnable() {
@Override
public void run() {
view.dispatchTouchEvent(event);
}
});
// Forget about removed pointers
if (eventType == MotionEvent.ACTION_POINTER_UP ||
eventType == MotionEvent.ACTION_UP ||
eventType == MotionEvent.ACTION_CANCEL ||
eventType == MotionEvent.ACTION_HOVER_MOVE)
{
mPointerState.pointers.remove(pointerIndex);
}
}
@WrapForJNI(calledFrom = "gecko")
private void synthesizeNativeTouchPoint(int pointerId, int eventType, int screenX,
int screenY, double pressure, int orientation)
{
if (pointerId == PointerInfo.RESERVED_MOUSE_POINTER_ID) {
throw new IllegalArgumentException("Pointer ID reserved for mouse");
}
synthesizeNativePointer(InputDevice.SOURCE_TOUCHSCREEN, pointerId,
eventType, screenX, screenY, pressure, orientation);
}
@WrapForJNI(calledFrom = "gecko")
private void synthesizeNativeMouseEvent(int eventType, int screenX, int screenY) {
synthesizeNativePointer(InputDevice.SOURCE_MOUSE,
PointerInfo.RESERVED_MOUSE_POINTER_ID,
eventType, screenX, screenY, 0, 0);
}
}