Bug 1416316 - 3. Update NPZC usages; r?rbarker
Remove NPZC references from LayerView, and add an NPZC getter in
LayerSession. Use the new APIs in GeckoView to forward events to NPZC.
MozReview-Commit-ID: 1UcJkpW0XuM
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
@@ -15,29 +15,36 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Build;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.InputDevice;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
public class GeckoView extends LayerView {
private static final String LOGTAG = "GeckoView";
private static final boolean DEBUG = false;
- private final Display mDisplay = new Display();
+ private static AccessibilityManager sAccessibilityManager;
+
+ protected final Display mDisplay = new Display();
protected GeckoSession mSession;
private boolean mStateSaved;
protected SurfaceView mSurfaceView;
private InputConnectionListener mInputConnectionListener;
private boolean mIsResettingFocus;
@@ -145,18 +152,16 @@ public class GeckoView extends LayerView
}
public GeckoView(final Context context, final AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
- initializeView();
-
setFocusable(true);
setFocusableInTouchMode(true);
// We are adding descendants to this LayerView, but we don't want the
// descendants to affect the way LayerView retains its focus.
setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
// This will stop PropertyAnimator from creating a drawing cache (i.e. a
@@ -200,16 +205,25 @@ public class GeckoView extends LayerView
if (Build.VERSION.SDK_INT >= 16) {
GeckoView.this.postInvalidateOnAnimation();
} else {
GeckoView.this.postInvalidateDelayed(10);
}
}
});
+ final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ final TypedValue outValue = new TypedValue();
+ if (context.getTheme().resolveAttribute(android.R.attr.listPreferredItemHeight,
+ outValue, true)) {
+ session.getPanZoomController().setScrollFactor(outValue.getDimension(metrics));
+ } else {
+ session.getPanZoomController().setScrollFactor(0.075f * metrics.densityDpi);
+ }
+
mSession = session;
}
public GeckoSession getSession() {
return mSession;
}
public EventDispatcher getEventDispatcher() {
@@ -237,17 +251,16 @@ public class GeckoView extends LayerView
attachCompositor(mSession);
super.onAttachedToWindow();
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
- super.destroy();
if (mStateSaved) {
// If we saved state earlier, we don't want to close the window.
return;
}
if (mSession != null && mSession.isOpen()) {
mSession.closeWindow();
@@ -406,9 +419,53 @@ public class GeckoView extends LayerView
@Override
public void dispatchDraw(final Canvas canvas) {
super.dispatchDraw(canvas);
if (mSession != null) {
mSession.getOverscrollEdgeEffect().draw(canvas);
}
}
+
+ @Override
+ public boolean onTouchEvent(final MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ requestFocus();
+ }
+
+ // NOTE: Treat mouse events as "touch" rather than as "mouse", so mouse can be
+ // used to pan/zoom. Call onMouseEvent() instead for behavior similar to desktop.
+ return mSession != null &&
+ mSession.getPanZoomController().onTouchEvent(event);
+ }
+
+ protected static boolean isAccessibilityEnabled(final Context context) {
+ if (sAccessibilityManager == null) {
+ sAccessibilityManager = (AccessibilityManager)
+ context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ }
+ return sAccessibilityManager.isEnabled() &&
+ sAccessibilityManager.isTouchExplorationEnabled();
+ }
+
+ @Override
+ public boolean onHoverEvent(final MotionEvent event) {
+ // If we get a touchscreen hover event, and accessibility is not enabled, don't
+ // send it to Gecko.
+ if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN &&
+ !isAccessibilityEnabled(getContext())) {
+ return false;
+ }
+
+ return mSession != null &&
+ mSession.getPanZoomController().onMotionEvent(event);
+ }
+
+ @Override
+ public boolean onGenericMotionEvent(final MotionEvent event) {
+ if (AndroidGamepadManager.handleMotionEvent(event)) {
+ return true;
+ }
+
+ return mSession != null &&
+ mSession.getPanZoomController().onMotionEvent(event);
+ }
}
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerSession.java
@@ -80,17 +80,17 @@ public class LayerSession {
LayerSession.this.onCompositorDetached();
disposeNative();
}
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
@Override protected native void disposeNative();
@WrapForJNI(calledFrom = "any", dispatchTo = "gecko")
- public native void attachToJava(NativePanZoomController npzc);
+ public native void attachNPZC(NativePanZoomController npzc);
@WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
public native void onBoundsChanged(int left, int top, int width, int height);
// Gecko thread creates compositor; blocks UI thread.
@WrapForJNI(calledFrom = "ui", dispatchTo = "proxy")
public native void createCompositor(int width, int height, Object surface);
@@ -162,16 +162,17 @@ public class LayerSession {
LayerSession.this.onSelectionCaretDrag(dragging);
}
}
protected final Compositor mCompositor = new Compositor();
// All fields are accessed on UI thread only.
private GeckoDisplay mDisplay;
+ private NativePanZoomController mNPZC;
private OverscrollEdgeEffect mOverscroll;
private DynamicToolbarAnimator mToolbar;
private boolean mAttachedCompositor;
private boolean mCalledCreateCompositor;
private boolean mCompositorReady;
private Surface mSurface;
@@ -189,16 +190,33 @@ public class LayerSession {
/* package */ GeckoDisplay getDisplay() {
if (DEBUG) {
ThreadUtils.assertOnUiThread();
}
return mDisplay;
}
/**
+ * Get the NativePanZoomController instance for this session.
+ *
+ * @return NativePanZoomController instance.
+ */
+ public NativePanZoomController getPanZoomController() {
+ ThreadUtils.assertOnUiThread();
+
+ if (mNPZC == null) {
+ mNPZC = new NativePanZoomController(this);
+ if (mAttachedCompositor) {
+ mCompositor.attachNPZC(mNPZC);
+ }
+ }
+ return mNPZC;
+ }
+
+ /**
* Get the OverscrollEdgeEffect instance for this session.
*
* @return OverscrollEdgeEffect instance.
*/
public OverscrollEdgeEffect getOverscrollEdgeEffect() {
ThreadUtils.assertOnUiThread();
if (mOverscroll == null) {
@@ -315,16 +333,20 @@ public class LayerSession {
/* package */ void onCompositorAttached() {
if (DEBUG) {
ThreadUtils.assertOnUiThread();
}
mAttachedCompositor = true;
+ if (mNPZC != null) {
+ mCompositor.attachNPZC(mNPZC);
+ }
+
if (mSurface != null) {
// If we have a valid surface, create the compositor now that we're attached.
// Leave mSurface alone because we'll need it later for onCompositorReady.
onSurfaceChanged(mSurface, mWidth, mHeight);
}
}
/* package */ void onCompositorDetached() {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
@@ -30,31 +30,27 @@ import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.view.InputDevice;
-import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import java.util.ArrayList;
import java.util.List;
/**
* A view rendered by the layer compositor.
*/
public class LayerView extends FrameLayout {
private static final String LOGTAG = "GeckoLayerView";
- private static AccessibilityManager sAccessibilityManager;
-
- private PanZoomController mPanZoomController;
private FullScreenState mFullScreenState;
private int mDefaultClearColor = Color.WHITE;
/* package */ GetPixelsResult mGetPixelsResult;
private final List<DrawListener> mDrawListeners;
private void postCompositorMessage(final int message) {
ThreadUtils.postToUiThread(new Runnable() {
@@ -85,110 +81,21 @@ public class LayerView extends FrameLayo
mDrawListeners = new ArrayList<DrawListener>();
}
public LayerView(Context context) {
this(context, null);
}
- public void initializeView() {
- mPanZoomController = PanZoomController.Factory.create(this);
- }
-
- /**
- * MotionEventHelper dragAsync() robocop tests can instruct
- * PanZoomController not to generate longpress events.
- * This call comes in from a thread other than the UI thread.
- * So dispatch to UI thread first to prevent assert in nsWindow.
- */
- public void setIsLongpressEnabled(final boolean isLongpressEnabled) {
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- mPanZoomController.setIsLongpressEnabled(isLongpressEnabled);
- }
- });
- }
-
private static Point getEventRadius(MotionEvent event) {
return new Point((int)event.getToolMajor() / 2,
(int)event.getToolMinor() / 2);
}
- protected void destroy() {
- if (mPanZoomController != null) {
- mPanZoomController.destroy();
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- requestFocus();
- }
-
- if (!isCompositorReady()) {
- // If gecko isn't loaded yet, don't try sending events to the
- // native code because it's just going to crash
- return true;
- }
- if (mPanZoomController != null && mPanZoomController.onTouchEvent(event)) {
- return true;
- }
- return false;
- }
-
- private boolean isAccessibilityEnabled() {
- if (sAccessibilityManager == null) {
- sAccessibilityManager = (AccessibilityManager)
- getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- }
- return sAccessibilityManager.isEnabled() &&
- sAccessibilityManager.isTouchExplorationEnabled();
- }
-
- @Override
- public boolean onHoverEvent(MotionEvent event) {
- // If we get a touchscreen hover event, and accessibility is not enabled,
- // don't send it to gecko.
- if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN &&
- !isAccessibilityEnabled()) {
- return false;
- }
-
- if (!isCompositorReady()) {
- // If gecko isn't loaded yet, don't try sending events to the
- // native code because it's just going to crash
- return true;
- } else if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) {
- return true;
- }
-
- return false;
- }
-
- @Override
- public boolean onGenericMotionEvent(MotionEvent event) {
- if (AndroidGamepadManager.handleMotionEvent(event)) {
- return true;
- }
- if (!isCompositorReady()) {
- // If gecko isn't loaded yet, don't try sending events to the
- // native code because it's just going to crash
- return true;
- }
- if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) {
- return true;
- }
- return false;
- }
-
- public PanZoomController getPanZoomController() { return mPanZoomController; }
-
public void setSurfaceBackgroundColor(int newColor) {
}
public interface GetPixelsResult {
public void onPixelsResult(int width, int height, IntBuffer pixels);
}
@RobocopTarget
@@ -217,26 +124,16 @@ public class LayerView extends FrameLayo
mGetPixelsResult = null;
}
}
protected void attachCompositor(final LayerSession session) {
mSession = session;
mCompositor = session.mCompositor;
mCompositor.layerView = this;
-
- final NativePanZoomController npzc = (NativePanZoomController) mPanZoomController;
-
- if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
- mCompositor.attachToJava(npzc);
- } else {
- GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
- mCompositor, "attachToJava",
- NativePanZoomController.class, npzc);
- }
}
@WrapForJNI(calledFrom = "ui")
private Object getCompositor() {
return isCompositorReady() ? mCompositor : null;
}
@RobocopTarget
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/MotionEventHelper.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/MotionEventHelper.java
@@ -1,45 +1,45 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.tests;
import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.GeckoView;
import org.mozilla.gecko.PrefsHelper;
import org.mozilla.gecko.R;
-import org.mozilla.gecko.gfx.LayerView;
import com.robotium.solo.Solo;
import android.app.Instrumentation;
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
class MotionEventHelper {
private static final String LOGTAG = "RobocopMotionEventHelper";
private static final long DRAG_EVENTS_PER_SECOND = 20; // 20 move events per second when doing a drag
private final Instrumentation mInstrumentation;
private final int mSurfaceOffsetX;
private final int mSurfaceOffsetY;
- private final LayerView layerView;
+ private final GeckoView layerView;
private boolean mApzEnabled;
private float mTouchStartTolerance;
private final int mDpi;
public MotionEventHelper(Instrumentation inst, Solo solo,
int surfaceOffsetX, int surfaceOffsetY) {
mInstrumentation = inst;
mSurfaceOffsetX = surfaceOffsetX;
mSurfaceOffsetY = surfaceOffsetY;
- layerView = (LayerView) solo.getCurrentActivity().findViewById(R.id.layer_view);
+ layerView = (GeckoView) solo.getCurrentActivity().findViewById(R.id.layer_view);
mApzEnabled = false;
mTouchStartTolerance = 0.0f;
mDpi = GeckoAppShell.getDpi();
Log.i(LOGTAG, "Initialized using offset (" + mSurfaceOffsetX + "," + mSurfaceOffsetY + ")");
PrefsHelper.getPref("layers.async-pan-zoom.enabled", new PrefsHelper.PrefHandlerBase() {
@Override public void prefValue(String pref, boolean value) {
mApzEnabled = value;
}
@@ -118,17 +118,17 @@ class MotionEventHelper {
return downTime;
}
public Thread dragAsync(final float startX, final float startY, final float endX, final float endY, final long durationMillis) {
Thread t = new Thread() {
@Override
public void run() {
if (layerView != null) {
- layerView.setIsLongpressEnabled(false);
+ layerView.getSession().getPanZoomController().setIsLongpressEnabled(false);
}
int numEvents = (int)(durationMillis * DRAG_EVENTS_PER_SECOND / 1000);
float eventDx = (endX - startX) / numEvents;
float eventDy = (endY - startY) / numEvents;
long downTime = movePastTouchStartTolerance(startX, startY, endX, endY);
for (int i = 0; i < numEvents - 1; i++) {
downTime = move(downTime, startX + (eventDx * i), startY + (eventDy * i));
@@ -145,17 +145,17 @@ class MotionEventHelper {
} catch (InterruptedException ie) {
ie.printStackTrace();
}
// do the last one using endX/endY directly to avoid rounding errors
downTime = move(downTime, endX, endY);
downTime = up(downTime, endX, endY);
if (layerView != null) {
- layerView.setIsLongpressEnabled(true);
+ layerView.getSession().getPanZoomController().setIsLongpressEnabled(true);
}
}
};
t.start();
return t;
}
public void dragSync(float startX, float startY, float endX, float endY, long durationMillis) {
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -892,17 +892,17 @@ private:
}
return child.forget();
}
/**
* Compositor methods
*/
public:
- void AttachToJava(jni::Object::Param aNPZC)
+ void AttachNPZC(jni::Object::Param aNPZC)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mWindow) {
return; // Already shut down.
}
MOZ_ASSERT(aNPZC);
MOZ_ASSERT(!mWindow->mNPZCSupport);
@@ -911,31 +911,16 @@ public:
jni::GetGeckoThreadEnv(),
NativePanZoomController::Ref::From(aNPZC));
mWindow->mNPZCSupport.Attach(npzc, mWindow, npzc);
DispatchToUiThread("LayerViewSupport::AttachNPZC",
[npzc = NativePanZoomController::GlobalRef(npzc)] {
npzc->SetAttached(true);
});
-
- if (RefPtr<nsThread> uiThread = GetAndroidUiThread()) {
- LayerSession::Compositor::GlobalRef compositor(mCompositor);
- uiThread->Dispatch(NS_NewRunnableFunction(
- "LayerViewSupport::AttachToJava",
- [compositor] {
- compositor->OnCompositorAttached();
- }));
- }
-
- // Set the first-paint flag so that we (re-)link any new Java objects
- // to Gecko, co-ordinate viewports, etc.
- if (RefPtr<CompositorBridgeChild> bridge = mWindow->GetCompositorBridgeChild()) {
- bridge->SendForceIsFirstPaint();
- }
}
void OnBoundsChanged(int32_t aLeft, int32_t aTop,
int32_t aWidth, int32_t aHeight)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mWindow) {
return; // Already shut down.
@@ -1372,23 +1357,33 @@ nsWindow::GeckoViewSupport::Transfer(con
if (window.mLayerViewSupport) {
window.mLayerViewSupport.Detach();
}
auto compositor = LayerSession::Compositor::LocalRef(
inst.Env(), LayerSession::Compositor::Ref::From(aCompositor));
window.mLayerViewSupport.Attach(compositor, &window, compositor);
- if (window.mAndroidView) {
- window.mAndroidView->mEventDispatcher->Attach(
- java::EventDispatcher::Ref::From(aDispatcher), mDOMWindow);
- window.mAndroidView->mSettings = java::GeckoBundle::Ref::From(aSettings);
- }
+ MOZ_ASSERT(window.mAndroidView);
+ window.mAndroidView->mEventDispatcher->Attach(
+ java::EventDispatcher::Ref::From(aDispatcher), mDOMWindow);
+ window.mAndroidView->mSettings = java::GeckoBundle::Ref::From(aSettings);
inst->OnTransfer(aDispatcher);
+
+ DispatchToUiThread(
+ "GeckoViewSupport::Transfer",
+ [compositor = LayerSession::Compositor::GlobalRef(compositor)] {
+ compositor->OnCompositorAttached();
+ });
+
+ // Set the first-paint flag so that we refresh viewports, etc.
+ if (RefPtr<CompositorBridgeChild> bridge = window.GetCompositorBridgeChild()) {
+ bridge->SendForceIsFirstPaint();
+ }
}
void
nsWindow::GeckoViewSupport::Attach(const GeckoSession::Window::LocalRef& inst,
jni::Object::Param aView)
{
// Associate our previous GeckoEditable with the new GeckoView.
MOZ_ASSERT(window.mEditable);