Chromium Code Reviews

Unified Diff: content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java

Issue 120513005: [Android] Perform eager gesture recognition on MotionEvents (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Undo some changes Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Index: content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
index be4e5f912ee2553c410dd10690b4449cace477c3..8553bc463b479ede23832524ab225f1aadff1020 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
@@ -22,6 +22,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.ResultReceiver;
+import android.os.SystemClock;
import android.provider.Browser;
import android.provider.Settings;
import android.text.Editable;
@@ -197,11 +198,6 @@ public class ContentViewCore
void onPinchGestureEnd();
/**
- * Called when the fling gesture is sent.
- */
- void onFlingStartGesture(int vx, int vy);
-
- /**
* Called when the fling cancel gesture is sent.
*/
void onFlingCancelGesture();
@@ -209,7 +205,7 @@ public class ContentViewCore
/**
* Called when a fling event was not handled by the renderer.
*/
- void onUnhandledFlingStartEvent();
+ void onUnhandledFlingStartEvent(int velocityX, int velocityY);
/**
* Called to indicate that a scroll update gesture had been consumed by the page.
@@ -372,7 +368,6 @@ public class ContentViewCore
private ContentViewGestureHandler mContentViewGestureHandler;
private GestureStateListener mGestureStateListener;
- private ZoomManager mZoomManager;
private ZoomControlsDelegate mZoomControlsDelegate;
private PopupZoomer mPopupZoomer;
@@ -460,12 +455,20 @@ public class ContentViewCore
// vsync.
private boolean mRequestedVSyncForInput = false;
+ // Used for tracking UMA ActionAfterDoubleTap to tell user's immediate
+ // action after a double tap.
+ private long mLastDoubleTapTimeMs;
+
+ // On single tap this will store the x, y coordinates of the touch.
+ private int mSingleTapX;
+ private int mSingleTapY;
+
private ViewAndroid mViewAndroid;
private SmartClipDataListener mSmartClipDataListener = null;
/** ActionAfterDoubleTap defined in tools/metrics/histograms/histograms.xml. */
- public static class UMAActionAfterDoubleTap {
+ private static class UMAActionAfterDoubleTap {
public static final int NAVIGATE_BACK = 0;
public static final int NAVIGATE_STOP = 1;
public static final int NO_ACTION = 2;
@@ -473,12 +476,25 @@ public class ContentViewCore
}
/** TapDelayType defined in tools/metrics/histograms/histograms.xml. */
- public static class UMASingleTapType {
+ private static class UMASingleTapType {
public static final int DELAYED_TAP = 0;
public static final int UNDELAYED_TAP = 1;
public static final int COUNT = 2;
}
+ // These have to be kept in sync with content/port/common/input_event_ack_state.h
+ static final int INPUT_EVENT_ACK_STATE_UNKNOWN = 0;
Ted C 2014/01/30 02:29:48 Any chance we could convert the shared constants t
jdduke (slow) 2014/01/30 18:46:24 Hmm, we may not actually need them, I've reworked
+ static final int INPUT_EVENT_ACK_STATE_CONSUMED = 1;
+ static final int INPUT_EVENT_ACK_STATE_NOT_CONSUMED = 2;
+ static final int INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS = 3;
+ static final int INPUT_EVENT_ACK_STATE_IGNORED = 4;
+
+ /**
+ * Used by UMA stat for tracking accidental double tap navigations. Specifies the amount of
+ * time after a double tap within which actions will be recorded to the UMA stat.
+ */
+ private static final long ACTION_AFTER_DOUBLE_TAP_WINDOW_MS = 5000;
+
/**
* Constructs a new ContentViewCore. Embedders must call initialize() after constructing
* a ContentViewCore and before using it.
@@ -719,8 +735,7 @@ public class ContentViewCore
// Note ContentViewGestureHandler initialization must occur before nativeInit
// because nativeInit may callback into hasTouchEventHandlers.
- mZoomManager = new ZoomManager(mContext, this);
- mContentViewGestureHandler = new ContentViewGestureHandler(mContext, this, mZoomManager);
+ mContentViewGestureHandler = new ContentViewGestureHandler(mContext, this);
mZoomControlsDelegate = new ZoomControlsDelegate() {
@Override
public void invokeZoomPicker() {}
@@ -955,7 +970,7 @@ public class ContentViewCore
* Stops loading the current web contents.
*/
public void stopLoading() {
- reportActionAfterDoubleTapUMA(ContentViewCore.UMAActionAfterDoubleTap.NAVIGATE_STOP);
+ reportActionAfterDoubleTapUMA(UMAActionAfterDoubleTap.NAVIGATE_STOP);
if (mNativeContentViewCore != 0) nativeStopLoading(mNativeContentViewCore);
}
@@ -1263,41 +1278,14 @@ public class ContentViewCore
mContentViewGestureHandler.setIgnoreRemainingTouchEvents();
}
- /**
- * @return ContentViewGestureHandler for all MotionEvent and gesture related calls.
- */
- ContentViewGestureHandler getContentViewGestureHandler() {
- return mContentViewGestureHandler;
- }
-
- @Override
- public boolean sendTouchEvent(long timeMs, int action, TouchPoint[] pts) {
- if (mNativeContentViewCore != 0) {
- return nativeSendTouchEvent(mNativeContentViewCore, timeMs, action, pts);
- }
- return false;
- }
-
- @SuppressWarnings("unused")
- @CalledByNative
- private void hasTouchEventHandlers(boolean hasTouchHandlers) {
- mContentViewGestureHandler.hasTouchEventHandlers(hasTouchHandlers);
- }
-
@SuppressWarnings("unused")
@CalledByNative
- private void confirmTouchEvent(int ackResult) {
- mContentViewGestureHandler.confirmTouchEvent(ackResult);
- }
-
- @SuppressWarnings("unused")
- @CalledByNative
- private void onFlingStartEventAck(int ackResult) {
- if (ackResult == ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
+ private void onFlingStartEventAck(int ackResult, float vx, float vy) {
+ if (ackResult == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
&& mGestureStateListener != null) {
- mGestureStateListener.onUnhandledFlingStartEvent();
+ mGestureStateListener.onUnhandledFlingStartEvent((int) vx, (int) vy);
}
- if (ackResult != ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED) {
+ if (ackResult != INPUT_EVENT_ACK_STATE_CONSUMED) {
// No fling happened for the fling start event.
// Cancel the fling status set when sending GestureFlingStart.
getContentViewClient().onFlingStopped();
@@ -1324,19 +1312,51 @@ public class ContentViewCore
getContentViewClient().onScrollEndEvent();
}
- private void reportActionAfterDoubleTapUMA(int type) {
- mContentViewGestureHandler.reportActionAfterDoubleTapUMA(type);
+ /**
+ * Called just prior to a gesture being forwarded to the InputRouter, where
+ * it will be dispatched to the renderer. Any listening for the sending
+ * of (synthetic or touch-derived) gesture events should take place here.
+ */
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private boolean onForwardingGestureEvent(int type, int x, int y) {
+ if (offerGestureToEmbedder(type)) return false;
+ updateTextHandlesForGesture(type);
+ updateGestureStateListener(type);
+ updateForTapOrPress(type, x, y);
+ updateForDoubleTapUMA(type);
+ // TODO(jdduke): Determine if this should be called while a pinch is active.
+ if (type == ContentViewGestureHandler.GESTURE_SCROLL_BY) {
+ mZoomControlsDelegate.invokeZoomPicker();
+ } else if (type == ContentViewGestureHandler.GESTURE_FLING_START) {
+ mContentViewClient.onFlingStarted();
+ }
+ return true;
}
@Override
- public boolean sendGesture(int type, long timeMs, int x, int y, Bundle b) {
- if (offerGestureToEmbedder(type)) return false;
+ public void onTouchEventHandlingBegin(long timeMs, int action, TouchPoint[] pts) {
+ if (mNativeContentViewCore == 0) return;
+ nativeOnTouchEventHandlingBegin(mNativeContentViewCore, timeMs, action, pts);
+ }
+
+ @Override
+ public void onTouchEventHandlingEnd() {
+ if (mNativeContentViewCore == 0) return;
+ nativeOnTouchEventHandlingEnd(mNativeContentViewCore);
+ }
+
+ /**
+ * Note: These events may or may not actually be forwarded to the renderer,
+ * depending on ack disposition of the underlying touch events. All listening
+ * for sent events should take place in {@link #onForwardingGestureEvent(int, int, int)}.
+ */
+ @Override
+ public boolean onGestureEventCreated(int type, long timeMs, int x, int y, Bundle b) {
if (mNativeContentViewCore == 0) return false;
- updateTextHandlesForGesture(type);
- updateGestureStateListener(type, b);
switch (type) {
- case ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE:
- nativeShowPressState(mNativeContentViewCore, timeMs, x, y);
+ case ContentViewGestureHandler.GESTURE_SHOW_PRESS:
+ nativeShowPress(mNativeContentViewCore, timeMs, x, y);
return true;
case ContentViewGestureHandler.GESTURE_TAP_CANCEL:
nativeTapCancel(mNativeContentViewCore, timeMs, x, y);
@@ -1351,17 +1371,19 @@ public class ContentViewCore
nativeSingleTap(mNativeContentViewCore, timeMs, x, y, false);
return true;
case ContentViewGestureHandler.GESTURE_SINGLE_TAP_CONFIRMED:
- handleTapOrPress(timeMs, x, y, 0,
- b.getBoolean(ContentViewGestureHandler.SHOW_PRESS, false));
+ if (!b.getBoolean(ContentViewGestureHandler.SHOW_PRESS, false)) {
+ nativeShowPress(mNativeContentViewCore, timeMs, x, y);
+ }
+ nativeSingleTap(mNativeContentViewCore, timeMs, x, y, false);
return true;
case ContentViewGestureHandler.GESTURE_SINGLE_TAP_UNCONFIRMED:
nativeSingleTapUnconfirmed(mNativeContentViewCore, timeMs, x, y);
return true;
case ContentViewGestureHandler.GESTURE_LONG_PRESS:
- handleTapOrPress(timeMs, x, y, IS_LONG_PRESS, false);
+ nativeLongPress(mNativeContentViewCore, timeMs, x, y, false);
return true;
case ContentViewGestureHandler.GESTURE_LONG_TAP:
- handleTapOrPress(timeMs, x, y, IS_LONG_TAP, false);
+ nativeLongTap(mNativeContentViewCore, timeMs, x, y, false);
return true;
case ContentViewGestureHandler.GESTURE_SCROLL_START: {
int dx = b.getInt(ContentViewGestureHandler.DELTA_HINT_X);
@@ -1379,7 +1401,6 @@ public class ContentViewCore
nativeScrollEnd(mNativeContentViewCore, timeMs);
return true;
case ContentViewGestureHandler.GESTURE_FLING_START:
- mContentViewClient.onFlingStarted();
nativeFlingStart(mNativeContentViewCore, timeMs, x, y,
b.getInt(ContentViewGestureHandler.VELOCITY_X, 0),
b.getInt(ContentViewGestureHandler.VELOCITY_Y, 0));
@@ -1403,35 +1424,24 @@ public class ContentViewCore
}
@VisibleForTesting
- public void sendDoubleTapForTest(long timeMs, int x, int y, Bundle b) {
- sendGesture(ContentViewGestureHandler.GESTURE_DOUBLE_TAP, timeMs, x, y, b);
- }
-
- @Override
- public void sendSingleTapUMA(int type) {
+ public void sendDoubleTapForTest(long timeMs, int x, int y) {
if (mNativeContentViewCore == 0) return;
- nativeSendSingleTapUma(
- mNativeContentViewCore,
- type,
- UMASingleTapType.COUNT);
+ nativeDoubleTap(mNativeContentViewCore, timeMs, x, y);
}
- @Override
- public void sendActionAfterDoubleTapUMA(int type,
- boolean clickDelayEnabled) {
+ @VisibleForTesting
+ public void flingForTest(long timeMs, int x, int y, int velocityX, int velocityY) {
if (mNativeContentViewCore == 0) return;
- nativeSendActionAfterDoubleTapUma(
- mNativeContentViewCore,
- type,
- clickDelayEnabled,
- UMAActionAfterDoubleTap.COUNT);
+ nativeFlingCancel(mNativeContentViewCore, timeMs);
+ nativeScrollBegin(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
+ nativeFlingStart(mNativeContentViewCore, timeMs, x, y, velocityX, velocityY);
}
public void setGestureStateListener(GestureStateListener pinchGestureStateListener) {
mGestureStateListener = pinchGestureStateListener;
}
- void updateGestureStateListener(int gestureType, Bundle b) {
+ void updateGestureStateListener(int gestureType) {
if (mGestureStateListener == null) return;
switch (gestureType) {
@@ -1441,11 +1451,6 @@ public class ContentViewCore
case ContentViewGestureHandler.GESTURE_PINCH_END:
mGestureStateListener.onPinchGestureEnd();
break;
- case ContentViewGestureHandler.GESTURE_FLING_START:
- mGestureStateListener.onFlingStartGesture(
- b.getInt(ContentViewGestureHandler.VELOCITY_X, 0),
- b.getInt(ContentViewGestureHandler.VELOCITY_Y, 0));
- break;
case ContentViewGestureHandler.GESTURE_FLING_CANCEL:
mGestureStateListener.onFlingCancelGesture();
break;
@@ -1976,8 +1981,12 @@ public class ContentViewCore
}
}
- private void handleTapOrPress(
- long timeMs, float xPix, float yPix, int isLongPressOrTap, boolean showPress) {
+ private void updateForTapOrPress(int type, float xPix, float yPix) {
+ if (type != ContentViewGestureHandler.GESTURE_SINGLE_TAP_CONFIRMED
+ && type != ContentViewGestureHandler.GESTURE_SINGLE_TAP_UP
+ && type != ContentViewGestureHandler.GESTURE_LONG_PRESS
+ && type != ContentViewGestureHandler.GESTURE_LONG_TAP) return;
Ted C 2014/01/30 02:29:48 if the conditional and statement don't all fit on
jdduke (slow) 2014/01/30 18:46:24 Done.
+
if (mContainerView.isFocusable() && mContainerView.isFocusableInTouchMode()
&& !mContainerView.isFocused()) {
mContainerView.requestFocus();
@@ -1985,35 +1994,101 @@ public class ContentViewCore
if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
- if (isLongPressOrTap == IS_LONG_PRESS) {
- getInsertionHandleController().allowAutomaticShowing();
- getSelectionHandleController().allowAutomaticShowing();
- if (mNativeContentViewCore != 0) {
- nativeLongPress(mNativeContentViewCore, timeMs, xPix, yPix, false);
- }
- } else if (isLongPressOrTap == IS_LONG_TAP) {
+ if (type == ContentViewGestureHandler.GESTURE_LONG_PRESS
+ || type == ContentViewGestureHandler.GESTURE_LONG_TAP) {
getInsertionHandleController().allowAutomaticShowing();
getSelectionHandleController().allowAutomaticShowing();
- if (mNativeContentViewCore != 0) {
- nativeLongTap(mNativeContentViewCore, timeMs, xPix, yPix, false);
- }
} else {
- if (!showPress && mNativeContentViewCore != 0) {
- nativeShowPressState(mNativeContentViewCore, timeMs, xPix, yPix);
- }
+ setClickXAndY((int) xPix, (int) yPix);
if (mSelectionEditable) getInsertionHandleController().allowAutomaticShowing();
- if (mNativeContentViewCore != 0) {
- nativeSingleTap(mNativeContentViewCore, timeMs, xPix, yPix, false);
+ }
+ }
+
+ private void setClickXAndY(int x, int y) {
+ mSingleTapX = x;
+ mSingleTapY = y;
+ }
+
+ /**
+ * @return The x coordinate for the last point that a singleTap gesture was initiated from.
+ */
+ public int getSingleTapX() {
+ return mSingleTapX;
+ }
+
+ /**
+ * @return The y coordinate for the last point that a singleTap gesture was initiated from.
+ */
+ public int getSingleTapY() {
+ return mSingleTapY;
+ }
+
+ // Watch for the UMA "action after double tap" timer expiring and reset
+ // the timer if necessary.
+ private void updateDoubleTapUmaTimer() {
+ if (mLastDoubleTapTimeMs == 0) return;
+
+ long nowMs = SystemClock.uptimeMillis();
+ if ((nowMs - mLastDoubleTapTimeMs) >= ACTION_AFTER_DOUBLE_TAP_WINDOW_MS) {
+ // Time expired, user took no action (that we care about).
+ sendActionAfterDoubleTapUMA(UMAActionAfterDoubleTap.NO_ACTION);
+ mLastDoubleTapTimeMs = 0;
+ }
+ }
+
+ private void updateForDoubleTapUMA(int type) {
+ updateDoubleTapUmaTimer();
+
+ if (type == ContentViewGestureHandler.GESTURE_SINGLE_TAP_UP
+ || type == ContentViewGestureHandler.GESTURE_SINGLE_TAP_CONFIRMED) {
+ sendSingleTapUMA(mContentViewGestureHandler.isDoubleTapDisabled() ?
+ UMASingleTapType.UNDELAYED_TAP : UMASingleTapType.DELAYED_TAP);
+ } else if (type == ContentViewGestureHandler.GESTURE_DOUBLE_TAP) {
+ // Make sure repeated double taps don't get silently dropped from
+ // the statistics.
+ if (mLastDoubleTapTimeMs > 0) {
+ sendActionAfterDoubleTapUMA(UMAActionAfterDoubleTap.NO_ACTION);
}
+
+ mLastDoubleTapTimeMs = SystemClock.uptimeMillis();
}
}
+ private void reportActionAfterDoubleTapUMA(int type) {
+ updateDoubleTapUmaTimer();
+
+ if (mLastDoubleTapTimeMs == 0) return;
+
+ long nowMs = SystemClock.uptimeMillis();
+ if ((nowMs - mLastDoubleTapTimeMs) < ACTION_AFTER_DOUBLE_TAP_WINDOW_MS) {
+ sendActionAfterDoubleTapUMA(type);
+ mLastDoubleTapTimeMs = 0;
+ }
+ }
+
+ private void sendSingleTapUMA(int type) {
+ if (mNativeContentViewCore == 0) return;
+ nativeSendSingleTapUma(
+ mNativeContentViewCore,
+ type,
+ UMASingleTapType.COUNT);
+ }
+
+ private void sendActionAfterDoubleTapUMA(int type) {
+ if (mNativeContentViewCore == 0) return;
+ nativeSendActionAfterDoubleTapUma(
+ mNativeContentViewCore,
+ type,
+ !mContentViewGestureHandler.isClickDelayDisabled(),
+ UMAActionAfterDoubleTap.COUNT);
+ }
+
public void setZoomControlsDelegate(ZoomControlsDelegate zoomControlsDelegate) {
mZoomControlsDelegate = zoomControlsDelegate;
}
public void updateMultiTouchZoomSupport(boolean supportsMultiTouchZoom) {
- mZoomManager.updateMultiTouchSupport(supportsMultiTouchZoom);
+ mContentViewGestureHandler.updateMultiTouchSupport(supportsMultiTouchZoom);
}
public void updateDoubleTapSupport(boolean supportsDoubleTap) {
@@ -2732,17 +2807,15 @@ public class ContentViewCore
* @return whether the gesture was sent.
*/
public boolean pinchByDelta(float delta) {
- if (mNativeContentViewCore == 0) {
- return false;
- }
+ if (mNativeContentViewCore == 0) return false;
long timeMs = System.currentTimeMillis();
int xPix = getViewportWidthPix() / 2;
int yPix = getViewportHeightPix() / 2;
- getContentViewGestureHandler().pinchBegin(timeMs, xPix, yPix);
- getContentViewGestureHandler().pinchBy(timeMs, xPix, yPix, delta);
- getContentViewGestureHandler().pinchEnd(timeMs);
+ nativePinchBegin(mNativeContentViewCore, timeMs, xPix, yPix);
+ nativePinchBy(mNativeContentViewCore, timeMs, xPix, yPix, delta);
+ nativePinchEnd(mNativeContentViewCore, timeMs);
return true;
}
@@ -2750,7 +2823,6 @@ public class ContentViewCore
/**
* Invokes the graphical zoom picker widget for this ContentView.
*/
- @Override
public void invokeZoomPicker() {
mZoomControlsDelegate.invokeZoomPicker();
}
@@ -3250,9 +3322,11 @@ public class ContentViewCore
long nativeContentViewCoreImpl, int orientation);
// All touch events (including flings, scrolls etc) accept coordinates in physical pixels.
- private native boolean nativeSendTouchEvent(
+ private native void nativeOnTouchEventHandlingBegin(
long nativeContentViewCoreImpl, long timeMs, int action, TouchPoint[] pts);
+ private native void nativeOnTouchEventHandlingEnd(long nativeContentViewCoreImpl);
+
private native int nativeSendMouseMoveEvent(
long nativeContentViewCoreImpl, long timeMs, float x, float y);
@@ -3280,7 +3354,7 @@ public class ContentViewCore
private native void nativeSingleTapUnconfirmed(
long nativeContentViewCoreImpl, long timeMs, float x, float y);
- private native void nativeShowPressState(
+ private native void nativeShowPress(
long nativeContentViewCoreImpl, long timeMs, float x, float y);
private native void nativeTapCancel(

Powered by Google App Engine