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 5bd4ed79fe778231d725e699352fe3364bdfc25d..a9bf7addebc6d7cd35ce9935b902b4f0ca75ff9c 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 |
@@ -23,6 +23,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; |
@@ -198,11 +199,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(); |
@@ -210,7 +206,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. |
@@ -464,12 +460,16 @@ 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; |
+ |
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; |
@@ -477,13 +477,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. |
* |
@@ -1027,7 +1033,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); |
} |
@@ -1338,30 +1344,22 @@ public class ContentViewCore |
} |
@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); |
+ public void onTouchEventHandlingBegin(long timeMs, int action, TouchPoint[] pts) { |
+ if (mNativeContentViewCore == 0) return; |
+ nativeOnTouchEventHandlingBegin(mNativeContentViewCore, timeMs, action, pts); |
} |
- @SuppressWarnings("unused") |
- @CalledByNative |
- private void confirmTouchEvent(int ackResult) { |
- mContentViewGestureHandler.confirmTouchEvent(ackResult); |
+ @Override |
+ public void onTouchEventHandlingEnd() { |
+ if (mNativeContentViewCore == 0) return; |
+ nativeOnTouchEventHandlingEnd(mNativeContentViewCore); |
} |
@SuppressWarnings("unused") |
@CalledByNative |
- private void unhandledFlingStartEvent() { |
+ private void unhandledFlingStartEvent(float vx, float vy) { |
if (mGestureStateListener != null) { |
- mGestureStateListener.onUnhandledFlingStartEvent(); |
+ mGestureStateListener.onUnhandledFlingStartEvent((int) vx, (int) vy); |
} |
} |
@@ -1373,16 +1371,23 @@ public class ContentViewCore |
} |
} |
- private void reportActionAfterDoubleTapUMA(int type) { |
- mContentViewGestureHandler.reportActionAfterDoubleTapUMA(type); |
+ @SuppressWarnings("unused") |
+ @CalledByNative |
+ private boolean onForwardingGestureEvent(int type, int x, int y) { |
+ if (offerGestureToEmbedder(type)) return true; |
+ updateTextHandlesForGesture(type); |
+ updateGestureStateListener(type); |
+ updateForTapOrPress(type, x, y); |
+ updateForDoubleTapUMA(type); |
+ if (type == ContentViewGestureHandler.GESTURE_SCROLL_BY) { |
+ mZoomControlsDelegate.invokeZoomPicker(); |
+ } |
+ return false; |
} |
@Override |
public boolean sendGesture(int type, long timeMs, int x, int y, Bundle b) { |
- if (offerGestureToEmbedder(type)) return false; |
if (mNativeContentViewCore == 0) return false; |
- updateTextHandlesForGesture(type); |
- updateGestureStateListener(type, b); |
switch (type) { |
case ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE: |
nativeShowPressState(mNativeContentViewCore, timeMs, x, y); |
@@ -1400,17 +1405,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)) { |
+ nativeShowPressState(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); |
@@ -1455,31 +1462,11 @@ public class ContentViewCore |
sendGesture(ContentViewGestureHandler.GESTURE_DOUBLE_TAP, timeMs, x, y, b); |
} |
- @Override |
- public void sendSingleTapUMA(int type) { |
- if (mNativeContentViewCore == 0) return; |
- nativeSendSingleTapUma( |
- mNativeContentViewCore, |
- type, |
- UMASingleTapType.COUNT); |
- } |
- |
- @Override |
- public void sendActionAfterDoubleTapUMA(int type, |
- boolean clickDelayEnabled) { |
- if (mNativeContentViewCore == 0) return; |
- nativeSendActionAfterDoubleTapUma( |
- mNativeContentViewCore, |
- type, |
- clickDelayEnabled, |
- UMAActionAfterDoubleTap.COUNT); |
- } |
- |
public void setGestureStateListener(GestureStateListener pinchGestureStateListener) { |
mGestureStateListener = pinchGestureStateListener; |
} |
- void updateGestureStateListener(int gestureType, Bundle b) { |
+ void updateGestureStateListener(int gestureType) { |
if (mGestureStateListener == null) return; |
switch (gestureType) { |
@@ -1489,11 +1476,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; |
@@ -2024,8 +2006,11 @@ 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_LONG_PRESS |
+ && type != ContentViewGestureHandler.GESTURE_LONG_TAP) return; |
+ |
if (mContainerView.isFocusable() && mContainerView.isFocusableInTouchMode() |
&& !mContainerView.isFocused()) { |
mContainerView.requestFocus(); |
@@ -2033,29 +2018,77 @@ public class ContentViewCore |
if (!mPopupZoomer.isShowing()) mPopupZoomer.setLastTouch(xPix, yPix); |
- if (isLongPressOrTap == IS_LONG_PRESS) { |
+ if (type == ContentViewGestureHandler.GESTURE_LONG_PRESS) { |
getInsertionHandleController().allowAutomaticShowing(); |
getSelectionHandleController().allowAutomaticShowing(); |
- if (mNativeContentViewCore != 0) { |
- nativeLongPress(mNativeContentViewCore, timeMs, xPix, yPix, false); |
- } |
- } else if (isLongPressOrTap == IS_LONG_TAP) { |
+ } else if (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); |
- } |
if (mSelectionEditable) getInsertionHandleController().allowAutomaticShowing(); |
- if (mNativeContentViewCore != 0) { |
- nativeSingleTap(mNativeContentViewCore, timeMs, xPix, yPix, false); |
+ } |
+ } |
+ |
+ // 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; |
} |
@@ -2792,14 +2825,6 @@ public class ContentViewCore |
} |
/** |
- * Invokes the graphical zoom picker widget for this ContentView. |
- */ |
- @Override |
- public void invokeZoomPicker() { |
- mZoomControlsDelegate.invokeZoomPicker(); |
- } |
- |
- /** |
* This will mimic {@link #addPossiblyUnsafeJavascriptInterface(Object, String, Class)} |
* and automatically pass in {@link JavascriptInterface} as the required annotation. |
* |
@@ -3289,9 +3314,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); |