Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1102)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java

Issue 2703473002: [TTS] Extract tapped text before showing UI. (Closed)
Patch Set: Moved handleHideContextualSearch into hideContextualSearchUI, and updated comments in response to T… Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
index 5fc766a0659bbd4fcbf7dd7c6334816a405190ae..4426bcfdaa3a46cb27256b8f517fc864ee941c9a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -4,6 +4,7 @@
package org.chromium.chrome.browser.contextualsearch;
+import android.os.Handler;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
@@ -23,6 +24,7 @@ import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChange
import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelContentViewDelegate;
import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanel;
import org.chromium.chrome.browser.contextualsearch.ContextualSearchBlacklist.BlacklistReason;
+import org.chromium.chrome.browser.contextualsearch.ContextualSearchInternalStateController.InternalState;
import org.chromium.chrome.browser.contextualsearch.ContextualSearchSelectionController.SelectionType;
import org.chromium.chrome.browser.externalnav.ExternalNavigationHandler;
import org.chromium.chrome.browser.externalnav.ExternalNavigationHandler.OverrideUrlLoadingResult;
@@ -50,7 +52,6 @@ import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.common.BrowserControlsState;
import org.chromium.content_public.common.ContentUrlConstants;
import org.chromium.net.NetworkChangeNotifier;
-import org.chromium.ui.base.WindowAndroid;
import java.net.MalformedURLException;
import java.net.URL;
@@ -58,16 +59,17 @@ import java.util.regex.Pattern;
import javax.annotation.Nullable;
-
/**
- * Manager for the Contextual Search feature.
- * This class keeps track of the status of Contextual Search and coordinates the control
- * with the layout.
+ * Manager for the Contextual Search feature. This class keeps track of the status of Contextual
+ * Search and coordinates the control with the layout.
*/
public class ContextualSearchManager implements ContextualSearchManagementDelegate,
ContextualSearchTranslateInterface,
ContextualSearchNetworkCommunicator,
ContextualSearchSelectionHandler, SelectionClient {
+ // TODO(donnd): provide an inner class that implements some of these interfaces (like the
+ // ContextualSearchTranslateInterface) rather than having the manager itself implement the
+ // interface because that exposes all the public methods of that interface at the manager level.
private static final String INTENT_URL_PREFIX = "intent:";
// The animation duration of a URL being promoted to a tab when triggered by an
@@ -83,6 +85,15 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
// When we don't need to send any "home country" code we can just pass the empty string.
private static final String NO_HOME_COUNTRY = "";
+ // How long to wait for a tap near a previous tap before hiding the UI or showing a re-Tap.
+ // This setting is not critical: in practice it determines how long to wait after an invalid
+ // tap for the page to respond before hiding the UI. Specifically this setting just needs to be
+ // long enough for Blink's decisions before calling handleShowUnhandledTapUIIfNeeded (which
+ // probably are page-dependent), and short enough that the Bar goes away fairly quickly after a
+ // tap on non-text or whitespace: We currently do not get notification in these cases (hence the
+ // timer).
+ private static final int TAP_NEAR_PREVIOUS_DETECTION_DELAY_MS = 100;
+
private final ObserverList<ContextualSearchObserver> mObservers =
new ObserverList<ContextualSearchObserver>();
@@ -94,6 +105,7 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
private ContextualSearchSelectionController mSelectionController;
private ContextualSearchNetworkCommunicator mNetworkCommunicator;
private ContextualSearchPolicy mPolicy;
+ private ContextualSearchInternalStateController mInternalStateController;
@VisibleForTesting
protected ContextualSearchTranslateController mTranslateController;
@@ -117,7 +129,7 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
private boolean mWereSearchResultsSeen;
private boolean mWereInfoBarsHidden;
private boolean mDidPromoteSearchNavigation;
- private boolean mDidBasePageLoadJustStart;
+
private boolean mWasActivatedByTap;
private boolean mIsInitialized;
@@ -148,14 +160,10 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
private ContextualSearchRequest mSearchRequest;
private ContextualSearchRequest mLastSearchRequestLoaded;
- /**
- * Whether the Accessibility Mode is enabled.
- */
+ /** Whether the Accessibility Mode is enabled. */
private boolean mIsAccessibilityModeEnabled;
- /**
- * Tap Experiments and other variable behavior.
- */
+ /** Tap Experiments and other variable behavior. */
private ContextualSearchHeuristics mHeuristics;
private QuickAnswersHeuristic mQuickAnswersHeuristic;
@@ -173,14 +181,12 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
/**
* Constructs the manager for the given activity, and will attach views to the given parent.
- * @param activity The {@code ChromeActivity} in use.
- * @param windowAndroid The {@code WindowAndroid} associated with Chrome.
+ * @param activity The {@code ChromeActivity} in use.
* @param tabPromotionDelegate The {@link ContextualSearchTabPromotionDelegate} that is
- * responsible for building tabs from contextual search
- * {@link ContentViewCore}s.
+ * responsible for building tabs from contextual search {@link ContentViewCore}s.
*/
- public ContextualSearchManager(ChromeActivity activity, WindowAndroid windowAndroid,
- ContextualSearchTabPromotionDelegate tabPromotionDelegate) {
+ public ContextualSearchManager(
+ ChromeActivity activity, ContextualSearchTabPromotionDelegate tabPromotionDelegate) {
mActivity = activity;
mTabPromotionDelegate = tabPromotionDelegate;
@@ -220,6 +226,9 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
mPolicy = new ContextualSearchPolicy(mSelectionController, mNetworkCommunicator);
mTranslateController = new ContextualSearchTranslateController(activity, mPolicy, this);
+
+ mInternalStateController = new ContextualSearchInternalStateController(
+ mPolicy, getContextualSearchInternalStateHandler());
}
/**
@@ -240,6 +249,8 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
mWereSearchResultsSeen = false;
mIsInitialized = true;
+ mInternalStateController.reset(StateChangeReason.UNKNOWN);
+
listenForTabModelSelectorNotifications();
}
@@ -284,6 +295,7 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
mFindToolbarManager = null;
mFindToolbarObserver = null;
}
+ mInternalStateController.enter(InternalState.UNDEFINED);
}
@Override
@@ -297,51 +309,33 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
return mActivity;
}
- /**
- * @return Whether the Search Panel is opened. That is, whether it is EXPANDED or MAXIMIZED.
- */
+ /** @return Whether the Search Panel is opened. That is, whether it is EXPANDED or MAXIMIZED. */
public boolean isSearchPanelOpened() {
return mSearchPanel.isPanelOpened();
}
- /**
- * @return The Base Page's {@link ContentViewCore}.
- */
+ /** @return The Base Page's {@link ContentViewCore}. */
@Nullable
private WebContents getBaseWebContents() {
return mSelectionController.getBaseWebContents();
}
- /**
- * Notifies that the base page has started loading a page.
- */
+ /** Notifies that the base page has started loading a page. */
public void onBasePageLoadStarted() {
mSelectionController.onBasePageLoadStarted();
}
- /**
- * Notifies that a Context Menu has been shown.
- */
+ /** Notifies that a Context Menu has been shown. */
void onContextMenuShown() {
mSelectionController.onContextMenuShown();
}
/**
- * Hides the Contextual Search UX.
+ * Hides the Contextual Search UX by changing into the IDLE state.
* @param reason The {@link StateChangeReason} for hiding Contextual Search.
*/
public void hideContextualSearch(StateChangeReason reason) {
- if (mContext != null) mContext.destroy();
- mContext = null;
- if (mSearchPanel == null) return;
-
- if (mSearchPanel.isShowing()) {
- mSearchPanel.closePanel(reason, false);
- } else {
- if (mSelectionController.getSelectionType() == SelectionType.TAP) {
- mSelectionController.clearSelection();
- }
- }
+ mInternalStateController.reset(reason);
}
@Override
@@ -384,9 +378,7 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
notifyHideContextualSearch();
}
- /**
- * Called when the system back button is pressed. Will hide the layout.
- */
+ /** Called when the system back button is pressed. Will hide the layout. */
public boolean onBackPressed() {
if (!mIsInitialized || !mSearchPanel.isShowing()) return false;
hideContextualSearch(StateChangeReason.BACK_PRESS);
@@ -395,7 +387,6 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
/**
* Shows the Contextual Search UX.
- * Calls back into onGetContextualSearchQueryResponse.
* @param stateChangeReason The reason explaining the change of state.
*/
private void showContextualSearch(StateChangeReason stateChangeReason) {
@@ -423,23 +414,15 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
removeLastSearchVisit();
}
- // TODO(pedrosimonetti): Fix for M47. Replace this with a better delayed load approach.
mSearchPanel.destroyContent();
String selection = mSelectionController.getSelectedText();
boolean isTap = mSelectionController.getSelectionType() == SelectionType.TAP;
- boolean didRequestSurroundings = false;
if (isTap) {
- // If the user action was not a long-press, immediately start loading content.
+ // If the user action was not a long-press, we should not delay before loading content.
mShouldLoadDelayedSearch = false;
}
if (isTap && mPolicy.shouldPreviousTapResolve()) {
- if (mContext != null) mContext.destroy();
- mContext = new ContextualSearchContext(
- selection, mPolicy.getHomeCountry(mActivity), mPolicy.maySendBasePageUrl());
- nativeGatherSurroundingText(
- mNativeContextualSearchManagerPtr, mContext, getBaseWebContents());
- didRequestSurroundings = true;
// Cache the native translate data, so JNI calls won't be made when time-critical.
mTranslateController.cacheNativeTranslateData();
} else {
@@ -459,19 +442,6 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
: "ContextualSearch.ManualRefineMultiWord");
}
}
-
- if (!didRequestSurroundings && getBaseWebContents() != null) {
- // Gather surrounding text for Icing integration, which will make the selection and
- // a shorter version of the surroundings available for Conversational Search.
- // Although the surroundings are extracted, they will not be sent to the server as
- // part of search term resolution, just sent to Icing which keeps them local until
- // the user activates a Voice Search.
- if (mContext != null) mContext.destroy();
- mContext = new ContextualSearchContext();
- nativeGatherSurroundingText(
- mNativeContextualSearchManagerPtr, mContext, getBaseWebContents());
- }
-
mWereSearchResultsSeen = false;
// Show the Peek Promo only when the Panel wasn't previously visible, provided
@@ -505,14 +475,18 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
@Override
public void startSearchTermResolutionRequest(String selection) {
WebContents baseWebContents = getBaseWebContents();
- if (baseWebContents != null && mContext != null) {
+ if (baseWebContents != null && mContext != null && mContext.canResolve()) {
nativeStartSearchTermResolutionRequest(
mNativeContextualSearchManagerPtr, mContext, getBaseWebContents());
+ } else {
+ // Something went wrong and we couldn't resolve.
+ hideContextualSearch(StateChangeReason.UNKNOWN);
}
}
@Override
- @Nullable public URL getBasePageUrl() {
+ @Nullable
+ public URL getBasePageUrl() {
WebContents baseWebContents = getBaseWebContents();
if (baseWebContents == null) return null;
@@ -535,25 +509,21 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
return new ContextualSearchRequest(term, altTerm, mid, isLowPriorityEnabled);
}
- /**
- * Accessor for the {@code InfoBarContainer} currently attached to the {@code Tab}.
- */
+ /** Accessor for the {@code InfoBarContainer} currently attached to the {@code Tab}. */
private InfoBarContainer getInfoBarContainer() {
Tab tab = mActivity.getActivityTab();
return tab == null ? null : tab.getInfoBarContainer();
}
- /**
- * Listens for notifications that should hide the Contextual Search bar.
- */
+ /** Listens for notifications that should hide the Contextual Search bar. */
private void listenForTabModelSelectorNotifications() {
TabModelSelector selector = mActivity.getTabModelSelector();
mTabModelSelectorTabObserver = new TabModelSelectorTabObserver(selector) {
@Override
public void onPageLoadStarted(Tab tab, String url) {
+ // Detects navigation of the base page for crbug.com/428368 (navigation-detection).
hideContextualSearch(StateChangeReason.UNKNOWN);
- mDidBasePageLoadJustStart = true;
}
@Override
@@ -575,9 +545,7 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
}
}
- /**
- * Stops listening for notifications that should hide the Contextual Search bar.
- */
+ /** Stops listening for notifications that should hide the Contextual Search bar. */
private void stopListeningForHideNotifications() {
if (mTabModelSelectorTabObserver != null) mTabModelSelectorTabObserver.destroy();
@@ -589,9 +557,7 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
}
}
- /**
- * Clears our private member referencing the native manager.
- */
+ /** Clears our private member referencing the native manager. */
@CalledByNative
public void clearNativeManager() {
assert mNativeContextualSearchManagerPtr != 0;
@@ -610,6 +576,7 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
/**
* Called by native code when the surrounding text and selection range are available.
+ * This is done for both Tap and Long-press gestures.
* @param encoding The original encoding used on the base page.
* @param surroundingText The Text surrounding the selection.
* @param startOffset The start offset of the selection.
@@ -618,37 +585,34 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
@CalledByNative
private void onTextSurroundingSelectionAvailable(
final String encoding, final String surroundingText, int startOffset, int endOffset) {
- if (mContext != null && mContext.canResolve() && endOffset <= surroundingText.length()) {
- String afterText = surroundingText.substring(endOffset);
- String selection = mSelectionController.getSelectedText();
- // TODO(donnd): check if panel has been requested to show.
- // We used to call mSearchPanel.isShowing() here, but that's unreliable
- // (crbug.com/669600).
- mSearchPanel.setContextDetails(selection, afterText);
- mNetworkCommunicator.startSearchTermResolutionRequest(selection);
- }
- if (!ContextualSearchFieldTrial.isPageContentNotificationDisabled()) {
- GSAContextDisplaySelection selection = new GSAContextDisplaySelection(
- encoding, surroundingText, startOffset, endOffset);
- notifyShowContextualSearch(selection);
+ if (mInternalStateController.isStillWorkingOn(InternalState.GATHERING_SURROUNDINGS)) {
+ assert mContext != null;
+ // Sometimes Blink returns empty surroundings and 0 offsets so reset in that case.
+ // See crbug.com/393100.
+ if (surroundingText.length() == 0) {
+ mInternalStateController.reset(StateChangeReason.UNKNOWN);
+ } else {
+ mContext.setSurroundingText(encoding, surroundingText, startOffset, endOffset);
+ mInternalStateController.notifyFinishedWorkOn(InternalState.GATHERING_SURROUNDINGS);
+ }
}
- mSearchPanel.setWasSelectionPartOfUrl(
- ContextualSearchSelectionController.isSelectionPartOfUrl(
- surroundingText, startOffset, endOffset));
}
/**
* Called in response to the
* {@link ContextualSearchManager#nativeStartSearchTermResolutionRequest} method.
+ * If {@code nativeStartSearchTermResolutionRequest} is called with a previous request sill
+ * pending our native delegate is supposed to cancel all previous requests. So this code
+ * should only be called with data corresponding to the most recent request.
* @param isNetworkUnavailable Indicates if the network is unavailable, in which case all other
* parameters should be ignored.
- * @param responseCode The HTTP response code. If the code is not OK, the query
- * should be ignored.
+ * @param responseCode The HTTP response code. If the code is not OK, the query should be
+ * ignored.
* @param searchTerm The term to use in our subsequent search.
* @param displayText The text to display in our UX.
* @param alternateTerm The alternate term to display on the results page.
* @param mid the MID for an entity to use to trigger a Knowledge Panel, or an empty string.
- * A MID is a unique identifier for an entity in the Search Knowledge Graph.
+ * A MID is a unique identifier for an entity in the Search Knowledge Graph.
* @param selectionStartAdjust A positive number of characters that the start of the existing
* selection should be expanded by.
* @param selectionEndAdjust A positive number of characters that the end of the existing
@@ -677,6 +641,8 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
boolean doPreventPreload, int selectionStartAdjust, int selectionEndAdjust,
String contextLanguage, String thumbnailUrl, String caption, String quickActionUri,
int quickActionCategory) {
+ if (!mInternalStateController.isStillWorkingOn(InternalState.RESOLVING)) return;
+
// Show an appropriate message for what to search for.
String message;
boolean doLiteralSearch = false;
@@ -749,15 +715,17 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
}
// Adjust the selection unless the user changed it since we initiated the search.
- if (selectionStartAdjust != 0
- || selectionEndAdjust != 0
- && mSelectionController.getSelectionType() == SelectionType.TAP) {
- String originalSelection = mContext == null ? null : mContext.getSelection();
+ if ((selectionStartAdjust != 0 || selectionEndAdjust != 0)
+ && mSelectionController.getSelectionType() == SelectionType.TAP) {
+ String originalSelection = mContext == null ? null : mContext.getInitialSelectedWord();
if (originalSelection != null
&& originalSelection.equals(mSelectionController.getSelectedText())) {
mSelectionController.adjustSelection(selectionStartAdjust, selectionEndAdjust);
+ mContext.onSelectionAdjusted(selectionStartAdjust, selectionEndAdjust);
}
}
+
+ mInternalStateController.notifyFinishedWorkOn(InternalState.RESOLVING);
}
/**
@@ -769,17 +737,13 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
return mNetworkCommunicator.isOnline();
}
- /**
- * Handles this {@link ContextualSearchNetworkCommunicator} vector when not under test.
- */
+ /** Handles this {@link ContextualSearchNetworkCommunicator} vector when not under test. */
@Override
public boolean isOnline() {
return NetworkChangeNotifier.isOnline();
}
- /**
- * Loads a Search Request in the Contextual Search's Content View.
- */
+ /** Loads a Search Request in the Contextual Search's Content View. */
private void loadSearchUrl() {
mLoadedSearchUrlTimeMs = System.currentTimeMillis();
mLastSearchRequestLoaded = mSearchRequest;
@@ -792,23 +756,12 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
// to coordinate with Chrome-Android folks to come up with a proper fix for this.
// For now, we force the ContentView to be displayed by calling onShow() again
// when a URL is being loaded. See: crbug.com/398206
- if (mSearchPanel.isContentShowing()
- && mSearchPanel.getContentViewCore() != null) {
+ if (mSearchPanel.isContentShowing() && mSearchPanel.getContentViewCore() != null) {
mSearchPanel.getContentViewCore().onShow();
}
}
/**
- * @return Whether a Tap gesture is currently supported.
- */
- private boolean isTapSupported() {
- // Base page just started navigating away, so taps should be ignored.
- if (mDidBasePageLoadJustStart) return false;
-
- return mPolicy.isTapSupported();
- }
-
- /**
* Called to set a caption. The caption may either be included with the search term resolution
* response or set by the page through the CS JavaScript API used to notify CS that there is
* a caption available on the current overlay.
@@ -860,21 +813,42 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
// Observers
// ============================================================================================
- /**
- * @param observer An observer to notify when the user performs a contextual search.
- */
+ /** @param observer An observer to notify when the user performs a contextual search. */
public void addObserver(ContextualSearchObserver observer) {
mObservers.addObserver(observer);
}
- /**
- * @param observer An observer to no longer notify when the user performs a contextual search.
+ /** @param observer An observer to no longer notify when the user performs a contextual search.
*/
public void removeObserver(ContextualSearchObserver observer) {
mObservers.removeObserver(observer);
}
/**
+ * Notifies that a new selection has been established and available for Contextual Search.
+ * Should be called when the selection changes to notify listeners that care about the selection
+ * and surrounding text.
+ * Specifically this means we're showing the Contextual Search UX for the given selection.
+ * Notifies Icing of the current selection.
+ * Also notifies the panel whether the selection was part of a URL.
+ */
+ private void notifyObserversOfContextSelectionChanged() {
+ assert mContext != null;
+ String surroundingText = mContext.getSurroundingText();
+ assert surroundingText != null;
+ int startOffset = mContext.getSelectionStartOffset();
+ int endOffset = mContext.getSelectionEndOffset();
+ if (!ContextualSearchFieldTrial.isPageContentNotificationDisabled()) {
+ GSAContextDisplaySelection selection = new GSAContextDisplaySelection(
+ mContext.getEncoding(), surroundingText, startOffset, endOffset);
+ notifyShowContextualSearch(selection);
+ }
+ mSearchPanel.setWasSelectionPartOfUrl(
+ ContextualSearchSelectionController.isSelectionPartOfUrl(
+ surroundingText, startOffset, endOffset));
+ }
+
+ /**
* Notifies all Contextual Search observers that a search has occurred.
* @param selectionContext The selection and context that triggered the search.
*/
@@ -886,9 +860,7 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
}
}
- /**
- * Notifies all Contextual Search observers that a search ended and is no longer in effect.
- */
+ /** Notifies all Contextual Search observers that a search ended and is no longer in effect. */
private void notifyHideContextualSearch() {
for (ContextualSearchObserver observer : mObservers) {
observer.onHideContextualSearch();
@@ -922,9 +894,7 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
return new SearchOverlayContentDelegate();
}
- /**
- * Implementation of OverlayContentDelegate. Made public for testing purposes.
- */
+ /** Implementation of OverlayContentDelegate. Made public for testing purposes. */
public class SearchOverlayContentDelegate extends OverlayContentDelegate {
// Note: New navigation or changes to the WebContents are not advised in this class since
// the WebContents is being observed and navigation is already being performed.
@@ -975,8 +945,8 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
mSelectionController.getSelectedText(), null, null, false);
mDidStartLoadingResolvedSearchRequest = false;
}
- if (mSearchRequest != null && (!mDidStartLoadingResolvedSearchRequest
- || mShouldLoadDelayedSearch)) {
+ if (mSearchRequest != null
+ && (!mDidStartLoadingResolvedSearchRequest || mShouldLoadDelayedSearch)) {
// mShouldLoadDelayedSearch is used in the long-press case to load content.
// Since content is now created and destroyed for each request, was impossible
// to know if content was already loaded or recently needed to be; this is for
@@ -992,7 +962,7 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
@Override
public void onContentViewCreated(ContentViewCore contentViewCore) {
// TODO(donnd): Consider moving to OverlayPanelContent.
- // Enable the Contextual Search JavaScript API between our service and the new view.
+ // Enable the Contextual Search JavaScript API between our service and the new view.
nativeEnableContextualSearchJsApiForOverlay(
mNativeContextualSearchManagerPtr, contentViewCore.getWebContents());
@@ -1013,19 +983,21 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
}
@Override
- public boolean shouldInterceptNavigation(ExternalNavigationHandler externalNavHandler,
- NavigationParams navigationParams) {
+ public boolean shouldInterceptNavigation(
+ ExternalNavigationHandler externalNavHandler, NavigationParams navigationParams) {
mTabRedirectHandler.updateNewUrlLoading(navigationParams.pageTransitionType,
navigationParams.isRedirect,
navigationParams.hasUserGesture || navigationParams.hasUserGestureCarryover,
mActivity.getLastUserInteractionTime(), TabRedirectHandler.INVALID_ENTRY_INDEX);
- ExternalNavigationParams params = new ExternalNavigationParams.Builder(
- navigationParams.url, false, navigationParams.referrer,
- navigationParams.pageTransitionType, navigationParams.isRedirect)
- .setApplicationMustBeInForeground(true)
- .setRedirectHandler(mTabRedirectHandler)
- .setIsMainFrame(navigationParams.isMainFrame)
- .build();
+ ExternalNavigationParams params =
+ new ExternalNavigationParams
+ .Builder(navigationParams.url, false, navigationParams.referrer,
+ navigationParams.pageTransitionType,
+ navigationParams.isRedirect)
+ .setApplicationMustBeInForeground(true)
+ .setRedirectHandler(mTabRedirectHandler)
+ .setIsMainFrame(navigationParams.isMainFrame)
+ .build();
if (externalNavHandler.shouldOverrideUrlLoading(params)
!= OverrideUrlLoadingResult.NO_OVERRIDE) {
mSearchPanel.maximizePanelThenPromoteToTab(StateChangeReason.TAB_PROMOTION,
@@ -1051,14 +1023,12 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
mSearchContentViewDelegate = delegate;
}
- /**
- * Removes the last resolved search URL from the Chrome history.
- */
+ /** Removes the last resolved search URL from the Chrome history. */
private void removeLastSearchVisit() {
if (mLastSearchRequestLoaded != null) {
// TODO(pedrosimonetti): Consider having this feature builtin into OverlayPanelContent.
- mSearchPanel.removeLastHistoryEntry(mLastSearchRequestLoaded.getSearchUrl(),
- mLoadedSearchUrlTimeMs);
+ mSearchPanel.removeLastHistoryEntry(
+ mLastSearchRequestLoaded.getSearchUrl(), mLoadedSearchUrlTimeMs);
}
}
@@ -1110,22 +1080,17 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
}
}
- /**
- * @return Whether the given HTTP result code represents a failure or not.
- */
+ /** @return Whether the given HTTP result code represents a failure or not. */
private boolean isHttpFailureCode(int httpResultCode) {
return httpResultCode <= 0 || httpResultCode >= 400;
}
- /**
- * @return whether a navigation in the search content view should promote to a separate tab.
- */
+ /** @return whether a navigation in the search content view should promote to a separate tab. */
private boolean shouldPromoteSearchNavigation() {
// A navigation can be due to us loading a URL, or a touch in the search content view.
// Require a touch, but no recent loading, in order to promote to a separate tab.
// Note that tapping the opt-in button requires checking for recent loading.
- return mSearchPanel.didTouchContent()
- && !mSearchPanel.isProcessingPendingNavigation();
+ return mSearchPanel.didTouchContent() && !mSearchPanel.isProcessingPendingNavigation();
}
/**
@@ -1204,8 +1169,8 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
// not yet committed being processed. Otherwise, get the URL from the WebContents.
NavigationEntry entry =
searchContentViewCore.getWebContents().getNavigationController().getPendingEntry();
- String url = entry != null
- ? entry.getUrl() : searchContentViewCore.getWebContents().getUrl();
+ String url =
+ entry != null ? entry.getUrl() : searchContentViewCore.getWebContents().getUrl();
return url;
}
@@ -1235,7 +1200,6 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
@Override
public void showUnhandledTapUIIfNeeded(final int x, final int y) {
- mDidBasePageLoadJustStart = false;
if (!isOverlayVideoMode()) {
mSelectionController.handleShowUnhandledTapUIIfNeeded(x, y);
}
@@ -1246,6 +1210,11 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
return false;
}
+ // TODO(donnd): add handling of an ACK to selectWordAroundCaret (crbug.com/435778 has details).
+
+ /**
+ * @return Whether the display is in a full-screen video overlay mode.
+ */
private boolean isOverlayVideoMode() {
return mActivity.getFullscreenManager() != null
&& mActivity.getFullscreenManager().isOverlayVideoMode();
@@ -1286,6 +1255,13 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
}
@Override
+ public void handleNonSuppressedTap() {
+ if (mIsAccessibilityModeEnabled) return;
+
+ mInternalStateController.notifyFinishedWorkOn(InternalState.DECIDING_SUPPRESSION);
+ }
+
+ @Override
public void handleMetricsForWouldSuppressTap(ContextualSearchHeuristics tapHeuristics) {
mHeuristics = tapHeuristics;
@@ -1303,42 +1279,42 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
public void handleValidTap() {
if (mIsAccessibilityModeEnabled) return;
- if (isTapSupported()) {
- // Here we are probably starting a new Contextual Search with a Tap gesture (or we'll
- // ignore the tap), therefore we need to clear to properly reflect that a search just
- // started and we don't have the resolved search term yet.
- mSearchRequest = null;
-
- if (mContext != null) mContext.destroy();
- mContext = null;
-
- WebContents baseWebContents = getBaseWebContents();
- if (baseWebContents != null) {
- // Let the policy know that a tap gesture has been received.
- mPolicy.registerTap();
-
- baseWebContents.selectWordAroundCaret();
- }
- }
+ mInternalStateController.enter(InternalState.TAP_RECOGNIZED);
}
+ /**
+ * Notifies this class that the selection has changed. This may be due to the user moving the
+ * selection handles after a long-press, or after a Tap gesture has called selectWordAroundCaret
+ * to expand the selection to a whole word.
+ */
@Override
public void handleSelection(String selection, boolean selectionValid, SelectionType type,
float x, float y) {
if (mIsAccessibilityModeEnabled) return;
if (!selection.isEmpty()) {
- StateChangeReason stateChangeReason = type == SelectionType.TAP
- ? StateChangeReason.TEXT_SELECT_TAP : StateChangeReason.TEXT_SELECT_LONG_PRESS;
ContextualSearchUma.logSelectionIsValid(selectionValid);
+
+ // Update the context so it knows the selection has changed.
+ if (mContext != null) mContext.updateContextFromSelection(selection);
+
if (selectionValid && mSearchPanel != null) {
mSearchPanel.updateBasePageSelectionYPx(y);
if (!mSearchPanel.isShowing()) {
mSearchPanel.getPanelMetrics().onSelectionEstablished(selection);
}
- showContextualSearch(stateChangeReason);
+ showSelectionAsSearchInBar(selection);
+
+ // TODO(donnd): remove this complication when we get an ACK message from
+ // selectWordAroundCaret (see crbug.com/435778).
+ if (type == SelectionType.TAP) {
+ mInternalStateController.notifyFinishedWorkOn(
+ InternalState.START_SHOWING_TAP_UI);
+ } else {
+ mInternalStateController.enter(InternalState.LONG_PRESS_RECOGNIZED);
+ }
} else {
- hideContextualSearch(stateChangeReason);
+ hideContextualSearch(StateChangeReason.INVALID_SELECTION);
}
}
}
@@ -1380,6 +1356,141 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
if (mSearchPanel != null) mSearchPanel.getPanelMetrics().setBlacklistReason(reason);
}
+ @Override
+ public void handleSelectionCleared() {
+ // The selection was just cleared, so we'll want to remove our UX unless it was due to
+ // another Tap while the Bar is showing.
+ mInternalStateController.enter(InternalState.SELECTION_CLEARED_RECOGNIZED);
+ }
+
+ /** Shows the given selection as the Search Term in the Bar. */
+ private void showSelectionAsSearchInBar(String selection) {
+ if (mSearchPanel.isShowing()) mSearchPanel.setSearchTerm(selection);
+ }
+
+ // ============================================================================================
+ // ContextualSearchInternalStateHandler implementation.
+ // ============================================================================================
+
+ @VisibleForTesting
+ ContextualSearchInternalStateHandler getContextualSearchInternalStateHandler() {
+ return new ContextualSearchInternalStateHandler() {
+ @Override
+ public void hideContextualSearchUi(StateChangeReason reason) {
+ // Called when the IDLE state has been entered.
+ if (mContext != null) mContext.destroy();
+ mContext = null;
+ if (mSearchPanel == null) return;
+
+ if (mSearchPanel.isShowing()) {
+ mSearchPanel.closePanel(reason, false);
+ } else {
+ if (mSelectionController.getSelectionType() == SelectionType.TAP) {
+ mSelectionController.clearSelection();
+ }
+ }
+ }
+
+ @Override
+ public void gatherSurroundingText() {
+ if (mContext != null) mContext.destroy();
+ mContext = new ContextualSearchContext() {
+ @Override
+ void onSelectionChanged() {
+ notifyObserversOfContextSelectionChanged();
+ }
+ };
+
+ boolean isTap = mSelectionController.getSelectionType() == SelectionType.TAP;
+ if (isTap && mPolicy.shouldPreviousTapResolve()) {
+ mContext.setResolveProperties(
+ mPolicy.getHomeCountry(mActivity), mPolicy.maySendBasePageUrl());
+ }
+
+ mInternalStateController.notifyStartingWorkOn(InternalState.GATHERING_SURROUNDINGS);
+ nativeGatherSurroundingText(
+ mNativeContextualSearchManagerPtr, mContext, getBaseWebContents());
+ }
+
+ /** Starts the process of deciding if we'll suppress the current Tap gesture or not. */
+ @Override
+ public void decideSuppression() {
+ mInternalStateController.notifyStartingWorkOn(InternalState.DECIDING_SUPPRESSION);
+ mSelectionController.handleShouldSuppressTap();
+ }
+
+ /** Starts showing the Tap UI by selecting a word around the current caret. */
+ @Override
+ public void startShowingTapUi() {
+ WebContents baseWebContents = getBaseWebContents();
+ // TODO(donnd): Call isTapSupported earlier so we don't waste time gathering
+ // surrounding text and deciding suppression when unsupported, or remove the whole
+ // idea of unsupported taps in favor of deciding suppression better.
+ // Details in crbug.com/715297.
+ if (baseWebContents != null && mPolicy.isTapSupported()) {
+ mInternalStateController.notifyStartingWorkOn(
+ InternalState.START_SHOWING_TAP_UI);
+ baseWebContents.selectWordAroundCaret();
+ // Let the policy know that a valid tap gesture has been received.
+ mPolicy.registerTap();
+ } else {
+ mInternalStateController.reset(StateChangeReason.UNKNOWN);
+ }
+ }
+
+ /**
+ * Waits for possible Tap gesture that's near enough to the previous tap to be
+ * considered a "re-tap". We've done some work on the previous Tap and we just saw the
+ * selection get cleared (probably due to a Tap that may or may not be valid).
+ * If it's invalid we'll want to hide the UI. If it's valid we'll want to just update
+ * the UI rather than having the Bar hide and re-show.
+ */
+ @Override
+ public void waitForPossibleTapNearPrevious() {
+ mInternalStateController.notifyStartingWorkOn(
+ InternalState.WAITING_FOR_POSSIBLE_TAP_NEAR_PREVIOUS);
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mInternalStateController.notifyFinishedWorkOn(
+ InternalState.WAITING_FOR_POSSIBLE_TAP_NEAR_PREVIOUS);
+ }
+ }, TAP_NEAR_PREVIOUS_DETECTION_DELAY_MS);
+ }
+
+ /** Starts a Resolve request to our server for the best Search Term. */
+ @Override
+ public void resolveSearchTerm() {
+ mInternalStateController.notifyStartingWorkOn(InternalState.RESOLVING);
+ String selection = mSelectionController.getSelectedText();
+ assert !TextUtils.isEmpty(selection);
+ mNetworkCommunicator.startSearchTermResolutionRequest(selection);
+
+ // Update the UI to show the resolve is in progress.
+ assert mContext != null;
+ assert mContext.getTextContentFollowingSelection() != null;
+ mSearchPanel.setContextDetails(
+ selection, mContext.getTextContentFollowingSelection());
+ }
+
+ @Override
+ public void showContextualSearchTapUi() {
+ mInternalStateController.notifyStartingWorkOn(InternalState.SHOW_FULL_TAP_UI);
+ showContextualSearch(StateChangeReason.TEXT_SELECT_TAP);
+ mInternalStateController.notifyFinishedWorkOn(InternalState.SHOW_FULL_TAP_UI);
+ }
+
+ @Override
+ public void showContextualSearchLongpressUi() {
+ mInternalStateController.notifyStartingWorkOn(
+ InternalState.SHOWING_LONGPRESS_SEARCH);
+ showContextualSearch(StateChangeReason.TEXT_SELECT_LONG_PRESS);
+ mInternalStateController.notifyFinishedWorkOn(
+ InternalState.SHOWING_LONGPRESS_SEARCH);
+ }
+ };
+ }
+
// ============================================================================================
// Test helpers
// ============================================================================================
@@ -1394,54 +1505,58 @@ public class ContextualSearchManager implements ContextualSearchManagementDelega
mPolicy.setNetworkCommunicator(mNetworkCommunicator);
}
- /**
- * @return The ContextualSearchPolicy currently being used.
- */
+ /** @return The ContextualSearchPolicy currently being used. */
@VisibleForTesting
ContextualSearchPolicy getContextualSearchPolicy() {
return mPolicy;
}
- /**
- * @param policy The {@link ContextualSearchPolicy} for testing.
- */
+ /** @param policy The {@link ContextualSearchPolicy} for testing. */
@VisibleForTesting
void setContextualSearchPolicy(ContextualSearchPolicy policy) {
mPolicy = policy;
}
- /**
- * @return The {@link ContextualSearchPanel}, for testing purposes only.
- */
+ /** @return The {@link ContextualSearchPanel}, for testing purposes only. */
@VisibleForTesting
ContextualSearchPanel getContextualSearchPanel() {
return mSearchPanel;
}
- /**
- * @return The selection controller, for testing purposes.
- */
+ /** @return The selection controller, for testing purposes. */
@VisibleForTesting
ContextualSearchSelectionController getSelectionController() {
return mSelectionController;
}
- /**
- * @param controller The {@link ContextualSearchSelectionController}, for testing purposes.
- */
+ /** @param controller The {@link ContextualSearchSelectionController}, for testing purposes. */
@VisibleForTesting
void setSelectionController(ContextualSearchSelectionController controller) {
mSelectionController = controller;
}
- /**
- * @return The current search request, or {@code null} if there is none, for testing.
- */
+ /** @return The current search request, or {@code null} if there is none, for testing. */
@VisibleForTesting
ContextualSearchRequest getRequest() {
return mSearchRequest;
}
+ @VisibleForTesting
+ ContextualSearchTabPromotionDelegate getTabPromotionDelegate() {
+ return mTabPromotionDelegate;
+ }
+
+ @VisibleForTesting
+ void setContextualSearchInternalStateController(
+ ContextualSearchInternalStateController controller) {
+ mInternalStateController = controller;
+ }
+
+ @VisibleForTesting
+ protected ContextualSearchInternalStateController getContextualSearchInternalStateController() {
+ return mInternalStateController;
+ }
+
// ============================================================================================
// Native calls
// ============================================================================================

Powered by Google App Engine
This is Rietveld 408576698