Chromium Code Reviews| 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..06c8eeb0bd600563374d44927c20220c8c1961f9 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) { |
|
jdduke (slow)
2014/01/18 16:36:01
TODO: Fix |type|, as it's completely bogus. WebIn
|
| + 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 |
|
jdduke (slow)
2014/01/18 16:36:01
TODO(jdduke): These types are all wrong. We need
|
| + && 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; |
| } |
| @@ -2794,7 +2827,6 @@ public class ContentViewCore |
| /** |
| * Invokes the graphical zoom picker widget for this ContentView. |
| */ |
| - @Override |
| public void invokeZoomPicker() { |
| mZoomControlsDelegate.invokeZoomPicker(); |
| } |
| @@ -3289,9 +3321,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); |