Index: chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapSuppression.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapSuppression.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapSuppression.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..07d6558c773ea3761c6982a979fbefb5f0be76f0 |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/TapSuppression.java |
@@ -0,0 +1,95 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser.contextualsearch; |
+ |
+/** |
+ * Heuristic for general Tap suppression that factors in a variety of signals. |
+ */ |
+class TapSuppression extends ContextualSearchHeuristic { |
+ private static final int TIME_THRESHOLD_MILLISECONDS = 3000; |
+ private static final int TAP_RADIUS_DPS = 30; |
+ |
+ private final boolean mIsTapSuppressionEnabled; |
+ private final int mExperimentThresholdTaps; |
+ private final int mTapsSinceOpen; |
+ private final float mPxToDp; |
+ private final boolean mIsSecondTap; |
+ private final boolean mIsConditionSatisfied; // whether to suppress or not. |
+ |
+ /** |
+ * Constructs a heuristic to decide if a Tap should be suppressed or not. |
+ * Combines various signals to determine suppression, including whether the previous |
+ * Tap was suppressed for any reason. |
+ * @param controller The Selection Controller. |
+ * @param previousTapState The specifics regarding the previous Tap. |
+ * @param x The x coordinate of the current tap. |
+ * @param y The y coordinate of the current tap. |
+ * @param tapsSinceOpen the number of Tap gestures since the last open of the panel. |
+ */ |
+ TapSuppression(ContextualSearchSelectionController controller, |
+ ContextualSearchTapState previousTapState, int x, int y, int tapsSinceOpen) { |
+ mIsTapSuppressionEnabled = ContextualSearchFieldTrial.isTapSuppressionEnabled(); |
+ mExperimentThresholdTaps = ContextualSearchFieldTrial.getSuppressionTaps(); |
+ mPxToDp = controller.getPxToDp(); |
+ mTapsSinceOpen = tapsSinceOpen; |
+ mIsSecondTap = previousTapState != null && previousTapState.wasSuppressed() |
Theresa
2016/06/29 01:51:57
Could the previous tap have been suppressed for a
Donn Denman
2016/06/29 02:30:36
Yes, it could have for TapFarFromPreviousSuppressi
|
+ && !shouldHandleFirstTap(); |
+ |
+ boolean doSuppressTap = false; |
+ if (mIsTapSuppressionEnabled) { |
+ if (mIsSecondTap) { |
+ boolean shouldHandle = shouldHandleSecondTap(previousTapState, x, y); |
+ doSuppressTap = !shouldHandle; |
Theresa
2016/06/29 01:51:57
nit: combine with line above?
Donn Denman
2016/06/29 02:30:36
Done.
|
+ } else { |
+ doSuppressTap = !shouldHandleFirstTap(); |
+ } |
+ } |
+ mIsConditionSatisfied = doSuppressTap; |
+ } |
+ |
+ @Override |
+ protected boolean isConditionSatisfied() { |
+ return mIsConditionSatisfied; |
+ } |
+ |
+ @Override |
+ protected void logResultsSeen(boolean wasSearchContentViewSeen, boolean wasActivatedByTap) { |
+ // TODO(donnd): consider logging counter-factual data rather than checking if enabled. |
+ if (wasActivatedByTap && mIsTapSuppressionEnabled) { |
+ ContextualSearchUma.logTapSuppressionResultsSeen( |
+ wasSearchContentViewSeen, mIsSecondTap); |
+ } |
+ } |
+ |
+ /** |
+ * @return whether a first tap should be handled or not. |
+ */ |
+ private boolean shouldHandleFirstTap() { |
+ return mTapsSinceOpen < mExperimentThresholdTaps; |
+ } |
+ |
+ /** |
+ * Determines whether a second tap at the given coordinates should be handled. |
+ * @param tapState The specifics regarding the previous Tap. |
+ * @param x The x coordinate of the current tap. |
+ * @param y The y coordinate of the current tap. |
+ * @return whether a second tap at the given coordinates should be handled or not. |
+ */ |
+ private boolean shouldHandleSecondTap(ContextualSearchTapState tapState, int x, int y) { |
+ // The second tap needs to be close to the first tap in both time and space. |
+ // Recent enough? |
+ if (System.nanoTime() - tapState.tapTimeNanoseconds() |
+ > (long) TIME_THRESHOLD_MILLISECONDS * NANOSECONDS_IN_A_MILLISECOND) { |
+ return false; |
+ } |
+ |
+ // Within our radius? |
+ float deltaXDp = (tapState.getX() - x) * mPxToDp; |
+ float deltaYDp = (tapState.getY() - y) * mPxToDp; |
+ // Use x^2 * y^2 = r^2 |
+ float distanceSquaredDp = deltaXDp * deltaXDp + deltaYDp * deltaYDp; |
+ return distanceSquaredDp <= TAP_RADIUS_DPS * TAP_RADIUS_DPS; |
+ } |
+} |