Commit c845b226 authored by Donn Denman's avatar Donn Denman Committed by Commit Bot

[TTS] Persist and resend event IDs for sawmill.

Adds support for persisting and later reporting user interaction
to the server in support of the offline sanitized click-log.

BACKGROUND:
This change allows the server to log outcomes from interactions and associate
them with the privacy-safe documents in the Search Index. See go/cs-sanitized
for more details on this design.

DESIGN OVERVIEW:
When the server recognizes a document it generates a random EventID and logs
it to sawmill and returns it to the client in the response to the Resolve
request.  The client passes the EventID up to Java and persists it in local
device storage along with the user-interaction outcomes.  The next time
the user makes a Resolve request the client checks for previous data, and if
present it sends it as part of the Context to the server (and erases it
from storage).

DETAILS:
This CL introduces a new interface ContextualSearchInteractionPersister to
persist user interactions, and an implementation that uses local storage.

The manager detects a new parameter for the EventID returned in the
Resolve request. It stores this and an encoded set of bits that describe
the user-interaction for that event in local storage.
Any subsequent Resolve request checks for a previous EventID and when
non-zero sends it along with the user-interaction results in CGI params
to the server.  Support for these params on the client and server have
already landed.

If the server never sends an Event ID to log, then the client won't
change it's behavior.  The server does not yet send these Event IDs.

BUG=872902

