Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6)

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: More testing Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 0d2ee7791ef1932e05788ca496ddf32fd34ec9ce..fc377c03ee9229e5e3a8ed5f567013db23c1c815 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;
@@ -337,7 +338,6 @@ public class ContentViewCore
private ContentViewGestureHandler mContentViewGestureHandler;
private final ObserverList<GestureStateListener> mGestureStateListeners;
private final RewindableIterator<GestureStateListener> mGestureStateListenersIterator;
- private ZoomManager mZoomManager;
private ZoomControlsDelegate mZoomControlsDelegate;
private PopupZoomer mPopupZoomer;
@@ -425,12 +425,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;
@@ -438,13 +446,19 @@ 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;
}
/**
+ * 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.
*
@@ -686,8 +700,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() {}
@@ -923,7 +936,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);
}
@@ -1231,53 +1244,35 @@ 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);
+ private void onFlingStartEventConsumed(int vx, int vy) {
+ for (mGestureStateListenersIterator.rewind();
+ mGestureStateListenersIterator.hasNext();) {
+ mGestureStateListenersIterator.next().onFlingStartGesture(
+ vx, vy, computeVerticalScrollOffset(), computeVerticalScrollExtent());
+ }
}
@SuppressWarnings("unused")
@CalledByNative
- private void confirmTouchEvent(int ackResult) {
- mContentViewGestureHandler.confirmTouchEvent(ackResult);
+ private void onFlingStartEventHadNoConsumer(int vx, int vy) {
+ for (mGestureStateListenersIterator.rewind();
+ mGestureStateListenersIterator.hasNext();) {
+ mGestureStateListenersIterator.next().onUnhandledFlingStartEvent(vx, vy);
+ }
}
@SuppressWarnings("unused")
@CalledByNative
- private void onFlingStartEventAck(int ackResult) {
- if (ackResult == ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) {
- for (mGestureStateListenersIterator.rewind();
- mGestureStateListenersIterator.hasNext();) {
- mGestureStateListenersIterator.next().onUnhandledFlingStartEvent();
- }
- }
- if (ackResult != ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED) {
- // No fling happened for the fling start event.
- // Cancel the fling status set when sending GestureFlingStart.
- updateGestureStateListener(ContentViewGestureHandler.GESTURE_FLING_END, null);
- }
+ private void onFlingCancelEventAck() {
+ updateGestureStateListener(ContentViewGestureHandler.GESTURE_FLING_CANCEL);
}
@SuppressWarnings("unused")
@CalledByNative
private void onScrollBeginEventAck() {
- updateGestureStateListener(ContentViewGestureHandler.GESTURE_SCROLL_START, null);
+ updateGestureStateListener(ContentViewGestureHandler.GESTURE_SCROLL_START);
}
@SuppressWarnings("unused")
@@ -1292,22 +1287,62 @@ public class ContentViewCore
@SuppressWarnings("unused")
@CalledByNative
private void onScrollEndEventAck() {
- updateGestureStateListener(ContentViewGestureHandler.GESTURE_SCROLL_END, null);
+ updateGestureStateListener(ContentViewGestureHandler.GESTURE_SCROLL_END);
}
- private void reportActionAfterDoubleTapUMA(int type) {
- mContentViewGestureHandler.reportActionAfterDoubleTapUMA(type);
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private void onPinchBeginEventAck() {
+ updateGestureStateListener(ContentViewGestureHandler.GESTURE_PINCH_BEGIN);
+ }
+
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private void onPinchEndEventAck() {
+ updateGestureStateListener(ContentViewGestureHandler.GESTURE_PINCH_END);
+ }
+
+ /**
+ * Called just prior to a gesture being forwarded to the renderer. All listening
+ * for the sending of (synthetic or touch-derived) gestures should occur here.
+ */
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private boolean filterGestureEvent(int type, int x, int y) {
+ if (offerGestureToEmbedder(type)) return true;
+ updateTextHandlesForGesture(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();
+ }
+ return false;
+ }
+
+ @Override
+ public void onTouchEventHandlingBegin(long timeMs, int action, TouchPoint[] pts) {
+ if (mNativeContentViewCore == 0) return;
+ nativeOnTouchEventHandlingBegin(mNativeContentViewCore, timeMs, action, pts);
}
@Override
- public boolean sendGesture(int type, long timeMs, int x, int y, Bundle b) {
- if (offerGestureToEmbedder(type)) return false;
+ 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 gestures should take place in {@link #filterGestureEvent(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);
@@ -1322,17 +1357,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);
@@ -1373,28 +1410,17 @@ 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);
}
/**
@@ -1413,23 +1439,16 @@ public class ContentViewCore
mGestureStateListeners.removeObserver(listener);
}
- void updateGestureStateListener(int gestureType, Bundle b) {
+ void updateGestureStateListener(int gestureType) {
for (mGestureStateListenersIterator.rewind();
mGestureStateListenersIterator.hasNext();) {
GestureStateListener listener = mGestureStateListenersIterator.next();
switch (gestureType) {
case ContentViewGestureHandler.GESTURE_PINCH_BEGIN:
- listener.onPinchGestureStart();
+ listener.onPinchStarted();
break;
case ContentViewGestureHandler.GESTURE_PINCH_END:
- listener.onPinchGestureEnd();
- break;
- case ContentViewGestureHandler.GESTURE_FLING_START:
- listener.onFlingStartGesture(
- b.getInt(ContentViewGestureHandler.VELOCITY_X, 0),
- b.getInt(ContentViewGestureHandler.VELOCITY_Y, 0),
- computeVerticalScrollOffset(),
- computeVerticalScrollExtent());
+ listener.onPinchEnded();
break;
case ContentViewGestureHandler.GESTURE_FLING_END:
listener.onFlingEndGesture(
@@ -1977,8 +1996,14 @@ 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;
+ }
+
if (mContainerView.isFocusable() && mContainerView.isFocusableInTouchMode()
&& !mContainerView.isFocused()) {
mContainerView.requestFocus();
@@ -1986,35 +2011,101 @@ public class ContentViewCore
if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix);
- if (isLongPressOrTap == IS_LONG_PRESS) {
+ if (type == ContentViewGestureHandler.GESTURE_LONG_PRESS
+ || type == ContentViewGestureHandler.GESTURE_LONG_TAP) {
getInsertionHandleController().allowAutomaticShowing();
getSelectionHandleController().allowAutomaticShowing();
- if (mNativeContentViewCore != 0) {
- nativeLongPress(mNativeContentViewCore, timeMs, xPix, yPix, false);
- }
- } else if (isLongPressOrTap == IS_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) {
@@ -2738,17 +2829,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;
}
@@ -2756,7 +2845,6 @@ public class ContentViewCore
/**
* Invokes the graphical zoom picker widget for this ContentView.
*/
- @Override
public void invokeZoomPicker() {
mZoomControlsDelegate.invokeZoomPicker();
}
@@ -3221,7 +3309,7 @@ public class ContentViewCore
@CalledByNative
private void onNativeFlingStopped() {
- updateGestureStateListener(ContentViewGestureHandler.GESTURE_FLING_END, null);
+ updateGestureStateListener(ContentViewGestureHandler.GESTURE_FLING_END);
}
private native WebContents nativeGetWebContentsAndroid(long nativeContentViewCoreImpl);
@@ -3256,9 +3344,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);
@@ -3286,7 +3376,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
This is Rietveld 408576698