Bug 1557411 - Add GeckoView API to expose how touch events are handled. r=geckoview-reviewers,botond,agi

Differential Revision: https://phabricator.services.mozilla.com/D46601

--HG--
extra : moz-landing-system : lando
This commit is contained in:
James Willcox 2019-09-23 21:00:44 +00:00
parent d8ca8b5902
commit ec2812da2f
5 changed files with 121 additions and 46 deletions

View File

@ -1054,6 +1054,8 @@ package org.mozilla.geckoview {
method @NonNull public DynamicToolbarAnimator getDynamicToolbarAnimator();
method @NonNull public PanZoomController getPanZoomController();
method @AnyThread @Nullable public GeckoSession getSession();
method public int onGenericMotionEventForResult(@NonNull MotionEvent);
method public int onTouchEventForResult(@NonNull MotionEvent);
method @UiThread @Nullable public GeckoSession releaseSession();
method @UiThread public void setSession(@NonNull GeckoSession);
method public void setVerticalClipping(int);
@ -1155,9 +1157,9 @@ package org.mozilla.geckoview {
@UiThread public class PanZoomController {
ctor protected PanZoomController(GeckoSession);
method public float getScrollFactor();
method public boolean onMotionEvent(@NonNull MotionEvent);
method public boolean onMouseEvent(@NonNull MotionEvent);
method public boolean onTouchEvent(@NonNull MotionEvent);
method public int onMotionEvent(@NonNull MotionEvent);
method public int onMouseEvent(@NonNull MotionEvent);
method public int onTouchEvent(@NonNull MotionEvent);
method @UiThread public void scrollBy(@NonNull ScreenLength, @NonNull ScreenLength);
method @UiThread public void scrollBy(@NonNull ScreenLength, @NonNull ScreenLength, int);
method @UiThread public void scrollTo(@NonNull ScreenLength, @NonNull ScreenLength);
@ -1166,6 +1168,9 @@ package org.mozilla.geckoview {
method @UiThread public void scrollToTop();
method public void setIsLongpressEnabled(boolean);
method public void setScrollFactor(float);
field public static final int INPUT_RESULT_HANDLED = 1;
field public static final int INPUT_RESULT_HANDLED_CONTENT = 2;
field public static final int INPUT_RESULT_UNHANDLED = 0;
field public static final int SCROLL_BEHAVIOR_AUTO = 1;
field public static final int SCROLL_BEHAVIOR_SMOOTH = 0;
}

View File

@ -654,28 +654,58 @@ public class GeckoView extends FrameLayout {
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(final MotionEvent event) {
return onTouchEventForResult(event) != PanZoomController.INPUT_RESULT_UNHANDLED;
}
/**
* Dispatches a {@link MotionEvent} to the {@link PanZoomController}. This is the same as
* {@link #onTouchEvent(MotionEvent)}, but instead returns a {@link PanZoomController.InputResult}
* indicating how the event was handled.
*
* @param event A {@link MotionEvent}
* @return One of the {@link PanZoomController#INPUT_RESULT_UNHANDLED INPUT_RESULT_*}) indicating how the event was handled.
*/
public @PanZoomController.InputResult int onTouchEventForResult(final @NonNull MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
requestFocus();
}
if (mSession == null) {
return PanZoomController.INPUT_RESULT_UNHANDLED;
}
// 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);
return mSession.getPanZoomController().onTouchEvent(event);
}
@Override
public boolean onGenericMotionEvent(final MotionEvent event) {
return onGenericMotionEventForResult(event) != PanZoomController.INPUT_RESULT_HANDLED;
}
/**
* Dispatches a {@link MotionEvent} to the {@link PanZoomController}. This is the same as
* {@link #onGenericMotionEvent(MotionEvent)} (MotionEvent)}, but instead returns
* a {@link PanZoomController.InputResult} indicating how the event was handled.
*
* @param event A {@link MotionEvent}
* @return One of the {@link PanZoomController#INPUT_RESULT_UNHANDLED INPUT_RESULT_*}) indicating how the event was handled.
*/
public @PanZoomController.InputResult int onGenericMotionEventForResult(final @NonNull MotionEvent event) {
if (AndroidGamepadManager.handleMotionEvent(event)) {
return true;
return PanZoomController.INPUT_RESULT_HANDLED;
}
if (mSession == null) {
return false;
return PanZoomController.INPUT_RESULT_UNHANDLED;
}
return mSession.getAccessibility().onMotionEvent(event) ||
mSession.getPanZoomController().onMotionEvent(event);
if (mSession.getAccessibility().onMotionEvent(event)) {
return PanZoomController.INPUT_RESULT_HANDLED;
}
return mSession.getPanZoomController().onMotionEvent(event);
}
@Override

View File

@ -58,6 +58,30 @@ public class PanZoomController {
*/
public static final int SCROLL_BEHAVIOR_AUTO = 1;
@Retention(RetentionPolicy.SOURCE)
@IntDef({INPUT_RESULT_UNHANDLED, INPUT_RESULT_HANDLED, INPUT_RESULT_HANDLED_CONTENT})
/* package */ @interface InputResult {}
/**
* Specifies that an input event was not handled by the PanZoomController.
*/
@WrapForJNI
public static final int INPUT_RESULT_UNHANDLED = 0;
/**
* Specifies that an input event was handled by the PanZoomController, but likely
* not by any touch event listeners in Web content.
*/
@WrapForJNI
public static final int INPUT_RESULT_HANDLED = 1;
/**
* Specifies that an input event was handled by the PanZoomController and passed on
* to touch event listeners in Web content.
*/
@WrapForJNI
public static final int INPUT_RESULT_HANDLED_CONTENT = 2;
private SynthesizedEventState mPointerState;
private ArrayList<Pair<Integer, MotionEvent>> mQueuedEvents;
@ -72,19 +96,19 @@ public class PanZoomController {
}
@WrapForJNI(calledFrom = "ui")
public native boolean handleMotionEvent(
private native @InputResult int handleMotionEvent(
int action, int actionIndex, long time, int metaState, float screenX, float screenY,
int pointerId[], float x[], float y[], float orientation[], float pressure[],
float toolMajor[], float toolMinor[]);
@WrapForJNI(calledFrom = "ui")
private native boolean handleScrollEvent(
private native @InputResult int handleScrollEvent(
long time, int metaState,
float x, float y,
float hScroll, float vScroll);
@WrapForJNI(calledFrom = "ui")
private native boolean handleMouseEvent(
private native @InputResult int handleMouseEvent(
int action, long time, int metaState,
float x, float y, int buttons);
@ -124,10 +148,10 @@ public class PanZoomController {
/* package */ final NativeProvider mNative = new NativeProvider();
private boolean handleMotionEvent(final MotionEvent event) {
private @InputResult int handleMotionEvent(final MotionEvent event) {
if (!mAttached) {
mQueuedEvents.add(new Pair<>(EVENT_SOURCE_MOTION, event));
return false;
return INPUT_RESULT_UNHANDLED;
}
final int action = event.getActionMasked();
@ -136,7 +160,7 @@ public class PanZoomController {
if (action == MotionEvent.ACTION_DOWN) {
mLastDownTime = event.getDownTime();
} else if (mLastDownTime != event.getDownTime()) {
return false;
return INPUT_RESULT_UNHANDLED;
}
final int[] pointerId = new int[count];
@ -179,16 +203,16 @@ public class PanZoomController {
orientation, pressure, toolMajor, toolMinor);
}
private boolean handleScrollEvent(final MotionEvent event) {
private @InputResult int handleScrollEvent(final MotionEvent event) {
if (!mAttached) {
mQueuedEvents.add(new Pair<>(EVENT_SOURCE_SCROLL, event));
return false;
return INPUT_RESULT_UNHANDLED;
}
final int count = event.getPointerCount();
if (count <= 0) {
return false;
return INPUT_RESULT_UNHANDLED;
}
final MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
@ -208,16 +232,16 @@ public class PanZoomController {
hScroll, vScroll);
}
private boolean handleMouseEvent(final MotionEvent event) {
private @InputResult int handleMouseEvent(final MotionEvent event) {
if (!mAttached) {
mQueuedEvents.add(new Pair<>(EVENT_SOURCE_MOUSE, event));
return false;
return INPUT_RESULT_UNHANDLED;
}
final int count = event.getPointerCount();
if (count <= 0) {
return false;
return INPUT_RESULT_UNHANDLED;
}
final MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
@ -288,9 +312,9 @@ public class PanZoomController {
* display surface.
*
* @param event MotionEvent to process.
* @return True if the event was handled.
* @return One of the {@link PanZoomController#INPUT_RESULT_UNHANDLED INPUT_RESULT_*}) constants indicating how the event was handled.
*/
public boolean onTouchEvent(final @NonNull MotionEvent event) {
public @InputResult int onTouchEvent(final @NonNull MotionEvent event) {
ThreadUtils.assertOnUiThread();
if (!sTreatMouseAsTouch && event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
@ -305,9 +329,9 @@ public class PanZoomController {
* display surface.
*
* @param event MotionEvent to process.
* @return True if the event was handled.
* @return One of the {@link PanZoomController#INPUT_RESULT_UNHANDLED INPUT_RESULT_*}) constants indicating how the event was handled.
*/
public boolean onMouseEvent(final @NonNull MotionEvent event) {
public @InputResult int onMouseEvent(final @NonNull MotionEvent event) {
ThreadUtils.assertOnUiThread();
if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
@ -327,9 +351,9 @@ public class PanZoomController {
* display surface.
*
* @param event MotionEvent to process.
* @return True if the event was handled.
* @return One of the {@link PanZoomController#INPUT_RESULT_UNHANDLED INPUT_RESULT_*}) indicating how the event was handled.
*/
public boolean onMotionEvent(final @NonNull MotionEvent event) {
public @InputResult int onMotionEvent(final @NonNull MotionEvent event) {
ThreadUtils.assertOnUiThread();
final int action = event.getActionMasked();
@ -339,7 +363,7 @@ public class PanZoomController {
} else if ((InputDevice.getDevice(event.getDeviceId()) != null) &&
(InputDevice.getDevice(event.getDeviceId()).getSources() &
InputDevice.SOURCE_TOUCHPAD) == InputDevice.SOURCE_TOUCHPAD) {
return false;
return INPUT_RESULT_UNHANDLED;
}
return handleScrollEvent(event);
} else if ((action == MotionEvent.ACTION_HOVER_MOVE) ||
@ -347,7 +371,7 @@ public class PanZoomController {
(action == MotionEvent.ACTION_HOVER_EXIT)) {
return handleMouseEvent(event);
} else {
return false;
return INPUT_RESULT_UNHANDLED;
}
}

View File

@ -38,6 +38,9 @@ exclude: true
([bug 1578947]({{bugzilla}}1578947))
- Added `setEnhancedTrackingProtectionLevel` to [`ContentBlocking.Settings`][71.14].
([bug 1580854]({{bugzilla}}1580854))
- ⚠️ Added [`GeckoView.onTouchEventForResult`][71.15] and modified
[`PanZoomController.onTouchEvent`][71.16] to return how the touch event was handled. This
allows apps to know if an event is handled by touch event listeners in web content. The methods in `PanZoomController` now return `int` instead of `boolean`.
[71.1]: {{javadoc_uri}}/RuntimeTelemetry.Delegate.html#onBooleanScalar-org.mozilla.geckoview.RuntimeTelemetry.Metric-
[71.2]: {{javadoc_uri}}/RuntimeTelemetry.Delegate.html#onLongScalar-org.mozilla.geckoview.RuntimeTelemetry.Metric-
@ -52,6 +55,8 @@ exclude: true
[71.11]: https://developer.mozilla.org/en-US/docs/Web/API/Clients/openWindow
[71.12]: {{javadoc_uri}}/GeckoRuntimeSettings.Builder.html#aboutConfigEnabled-boolean-
[71.13]: {{javadoc_uri}}/GeckoSession.ContentDelegate.html#onFirstContentfulPaint-org.mozilla.geckoview.GeckoSession-
[71.15]: {{javadoc_uri}}/GeckoView.html#onTouchEventForResult-android.view.MotionEvent-
[71.16]: {{javadoc_uri}}/PanZoomController.html#onTouchEvent-android.view.MotionEvent-
## v70
- Added API for session context assignment
@ -374,4 +379,4 @@ exclude: true
[65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
[65.25]: {{javadoc_uri}}/GeckoResult.html
[api-version]: bc4c4b661a4dd390c2a10c1057a2ce2aa09e3483
[api-version]: 68710f52723909eea09a02b94b618a527cc9dfc8

View File

@ -125,6 +125,13 @@ static bool sFailedToCreateGLContext = false;
static const double SWIPE_MAX_PINCH_DELTA_INCHES = 0.4;
static const double SWIPE_MIN_DISTANCE_INCHES = 0.6;
static const int32_t INPUT_RESULT_UNHANDLED =
java::PanZoomController::INPUT_RESULT_UNHANDLED;
static const int32_t INPUT_RESULT_HANDLED =
java::PanZoomController::INPUT_RESULT_HANDLED;
static const int32_t INPUT_RESULT_HANDLED_CONTENT =
java::PanZoomController::INPUT_RESULT_HANDLED_CONTENT;
template <typename Lambda, bool IsStatic, typename InstanceType, class Impl>
class nsWindow::WindowEvent : public Runnable {
bool IsStaleCall() {
@ -485,8 +492,8 @@ class nsWindow::NPZCSupport final
}
}
bool HandleScrollEvent(int64_t aTime, int32_t aMetaState, float aX, float aY,
float aHScroll, float aVScroll) {
int32_t HandleScrollEvent(int64_t aTime, int32_t aMetaState, float aX,
float aY, float aHScroll, float aVScroll) {
MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
RefPtr<IAPZCTreeManager> controller;
@ -496,7 +503,7 @@ class nsWindow::NPZCSupport final
}
if (!controller) {
return false;
return INPUT_RESULT_UNHANDLED;
}
ScreenPoint origin = ScreenPoint(aX, aY);
@ -520,7 +527,7 @@ class nsWindow::NPZCSupport final
APZEventResult result = controller->InputBridge()->ReceiveInputEvent(input);
if (result.mStatus == nsEventStatus_eConsumeNoDefault) {
return true;
return INPUT_RESULT_HANDLED;
}
PostInputEvent([input, result](nsWindow* window) {
@ -528,7 +535,8 @@ class nsWindow::NPZCSupport final
window->ProcessUntransformedAPZEvent(&wheelEvent, result);
});
return true;
return result.mHitRegionWithApzAwareListeners ? INPUT_RESULT_HANDLED_CONTENT
: INPUT_RESULT_HANDLED;
}
private:
@ -575,8 +583,8 @@ class nsWindow::NPZCSupport final
}
public:
bool HandleMouseEvent(int32_t aAction, int64_t aTime, int32_t aMetaState,
float aX, float aY, int buttons) {
int32_t HandleMouseEvent(int32_t aAction, int64_t aTime, int32_t aMetaState,
float aX, float aY, int buttons) {
MOZ_ASSERT(AndroidBridge::IsJavaUiThread());
RefPtr<IAPZCTreeManager> controller;
@ -586,7 +594,7 @@ class nsWindow::NPZCSupport final
}
if (!controller) {
return false;
return INPUT_RESULT_UNHANDLED;
}
MouseInput::MouseType mouseType = MouseInput::MOUSE_NONE;
@ -619,7 +627,7 @@ class nsWindow::NPZCSupport final
}
if (mouseType == MouseInput::MOUSE_NONE) {
return false;
return INPUT_RESULT_UNHANDLED;
}
ScreenPoint origin = ScreenPoint(aX, aY);
@ -632,7 +640,7 @@ class nsWindow::NPZCSupport final
APZEventResult result = controller->InputBridge()->ReceiveInputEvent(input);
if (result.mStatus == nsEventStatus_eConsumeNoDefault) {
return true;
return INPUT_RESULT_HANDLED;
}
PostInputEvent([input, result](nsWindow* window) {
@ -640,10 +648,11 @@ class nsWindow::NPZCSupport final
window->ProcessUntransformedAPZEvent(&mouseEvent, result);
});
return true;
return result.mHitRegionWithApzAwareListeners ? INPUT_RESULT_HANDLED_CONTENT
: INPUT_RESULT_HANDLED;
}
bool HandleMotionEvent(
int32_t HandleMotionEvent(
const PanZoomController::NativeProvider::LocalRef& aInstance,
int32_t aAction, int32_t aActionIndex, int64_t aTime, int32_t aMetaState,
float aScreenX, float aScreenY, jni::IntArray::Param aPointerId,
@ -659,7 +668,7 @@ class nsWindow::NPZCSupport final
}
if (!controller) {
return false;
return INPUT_RESULT_UNHANDLED;
}
nsTArray<int32_t> pointerId(aPointerId->GetElements());
@ -688,7 +697,7 @@ class nsWindow::NPZCSupport final
type = MultiTouchInput::MULTITOUCH_CANCEL;
break;
default:
return false;
return INPUT_RESULT_UNHANDLED;
}
MultiTouchInput input(type, aTime, GetEventTimeStamp(aTime), 0);
@ -747,7 +756,7 @@ class nsWindow::NPZCSupport final
APZEventResult result = controller->InputBridge()->ReceiveInputEvent(input);
if (result.mStatus == nsEventStatus_eConsumeNoDefault) {
return true;
return INPUT_RESULT_HANDLED;
}
// Dispatch APZ input event on Gecko thread.
@ -756,7 +765,9 @@ class nsWindow::NPZCSupport final
window->ProcessUntransformedAPZEvent(&touchEvent, result);
window->DispatchHitTest(touchEvent);
});
return true;
return result.mHitRegionWithApzAwareListeners ? INPUT_RESULT_HANDLED_CONTENT
: INPUT_RESULT_HANDLED;
}
};