| 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 9740c7741cff73745a9137d44a7d2691c9400baf..daed1914d66e842c7d0d7fd2829845bbe429f4ef 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;
|
| @@ -196,11 +197,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();
|
| @@ -208,7 +204,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.
|
| @@ -459,12 +455,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;
|
| @@ -472,13 +472,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.
|
| *
|
| @@ -954,7 +960,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);
|
| }
|
|
|
| @@ -1270,37 +1276,26 @@ 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 onFlingStartEventAck(int ackResult) {
|
| - if (ackResult == ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
|
| - && mGestureStateListener != null) {
|
| - mGestureStateListener.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.
|
| - getContentViewClient().onFlingStopped();
|
| + private void onUnhandledFlingStartEventAck(boolean hadConsumer, float vx, float vy) {
|
| + if (mGestureStateListener != null && !hadConsumer) {
|
| + mGestureStateListener.onUnhandledFlingStartEvent((int) vx, (int) vy);
|
| }
|
| + // No fling happened for the fling start event.
|
| + // Cancel the fling status set when sending GestureFlingStart.
|
| + getContentViewClient().onFlingStopped();
|
| }
|
|
|
| @SuppressWarnings("unused")
|
| @@ -1323,19 +1318,29 @@ public class ContentViewCore
|
| getContentViewClient().onScrollEndEvent();
|
| }
|
|
|
| - private void reportActionAfterDoubleTapUMA(int type) {
|
| - mContentViewGestureHandler.reportActionAfterDoubleTapUMA(type);
|
| + @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;
|
| 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);
|
| @@ -1350,17 +1355,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);
|
| @@ -1378,7 +1385,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));
|
| @@ -1406,31 +1412,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) {
|
| @@ -1440,11 +1426,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;
|
| @@ -1975,8 +1956,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();
|
| @@ -1984,29 +1968,75 @@ 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);
|
| - }
|
| 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;
|
| }
|
| @@ -2731,17 +2761,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;
|
| }
|
| @@ -2749,7 +2777,6 @@ public class ContentViewCore
|
| /**
|
| * Invokes the graphical zoom picker widget for this ContentView.
|
| */
|
| - @Override
|
| public void invokeZoomPicker() {
|
| mZoomControlsDelegate.invokeZoomPicker();
|
| }
|
| @@ -3249,9 +3276,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);
|
|
|
| @@ -3279,7 +3308,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(
|
|
|