Change-Id: Ief9a95625b16a4b0a3be5325ed1adadff9c791b9
Reviewed-on: https://chromium-review.googlesource.com/c/1170352
Commit-Queue: Donn Denman <donnd@chromium.org>
Reviewed-by: default avatarBrian White <bcwhite@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#622993}
parent 2de7817b
...@@ -70,6 +70,10 @@ public abstract class ContextualSearchContext { ...@@ -70,6 +70,10 @@ public abstract class ContextualSearchContext {
private String mWordFollowingTap; private String mWordFollowingTap;
private int mWordFollowingTapOffset = INVALID_OFFSET; private int mWordFollowingTapOffset = INVALID_OFFSET;
// Data about the previous user interactions and the event-ID from the server that will log it.
private long mPreviousEventId;
private int mPreviousUserInteractions;
/** /**
* Constructs a context that tracks the selection and some amount of page content. * Constructs a context that tracks the selection and some amount of page content.
*/ */
...@@ -84,11 +88,18 @@ public abstract class ContextualSearchContext { ...@@ -84,11 +88,18 @@ public abstract class ContextualSearchContext {
* @param homeCountry The country where the user usually resides, or an empty string if not * @param homeCountry The country where the user usually resides, or an empty string if not
* known. * known.
* @param maySendBasePageUrl Whether policy allows sending the base-page URL to the server. * @param maySendBasePageUrl Whether policy allows sending the base-page URL to the server.
* @param previousEventId An EventID from the server to send along with the resolve request.
* @param previousUserInteractions Persisted interaction outcomes to send along with the resolve
* request.
*/ */
void setResolveProperties(String homeCountry, boolean maySendBasePageUrl) { void setResolveProperties(String homeCountry, boolean maySendBasePageUrl, long previousEventId,
int previousUserInteractions) {
mHasSetResolveProperties = true; mHasSetResolveProperties = true;
mHomeCountry = homeCountry; mHomeCountry = homeCountry;
nativeSetResolveProperties(getNativePointer(), homeCountry, maySendBasePageUrl); mPreviousEventId = previousEventId;
mPreviousUserInteractions = previousUserInteractions;
nativeSetResolveProperties(getNativePointer(), homeCountry, maySendBasePageUrl,
previousEventId, previousUserInteractions);
} }
/** /**
...@@ -452,6 +463,20 @@ public abstract class ContextualSearchContext { ...@@ -452,6 +463,20 @@ public abstract class ContextualSearchContext {
&& mSurroundingText.charAt(index) != SOFT_HYPHEN_CHAR; && mSurroundingText.charAt(index) != SOFT_HYPHEN_CHAR;
} }
// ============================================================================================
// Test support.
// ============================================================================================
@VisibleForTesting
int getPreviousUserInteractions() {
return mPreviousUserInteractions;
}
@VisibleForTesting
long getPreviousEventId() {
return mPreviousEventId;
}
// ============================================================================================ // ============================================================================================
// Native callback support. // Native callback support.
// ============================================================================================ // ============================================================================================
...@@ -470,8 +495,9 @@ public abstract class ContextualSearchContext { ...@@ -470,8 +495,9 @@ public abstract class ContextualSearchContext {
@VisibleForTesting @VisibleForTesting
protected native void nativeDestroy(long nativeContextualSearchContext); protected native void nativeDestroy(long nativeContextualSearchContext);
@VisibleForTesting @VisibleForTesting
protected native void nativeSetResolveProperties( protected native void nativeSetResolveProperties(long nativeContextualSearchContext,
long nativeContextualSearchContext, String homeCountry, boolean maySendBasePageUrl); String homeCountry, boolean maySendBasePageUrl, long previousEventId,
int previousEventResults);
@VisibleForTesting @VisibleForTesting
protected native void nativeAdjustSelection( protected native void nativeAdjustSelection(
long nativeContextualSearchContext, int startAdjust, int endAdjust); long nativeContextualSearchContext, int startAdjust, int endAdjust);
......
// Copyright 2019 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;
import java.util.Map;
/** Persists user-interaction outcomes and the associated EventID used to track them. */
interface ContextualSearchInteractionPersister {
/** An EventID of 0 means no event ID is available. Don't persist. */
public static final long NO_EVENT_ID = 0;
/** An interaction value of 0 means no interaction. */
public static final int NO_INTERACTION = 0;
/**
* Gets the current persisted interaction and returns it, after clearing the persisted state.
* After this call {@link #persistInteractions} must be called to store a non-empty interaction.
* @return A new {@link PersistedInteraction} that is often empty due to the server not
* providing any EventID.
*/
PersistedInteraction getAndClearPersistedInteraction();
/**
* Persists the given ID and outcomes to local storage.
* The outcomes Map keys should contain @Feature int values only. The Objects that are values in
* this map must be Booleans, but could some day be Integers.
* @param eventId A non-zero event ID to store.
* @param outcomesMap A map of outcome features and associated values.
*/
void persistInteractions(
long eventId, Map</* @Feature */ Integer, /* Boolean */ Object> outcomesMap);
/** Provides access to a {@link PersistedInteraction} through getters. */
interface PersistedInteraction {
/** Gets the Event ID previously sent by the server during a resolve request. */
long getEventId();
/** Gets the bit-encoded user interactions associated with that event. */
int getEncodedUserInteractions();
/** Gets the time stamp of this user interaction in milliseconds. */
long getTimestampMs();
}
}
// Copyright 2019 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;
import org.chromium.chrome.browser.contextualsearch.ContextualSearchInteractionRecorder.Feature;
import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Implements persisting an interaction event and outcomes to local storage.
* Call {@link #persistInteractions} to persist a pair of values given ID and outcomes.
* Call the static getAndResetPersistedInteractionOutcomes to get the previous persisted pair
* and reset the storage (so we can't get the same value twice).
* A pair with an Event ID of {@link ContextualSearchInteractionPersister#NO_EVENT_ID} represents no
* persisted value.
*/
class ContextualSearchInteractionPersisterImpl implements ContextualSearchInteractionPersister {
static final int BITMASK_PANEL_OPENED = 1 << 0;
static final int BITMASK_QUICK_ACTION_CLICKED = 1 << 1;
static final int BITMASK_QUICK_ANSWER_SEEN = 1 << 2;
static final int BITMASK_CARDS_DATA_SHOWN = 1 << 3;
static final Map<Integer, Integer> OUTCOME_ENCODING_BIT_MAP;
static {
// Map keys should contain @Feature int values only.
// Map values should be int powers of 2 only.
Map<Integer, Integer> outcome_encoding_bit_map = new HashMap<Integer, Integer>();
outcome_encoding_bit_map.put(Feature.OUTCOME_WAS_PANEL_OPENED, BITMASK_PANEL_OPENED);
outcome_encoding_bit_map.put(
Feature.OUTCOME_WAS_QUICK_ACTION_CLICKED, BITMASK_QUICK_ACTION_CLICKED);
outcome_encoding_bit_map.put(
Feature.OUTCOME_WAS_QUICK_ANSWER_SEEN, BITMASK_QUICK_ANSWER_SEEN);
outcome_encoding_bit_map.put(
Feature.OUTCOME_WAS_CARDS_DATA_SHOWN, BITMASK_CARDS_DATA_SHOWN);
OUTCOME_ENCODING_BIT_MAP = Collections.unmodifiableMap(outcome_encoding_bit_map);
}
@Override
public PersistedInteraction getAndClearPersistedInteraction() {
long previousEventId = readEventIdFromPersistantStorage();
if (previousEventId == NO_EVENT_ID) return new PersistedInteractionImpl();
int encodedInteractionOutcomes = readOutcomesFromPersistantStorage();
long timestamp = readTimestampFromPersistantStorage();
assert timestamp != 0;
ContextualSearchUma.logOutcomesTimestamp(System.currentTimeMillis() - timestamp);
writeEventIDToPersistantStorage(NO_EVENT_ID);
writeOutcomesToPersistantStorage(NO_INTERACTION);
writeTimestampToPersistantStorage(0);
return new PersistedInteractionImpl(previousEventId, encodedInteractionOutcomes, timestamp);
}
@Override
public void persistInteractions(long eventId, Map<Integer, Object> outcomesMap) {
assert eventId != NO_EVENT_ID;
assert outcomesMap != null;
int encodedInteractionResults = NO_INTERACTION;
// Only the outcomes will be present, since we logged inference features at
// inference time.
for (Map.Entry<Integer, Object> entry : outcomesMap.entrySet()) {
// Bit-wise encode into an int with all the boolean outcomes.
if ((boolean) entry.getValue()) {
encodedInteractionResults |= OUTCOME_ENCODING_BIT_MAP.get(entry.getKey());
}
}
writeOutcomesToPersistantStorage(encodedInteractionResults);
writeEventIDToPersistantStorage(eventId);
writeTimestampToPersistantStorage(System.currentTimeMillis());
}
/** @param eventId An event ID to write to local storage. */
private void writeEventIDToPersistantStorage(long eventId) {
ChromePreferenceManager.getInstance().writeLong(
ChromePreferenceManager.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_EVENT_ID, eventId);
}
/** @return The event ID from local storage. */
private long readEventIdFromPersistantStorage() {
return ChromePreferenceManager.getInstance().readLong(
ChromePreferenceManager.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_EVENT_ID,
NO_EVENT_ID);
}
/** @param bitEncodedValue An encoded outcome to write to local storage. */
private void writeOutcomesToPersistantStorage(int bitEncodedValue) {
ChromePreferenceManager.getInstance().writeInt(
ChromePreferenceManager.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_ENCODED_OUTCOMES,
bitEncodedValue);
}
/** @return The encoded outcome from local storage. */
private int readOutcomesFromPersistantStorage() {
return ChromePreferenceManager.getInstance().readInt(
ChromePreferenceManager.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_ENCODED_OUTCOMES);
}
/** Writes the current time stamp to local storage. */
private void writeTimestampToPersistantStorage(long timestamp) {
ChromePreferenceManager.getInstance().writeLong(
ChromePreferenceManager.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_TIMESTAMP,
timestamp);
}
/** @return The time stamp when we wrote the outcome to local storage. */
private long readTimestampFromPersistantStorage() {
return ChromePreferenceManager.getInstance().readLong(
ChromePreferenceManager.CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_TIMESTAMP, 0);
}
/**
* Provides a read-only encapsulation of a persisted user interaction and the ID from the
* server used to identify it, and a timestamp.
*/
static class PersistedInteractionImpl implements PersistedInteraction {
/**
* The identifier from the server for this interaction outcome, or
* {@link ContextualSearchInteractionPersister#NO_EVENT_ID} if none.
*/
private final long mEventId;
/**
* The encoded results of the user interaction, often
* {@link ContextualSearchInteractionPersister#NO_INTERACTION} meaning no interaction.
*/
private final int mEncodedUserInteractions;
/**
* The time stamp of the user interaction, in milliseconds, or 0 if not known.
*/
private final long mTimestampMs;
/** Creates an empty interaction. */
PersistedInteractionImpl() {
this(NO_EVENT_ID, NO_INTERACTION, 0);
}
/** Creates an interaction with the given EventID and encoded user interactions. */
PersistedInteractionImpl(long eventId, int encodedUserInteractions, long timestamp) {
mEventId = eventId;
mEncodedUserInteractions = encodedUserInteractions;
mTimestampMs = timestamp;
}
@Override
public long getEventId() {
return mEventId;
}
@Override
public int getEncodedUserInteractions() {
return mEncodedUserInteractions;
}
@Override
public long getTimestampMs() {
return mTimestampMs;
}
}
}
...@@ -120,6 +120,12 @@ public interface ContextualSearchInteractionRecorder { ...@@ -120,6 +120,12 @@ public interface ContextualSearchInteractionRecorder {
@AssistRankerPrediction @AssistRankerPrediction
int getPredictionForTapSuppression(); int getPredictionForTapSuppression();
/** Stores an Event ID from the server that we should persist along with user interactions. */
void persistInteraction(long eventId);
/* Gets the {@link ContextualSearchInteractionPersister} managed by this interface. */
ContextualSearchInteractionPersister getInteractionPersister();
/** /**
* Resets the logger so that future log calls accumulate into a new record. * Resets the logger so that future log calls accumulate into a new record.
* Any accumulated logging for the current record is discarded. * Any accumulated logging for the current record is discarded.
......
...@@ -125,8 +125,11 @@ public class ContextualSearchManager ...@@ -125,8 +125,11 @@ public class ContextualSearchManager
private final ViewTreeObserver.OnGlobalFocusChangeListener mOnFocusChangeListener; private final ViewTreeObserver.OnGlobalFocusChangeListener mOnFocusChangeListener;
private final TabModelObserver mTabModelObserver; private final TabModelObserver mTabModelObserver;
// The Ranker logger to use to write Tap Suppression Ranker logs to UMA. /**
private final ContextualSearchInteractionRecorder mTapSuppressionInteractionRecorder; * The {@link ContextualSearchInteractionRecorder} to use to record user interactions and apply
* ML, etc.
*/
private final ContextualSearchInteractionRecorder mInteractionRecorder;
private final ContextualSearchSelectionClient mContextualSearchSelectionClient; private final ContextualSearchSelectionClient mContextualSearchSelectionClient;
...@@ -259,7 +262,7 @@ public class ContextualSearchManager ...@@ -259,7 +262,7 @@ public class ContextualSearchManager
mTranslateController = new ContextualSearchTranslateController(mPolicy, this); mTranslateController = new ContextualSearchTranslateController(mPolicy, this);
mInternalStateController = new ContextualSearchInternalStateController( mInternalStateController = new ContextualSearchInternalStateController(
mPolicy, getContextualSearchInternalStateHandler()); mPolicy, getContextualSearchInternalStateHandler());
mTapSuppressionInteractionRecorder = new ContextualSearchRankerLoggerImpl(); mInteractionRecorder = new ContextualSearchRankerLoggerImpl();
mContextualSearchSelectionClient = new ContextualSearchSelectionClient(); mContextualSearchSelectionClient = new ContextualSearchSelectionClient();
mInProductHelp = new ContextualSearchIPH(); mInProductHelp = new ContextualSearchIPH();
} }
...@@ -669,17 +672,20 @@ public class ContextualSearchManager ...@@ -669,17 +672,20 @@ public class ContextualSearchManager
* @param caption The caption to display. * @param caption The caption to display.
* @param quickActionUri The URI for the intent associated with the quick action. * @param quickActionUri The URI for the intent associated with the quick action.
* @param quickActionCategory The {@link QuickActionCategory} for the quick action. * @param quickActionCategory The {@link QuickActionCategory} for the quick action.
* @param loggedEventId The EventID logged by the server, which should be recorded and sent back
* to the server along with user action results in a subsequent request.
*/ */
@CalledByNative @CalledByNative
public void onSearchTermResolutionResponse(boolean isNetworkUnavailable, int responseCode, public void onSearchTermResolutionResponse(boolean isNetworkUnavailable, int responseCode,
final String searchTerm, final String displayText, final String alternateTerm, final String searchTerm, final String displayText, final String alternateTerm,
final String mid, boolean doPreventPreload, int selectionStartAdjust, final String mid, boolean doPreventPreload, int selectionStartAdjust,
int selectionEndAdjust, final String contextLanguage, final String thumbnailUrl, int selectionEndAdjust, final String contextLanguage, final String thumbnailUrl,
final String caption, final String quickActionUri, final int quickActionCategory) { final String caption, final String quickActionUri, final int quickActionCategory,
final long loggedEventId) {
mNetworkCommunicator.handleSearchTermResolutionResponse(isNetworkUnavailable, responseCode, mNetworkCommunicator.handleSearchTermResolutionResponse(isNetworkUnavailable, responseCode,
searchTerm, displayText, alternateTerm, mid, doPreventPreload, selectionStartAdjust, searchTerm, displayText, alternateTerm, mid, doPreventPreload, selectionStartAdjust,
selectionEndAdjust, contextLanguage, thumbnailUrl, caption, quickActionUri, selectionEndAdjust, contextLanguage, thumbnailUrl, caption, quickActionUri,
quickActionCategory); quickActionCategory, loggedEventId);
} }
@Override @Override
...@@ -687,7 +693,7 @@ public class ContextualSearchManager ...@@ -687,7 +693,7 @@ public class ContextualSearchManager
String searchTerm, String displayText, String alternateTerm, String mid, String searchTerm, String displayText, String alternateTerm, String mid,
boolean doPreventPreload, int selectionStartAdjust, int selectionEndAdjust, boolean doPreventPreload, int selectionStartAdjust, int selectionEndAdjust,
String contextLanguage, String thumbnailUrl, String caption, String quickActionUri, String contextLanguage, String thumbnailUrl, String caption, String quickActionUri,
int quickActionCategory) { int quickActionCategory, long loggedEventId) {
if (!mInternalStateController.isStillWorkingOn(InternalState.RESOLVING)) return; if (!mInternalStateController.isStillWorkingOn(InternalState.RESOLVING)) return;
// Show an appropriate message for what to search for. // Show an appropriate message for what to search for.
...@@ -779,6 +785,9 @@ public class ContextualSearchManager ...@@ -779,6 +785,9 @@ public class ContextualSearchManager
} }
} }
// Tell the Interaction Recorder about the current Event ID for persisted interaction.
mInteractionRecorder.persistInteraction(loggedEventId);
mInternalStateController.notifyFinishedWorkOn(InternalState.RESOLVING); mInternalStateController.notifyFinishedWorkOn(InternalState.RESOLVING);
} }
...@@ -1270,16 +1279,12 @@ public class ContextualSearchManager ...@@ -1270,16 +1279,12 @@ public class ContextualSearchManager
mInProductHelp.updateBubblePosition(); mInProductHelp.updateBubblePosition();
} }
/** /** @return The {@link SelectionClient} used by Contextual Search. */
* @return The {@link SelectionClient} used by Contextual Search.
*/
SelectionClient getContextualSearchSelectionClient() { SelectionClient getContextualSearchSelectionClient() {
return mContextualSearchSelectionClient; return mContextualSearchSelectionClient;
} }
/** /** Notifies Contextual Search whether the UI should be suppressed for Smart Selection. */
* Notifies Contextual Search whether the UI should be suppressed for Smart Selection.
*/
void suppressContextualSearchForSmartSelection(boolean isSmartSelectionEnabled) { void suppressContextualSearchForSmartSelection(boolean isSmartSelectionEnabled) {
mDoSuppressContextualSearchForSmartSelection = isSmartSelectionEnabled; mDoSuppressContextualSearchForSmartSelection = isSmartSelectionEnabled;
} }
...@@ -1349,9 +1354,7 @@ public class ContextualSearchManager ...@@ -1349,9 +1354,7 @@ public class ContextualSearchManager
} }
} }
/** /** @return Whether the display is in a full-screen video overlay mode. */
* @return Whether the display is in a full-screen video overlay mode.
*/
private boolean isOverlayVideoMode() { private boolean isOverlayVideoMode() {
return mActivity.getFullscreenManager() != null return mActivity.getFullscreenManager() != null
&& mActivity.getFullscreenManager().isOverlayVideoMode(); && mActivity.getFullscreenManager().isOverlayVideoMode();
...@@ -1424,7 +1427,7 @@ public class ContextualSearchManager ...@@ -1424,7 +1427,7 @@ public class ContextualSearchManager
if (mInternalStateController.isStillWorkingOn(InternalState.DECIDING_SUPPRESSION)) { if (mInternalStateController.isStillWorkingOn(InternalState.DECIDING_SUPPRESSION)) {
mInternalStateController.notifyFinishedWorkOn(InternalState.DECIDING_SUPPRESSION); mInternalStateController.notifyFinishedWorkOn(InternalState.DECIDING_SUPPRESSION);
} else { } else {
mTapSuppressionInteractionRecorder.reset(); mInteractionRecorder.reset();
} }
} }
...@@ -1577,8 +1580,12 @@ public class ContextualSearchManager ...@@ -1577,8 +1580,12 @@ public class ContextualSearchManager
} }
if (isTap && mPolicy.shouldPreviousTapResolve()) { if (isTap && mPolicy.shouldPreviousTapResolve()) {
mContext.setResolveProperties( ContextualSearchInteractionPersister.PersistedInteraction interaction =
mPolicy.getHomeCountry(mActivity), mPolicy.maySendBasePageUrl()); mInteractionRecorder.getInteractionPersister()
.getAndClearPersistedInteraction();
mContext.setResolveProperties(mPolicy.getHomeCountry(mActivity),
mPolicy.maySendBasePageUrl(), interaction.getEventId(),
interaction.getEncodedUserInteractions());
} }
WebContents webContents = getBaseWebContents(); WebContents webContents = getBaseWebContents();
if (webContents != null) { if (webContents != null) {
...@@ -1606,9 +1613,8 @@ public class ContextualSearchManager ...@@ -1606,9 +1613,8 @@ public class ContextualSearchManager
mSearchPanel.getPanelMetrics().writeInteractionOutcomesAndReset(); mSearchPanel.getPanelMetrics().writeInteractionOutcomesAndReset();
} }
// Set up the next batch of Ranker logging. // Set up the next batch of Ranker logging.
mTapSuppressionInteractionRecorder.setupLoggingForPage(getBaseWebContents()); mInteractionRecorder.setupLoggingForPage(getBaseWebContents());
mSearchPanel.getPanelMetrics().setInteractionRecorder( mSearchPanel.getPanelMetrics().setInteractionRecorder(mInteractionRecorder);
mTapSuppressionInteractionRecorder);
ContextualSearchUma.logRankerFeaturesAvailable(false); ContextualSearchUma.logRankerFeaturesAvailable(false);
mInternalStateController.notifyFinishedWorkOn(InternalState.TAP_GESTURE_COMMIT); mInternalStateController.notifyFinishedWorkOn(InternalState.TAP_GESTURE_COMMIT);
} }
...@@ -1618,8 +1624,7 @@ public class ContextualSearchManager ...@@ -1618,8 +1624,7 @@ public class ContextualSearchManager
public void decideSuppression() { public void decideSuppression() {
mInternalStateController.notifyStartingWorkOn(InternalState.DECIDING_SUPPRESSION); mInternalStateController.notifyStartingWorkOn(InternalState.DECIDING_SUPPRESSION);
// TODO(donnd): Move handleShouldSuppressTap out of the Selection Controller. // TODO(donnd): Move handleShouldSuppressTap out of the Selection Controller.
mSelectionController.handleShouldSuppressTap( mSelectionController.handleShouldSuppressTap(mContext, mInteractionRecorder);
mContext, mTapSuppressionInteractionRecorder);
} }
/** Starts showing the Tap UI by selecting a word around the current caret. */ /** Starts showing the Tap UI by selecting a word around the current caret. */
...@@ -1820,7 +1825,12 @@ public class ContextualSearchManager ...@@ -1820,7 +1825,12 @@ public class ContextualSearchManager
@VisibleForTesting @VisibleForTesting
ContextualSearchInteractionRecorder getRankerLogger() { ContextualSearchInteractionRecorder getRankerLogger() {
return mTapSuppressionInteractionRecorder; return mInteractionRecorder;
}
@VisibleForTesting
ContextualSearchContext getContext() {
return mContext;
} }
// ============================================================================================ // ============================================================================================
......
...@@ -39,12 +39,14 @@ public interface ContextualSearchNetworkCommunicator { ...@@ -39,12 +39,14 @@ public interface ContextualSearchNetworkCommunicator {
* @param caption The caption to display. * @param caption The caption to display.
* @param quickActionUri The URI for the intent associated with the quick action. * @param quickActionUri The URI for the intent associated with the quick action.
* @param quickActionCategory The {@link QuickActionCategory} for the quick action. * @param quickActionCategory The {@link QuickActionCategory} for the quick action.
* @param loggedEventId The EventID logged by the server, which should be recorded and sent back
* to the server along with user action results in a subsequent request.
*/ */
void handleSearchTermResolutionResponse(boolean isNetworkUnavailable, int responseCode, void handleSearchTermResolutionResponse(boolean isNetworkUnavailable, int responseCode,
String searchTerm, String displayText, String alternateTerm, String mid, String searchTerm, String displayText, String alternateTerm, String mid,
boolean doPreventPreload, int selectionStartAdjust, int selectionEndAdjust, boolean doPreventPreload, int selectionStartAdjust, int selectionEndAdjust,
String contextLanguage, String thumbnailUrl, String caption, String quickActionUri, String contextLanguage, String thumbnailUrl, String caption, String quickActionUri,
int quickActionCategory); int quickActionCategory, long loggedEventId);
/** /**
* @return Whether the device is currently online. * @return Whether the device is currently online.
......
...@@ -6,7 +6,6 @@ package org.chromium.chrome.browser.contextualsearch; ...@@ -6,7 +6,6 @@ package org.chromium.chrome.browser.contextualsearch;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import org.chromium.base.Log;
import org.chromium.base.VisibleForTesting; import org.chromium.base.VisibleForTesting;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
...@@ -18,19 +17,17 @@ import java.util.Map; ...@@ -18,19 +17,17 @@ import java.util.Map;
* Implements the UMA logging for Ranker that's used for Contextual Search Tap Suppression. * Implements the UMA logging for Ranker that's used for Contextual Search Tap Suppression.
*/ */
public class ContextualSearchRankerLoggerImpl implements ContextualSearchInteractionRecorder { public class ContextualSearchRankerLoggerImpl implements ContextualSearchInteractionRecorder {
private static final String TAG = "ContextualSearch";
// Names for all our features and labels. // Names for all our features and labels.
// Integer values should contain @Feature values only. // Map keys should contain @Feature int values only.
private static final Map<Integer, String> ALL_NAMES; private static final Map<Integer, String> ALL_NAMES;
@VisibleForTesting @VisibleForTesting
// Integer values should contain @Feature values only. // Map keys should contain @Feature int values only.
static final Map<Integer, String> OUTCOMES; static final Map<Integer, String> OUTCOMES;
@VisibleForTesting @VisibleForTesting
// Integer values should contain @Feature values only. // Map keys should contain @Feature int values only.
static final Map<Integer, String> FEATURES; static final Map<Integer, String> FEATURES;
static { static {
// Integer values should contain @Feature values only. // Map keys should contain @Feature int values only.
Map<Integer, String> outcomes = new HashMap<Integer, String>(); Map<Integer, String> outcomes = new HashMap<Integer, String>();
outcomes.put(Feature.OUTCOME_WAS_PANEL_OPENED, "OutcomeWasPanelOpened"); outcomes.put(Feature.OUTCOME_WAS_PANEL_OPENED, "OutcomeWasPanelOpened");
outcomes.put(Feature.OUTCOME_WAS_QUICK_ACTION_CLICKED, "OutcomeWasQuickActionClicked"); outcomes.put(Feature.OUTCOME_WAS_QUICK_ACTION_CLICKED, "OutcomeWasQuickActionClicked");
...@@ -75,7 +72,7 @@ public class ContextualSearchRankerLoggerImpl implements ContextualSearchInterac ...@@ -75,7 +72,7 @@ public class ContextualSearchRankerLoggerImpl implements ContextualSearchInterac
features.put(Feature.QUICK_ACTIONS_IGNORED_COUNT, "QuickActionsIgnored"); features.put(Feature.QUICK_ACTIONS_IGNORED_COUNT, "QuickActionsIgnored");
FEATURES = Collections.unmodifiableMap(features); FEATURES = Collections.unmodifiableMap(features);
// Integer values should contain @Feature values only. // Map keys should contain @Feature int values only.
Map<Integer, String> allNames = new HashMap<Integer, String>(); Map<Integer, String> allNames = new HashMap<Integer, String>();
allNames.putAll(outcomes); allNames.putAll(outcomes);
allNames.putAll(features); allNames.putAll(features);
...@@ -100,21 +97,34 @@ public class ContextualSearchRankerLoggerImpl implements ContextualSearchInterac ...@@ -100,21 +97,34 @@ public class ContextualSearchRankerLoggerImpl implements ContextualSearchInterac
AssistRankerPrediction.UNDETERMINED; AssistRankerPrediction.UNDETERMINED;
// Map that accumulates all of the Features to log for a specific user-interaction. // Map that accumulates all of the Features to log for a specific user-interaction.
// Integer values should contain @Feature values only. // Map keys should contain @Feature int values only.
private Map<Integer, Object> mFeaturesToLog; private Map<Integer, Object> mFeaturesToLog;
// A for-testing copy of all the features to log setup so that it will survive a {@link #reset}. // A for-testing copy of all the features to log setup so that it will survive a {@link #reset}.
// Integer values should contain @Feature values only. // Map keys should contain @Feature int values only.
private Map<Integer, Object> mFeaturesLoggedForTesting; private Map<Integer, Object> mFeaturesLoggedForTesting;
private Map<Integer, Object> mOutcomesLoggedForTesting; private Map<Integer, Object> mOutcomesLoggedForTesting;
private ContextualSearchInteractionPersister mInteractionPersister;
private long mEventIdToPersist;
/** /**
* Constructs a Ranker Logger and associated native implementation to write Contextual Search * Constructs a Ranker Logger and associated native implementation to write Contextual Search
* ML data to Ranker. * ML data to Ranker.
*/ */
public ContextualSearchRankerLoggerImpl() { public ContextualSearchRankerLoggerImpl() {
// TODO(donnd): remove when behind-the-flag bug fixed (crbug.com/786589). this(new ContextualSearchInteractionPersisterImpl());
Log.i(TAG, "Consructing ContextualSearchRankerLoggerImpl, enabled: %s", isEnabled()); }
/**
* Constructs a Ranker Logger implementation for testing.
* @param interactionPersister The {@link ContextualSearchInteractionPersister} to use for this
* instance.
*/
public ContextualSearchRankerLoggerImpl(
ContextualSearchInteractionPersister interactionPersister) {
mInteractionPersister = interactionPersister;
if (isEnabled()) mNativePointer = nativeInit(); if (isEnabled()) mNativePointer = nativeInit();
} }
...@@ -210,12 +220,27 @@ public class ContextualSearchRankerLoggerImpl implements ContextualSearchInterac ...@@ -210,12 +220,27 @@ public class ContextualSearchRankerLoggerImpl implements ContextualSearchInterac
} }
mOutcomesLoggedForTesting = mFeaturesToLog; mOutcomesLoggedForTesting = mFeaturesToLog;
ContextualSearchUma.logRecordedOutcomesToRanker(); ContextualSearchUma.logRecordedOutcomesToRanker();
// Also persist the outcomes if we are persisting this interaction.
if (mEventIdToPersist != 0) {
mInteractionPersister.persistInteractions(mEventIdToPersist, mFeaturesToLog);
mEventIdToPersist = 0;
}
} }
nativeWriteLogAndReset(mNativePointer); nativeWriteLogAndReset(mNativePointer);
} }
reset(); reset();
} }
@Override
public void persistInteraction(long eventId) {
if (eventId != 0) mEventIdToPersist = eventId;
}
@Override
public ContextualSearchInteractionPersister getInteractionPersister() {
return mInteractionPersister;
}
/** /**
* Logs the given feature/value to the internal map that accumulates an entire record (which can * Logs the given feature/value to the internal map that accumulates an entire record (which can
* be logged by calling writeLogAndReset). * be logged by calling writeLogAndReset).
......
...@@ -28,6 +28,7 @@ public class ContextualSearchUma { ...@@ -28,6 +28,7 @@ public class ContextualSearchUma {
// Constants to use for the original selection gesture // Constants to use for the original selection gesture
private static final boolean LONG_PRESS = false; private static final boolean LONG_PRESS = false;
private static final boolean TAP = true; private static final boolean TAP = true;
private static final int MILLIS_IN_A_DAY = 1000 * 60 * 60 * 24;
// Constants used to log UMA "enum" histograms about the Contextual Search's preference state. // Constants used to log UMA "enum" histograms about the Contextual Search's preference state.
@IntDef({Preference.UNINITIALIZED, Preference.ENABLED, Preference.DISABLED}) @IntDef({Preference.UNINITIALIZED, Preference.ENABLED, Preference.DISABLED})
...@@ -1307,6 +1308,17 @@ public class ContextualSearchUma { ...@@ -1307,6 +1308,17 @@ public class ContextualSearchUma {
"Search.ContextualSearchPrevious28DayCtr", previous28DayCtr); "Search.ContextualSearchPrevious28DayCtr", previous28DayCtr);
} }
/**
* Logs a duration since the outcomes (and associated timestamp) were saved in persistent
* storage.
* @param durationMs The duration to log, in milliseconds.
*/
public static void logOutcomesTimestamp(long durationMs) {
int durationInDays = (int) (durationMs / MILLIS_IN_A_DAY);
RecordHistogram.recordCount100Histogram(
"Search.ContextualSearch.OutcomesDuration", durationInDays);
}
/** /**
* Get the encoded value to use for the Bar Overlap histogram by encoding all the input * Get the encoded value to use for the Bar Overlap histogram by encoding all the input
* parameters. * parameters.
......
...@@ -89,6 +89,22 @@ public class ChromePreferenceManager { ...@@ -89,6 +89,22 @@ public class ChromePreferenceManager {
*/ */
public static final String CONTEXTUAL_SEARCH_PRE_UNIFIED_CONSENT_PREF = public static final String CONTEXTUAL_SEARCH_PRE_UNIFIED_CONSENT_PREF =
"contextual_search_pre_unified_consent_pref"; "contextual_search_pre_unified_consent_pref";
/**
* A user interaction event ID for interaction with Contextual Search, stored as a long.
*/
public static final String CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_EVENT_ID =
"contextual_search_previous_interaction_event_id";
/**
* An encoded set of outcomes of user interaction with Contextual Search, stored as an int.
*/
public static final String CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_ENCODED_OUTCOMES =
"contextual_search_previous_interaction_encoded_outcomes";
/**
* A timestamp indicating when we updated the user interaction with Contextual Search, stored
* as a long, with resolution in days.
*/
public static final String CONTEXTUAL_SEARCH_PREVIOUS_INTERACTION_TIMESTAMP =
"contextual_search_previous_interaction_timestamp";
/** /**
* Whether the promotion for data reduction has been skipped on first invocation. * Whether the promotion for data reduction has been skipped on first invocation.
...@@ -552,7 +568,7 @@ public class ChromePreferenceManager { ...@@ -552,7 +568,7 @@ public class ChromePreferenceManager {
* @param key The name of the preference to modify. * @param key The name of the preference to modify.
* @param value The new value for the preference. * @param value The new value for the preference.
*/ */
private void writeLong(String key, long value) { public void writeLong(String key, long value) {
SharedPreferences.Editor ed = mSharedPreferences.edit(); SharedPreferences.Editor ed = mSharedPreferences.edit();
ed.putLong(key, value); ed.putLong(key, value);
ed.apply(); ed.apply();
...@@ -565,7 +581,7 @@ public class ChromePreferenceManager { ...@@ -565,7 +581,7 @@ public class ChromePreferenceManager {
* @param defaultValue The default value to return if there's no value stored. * @param defaultValue The default value to return if there's no value stored.
* @return The value of the preference if stored; defaultValue otherwise. * @return The value of the preference if stored; defaultValue otherwise.
*/ */
private long readLong(String key, long defaultValue) { public long readLong(String key, long defaultValue) {
return mSharedPreferences.getLong(key, defaultValue); return mSharedPreferences.getLong(key, defaultValue);
} }
......
...@@ -382,6 +382,8 @@ chrome_java_sources = [ ...@@ -382,6 +382,8 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchHeuristic.java", "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchHeuristic.java",
"java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchHeuristics.java", "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchHeuristics.java",
"java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchIPH.java", "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchIPH.java",
"java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInteractionPersister.java",
"java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInteractionPersisterImpl.java",
"java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInteractionRecorder.java", "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInteractionRecorder.java",
"java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateController.java", "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateController.java",
"java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateHandler.java", "java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateHandler.java",
......
...@@ -32,6 +32,7 @@ import java.util.concurrent.TimeoutException; ...@@ -32,6 +32,7 @@ import java.util.concurrent.TimeoutException;
@VisibleForTesting @VisibleForTesting
class ContextualSearchFakeServer class ContextualSearchFakeServer
implements ContextualSearchNetworkCommunicator, OverlayPanelContentFactory { implements ContextualSearchNetworkCommunicator, OverlayPanelContentFactory {
static final long LOGGED_EVENT_ID = 2 ^ 50; // Arbitrary value larger than 32 bits.
private final ContextualSearchPolicy mPolicy; private final ContextualSearchPolicy mPolicy;
...@@ -181,6 +182,7 @@ class ContextualSearchFakeServer ...@@ -181,6 +182,7 @@ class ContextualSearchFakeServer
private final String mCaption; private final String mCaption;
private final String mQuickActionUri; private final String mQuickActionUri;
private final int mQuickActionCategory; private final int mQuickActionCategory;
private final long mLoggedEventId;
boolean mDidStartResolution; boolean mDidStartResolution;
boolean mDidFinishResolution; boolean mDidFinishResolution;
...@@ -201,12 +203,15 @@ class ContextualSearchFakeServer ...@@ -201,12 +203,15 @@ class ContextualSearchFakeServer
* @param caption The caption to display. * @param caption The caption to display.
* @param quickActionUri The URI for the intent associated with the quick action. * @param quickActionUri The URI for the intent associated with the quick action.
* @param quickActionCategory The category for the quick action. * @param quickActionCategory The category for the quick action.
* @param loggedEventId The EventID logged by the server, which should be recorded
* and sent back to the server along with user action results
* in a subsequent request.
*/ */
FakeTapSearch(String nodeId, boolean isNetworkUnavailable, int responseCode, FakeTapSearch(String nodeId, boolean isNetworkUnavailable, int responseCode,
String searchTerm, String displayText, String alternateTerm, String mid, String searchTerm, String displayText, String alternateTerm, String mid,
boolean doPreventPreload, int startAdjust, int endAdjust, String contextLanguage, boolean doPreventPreload, int startAdjust, int endAdjust, String contextLanguage,
String thumbnailUrl, String caption, String quickActionUri, String thumbnailUrl, String caption, String quickActionUri, int quickActionCategory,
int quickActionCategory) { long loggedEventId) {
super(nodeId); super(nodeId);
mIsNetworkUnavailable = isNetworkUnavailable; mIsNetworkUnavailable = isNetworkUnavailable;
...@@ -223,6 +228,21 @@ class ContextualSearchFakeServer ...@@ -223,6 +228,21 @@ class ContextualSearchFakeServer
mCaption = caption; mCaption = caption;
mQuickActionUri = quickActionUri; mQuickActionUri = quickActionUri;
mQuickActionCategory = quickActionCategory; mQuickActionCategory = quickActionCategory;
mLoggedEventId = loggedEventId;
}
/**
* @param nodeId The id of the node where the touch event will be simulated.
* @param isNetworkUnavailable Whether the network is unavailable.
* @param responseCode The HTTP response code of the resolution.
* @param searchTerm The resolved search term.
* @param displayText The display text.
*/
FakeTapSearch(String nodeId, boolean isNetworkUnavailable, int responseCode,
String searchTerm, String displayText) {
this(nodeId, isNetworkUnavailable, responseCode, searchTerm, displayText,
"alternate-term", "", false, -7, 0, "", "", "", "", QuickActionCategory.NONE,
0L);
} }
@Override @Override
...@@ -295,7 +315,7 @@ class ContextualSearchFakeServer ...@@ -295,7 +315,7 @@ class ContextualSearchFakeServer
handleSearchTermResolutionResponse(mIsNetworkUnavailable, mResponseCode, handleSearchTermResolutionResponse(mIsNetworkUnavailable, mResponseCode,
mSearchTerm, mDisplayText, mAlternateTerm, mMid, mDoPreventPreload, mSearchTerm, mDisplayText, mAlternateTerm, mMid, mDoPreventPreload,
mStartAdjust, mEndAdjust, mContextLanguage, mThumbnailUrl, mCaption, mStartAdjust, mEndAdjust, mContextLanguage, mThumbnailUrl, mCaption,
mQuickActionUri, mQuickActionCategory); mQuickActionUri, mQuickActionCategory, mLoggedEventId);
mActiveFakeTapSearch = null; mActiveFakeTapSearch = null;
mDidFinishResolution = true; mDidFinishResolution = true;
...@@ -333,11 +353,11 @@ class ContextualSearchFakeServer ...@@ -333,11 +353,11 @@ class ContextualSearchFakeServer
FakeSlowResolveSearch(String nodeId, boolean isNetworkUnavailable, int responseCode, FakeSlowResolveSearch(String nodeId, boolean isNetworkUnavailable, int responseCode,
String searchTerm, String displayText, String alternateTerm, String mid, String searchTerm, String displayText, String alternateTerm, String mid,
boolean doPreventPreload, int startAdjust, int endAdjust, String contextLanguage, boolean doPreventPreload, int startAdjust, int endAdjust, String contextLanguage,
String thumbnailUrl, String caption, String quickActionUri, String thumbnailUrl, String caption, String quickActionUri, int quickActionCategory,
int quickActionCategory) { long loggedEventId) {
super(nodeId, isNetworkUnavailable, responseCode, searchTerm, displayText, super(nodeId, isNetworkUnavailable, responseCode, searchTerm, displayText,
alternateTerm, mid, doPreventPreload, startAdjust, endAdjust, contextLanguage, alternateTerm, mid, doPreventPreload, startAdjust, endAdjust, contextLanguage,
thumbnailUrl, caption, quickActionUri, quickActionCategory); thumbnailUrl, caption, quickActionUri, quickActionCategory, loggedEventId);
} }
@Override @Override
...@@ -563,11 +583,11 @@ class ContextualSearchFakeServer ...@@ -563,11 +583,11 @@ class ContextualSearchFakeServer
String searchTerm, String displayText, String alternateTerm, String mid, String searchTerm, String displayText, String alternateTerm, String mid,
boolean doPreventPreload, int selectionStartAdjust, int selectionEndAdjust, boolean doPreventPreload, int selectionStartAdjust, int selectionEndAdjust,
String contextLanguage, String thumbnailUrl, String caption, String quickActionUri, String contextLanguage, String thumbnailUrl, String caption, String quickActionUri,
int quickActionCategory) { int quickActionCategory, long loggedEventId) {
mBaseManager.handleSearchTermResolutionResponse(isNetworkUnavailable, responseCode, mBaseManager.handleSearchTermResolutionResponse(isNetworkUnavailable, responseCode,
searchTerm, displayText, alternateTerm, mid, doPreventPreload, selectionStartAdjust, searchTerm, displayText, alternateTerm, mid, doPreventPreload, selectionStartAdjust,
selectionEndAdjust, contextLanguage, thumbnailUrl, caption, quickActionUri, selectionEndAdjust, contextLanguage, thumbnailUrl, caption, quickActionUri,
quickActionCategory); quickActionCategory, loggedEventId);
} }
@Override @Override
...@@ -612,26 +632,27 @@ class ContextualSearchFakeServer ...@@ -612,26 +632,27 @@ class ContextualSearchFakeServer
registerFakeLongPressSearch(new FakeLongPressSearch("term", "Term")); registerFakeLongPressSearch(new FakeLongPressSearch("term", "Term"));
registerFakeLongPressSearch(new FakeLongPressSearch("resolution", "Resolution")); registerFakeLongPressSearch(new FakeLongPressSearch("resolution", "Resolution"));
registerFakeTapSearch(new FakeTapSearch("search", false, 200, "Search", "Search", registerFakeTapSearch(new FakeTapSearch("search", false, 200, "Search", "Search"));
"alternate-term", "", false, 0, 0, "", "", "", "", QuickActionCategory.NONE)); registerFakeTapSearch(new FakeTapSearch("term", false, 200, "Term", "Term"));
registerFakeTapSearch(new FakeTapSearch("term", false, 200, "Term", "Term", registerFakeTapSearch(
"alternate-term", "", false, 0, 0, "", "", "", "", QuickActionCategory.NONE)); new FakeTapSearch("resolution", false, 200, "Resolution", "Resolution"));
registerFakeTapSearch(new FakeTapSearch("resolution", false, 200, "Resolution",
"Resolution", "alternate-term", "", false, 0, 0, "", "", "", "",
QuickActionCategory.NONE));
registerFakeTapSearch(new FakeTapSearch("german", false, 200, "Deutsche", "Deutsche", registerFakeTapSearch(new FakeTapSearch("german", false, 200, "Deutsche", "Deutsche",
"alternate-term", "", false, 0, 0, "de", "", "", "", QuickActionCategory.NONE)); "alternate-term", "", false, 0, 0, "de", "", "", "", QuickActionCategory.NONE, 0));
registerFakeTapSearch(new FakeTapSearch("intelligence", false, 200, "Intelligence", registerFakeTapSearch(
"Intelligence", "alternate-term", "", false, 0, 0, "", "", "", "", new FakeTapSearch("intelligence", false, 200, "Intelligence", "Intelligence"));
QuickActionCategory.NONE));
// Register a fake tap search that will fake a logged event ID from the server.
registerFakeTapSearch(new FakeTapSearch("intelligence-logged-event-id", false, 200,
"Intelligence", "Intelligence", "alternate-term", "", false, 0, 0, "", "", "", "",
QuickActionCategory.NONE, LOGGED_EVENT_ID));
// Register a resolving search of "States" that expands to "United States". // Register a resolving search of "States" that expands to "United States".
registerFakeSlowResolveSearch(new FakeSlowResolveSearch("states", false, 200, "States", registerFakeSlowResolveSearch(new FakeSlowResolveSearch("states", false, 200, "States",
"States", "alternate-term", "", false, -7, 0, "", "", "", "", "States", "alternate-term", "", false, -7, 0, "", "", "", "",
QuickActionCategory.NONE)); QuickActionCategory.NONE, 0));
registerFakeSlowResolveSearch(new FakeSlowResolveSearch("search", false, 200, "Search", registerFakeSlowResolveSearch(new FakeSlowResolveSearch("search", false, 200, "Search",
"Search", "alternate-term", "", false, 0, 0, "", "", "", "", "Search", "alternate-term", "", false, 0, 0, "", "", "", "",
QuickActionCategory.NONE)); QuickActionCategory.NONE, 0));
} }
/** /**
......
...@@ -36,6 +36,7 @@ import org.junit.runner.RunWith; ...@@ -36,6 +36,7 @@ import org.junit.runner.RunWith;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils; import org.chromium.base.ThreadUtils;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.DisableIf; import org.chromium.base.test.util.DisableIf;
...@@ -274,8 +275,10 @@ public class ContextualSearchManagerTest { ...@@ -274,8 +275,10 @@ public class ContextualSearchManagerTest {
/** Clears stored metadata about preference state before Unified Consent became active. */ /** Clears stored metadata about preference state before Unified Consent became active. */
private void clearUnifiedConsentMetadata() { private void clearUnifiedConsentMetadata() {
mPolicy.applyUnifiedConsentGivenMetadata( if (mPolicy != null) {
ContextualSearchPreviousPreferenceMetadata.UNKNOWN); mPolicy.applyUnifiedConsentGivenMetadata(
ContextualSearchPreviousPreferenceMetadata.UNKNOWN);
}
} }
//============================================================================================ //============================================================================================
...@@ -469,12 +472,13 @@ public class ContextualSearchManagerTest { ...@@ -469,12 +472,13 @@ public class ContextualSearchManagerTest {
private final String mCaption; private final String mCaption;
private final String mQuickActionUri; private final String mQuickActionUri;
private final int mQuickActionCategory; private final int mQuickActionCategory;
private final long mLoggedEventId;
public FakeResponseOnMainThread(boolean isNetworkUnavailable, int responseCode, public FakeResponseOnMainThread(boolean isNetworkUnavailable, int responseCode,
String searchTerm, String displayText, String alternateTerm, String mid, String searchTerm, String displayText, String alternateTerm, String mid,
boolean doPreventPreload, int startAdjust, int endAdjudst, String contextLanguage, boolean doPreventPreload, int startAdjust, int endAdjudst, String contextLanguage,
String thumbnailUrl, String caption, String quickActionUri, String thumbnailUrl, String caption, String quickActionUri, int quickActionCategory,
int quickActionCategory) { long loggedEventId) {
mIsNetworkUnavailable = isNetworkUnavailable; mIsNetworkUnavailable = isNetworkUnavailable;
mResponseCode = responseCode; mResponseCode = responseCode;
mSearchTerm = searchTerm; mSearchTerm = searchTerm;
...@@ -489,6 +493,7 @@ public class ContextualSearchManagerTest { ...@@ -489,6 +493,7 @@ public class ContextualSearchManagerTest {
mCaption = caption; mCaption = caption;
mQuickActionUri = quickActionUri; mQuickActionUri = quickActionUri;
mQuickActionCategory = quickActionCategory; mQuickActionCategory = quickActionCategory;
mLoggedEventId = loggedEventId;
} }
@Override @Override
...@@ -496,7 +501,7 @@ public class ContextualSearchManagerTest { ...@@ -496,7 +501,7 @@ public class ContextualSearchManagerTest {
mFakeServer.handleSearchTermResolutionResponse(mIsNetworkUnavailable, mResponseCode, mFakeServer.handleSearchTermResolutionResponse(mIsNetworkUnavailable, mResponseCode,
mSearchTerm, mDisplayText, mAlternateTerm, mMid, mDoPreventPreload, mSearchTerm, mDisplayText, mAlternateTerm, mMid, mDoPreventPreload,
mStartAdjust, mEndAdjust, mContextLanguage, mThumbnailUrl, mCaption, mStartAdjust, mEndAdjust, mContextLanguage, mThumbnailUrl, mCaption,
mQuickActionUri, mQuickActionCategory); mQuickActionUri, mQuickActionCategory, mLoggedEventId);
} }
} }
...@@ -507,7 +512,7 @@ public class ContextualSearchManagerTest { ...@@ -507,7 +512,7 @@ public class ContextualSearchManagerTest {
private void fakeResponse(boolean isNetworkUnavailable, int responseCode, private void fakeResponse(boolean isNetworkUnavailable, int responseCode,
String searchTerm, String displayText, String alternateTerm, boolean doPreventPreload) { String searchTerm, String displayText, String alternateTerm, boolean doPreventPreload) {
fakeResponse(isNetworkUnavailable, responseCode, searchTerm, displayText, alternateTerm, fakeResponse(isNetworkUnavailable, responseCode, searchTerm, displayText, alternateTerm,
null, doPreventPreload, 0, 0, "", "", "", "", QuickActionCategory.NONE); null, doPreventPreload, 0, 0, "", "", "", "", QuickActionCategory.NONE, 0);
} }
/** /**
...@@ -517,12 +522,12 @@ public class ContextualSearchManagerTest { ...@@ -517,12 +522,12 @@ public class ContextualSearchManagerTest {
private void fakeResponse(boolean isNetworkUnavailable, int responseCode, String searchTerm, private void fakeResponse(boolean isNetworkUnavailable, int responseCode, String searchTerm,
String displayText, String alternateTerm, String mid, boolean doPreventPreload, String displayText, String alternateTerm, String mid, boolean doPreventPreload,
int startAdjust, int endAdjust, String contextLanguage, String thumbnailUrl, int startAdjust, int endAdjust, String contextLanguage, String thumbnailUrl,
String caption, String quickActionUri, int quickActionCategory) { String caption, String quickActionUri, int quickActionCategory, long loggedEventId) {
if (mFakeServer.getSearchTermRequested() != null) { if (mFakeServer.getSearchTermRequested() != null) {
InstrumentationRegistry.getInstrumentation().runOnMainSync(new FakeResponseOnMainThread( InstrumentationRegistry.getInstrumentation().runOnMainSync(new FakeResponseOnMainThread(
isNetworkUnavailable, responseCode, searchTerm, displayText, alternateTerm, mid, isNetworkUnavailable, responseCode, searchTerm, displayText, alternateTerm, mid,
doPreventPreload, startAdjust, endAdjust, contextLanguage, thumbnailUrl, doPreventPreload, startAdjust, endAdjust, contextLanguage, thumbnailUrl,
caption, quickActionUri, quickActionCategory)); caption, quickActionUri, quickActionCategory, loggedEventId));
} }
} }
...@@ -2378,7 +2383,7 @@ public class ContextualSearchManagerTest { ...@@ -2378,7 +2383,7 @@ public class ContextualSearchManagerTest {
waitForPanelToPeek(); waitForPanelToPeek();
fakeResponse(false, 200, "Intelligence", "United States Intelligence", "alternate-term", fakeResponse(false, 200, "Intelligence", "United States Intelligence", "alternate-term",
null, false, -14, 0, "", "", "", "", QuickActionCategory.NONE); null, false, -14, 0, "", "", "", "", QuickActionCategory.NONE, 0);
waitForSelectionToBe("United States Intelligence"); waitForSelectionToBe("United States Intelligence");
} }
...@@ -3320,4 +3325,30 @@ public class ContextualSearchManagerTest { ...@@ -3320,4 +3325,30 @@ public class ContextualSearchManagerTest {
} }
}); });
} }
@Test
@SmallTest
@Feature({"ContextualSearch"})
public void testLoggedEventId() throws InterruptedException, TimeoutException {
mFakeServer.reset();
simulateTapSearch("intelligence-logged-event-id");
tapPeekingBarToExpandAndAssert();
closePanel();
// Now the event and outcome should be in local storage.
simulateTapSearch("search");
// Check that we sent the logged event ID and outcome with the request.
Assert.assertEquals(ContextualSearchFakeServer.LOGGED_EVENT_ID,
mManager.getContext().getPreviousEventId());
Assert.assertEquals(1, mManager.getContext().getPreviousUserInteractions());
closePanel();
// Now that we've sent them to the server, the local storage should be clear.
simulateTapSearch("search");
Assert.assertEquals(0, mManager.getContext().getPreviousEventId());
Assert.assertEquals(0, mManager.getContext().getPreviousUserInteractions());
closePanel();
// Make sure a duration was recorded in bucket 0 (due to 0 days duration running this test).
Assert.assertEquals(1,
RecordHistogram.getHistogramValueCountForTesting(
"Search.ContextualSearch.OutcomesDuration", 0));
}
} }
...@@ -99,7 +99,7 @@ public class ContextualSearchTapEventTest { ...@@ -99,7 +99,7 @@ public class ContextualSearchTapEventTest {
public void startSearchTermResolutionRequest(String selection) { public void startSearchTermResolutionRequest(String selection) {
// Skip native calls and immediately "resolve" the search term. // Skip native calls and immediately "resolve" the search term.
onSearchTermResolutionResponse(true, 200, selection, selection, "", "", false, 0, 10, onSearchTermResolutionResponse(true, 200, selection, selection, "", "", false, 0, 10,
"", "", "", "", QuickActionCategory.NONE); "", "", "", "", QuickActionCategory.NONE, 0);
} }
@Override @Override
......
...@@ -37,8 +37,9 @@ public class ContextualSearchContextForTest extends ContextualSearchContext { ...@@ -37,8 +37,9 @@ public class ContextualSearchContextForTest extends ContextualSearchContext {
protected void nativeDestroy(long nativeContextualSearchContext) {} protected void nativeDestroy(long nativeContextualSearchContext) {}
@Override @Override
protected void nativeSetResolveProperties( protected void nativeSetResolveProperties(long nativeContextualSearchContext,
long nativeContextualSearchContext, String homeCountry, boolean maySendBasePageUrl) {} String homeCountry, boolean maySendBasePageUrl, long previousEventId,
int previousEventResults) {}
@Override @Override
protected void nativeAdjustSelection( protected void nativeAdjustSelection(
......
...@@ -56,13 +56,13 @@ public class ContextualSearchContextTest { ...@@ -56,13 +56,13 @@ public class ContextualSearchContextTest {
private void setupResolvingTapInBarak() { private void setupResolvingTapInBarak() {
setupTapInBarack(); setupTapInBarack();
mContext.setResolveProperties(HOME_COUNTRY, true); mContext.setResolveProperties(HOME_COUNTRY, true, 0, 0);
} }
private void setupResolvingTapInObama() { private void setupResolvingTapInObama() {
int obamaBeforeMOffset = "Now Barack Oba".length(); int obamaBeforeMOffset = "Now Barack Oba".length();
mContext.setSurroundingText(UTF_8, SAMPLE_TEXT, obamaBeforeMOffset, obamaBeforeMOffset); mContext.setSurroundingText(UTF_8, SAMPLE_TEXT, obamaBeforeMOffset, obamaBeforeMOffset);
mContext.setResolveProperties(HOME_COUNTRY, true); mContext.setResolveProperties(HOME_COUNTRY, true, 0, 0);
} }
@Test @Test
......
...@@ -56,10 +56,14 @@ void ContextualSearchContext::SetResolveProperties( ...@@ -56,10 +56,14 @@ void ContextualSearchContext::SetResolveProperties(
JNIEnv* env, JNIEnv* env,
jobject obj, jobject obj,
const base::android::JavaParamRef<jstring>& j_home_country, const base::android::JavaParamRef<jstring>& j_home_country,
jboolean j_may_send_base_page_url) { jboolean j_may_send_base_page_url,
jlong j_previous_event_id,
jint j_previous_event_results) {
can_resolve = true; can_resolve = true;
home_country = base::android::ConvertJavaStringToUTF8(env, j_home_country); home_country = base::android::ConvertJavaStringToUTF8(env, j_home_country);
can_send_base_page_url = j_may_send_base_page_url; can_send_base_page_url = j_may_send_base_page_url;
previous_event_id = j_previous_event_id;
previous_event_results = j_previous_event_results;
} }
void ContextualSearchContext::AdjustSelection(JNIEnv* env, void ContextualSearchContext::AdjustSelection(JNIEnv* env,
...@@ -105,6 +109,14 @@ const std::string ContextualSearchContext::GetHomeCountry() const { ...@@ -105,6 +109,14 @@ const std::string ContextualSearchContext::GetHomeCountry() const {
return home_country; return home_country;
} }
int64_t ContextualSearchContext::GetPreviousEventId() const {
return previous_event_id;
}
int ContextualSearchContext::GetPreviousEventResults() const {
return previous_event_results;
}
void ContextualSearchContext::SetSelectionSurroundings( void ContextualSearchContext::SetSelectionSurroundings(
int start_offset, int start_offset,
int end_offset, int end_offset,
......
...@@ -44,7 +44,9 @@ struct ContextualSearchContext { ...@@ -44,7 +44,9 @@ struct ContextualSearchContext {
JNIEnv* env, JNIEnv* env,
jobject obj, jobject obj,
const base::android::JavaParamRef<jstring>& j_home_country, const base::android::JavaParamRef<jstring>& j_home_country,
jboolean j_may_send_base_page_url); jboolean j_may_send_base_page_url,
jlong j_previous_event_id,
jint j_previous_event_results);
// Adjust the current selection offsets by the given signed amounts. // Adjust the current selection offsets by the given signed amounts.
void AdjustSelection(JNIEnv* env, void AdjustSelection(JNIEnv* env,
...@@ -81,6 +83,9 @@ struct ContextualSearchContext { ...@@ -81,6 +83,9 @@ struct ContextualSearchContext {
// characters). // characters).
int GetEndOffset() const; int GetEndOffset() const;
int64_t GetPreviousEventId() const;
int GetPreviousEventResults() const;
// Detects the language of the context using CLD from the translate utility. // Detects the language of the context using CLD from the translate utility.
base::android::ScopedJavaLocalRef<jstring> DetectLanguage( base::android::ScopedJavaLocalRef<jstring> DetectLanguage(
JNIEnv* env, JNIEnv* env,
...@@ -99,6 +104,8 @@ struct ContextualSearchContext { ...@@ -99,6 +104,8 @@ struct ContextualSearchContext {
base::string16 surrounding_text; base::string16 surrounding_text;
int start_offset; int start_offset;
int end_offset; int end_offset;
int64_t previous_event_id;
int previous_event_results;
// The linked Java object. // The linked Java object.
base::android::ScopedJavaGlobalRef<jobject> java_object_; base::android::ScopedJavaGlobalRef<jobject> java_object_;
......
...@@ -146,7 +146,7 @@ void ContextualSearchDelegate::StartSearchTermResolutionRequest( ...@@ -146,7 +146,7 @@ void ContextualSearchDelegate::StartSearchTermResolutionRequest(
void ContextualSearchDelegate::ResolveSearchTermFromContext() { void ContextualSearchDelegate::ResolveSearchTermFromContext() {
DCHECK(context_ != nullptr); DCHECK(context_ != nullptr);
GURL request_url(BuildRequestUrl(context_->GetHomeCountry())); GURL request_url(BuildRequestUrl(context_.get()));
DCHECK(request_url.is_valid()); DCHECK(request_url.is_valid());
auto resource_request = std::make_unique<network::ResourceRequest>(); auto resource_request = std::make_unique<network::ResourceRequest>();
...@@ -212,12 +212,14 @@ ContextualSearchDelegate::GetResolvedSearchTermFromJson( ...@@ -212,12 +212,14 @@ ContextualSearchDelegate::GetResolvedSearchTermFromJson(
std::string thumbnail_url = ""; std::string thumbnail_url = "";
std::string caption = ""; std::string caption = "";
std::string quick_action_uri = ""; std::string quick_action_uri = "";
int64_t logged_event_id = 0;
QuickActionCategory quick_action_category = QUICK_ACTION_CATEGORY_NONE; QuickActionCategory quick_action_category = QUICK_ACTION_CATEGORY_NONE;
DecodeSearchTermFromJsonResponse( DecodeSearchTermFromJsonResponse(
json_string, &search_term, &display_text, &alternate_term, &mid, json_string, &search_term, &display_text, &alternate_term, &mid,
&prevent_preload, &mention_start, &mention_end, &context_language, &prevent_preload, &mention_start, &mention_end, &context_language,
&thumbnail_url, &caption, &quick_action_uri, &quick_action_category); &thumbnail_url, &caption, &quick_action_uri, &quick_action_category,
&logged_event_id);
if (mention_start != 0 || mention_end != 0) { if (mention_start != 0 || mention_end != 0) {
// Sanity check that our selection is non-zero and it is less than // Sanity check that our selection is non-zero and it is less than
// 100 characters as that would make contextual search bar hide. // 100 characters as that would make contextual search bar hide.
...@@ -239,11 +241,11 @@ ContextualSearchDelegate::GetResolvedSearchTermFromJson( ...@@ -239,11 +241,11 @@ ContextualSearchDelegate::GetResolvedSearchTermFromJson(
is_invalid, response_code, search_term, display_text, alternate_term, mid, is_invalid, response_code, search_term, display_text, alternate_term, mid,
prevent_preload == kDoPreventPreloadValue, start_adjust, end_adjust, prevent_preload == kDoPreventPreloadValue, start_adjust, end_adjust,
context_language, thumbnail_url, caption, quick_action_uri, context_language, thumbnail_url, caption, quick_action_uri,
quick_action_category)); quick_action_category, logged_event_id));
} }
std::string ContextualSearchDelegate::BuildRequestUrl( std::string ContextualSearchDelegate::BuildRequestUrl(
std::string home_country) { ContextualSearchContext* context) {
if (!template_url_service_ || if (!template_url_service_ ||
!template_url_service_->GetDefaultSearchProvider()) { !template_url_service_->GetDefaultSearchProvider()) {
return std::string(); return std::string();
...@@ -261,8 +263,9 @@ std::string ContextualSearchDelegate::BuildRequestUrl( ...@@ -261,8 +263,9 @@ std::string ContextualSearchDelegate::BuildRequestUrl(
} }
TemplateURLRef::SearchTermsArgs::ContextualSearchParams params( TemplateURLRef::SearchTermsArgs::ContextualSearchParams params(
kContextualSearchRequestVersion, contextual_cards_version, home_country, kContextualSearchRequestVersion, contextual_cards_version,
0L, 0); context->GetHomeCountry(), context->GetPreviousEventId(),
context->GetPreviousEventResults());
search_terms_args.contextual_search_params = params; search_terms_args.contextual_search_params = params;
...@@ -427,7 +430,8 @@ void ContextualSearchDelegate::DecodeSearchTermFromJsonResponse( ...@@ -427,7 +430,8 @@ void ContextualSearchDelegate::DecodeSearchTermFromJsonResponse(
std::string* thumbnail_url, std::string* thumbnail_url,
std::string* caption, std::string* caption,
std::string* quick_action_uri, std::string* quick_action_uri,
QuickActionCategory* quick_action_category) { QuickActionCategory* quick_action_category,
int64_t* logged_event_id) {
bool contains_xssi_escape = bool contains_xssi_escape =
base::StartsWith(response, kXssiEscape, base::CompareCase::SENSITIVE); base::StartsWith(response, kXssiEscape, base::CompareCase::SENSITIVE);
const std::string& proper_json = const std::string& proper_json =
...@@ -511,6 +515,13 @@ void ContextualSearchDelegate::DecodeSearchTermFromJsonResponse( ...@@ -511,6 +515,13 @@ void ContextualSearchDelegate::DecodeSearchTermFromJsonResponse(
DVLOG(0) << "The Contextual Cards backend response: "; DVLOG(0) << "The Contextual Cards backend response: ";
DVLOG(0) << contextual_cards_diagnostic; DVLOG(0) << contextual_cards_diagnostic;
} }
// Get the Event ID to use for sending event outcomes back to the server.
std::string logged_event_id_string;
dict->GetString("logged_event_id", &logged_event_id_string);
if (!logged_event_id_string.empty()) {
*logged_event_id = std::stoll(logged_event_id_string, nullptr);
}
} }
// Extract the Start/End of the mentions in the surrounding text // Extract the Start/End of the mentions in the surrounding text
......
...@@ -104,8 +104,8 @@ class ContextualSearchDelegate ...@@ -104,8 +104,8 @@ class ContextualSearchDelegate
void ResolveSearchTermFromContext(); void ResolveSearchTermFromContext();
// Builds and returns the search term resolution request URL. // Builds and returns the search term resolution request URL.
// |selection| is used as the default query. // |context| is used to help build the query.
std::string BuildRequestUrl(std::string selection); std::string BuildRequestUrl(ContextualSearchContext* context);
// Uses the TemplateURL service to construct a search term resolution URL from // Uses the TemplateURL service to construct a search term resolution URL from
// the given parameters. // the given parameters.
...@@ -149,7 +149,8 @@ class ContextualSearchDelegate ...@@ -149,7 +149,8 @@ class ContextualSearchDelegate
std::string* thumbnail_url, std::string* thumbnail_url,
std::string* caption, std::string* caption,
std::string* quick_action_uri, std::string* quick_action_uri,
QuickActionCategory* quick_action_category); QuickActionCategory* quick_action_category,
int64_t* logged_event_id);
// Extracts the start and end location from a mentions list, and sets the // Extracts the start and end location from a mentions list, and sets the
// integers referenced by |startResult| and |endResult|. // integers referenced by |startResult| and |endResult|.
......
...@@ -527,7 +527,8 @@ TEST_F(ContextualSearchDelegateTest, DecodeSearchTermFromJsonResponse) { ...@@ -527,7 +527,8 @@ TEST_F(ContextualSearchDelegateTest, DecodeSearchTermFromJsonResponse) {
"{\"mid\":\"/m/02mjmr\", \"search_term\":\"obama\"," "{\"mid\":\"/m/02mjmr\", \"search_term\":\"obama\","
"\"info_text\":\"44th U.S. President\"," "\"info_text\":\"44th U.S. President\","
"\"display_text\":\"Barack Obama\", \"mentions\":[0,15]," "\"display_text\":\"Barack Obama\", \"mentions\":[0,15],"
"\"selected_text\":\"obama\", \"resolved_term\":\"barack obama\"}"; "\"selected_text\":\"obama\", \"resolved_term\":\"barack obama\","
"\"logged_event_id\":\"1234567890123456789\"}";
std::string search_term; std::string search_term;
std::string display_text; std::string display_text;
std::string alternate_term; std::string alternate_term;
...@@ -539,12 +540,14 @@ TEST_F(ContextualSearchDelegateTest, DecodeSearchTermFromJsonResponse) { ...@@ -539,12 +540,14 @@ TEST_F(ContextualSearchDelegateTest, DecodeSearchTermFromJsonResponse) {
std::string thumbnail_url; std::string thumbnail_url;
std::string caption; std::string caption;
std::string quick_action_uri; std::string quick_action_uri;
int64_t logged_event_id;
QuickActionCategory quick_action_category = QUICK_ACTION_CATEGORY_NONE; QuickActionCategory quick_action_category = QUICK_ACTION_CATEGORY_NONE;
delegate_->DecodeSearchTermFromJsonResponse( delegate_->DecodeSearchTermFromJsonResponse(
json_with_escape, &search_term, &display_text, &alternate_term, &mid, json_with_escape, &search_term, &display_text, &alternate_term, &mid,
&prevent_preload, &mention_start, &mention_end, &context_language, &prevent_preload, &mention_start, &mention_end, &context_language,
&thumbnail_url, &caption, &quick_action_uri, &quick_action_category); &thumbnail_url, &caption, &quick_action_uri, &quick_action_category,
&logged_event_id);
EXPECT_EQ("obama", search_term); EXPECT_EQ("obama", search_term);
EXPECT_EQ("Barack Obama", display_text); EXPECT_EQ("Barack Obama", display_text);
...@@ -556,6 +559,7 @@ TEST_F(ContextualSearchDelegateTest, DecodeSearchTermFromJsonResponse) { ...@@ -556,6 +559,7 @@ TEST_F(ContextualSearchDelegateTest, DecodeSearchTermFromJsonResponse) {
EXPECT_EQ("", caption); EXPECT_EQ("", caption);
EXPECT_EQ("", quick_action_uri); EXPECT_EQ("", quick_action_uri);
EXPECT_EQ(QUICK_ACTION_CATEGORY_NONE, quick_action_category); EXPECT_EQ(QUICK_ACTION_CATEGORY_NONE, quick_action_category);
EXPECT_EQ(1234567890123456789, logged_event_id);
} }
TEST_F(ContextualSearchDelegateTest, ResponseWithLanguage) { TEST_F(ContextualSearchDelegateTest, ResponseWithLanguage) {
......
...@@ -186,7 +186,8 @@ void ContextualSearchManager::OnSearchTermResolutionResponse( ...@@ -186,7 +186,8 @@ void ContextualSearchManager::OnSearchTermResolutionResponse(
resolved_search_term.selection_start_adjust, resolved_search_term.selection_start_adjust,
resolved_search_term.selection_end_adjust, j_context_language, resolved_search_term.selection_end_adjust, j_context_language,
j_thumbnail_url, j_caption, j_quick_action_uri, j_thumbnail_url, j_caption, j_quick_action_uri,
resolved_search_term.quick_action_category); resolved_search_term.quick_action_category,
resolved_search_term.logged_event_id);
} }
void ContextualSearchManager::OnTextSurroundingSelectionAvailable( void ContextualSearchManager::OnTextSurroundingSelectionAvailable(
......
...@@ -20,7 +20,8 @@ ResolvedSearchTerm::ResolvedSearchTerm(int response_code) ...@@ -20,7 +20,8 @@ ResolvedSearchTerm::ResolvedSearchTerm(int response_code)
thumbnail_url(""), thumbnail_url(""),
caption(""), caption(""),
quick_action_uri(""), quick_action_uri(""),
quick_action_category(QUICK_ACTION_CATEGORY_NONE) {} quick_action_category(QUICK_ACTION_CATEGORY_NONE),
logged_event_id(0) {}
ResolvedSearchTerm::ResolvedSearchTerm( ResolvedSearchTerm::ResolvedSearchTerm(
bool is_invalid, bool is_invalid,
...@@ -36,7 +37,8 @@ ResolvedSearchTerm::ResolvedSearchTerm( ...@@ -36,7 +37,8 @@ ResolvedSearchTerm::ResolvedSearchTerm(
const std::string& thumbnail_url, const std::string& thumbnail_url,
const std::string& caption, const std::string& caption,
const std::string& quick_action_uri, const std::string& quick_action_uri,
const QuickActionCategory& quick_action_category) const QuickActionCategory& quick_action_category,
int64_t logged_event_id)
: is_invalid(is_invalid), : is_invalid(is_invalid),
response_code(response_code), response_code(response_code),
search_term(search_term), search_term(search_term),
...@@ -50,6 +52,7 @@ ResolvedSearchTerm::ResolvedSearchTerm( ...@@ -50,6 +52,7 @@ ResolvedSearchTerm::ResolvedSearchTerm(
thumbnail_url(thumbnail_url), thumbnail_url(thumbnail_url),
caption(caption), caption(caption),
quick_action_uri(quick_action_uri), quick_action_uri(quick_action_uri),
quick_action_category(quick_action_category) {} quick_action_category(quick_action_category),
logged_event_id(logged_event_id) {}
ResolvedSearchTerm::~ResolvedSearchTerm() {} ResolvedSearchTerm::~ResolvedSearchTerm() {}
...@@ -40,7 +40,8 @@ struct ResolvedSearchTerm { ...@@ -40,7 +40,8 @@ struct ResolvedSearchTerm {
const std::string& thumbnail_url, const std::string& thumbnail_url,
const std::string& caption, const std::string& caption,
const std::string& quick_action_uri, const std::string& quick_action_uri,
const QuickActionCategory& quick_action_category); const QuickActionCategory& quick_action_category,
int64_t logged_event_id);
~ResolvedSearchTerm(); ~ResolvedSearchTerm();
const bool is_invalid; const bool is_invalid;
...@@ -58,6 +59,7 @@ struct ResolvedSearchTerm { ...@@ -58,6 +59,7 @@ struct ResolvedSearchTerm {
const std::string caption; const std::string caption;
const std::string quick_action_uri; const std::string quick_action_uri;
const QuickActionCategory quick_action_category; const QuickActionCategory quick_action_category;
const int64_t logged_event_id; // Often 0.
DISALLOW_COPY_AND_ASSIGN(ResolvedSearchTerm); DISALLOW_COPY_AND_ASSIGN(ResolvedSearchTerm);
}; };
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
<div id="ignores">Ignores clicks</div> <div id="ignores">Ignores clicks</div>
<div>United States <span id="intelligence">Intelligence</span></div> <div>United States <span id="intelligence">Intelligence</span></div>
<div>United <span id="states">States</span> Intelligence</div> <div>United <span id="states">States</span> Intelligence</div>
<div>United <span id="states-near">StatesNear</span> Intelligence</div> <div>United <span id="states-near">StatesNear</span> <span id="intelligence-logged-event-id">Intelligence</span></div>
<!-- These three spans should be close to each other so that taps from one to <!-- These three spans should be close to each other so that taps from one to
to the next are within our "near" threshold. --> to the next are within our "near" threshold. -->
<div><span id="search">Search</span> <span id="term">Term</span> <span id="resolution">Resolution</span></div> <div><span id="search">Search</span> <span id="term">Term</span> <span id="resolution">Resolution</span></div>
......
...@@ -99410,6 +99410,17 @@ uploading your change for review. ...@@ -99410,6 +99410,17 @@ uploading your change for review.
</summary> </summary>
</histogram> </histogram>
<histogram name="Search.ContextualSearch.OutcomesDuration" units="days"
expires_after="M75">
<owner>donnd@chromium.org</owner>
<owner>twellington@chromium.org</owner>
<summary>
Records the duration of persisted outcomes between the time they were stored
and the time that they were sent back to the server. Recorded when persisted
data is sent to the server. Implemented for Android.
</summary>
</histogram>
<histogram name="Search.ContextualSearch.Ranker.FeaturesAvailable" <histogram name="Search.ContextualSearch.Ranker.FeaturesAvailable"
enum="Boolean"> enum="Boolean">
<owner>donnd@chromium.org</owner> <owner>donnd@chromium.org</owner>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment