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

Side by Side Diff: chrome/android/java_staging/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.contextualsearch;
6
7 import android.app.Activity;
8 import android.os.Handler;
9 import android.util.Log;
10 import android.view.View;
11 import android.view.ViewGroup;
12 import android.view.ViewTreeObserver;
13 import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
14
15 import com.google.android.apps.chrome.R;
16
17 import org.chromium.base.ActivityState;
18 import org.chromium.base.ApplicationStatus;
19 import org.chromium.base.ApplicationStatus.ActivityStateListener;
20 import org.chromium.base.CalledByNative;
21 import org.chromium.base.SysUtils;
22 import org.chromium.base.VisibleForTesting;
23 import org.chromium.chrome.browser.ChromeActivity;
24 import org.chromium.chrome.browser.ContentViewUtil;
25 import org.chromium.chrome.browser.Tab;
26 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.Context ualSearchControl;
27 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.Context ualSearchPanel.PanelState;
28 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.Context ualSearchPanel.StateChangeReason;
29 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.Context ualSearchPanelDelegate;
30 import org.chromium.chrome.browser.contextualsearch.ContextualSearchSelectionCon troller.SelectionType;
31 import org.chromium.chrome.browser.device.DeviceClassManager;
32 import org.chromium.chrome.browser.externalnav.ExternalNavigationHandler;
33 import org.chromium.chrome.browser.externalnav.ExternalNavigationHandler.Overrid eUrlLoadingResult;
34 import org.chromium.chrome.browser.externalnav.ExternalNavigationParams;
35 import org.chromium.chrome.browser.gsa.GSAContextDisplaySelection;
36 import org.chromium.chrome.browser.infobar.InfoBarContainer;
37 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
38 import org.chromium.chrome.browser.tab.TabRedirectHandler;
39 import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
40 import org.chromium.chrome.browser.tabmodel.TabModel;
41 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
42 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
43 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
44 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
45 import org.chromium.chrome.browser.widget.findinpage.FindToolbarManager;
46 import org.chromium.chrome.browser.widget.findinpage.FindToolbarObserver;
47 import org.chromium.components.navigation_interception.InterceptNavigationDelega te;
48 import org.chromium.components.navigation_interception.NavigationParams;
49 import org.chromium.components.web_contents_delegate_android.WebContentsDelegate Android;
50 import org.chromium.content.browser.ContentView;
51 import org.chromium.content.browser.ContentViewCore;
52 import org.chromium.content.browser.ContextualSearchClient;
53 import org.chromium.content_public.browser.GestureStateListener;
54 import org.chromium.content_public.browser.JavaScriptCallback;
55 import org.chromium.content_public.browser.LoadUrlParams;
56 import org.chromium.content_public.browser.WebContents;
57 import org.chromium.content_public.browser.WebContentsObserver;
58 import org.chromium.content_public.common.TopControlsState;
59 import org.chromium.ui.base.WindowAndroid;
60
61 import java.net.MalformedURLException;
62 import java.net.URL;
63 import java.util.regex.Pattern;
64
65 import javax.annotation.Nullable;
66
67
68 /**
69 * Manager for the Contextual Search feature.
70 * This class keeps track of the status of Contextual Search and coordinates the control
71 * with the layout.
72 */
73 public class ContextualSearchManager extends ContextualSearchObservable
74 implements ContextualSearchManagementDelegate,
75 ContextualSearchNetworkCommunicator, ContextualSearchSelectionHa ndler,
76 ContextualSearchClient, ActivityStateListener {
77
78 private static final String TAG = "ContextualSearch";
79
80 private static final String CONTAINS_WORD_PATTERN = "(\\w|\\p{L}|\\p{N})+";
81
82 // Constants related to the first-run flow.
83 private static final String FIRST_RUN_URL_PREFIX = "chrome://contextual-sear ch-promo/";
84 private static final String FIRST_RUN_FLOW_URL = FIRST_RUN_URL_PREFIX + "pro mo.html";
85 private static final String FIRST_RUN_OPTIN_URL = FIRST_RUN_FLOW_URL + "#opt in";
86 private static final String FIRST_RUN_OPTOUT_URL = FIRST_RUN_FLOW_URL + "#op tout";
87 private static final String FIRST_RUN_LEARN_MORE_URL = FIRST_RUN_FLOW_URL + "#learn-more";
88
89 // Max selection length must be limited or the entire request URL can go pas t the 2K limit.
90 private static final int MAX_SELECTION_LENGTH = 100;
91
92 private static final boolean ALWAYS_USE_RESOLVED_SEARCH_TERM = true;
93 private static final boolean NEVER_USE_RESOLVED_SEARCH_TERM = false;
94
95 private static final String INTENT_URL_PREFIX = "intent:";
96
97 // The animation duration of a URL being promoted to a tab when triggered by an
98 // intercept navigation. This is faster than the standard tab promotion anim ation
99 // so that it completes before the navigation.
100 private static final long INTERCEPT_NAVIGATION_PROMOTION_ANIMATION_DURATION_ MS = 40;
101
102 // We blacklist this URL because malformed URLs may bring up this page.
103 private static final String BLACKLISTED_URL = "about:blank";
104
105 private final ContextualSearchSelectionController mSelectionController;
106 private final Pattern mContainsWordPattern;
107 private final ChromeActivity mActivity;
108 private ViewGroup mParentView;
109 private final ViewTreeObserver.OnGlobalFocusChangeListener mOnFocusChangeLis tener;
110
111 private final WindowAndroid mWindowAndroid;
112 private ContentViewCore mSearchContentViewCore;
113 private WebContentsObserver mSearchWebContentsObserver;
114 private final WebContentsDelegateAndroid mWebContentsDelegate;
115 private ContextualSearchContentViewDelegate mSearchContentViewDelegate;
116 private final ContextualSearchTabPromotionDelegate mTabPromotionDelegate;
117 private final FindToolbarObserver mFindToolbarObserver;
118 private FindToolbarManager mFindToolbarManager;
119 private TabModelSelectorTabObserver mTabModelSelectorTabObserver;
120 private TabModelObserver mTabModelObserver;
121 private boolean mIsSearchContentViewShowing;
122 private boolean mDidLoadResolvedSearchRequest;
123 private long mLoadedSearchUrlTimeMs;
124 // TODO(donnd): consider changing this member's name to indicate "opened" in stead of "seen".
125 private boolean mWereSearchResultsSeen;
126 private boolean mWereInfoBarsHidden;
127 private boolean mDidLoadAnyUrl;
128 private boolean mDidPromoteSearchNavigation;
129 private boolean mDidBasePageLoadJustStart;
130 private boolean mWasActivatedByTap;
131 private boolean mIsInitialized;
132
133 private boolean mIsShowingPromo;
134 private boolean mDidLogPromoOutcome;
135
136 private ContextualSearchNetworkCommunicator mNetworkCommunicator;
137 private ContextualSearchPanelDelegate mSearchPanelDelegate;
138
139 // TODO(pedrosimonetti): also store selected text, surroundings, url, boundi ng rect of selected
140 // text, and make sure that all states are cleared when starting a new conte xtual search to
141 // avoid having the values in a funky state.
142 private ContextualSearchRequest mSearchRequest;
143
144 // The native manager associated with this object.
145 private long mNativeContextualSearchManagerPtr;
146
147 private TabRedirectHandler mTabRedirectHandler;
148
149 /**
150 * The delegate that is notified when the Search Panel ContentViewCore is re ady to be rendered.
151 */
152 public interface ContextualSearchContentViewDelegate {
153 /**
154 * Sets the {@code ContentViewCore} associated to the Contextual Search Panel.
155 * @param contentViewCore Reference to the ContentViewCore.
156 */
157 void setContextualSearchContentViewCore(ContentViewCore contentViewCore) ;
158
159 /**
160 * Releases the {@code ContentViewCore} associated to the Contextual Sea rch Panel.
161 */
162 void releaseContextualSearchContentViewCore();
163 }
164
165 /**
166 * The delegate that is responsible for promoting a {@link ContentViewCore} to a {@link Tab}
167 * when necessary.
168 */
169 public interface ContextualSearchTabPromotionDelegate {
170 /**
171 * Called when {@code searchContentViewCore} should be promoted to a {@l ink Tab}.
172 * @param searchContentViewCore The {@link ContentViewCore} to promote.
173 * @return Whether or not {@code searchContentViewC ore}'s ownership
174 * was transferred to the new {@link Tab} o r not. If
175 * {@code false}, this {@link ContentViewCo re} might be
176 * destroyed after this call.
177 */
178 boolean createContextualSearchTab(ContentViewCore searchContentViewCore) ;
179 }
180
181 /**
182 * Constructs the manager for the given activity, and will attach views to t he given parent.
183 * @param activity The {@code ChromeActivity} in use.
184 * @param windowAndroid The {@code WindowAndroid} associated with Chr ome.
185 * @param tabPromotionDelegate The {@link ContextualSearchTabPromotionDelega te} that is
186 * responsible for building tabs from contextual search
187 * {@link ContentViewCore}s.
188 */
189 public ContextualSearchManager(ChromeActivity activity, WindowAndroid window Android,
190 ContextualSearchTabPromotionDelegate tabPromotionDelegate) {
191 super(activity);
192 mActivity = activity;
193 mWindowAndroid = windowAndroid;
194 mTabPromotionDelegate = tabPromotionDelegate;
195 mContainsWordPattern = Pattern.compile(CONTAINS_WORD_PATTERN);
196
197 mSelectionController = new ContextualSearchSelectionController(activity, this);
198
199 mWebContentsDelegate = new WebContentsDelegateAndroid() {
200 @Override
201 public void onLoadStarted() {
202 super.onLoadStarted();
203 mSearchPanelDelegate.onLoadStarted();
204 }
205
206 @Override
207 public void onLoadStopped() {
208 super.onLoadStopped();
209 mSearchPanelDelegate.onLoadStopped();
210 }
211
212 @Override
213 public void onLoadProgressChanged(int progress) {
214 super.onLoadProgressChanged(progress);
215 mSearchPanelDelegate.onLoadProgressChanged(progress);
216 }
217 };
218
219 mFindToolbarObserver = new FindToolbarObserver() {
220 @Override
221 public void onFindToolbarShown() {
222 hideContextualSearch(StateChangeReason.UNKNOWN);
223 }
224 };
225
226 final View controlContainer = mActivity.findViewById(R.id.control_contai ner);
227 mOnFocusChangeListener = new OnGlobalFocusChangeListener() {
228 @Override
229 public void onGlobalFocusChanged(View oldFocus, View newFocus) {
230 if (controlContainer != null && controlContainer.hasFocus()) {
231 hideContextualSearch(StateChangeReason.UNKNOWN);
232 }
233 }
234 };
235
236 mTabModelObserver = new EmptyTabModelObserver() {
237 @Override
238 public void didAddTab(Tab tab, TabLaunchType type) {
239 // If we're in the process of promoting this tab, just return an d don't mess with
240 // this state.
241 if (tab.getContentViewCore() == mSearchContentViewCore) return;
242 hideContextualSearch(StateChangeReason.UNKNOWN);
243 }
244 };
245 }
246
247 /**
248 * Sets the manager in charge of find in page.
249 * @param manager A {@link FindToolbarManager} instance.
250 */
251 public void setFindToolbarManager(FindToolbarManager manager) {
252 assert manager != null;
253 mFindToolbarManager = manager;
254 mFindToolbarManager.addObserver(mFindToolbarObserver);
255 }
256
257 /**
258 * Initializes this manager. Must be called before {@link #getContextualSea rchControl()}.
259 * @param parentView The parent view to attach Contextual Search UX to.
260 */
261 public void initialize(ViewGroup parentView) {
262 mParentView = parentView;
263 mParentView.getViewTreeObserver().addOnGlobalFocusChangeListener(mOnFocu sChangeListener);
264 mNativeContextualSearchManagerPtr = nativeInit();
265 listenForHideNotifications();
266 mTabRedirectHandler = new TabRedirectHandler(mActivity);
267
268 mIsShowingPromo = false;
269 mDidLogPromoOutcome = false;
270 mDidLoadResolvedSearchRequest = false;
271 mWereSearchResultsSeen = false;
272 mNetworkCommunicator = this;
273 ApplicationStatus.registerStateListenerForActivity(this, mActivity);
274 mIsInitialized = true;
275 }
276
277 /**
278 * Destroys the native Contextual Search Manager.
279 * Call this method before orphaning this object to allow it to be garbage c ollected.
280 */
281 public void destroy() {
282 if (!mIsInitialized) return;
283
284 hideContextualSearch(StateChangeReason.UNKNOWN);
285 mParentView.getViewTreeObserver().removeOnGlobalFocusChangeListener(mOnF ocusChangeListener);
286 nativeDestroy(mNativeContextualSearchManagerPtr);
287 stopListeningForHideNotifications();
288 mTabRedirectHandler.clear();
289 ApplicationStatus.unregisterActivityStateListener(this);
290 }
291
292 @Override
293 public void setContextualSearchPanelDelegate(ContextualSearchPanelDelegate d elegate) {
294 mSearchPanelDelegate = delegate;
295
296 boolean shouldHide = nativeShouldHidePromoHeader(mNativeContextualSearch ManagerPtr);
297 mSearchPanelDelegate.setShouldHidePromoHeader(shouldHide);
298 }
299
300 /**
301 * @return The {@link ContextualSearchPanelDelegate}, for testing purposes o nly.
302 */
303 @VisibleForTesting
304 public ContextualSearchPanelDelegate getContextualSearchPanelDelegate() {
305 return mSearchPanelDelegate;
306 }
307
308 /**
309 * Sets the selection controller for testing purposes.
310 */
311 @VisibleForTesting
312 ContextualSearchSelectionController getSelectionController() {
313 return mSelectionController;
314 }
315
316 @VisibleForTesting
317 boolean isSearchPanelShowing() {
318 return mSearchPanelDelegate.isShowing();
319 }
320
321 /**
322 * @return Whether the Search Panel is opened. That is, whether it is EXPAND ED or MAXIMIZED.
323 */
324 public boolean isSearchPanelOpened() {
325 PanelState state = mSearchPanelDelegate.getPanelState();
326 return state == PanelState.EXPANDED || state == PanelState.MAXIMIZED;
327 }
328
329 /**
330 * @return The Base Page's {@link ContentViewCore}.
331 */
332 @Nullable private ContentViewCore getBaseContentView() {
333 return mSelectionController.getBaseContentView();
334 }
335
336 @Override
337 public void setPreferenceState(boolean enabled) {
338 PrefServiceBridge.getInstance().setContextualSearchState(enabled);
339 }
340
341 @Override
342 public boolean isOptOutPromoAvailable() {
343 return mPolicy.isOptOutPromoAvailable();
344 }
345
346 /**
347 * Hides the Contextual Search UX.
348 * @param reason The {@link StateChangeReason} for hiding Contextual Search.
349 */
350 public void hideContextualSearch(StateChangeReason reason) {
351 if (mSearchPanelDelegate == null) return;
352
353 if (mSearchPanelDelegate.isShowing()) {
354 mSearchPanelDelegate.closePanel(reason, false);
355 }
356 }
357
358 @Override
359 public void onCloseContextualSearch() {
360 if (mSearchPanelDelegate == null) return;
361
362 // NOTE(pedrosimonetti): hideContextualSearch() will also be called afte r swiping the
363 // Panel down in order to dismiss it. In this case, hideContextualSearch () will be called
364 // after completing the hide animation, and at that moment the Panel wil l not be showing
365 // anymore. Therefore, we need to always clear selection, regardless of when the Panel
366 // was still visible, in order to make sure the selection will be cleare d appropriately.
367 if (mSelectionController.getSelectionType() == SelectionType.TAP) {
368 mSelectionController.clearSelection();
369 }
370
371 // Show the infobar container if it was visible before Contextual Search was shown.
372 if (mWereInfoBarsHidden) {
373 mWereInfoBarsHidden = false;
374 InfoBarContainer container = getInfoBarContainer();
375 if (container != null) {
376 container.setVisibility(View.VISIBLE);
377 container.setDoStayInvisible(false);
378 }
379 }
380
381 if (!mWereSearchResultsSeen && mLoadedSearchUrlTimeMs != 0L) {
382 removeLastSearchVisit();
383 }
384
385 // Clear the timestamp. This is to avoid future calls to hideContextualS earch clearing
386 // the current URL.
387 mLoadedSearchUrlTimeMs = 0L;
388 mWereSearchResultsSeen = false;
389
390 mNetworkCommunicator.destroySearchContentView();
391 mSearchRequest = null;
392
393 if (mIsShowingPromo && !mDidLogPromoOutcome) {
394 logPromoOutcome();
395 }
396
397 mIsShowingPromo = false;
398 mSearchPanelDelegate.setIsPromoActive(false);
399 }
400
401 /**
402 * Called when the system back button is pressed. Will hide the layout.
403 */
404 public boolean onBackPressed() {
405 if (!mIsInitialized || !mSearchPanelDelegate.isShowing()) return false;
406 hideContextualSearch(StateChangeReason.BACK_PRESS);
407 return true;
408 }
409
410 /**
411 * Called when the orientation of the device changes.
412 */
413 public void onOrientationChange() {
414 if (!mIsInitialized) return;
415
416 // NOTE(pedrosimonetti): Invalidates the existing height. This is to pre vent keeping
417 // the wrong height for a short period after rotating the device.
418 mSearchPanelDelegate.setPromoContentHeight(0.f);
419 hideContextualSearch(StateChangeReason.UNKNOWN);
420 }
421
422 /**
423 * Sets the {@link ContextualSearchNetworkCommunicator} to use for server re quests.
424 * @param networkCommunicator The communicator for all future requests.
425 */
426 @VisibleForTesting
427 public void setNetworkCommunicator(ContextualSearchNetworkCommunicator netwo rkCommunicator) {
428 mNetworkCommunicator = networkCommunicator;
429 }
430
431 /**
432 * Determines if the given selection is valid or not.
433 * @param selection The selection portion of the context.
434 * @return whether the given selection is considered a valid target for a se arch.
435 */
436 private boolean isValidSelection(String selection) {
437 return isValidSelection(selection, getBaseContentView());
438 }
439
440 @VisibleForTesting
441 boolean isValidSelection(String selection, ContentViewCore baseContentView) {
442 if (selection.length() > MAX_SELECTION_LENGTH || !doesContainAWord(selec tion)) {
443 return false;
444 }
445 return (baseContentView != null) && !baseContentView.isFocusedNodeEditab le();
446 }
447
448 /**
449 * Shows the Contextual Search UX.
450 * Calls back into onGetContextualSearchQueryResponse.
451 * @param stateChangeReason The reason explaining the change of state.
452 */
453 private void showContextualSearch(StateChangeReason stateChangeReason) {
454 if (!mSearchPanelDelegate.isShowing()) {
455 // If visible, hide the infobar container before showing the Context ual Search panel.
456 InfoBarContainer container = getInfoBarContainer();
457 if (container != null && container.getVisibility() == View.VISIBLE) {
458 mWereInfoBarsHidden = true;
459 container.setVisibility(View.INVISIBLE);
460 container.setDoStayInvisible(true);
461 }
462 }
463
464 // If the user is jumping from one unseen search to another search, remo ve the last search
465 // from history.
466 PanelState state = mSearchPanelDelegate.getPanelState();
467 if (!mWereSearchResultsSeen && mLoadedSearchUrlTimeMs != 0L
468 && state != PanelState.UNDEFINED && state != PanelState.CLOSED) {
469 removeLastSearchVisit();
470 }
471
472 // Make sure we'll create a new Content View when needed.
473 mNetworkCommunicator.destroySearchContentView();
474
475 boolean isTap = mSelectionController.getSelectionType() == SelectionType .TAP;
476 boolean didRequestSurroundings = false;
477 if (mPolicy.isOptInPromoAvailable()) {
478 showFirstRunFlow();
479 } else if (isTap && mPolicy.shouldPreviousTapResolve(
480 mNetworkCommunicator.getBasePageUrl())) {
481 mNetworkCommunicator.startSearchTermResolutionRequest(
482 mSelectionController.getSelectedText());
483 didRequestSurroundings = true;
484 } else {
485 boolean shouldPrefetch = mPolicy.shouldPrefetchSearchResult(isTap);
486 mSearchRequest = new ContextualSearchRequest(mSelectionController.ge tSelectedText(),
487 null, shouldPrefetch);
488 mDidLoadResolvedSearchRequest = false;
489 getContextualSearchControl().setCentralText(mSelectionController.get SelectedText());
490 if (shouldPrefetch) loadSearchUrl();
491 }
492 if (!didRequestSurroundings) {
493 // Gather surrounding text for Icing integration, which will make th e selection and
494 // a shorter version of the surroundings available for Conversationa l Search.
495 // Although the surroundings are extracted, they will not be sent to the server as
496 // part of search term resolution, just sent to Icing which keeps th em local until
497 // the user activates a Voice Search.
498 nativeGatherSurroundingText(mNativeContextualSearchManagerPtr,
499 mSelectionController.getSelectedText(), NEVER_USE_RESOLVED_S EARCH_TERM,
500 getBaseContentView());
501 }
502
503 mWereSearchResultsSeen = false;
504
505 // TODO(donnd): although we are showing the bar here, we have not yet se t the text!
506 // Refactor to show the bar and set the text at the same time!
507 // TODO(donnd): If there was a previously ongoing contextual search, we should ensure
508 // it's registered as closed.
509 mSearchPanelDelegate.peekPanel(stateChangeReason);
510
511 // Note: now that the contextual search has properly started, set the fi rst run involvement.
512 if (mPolicy.isOptOutPromoAvailable()) {
513 mIsShowingPromo = true;
514 mDidLogPromoOutcome = false;
515 mSearchPanelDelegate.setDidSearchInvolvePromo();
516 }
517
518 assert mSelectionController.getSelectionType() != SelectionType.UNDETERM INED;
519 mWasActivatedByTap = mSelectionController.getSelectionType() == Selectio nType.TAP;
520 }
521
522 @Override
523 public void startSearchTermResolutionRequest(String selection) {
524 ContentViewCore baseContentView = getBaseContentView();
525 if (baseContentView != null) {
526 nativeStartSearchTermResolutionRequest(mNativeContextualSearchManage rPtr, selection,
527 true, getBaseContentView());
528 }
529 }
530
531 @Override
532 public void continueSearchTermResolutionRequest() {
533 nativeContinueSearchTermResolutionRequest(mNativeContextualSearchManager Ptr);
534 }
535
536 @Override
537 @Nullable public URL getBasePageUrl() {
538 ContentViewCore baseContentViewCore = getBaseContentView();
539 if (baseContentViewCore == null) return null;
540
541 try {
542 return new URL(baseContentViewCore.getWebContents().getUrl());
543 } catch (MalformedURLException e) {
544 return null;
545 }
546 }
547
548 /**
549 * Loads the first-run flow in the search content view and sets the text on the contextual
550 * search bar.
551 */
552 private void showFirstRunFlow() {
553 mIsShowingPromo = true;
554 mDidLogPromoOutcome = false;
555 getContextualSearchControl().setFirstRunText(mSelectionController.getSel ectedText());
556 loadUrl(FIRST_RUN_FLOW_URL);
557 mSearchPanelDelegate.setIsPromoActive(true);
558 // Saved in the case a search needs to be made after first run.
559 if (mSelectionController.getSelectionType() == SelectionType.TAP) {
560 nativeGatherSurroundingText(mNativeContextualSearchManagerPtr,
561 mSelectionController.getSelectedText(),
562 ALWAYS_USE_RESOLVED_SEARCH_TERM, getBaseContentView());
563 }
564 }
565
566 @Override
567 public void updateTopControlsState(int current, boolean animate) {
568 Tab currentTab = mActivity.getActivityTab();
569 if (currentTab != null) {
570 currentTab.updateTopControlsState(current, animate);
571 }
572 }
573
574 /**
575 * Determines if the given selection contains a word or not.
576 * @param selection The the selection to check for a word.
577 * @return Whether the selection contains a word anywhere within it or not.
578 */
579 @VisibleForTesting
580 public boolean doesContainAWord(String selection) {
581 return mContainsWordPattern.matcher(selection).find();
582 }
583
584 /**
585 * Accessor for the {@code InfoBarContainer} currently attached to the {@cod e Tab}.
586 */
587 private InfoBarContainer getInfoBarContainer() {
588 Tab tab = mActivity.getActivityTab();
589 return tab == null ? null : tab.getInfoBarContainer();
590 }
591
592 /**
593 * Inflates the Contextual Search control, if needed.
594 */
595 private ContextualSearchControl getContextualSearchControl() {
596 return mSearchPanelDelegate.getContextualSearchControl();
597 }
598
599 /**
600 * Listens for notifications that should hide the Contextual Search bar.
601 */
602 private void listenForHideNotifications() {
603 TabModelSelector selector = mActivity.getTabModelSelector();
604
605 mTabModelSelectorTabObserver = new TabModelSelectorTabObserver(selector) {
606 @Override
607 public void onPageLoadStarted(Tab tab) {
608 hideContextualSearch(StateChangeReason.UNKNOWN);
609 mDidBasePageLoadJustStart = true;
610 }
611
612 @Override
613 public void onCrash(Tab tab, boolean sadTabShown) {
614 if (sadTabShown) {
615 // Hide contextual search if the foreground tab crashed
616 hideContextualSearch(StateChangeReason.UNKNOWN);
617 }
618 }
619
620 @Override
621 public void onClosingStateChanged(Tab tab, boolean closing) {
622 if (closing) hideContextualSearch(StateChangeReason.UNKNOWN);
623 }
624 };
625
626 for (TabModel tabModel : selector.getModels()) {
627 tabModel.addObserver(mTabModelObserver);
628 }
629 }
630
631 /**
632 * Stops listening for notifications that should hide the Contextual Search bar.
633 */
634 private void stopListeningForHideNotifications() {
635 if (mTabModelSelectorTabObserver != null) mTabModelSelectorTabObserver.d estroy();
636 if (mFindToolbarManager != null) mFindToolbarManager.removeObserver(mFin dToolbarObserver);
637
638 TabModelSelector selector = mActivity.getTabModelSelector();
639 if (selector != null) {
640 for (TabModel tabModel : selector.getModels()) {
641 tabModel.removeObserver(mTabModelObserver);
642 }
643 }
644 }
645
646 @Override
647 public void onActivityStateChange(Activity activity, int newState) {
648 if (newState == ActivityState.RESUMED || newState == ActivityState.STOPP ED
649 || newState == ActivityState.DESTROYED) {
650 hideContextualSearch(StateChangeReason.UNKNOWN);
651 }
652 }
653
654 /**
655 * Clears our private member referencing the native manager.
656 */
657 @CalledByNative
658 public void clearNativeManager() {
659 assert mNativeContextualSearchManagerPtr != 0;
660 mNativeContextualSearchManagerPtr = 0;
661 }
662
663 /**
664 * Sets our private member referencing the native manager.
665 * @param nativeManager The pointer to the native Contextual Search manager.
666 */
667 @CalledByNative
668 public void setNativeManager(long nativeManager) {
669 assert mNativeContextualSearchManagerPtr == 0;
670 mNativeContextualSearchManagerPtr = nativeManager;
671 }
672
673 /**
674 * Called when surrounding text is available.
675 * @param beforeText to be shown before the selected word.
676 * @param afterText to be shown after the selected word.
677 */
678 @CalledByNative
679 private void onSurroundingTextAvailable(final String beforeText, final Strin g afterText) {
680 if (mSearchPanelDelegate.isShowing()) {
681 getContextualSearchControl().setSearchContext(
682 mSelectionController.getSelectedText(), beforeText, afterTex t);
683 }
684 }
685
686 /**
687 * Called by native code when a selection is available to share with Icing ( for Conversational
688 * Search).
689 */
690 @CalledByNative
691 private void onIcingSelectionAvailable(
692 final String encoding, final String surroundingText, int startOffset , int endOffset) {
693 GSAContextDisplaySelection selection =
694 new GSAContextDisplaySelection(encoding, surroundingText, startO ffset, endOffset);
695 notifyShowContextualSearch(selection, mNetworkCommunicator.getBasePageUr l());
696 }
697
698 /**
699 * Called in response to the
700 * {@link ContextualSearchManager#nativeStartSearchTermResolutionRequest} me thod.
701 * @param isNetworkUnavailable Indicates if the network is unavailable, in w hich case all other
702 * parameters should be ignored.
703 * @param responseCode The HTTP response code. If the code is not OK, the q uery
704 * should be ignored.
705 * @param searchTerm The term to use in our subsequent search.
706 * @param displayText The text to display in our UX.
707 * @param alternateTerm The alternate term to display on the results page.
708 */
709 @CalledByNative
710 public void onSearchTermResolutionResponse(boolean isNetworkUnavailable, int responseCode,
711 final String searchTerm, final String displayText, final String alte rnateTerm,
712 boolean doPreventPreload) {
713 mNetworkCommunicator.handleSearchTermResolutionResponse(isNetworkUnavail able, responseCode,
714 searchTerm, displayText, alternateTerm, doPreventPreload);
715 }
716
717 @Override
718 public void handleSearchTermResolutionResponse(boolean isNetworkUnavailable, int responseCode,
719 String searchTerm, String displayText, String alternateTerm, boolean doPreventPreload) {
720 if (!mSearchPanelDelegate.isShowing()) return;
721
722 // Show an appropriate message for what to search for.
723 String message;
724 boolean doLiteralSearch = false;
725 if (isNetworkUnavailable) {
726 message = mActivity.getResources().getString(
727 R.string.contextual_search_network_unavailable);
728 } else if (!isHttpFailureCode(responseCode)) {
729 message = displayText;
730 } else if (!mPolicy.shouldShowErrorCodeInBar()) {
731 message = mSelectionController.getSelectedText();
732 doLiteralSearch = true;
733 } else {
734 message = mActivity.getResources().getString(
735 R.string.contextual_search_error, responseCode);
736 doLiteralSearch = true;
737 }
738 getContextualSearchControl().setCentralText(message);
739
740 // If there was an error, fall back onto a literal search for the select ion.
741 // Since we're showing the panel, there must be a selection.
742 if (doLiteralSearch) {
743 searchTerm = mSelectionController.getSelectedText();
744 alternateTerm = null;
745 doPreventPreload = true;
746 }
747 if (!searchTerm.isEmpty()) {
748 // TODO(donnd): Instead of preloading, we should prefetch (ie the UR L should not
749 // appear in the user's history until the user views it). See crbug .com/406446.
750 boolean shouldPreload = !doPreventPreload && mPolicy.shouldPrefetchS earchResult(true);
751 mSearchRequest = new ContextualSearchRequest(searchTerm, alternateTe rm, shouldPreload);
752 mDidLoadResolvedSearchRequest = false;
753 if (mIsSearchContentViewShowing) {
754 mSearchRequest.setNormalPriority();
755 }
756 if (mIsSearchContentViewShowing || shouldPreload) {
757 loadSearchUrl();
758 }
759 }
760 }
761
762 /**
763 * Loads a Search Request in the Contextual Search's Content View.
764 */
765 private void loadSearchUrl() {
766 mLoadedSearchUrlTimeMs = System.currentTimeMillis();
767 mNetworkCommunicator.loadUrl(mSearchRequest.getSearchUrl());
768 mSearchPanelDelegate.setIsPromoActive(false);
769 mDidLoadResolvedSearchRequest = true;
770
771 // TODO(pedrosimonetti): If the user taps on a word and quickly after th at taps on the
772 // peeking Search Bar, the Search Content View will not be displayed. It seems that
773 // calling ContentViewCore.onShow() while it's being created has no effe ct. Need
774 // to coordinate with Chrome-Android folks to come up with a proper fix for this.
775 // For now, we force the ContentView to be displayed by calling onShow() again
776 // when a URL is being loaded. See: crbug.com/398206
777 if (mIsSearchContentViewShowing && mSearchContentViewCore != null) {
778 mSearchContentViewCore.onShow();
779 }
780 }
781
782 /**
783 * @return Whether a Tap gesture is currently supported.
784 */
785 private boolean isTapSupported() {
786 // Base page just started navigating away, so taps should be ignored.
787 if (mDidBasePageLoadJustStart) return false;
788
789 return mPolicy.isTapSupported();
790 }
791
792 // ------------------------------------------------------------------------- -------------------
793 // Search Content View
794 // ------------------------------------------------------------------------- -------------------
795
796 /**
797 * Gets the {@code ContentViewCore} associated with Contextual Search Panel.
798 * @return Contextual Search Panel's {@code ContentViewCore}.
799 */
800 @Override
801 public ContentViewCore getSearchContentViewCore() {
802 return mSearchContentViewCore;
803 }
804
805 /**
806 * Sets the {@code ContextualSearchContentViewDelegate} associated with the Content View.
807 * @param delegate
808 */
809 public void setSearchContentViewDelegate(ContextualSearchContentViewDelegate delegate) {
810 mSearchContentViewDelegate = delegate;
811 }
812
813 /**
814 * Removes the last resolved search URL from the Chrome history.
815 */
816 private void removeLastSearchVisit() {
817 if (mSearchRequest != null) {
818 nativeRemoveLastSearchVisit(mNativeContextualSearchManagerPtr,
819 mSearchRequest.getSearchUrl(), mLoadedSearchUrlTimeMs);
820 }
821 }
822
823 /**
824 * Called when the Search content view navigates to a specific URL related t o the first run
825 * flow.
826 * @param url The new URL that is being navigated to.
827 */
828 private void onFirstRunNavigation(String url) {
829 if (FIRST_RUN_OPTIN_URL.equals(url)) {
830 mSearchPanelDelegate.setIsPromoActive(false);
831 mSearchPanelDelegate.animateAfterFirstRunSuccess();
832 PrefServiceBridge.getInstance().setContextualSearchState(true);
833 logPromoOutcome();
834 String selection = mSelectionController.getSelectedText();
835 if (mSelectionController.getSelectionType() == SelectionType.LONG_PR ESS) {
836 mSearchRequest = new ContextualSearchRequest(selection);
837 loadSearchUrl();
838 getContextualSearchControl().setCentralText(selection);
839 } else {
840 mNetworkCommunicator.continueSearchTermResolutionRequest();
841 }
842 } else if (FIRST_RUN_OPTOUT_URL.equals(url)) {
843 PrefServiceBridge.getInstance().setContextualSearchState(false);
844 logPromoOutcome();
845 mSearchPanelDelegate.closePanel(StateChangeReason.OPTOUT, true);
846 mSearchPanelDelegate.setIsPromoActive(false);
847 } else if (FIRST_RUN_LEARN_MORE_URL.equals(url)) {
848 openContextualSearchLearnMore();
849 mSearchPanelDelegate.setIsPromoActive(false);
850 }
851 }
852
853 /**
854 * Called when the Search content view navigates to a contextual search requ est URL.
855 * This navigation could be for a prefetch when the panel is still closed, o r
856 * a load of a user-visible search result.
857 * @param isFailure Whether the navigation failed.
858 */
859 private void onContextualSearchRequestNavigation(boolean isFailure) {
860 if (mSearchRequest == null) return;
861
862 if (mSearchRequest.isUsingLowPriority()) {
863 ContextualSearchUma.logLowPrioritySearchRequestOutcome(isFailure);
864 } else {
865 ContextualSearchUma.logNormalPrioritySearchRequestOutcome(isFailure) ;
866 if (mSearchRequest.getHasFailed()) {
867 ContextualSearchUma.logFallbackSearchRequestOutcome(isFailure);
868 }
869 }
870
871 if (isFailure && mSearchRequest.isUsingLowPriority()) {
872 // We're navigating to an error page, so we want to stop and retry.
873 // Stop loading the page that displays the error to the user.
874 if (mSearchContentViewCore != null) {
875 // When running tests the Content View might not exist.
876 mSearchContentViewCore.getWebContents().stop();
877 }
878 mSearchRequest.setHasFailed();
879 mSearchRequest.setNormalPriority();
880 // If the content view is showing, load at normal priority now.
881 if (mIsSearchContentViewShowing) {
882 loadSearchUrl();
883 } else {
884 mDidLoadResolvedSearchRequest = false;
885 }
886 }
887 }
888
889 /**
890 * Opens the Contextual Search "Learn More" experience.
891 */
892 private void openContextualSearchLearnMore() {
893 openUrlInNewTab(mActivity.getString(R.string.contextual_search_learn_mor e_url));
894 }
895
896 @Override
897 public void logPromoOutcome() {
898 ContextualSearchUma.logPromoOutcome(mWasActivatedByTap);
899 mDidLogPromoOutcome = true;
900 }
901
902 /**
903 * Called when the Search Content view has finished loading to record how lo ng it takes the SERP
904 * to load after opening the panel.
905 */
906 private void onSearchResultsLoaded() {
907 if (mSearchRequest == null) return;
908
909 mSearchPanelDelegate.onSearchResultsLoaded(mSearchRequest.wasPrefetch()) ;
910 }
911
912 /**
913 * Creates a new Content View Core to display search results, if needed.
914 */
915 private void createNewSearchContentViewCoreIfNeeded() {
916 if (mSearchContentViewCore == null) {
917 mNetworkCommunicator.createNewSearchContentView();
918 }
919 }
920
921 @Override
922 public void loadUrl(String url) {
923 createNewSearchContentViewCoreIfNeeded();
924 if (mSearchContentViewCore != null && mSearchContentViewCore.getWebConte nts() != null) {
925 mDidLoadAnyUrl = true;
926 mSearchContentViewCore.getWebContents().getNavigationController().lo adUrl(
927 new LoadUrlParams(url));
928 }
929 }
930
931 @Override
932 public void createNewSearchContentView() {
933 if (mSearchContentViewCore != null) {
934 mNetworkCommunicator.destroySearchContentView();
935 }
936
937 mSearchContentViewCore = new ContentViewCore(mActivity);
938 ContentView cv = new ContentView(mActivity, mSearchContentViewCore);
939 // Creates an initially hidden WebContents which gets shown when the pan el is opened.
940 mSearchContentViewCore.initialize(cv, cv,
941 ContentViewUtil.createWebContents(false, true), mWindowAndroid);
942
943 // Transfers the ownership of the WebContents to the native ContextualSe archManager.
944 nativeSetWebContents(mNativeContextualSearchManagerPtr, mSearchContentVi ewCore,
945 mWebContentsDelegate);
946
947 mSearchWebContentsObserver =
948 new WebContentsObserver(mSearchContentViewCore.getWebContents()) {
949 @Override
950 public void didStartLoading(String url) {
951 mDidPromoteSearchNavigation = false;
952 }
953
954 @Override
955 public void didStartProvisionalLoadForFrame(long frameId, lo ng parentFrameId,
956 boolean isMainFrame, String validatedUrl, boolean is ErrorPage,
957 boolean isIframeSrcdoc) {
958 if (isMainFrame) onExternalNavigation(validatedUrl);
959 }
960
961 @Override
962 public void didNavigateMainFrame(String url, String baseUrl,
963 boolean isNavigationToDifferentPage, boolean isNavig ationInPage,
964 int httpResultCode) {
965 mNetworkCommunicator.handleDidNavigateMainFrame(url, htt pResultCode);
966 }
967
968 @Override
969 public void didFinishLoad(long frameId, String validatedUrl,
970 boolean isMainFrame) {
971 onSearchResultsLoaded();
972 boolean shouldClearHistory = mSearchRequest != null
973 && mSearchRequest.getHasFailed();
974 if (validatedUrl.equals(FIRST_RUN_FLOW_URL)) {
975 extractFirstRunFlowContentHeight();
976 } else if (mIsShowingPromo
977 && !validatedUrl.startsWith(FIRST_RUN_URL_PREFIX )) {
978 shouldClearHistory = true;
979 }
980 // Any time we place a page in a ContentViewCore, clear history if needed.
981 // This prevents the First Run Flow URL or error URLs fr om
982 // appearing in the Tab's history stack.
983 // Also please note that clearHistory() will not
984 // clear the current entry (search results page in this case),
985 // and it will not work properly if there are pending na vigations.
986 // That's why we need to clear the history here, after t he navigation
987 // is completed.
988 if (shouldClearHistory && mSearchContentViewCore != null ) {
989 mSearchContentViewCore.getWebContents().getNavigatio nController()
990 .clearHistory();
991 }
992 }
993 };
994
995 mSearchContentViewDelegate.setContextualSearchContentViewCore(mSearchCon tentViewCore);
996 nativeSetInterceptNavigationDelegate(mNativeContextualSearchManagerPtr,
997 new InterceptNavigationDelegateImpl(), mSearchContentViewCore.ge tWebContents());
998 }
999
1000 @Override
1001 public void handleDidNavigateMainFrame(String url, int httpResultCode) {
1002 if (url.startsWith(FIRST_RUN_URL_PREFIX)) {
1003 // Navigation should not be done inside this WebContentsObserver,
1004 // and since FirstRun may navigate, we need to delay that navigation .
1005 final String firstRunUrl = url;
1006 new Handler().post(new Runnable() {
1007 @Override
1008 public void run() {
1009 onFirstRunNavigation(firstRunUrl);
1010 }
1011 });
1012 } else if (shouldPromoteSearchNavigation()) {
1013 onExternalNavigation(url);
1014 } else {
1015 // Could be just prefetching, check if that failed.
1016 boolean isFailure = isHttpFailureCode(httpResultCode);
1017 onContextualSearchRequestNavigation(isFailure);
1018 }
1019 mDidLoadAnyUrl = false;
1020 }
1021
1022 /**
1023 * @return Whether the given HTTP result code represents a failure or not.
1024 */
1025 private boolean isHttpFailureCode(int httpResultCode) {
1026 return httpResultCode <= 0 || httpResultCode >= 400;
1027 }
1028
1029 /**
1030 * @return whether a navigation in the search content view should promote to a separate tab.
1031 */
1032 private boolean shouldPromoteSearchNavigation() {
1033 // A navigation can be due to us loading a URL, or a touch in the search content view.
1034 // Require a touch, but no recent loading, in order to promote to a sepa rate tab.
1035 // Note that tapping the opt-in button requires checking for recent load ing.
1036 return mSearchPanelDelegate.didTouchSearchContentView() && !mDidLoadAnyU rl;
1037 }
1038
1039 /**
1040 * Called to check if an external navigation is being done and take the appr opriate action:
1041 * Auto-promotes the panel into a separate tab if that's not already being d one.
1042 * @param url The URL we are navigating to.
1043 */
1044 private void onExternalNavigation(String url) {
1045 if (!mDidPromoteSearchNavigation
1046 && !BLACKLISTED_URL.equals(url)
1047 && !url.startsWith(INTENT_URL_PREFIX)
1048 && shouldPromoteSearchNavigation()) {
1049 // Do not promote to a regular tab if we're loading our Resolved Sea rch
1050 // URL, otherwise we'll promote it when prefetching the Serp.
1051 // Don't promote URLs when they are navigating to an intent - this i s
1052 // handled by the InterceptNavigationDelegate which uses a faster
1053 // maximizing animation.
1054 mDidPromoteSearchNavigation = true;
1055 mSearchPanelDelegate.maximizePanelThenPromoteToTab(StateChangeReason .SERP_NAVIGATION);
1056 }
1057 }
1058
1059 /**
1060 * Grab the content height from the first run flow and store it.
1061 */
1062 private void extractFirstRunFlowContentHeight() {
1063 mSearchContentViewCore.getWebContents().evaluateJavaScript("getContentHe ight()",
1064 new JavaScriptCallback() {
1065 @Override
1066 public void handleJavaScriptResult(String result) {
1067 try {
1068 mSearchPanelDelegate.setPromoContentHeight(
1069 Float.parseFloat(result));
1070 } catch (NumberFormatException e) {
1071 Log.w(TAG, "Could not extract first-run content heig ht, using "
1072 + "default value.");
1073 }
1074 }
1075 });
1076 }
1077
1078 @Override
1079 public void destroySearchContentView() {
1080 if (mSearchContentViewCore != null && mSearchContentViewDelegate != null ) {
1081 nativeDestroyWebContents(mNativeContextualSearchManagerPtr);
1082 mSearchContentViewDelegate.releaseContextualSearchContentViewCore();
1083 mSearchContentViewCore.destroy();
1084 mSearchContentViewCore = null;
1085 if (mSearchWebContentsObserver != null) {
1086 mSearchWebContentsObserver.destroy();
1087 mSearchWebContentsObserver = null;
1088 }
1089 }
1090
1091 // This should be called last here. The setSearchContentViewVisibility m ethod
1092 // will change the visibility the SearchContentView but also set the val ue of the
1093 // internal property mIsSearchContentViewShowing. If we call this after deleting
1094 // the SearchContentView, it will be faster, because only the internal p roperty
1095 // will be changed, since there will be no need to change the visibility of the
1096 // SearchContentView.
1097 setSearchContentViewVisibility(false);
1098 }
1099
1100 @Override
1101 public void openResolvedSearchUrlInNewTab() {
1102 if (mSearchRequest != null && mSearchRequest.getSearchUrl() != null) {
1103 openUrlInNewTab(mSearchRequest.getSearchUrl());
1104 }
1105 }
1106
1107 /**
1108 * Convenience method for opening a specific |url| in a new Tab.
1109 */
1110 private void openUrlInNewTab(String url) {
1111 TabModelSelector tabModelSelector = mActivity.getTabModelSelector();
1112 tabModelSelector.openNewTab(
1113 new LoadUrlParams(url),
1114 TabLaunchType.FROM_MENU_OR_OVERVIEW,
1115 tabModelSelector.getCurrentTab(),
1116 tabModelSelector.isIncognitoSelected());
1117 }
1118
1119 @Override
1120 public boolean isRunningInCompatibilityMode() {
1121 return DeviceClassManager.isAccessibilityModeEnabled(mActivity)
1122 || SysUtils.isLowEndDevice();
1123 }
1124
1125 @Override
1126 public void promoteToTab(boolean shouldFocusOmnibox) {
1127 // If the request object is null that means that a Contextual Search has just started
1128 // and the Search Term Resolution response hasn't arrived yet. In this c ase, promoting
1129 // the Panel to a Tab will result in creating a new tab with URL about:b lank. To prevent
1130 // this problem, we are ignoring tap gestures in the Search Bar if we do n't know what
1131 // to search for.
1132 if (mSearchRequest != null
1133 && mSearchContentViewCore != null
1134 && mSearchContentViewCore.getWebContents() != null
1135 && !mSearchContentViewCore.getWebContents().getUrl().equals(FIRS T_RUN_FLOW_URL)) {
1136 mSelectionController.clearSelection();
1137 nativeReleaseWebContents(mNativeContextualSearchManagerPtr);
1138 mSearchContentViewDelegate.releaseContextualSearchContentViewCore();
1139 if (!mTabPromotionDelegate.createContextualSearchTab(mSearchContentV iewCore)) {
1140 nativeDestroyWebContentsFromContentViewCore(mNativeContextualSea rchManagerPtr,
1141 mSearchContentViewCore);
1142 mSearchContentViewCore.destroy();
1143 }
1144 if (mSearchWebContentsObserver != null) {
1145 mSearchWebContentsObserver.destroy();
1146 mSearchWebContentsObserver = null;
1147 }
1148 mSearchContentViewCore = null;
1149 mIsSearchContentViewShowing = false;
1150 mSearchRequest = null;
1151
1152 // NOTE(pedrosimonetti): The Panel should be closed after being prom oted to a Tab
1153 // to prevent Chrome-Android from animating the creation of the new Tab.
1154 mSearchPanelDelegate.closePanel(StateChangeReason.TAB_PROMOTION, fal se);
1155
1156 // Focus the Omnibox.
1157 if (shouldFocusOmnibox) {
1158 new Handler().post(new Runnable() {
1159 @Override
1160 public void run() {
1161 View urlBarView = mActivity.findViewById(R.id.url_bar);
1162 urlBarView.requestFocus();
1163 }
1164 });
1165 }
1166 }
1167 }
1168
1169 @Override
1170 public void resetSearchContentViewScroll() {
1171 if (mSearchContentViewCore != null) {
1172 mSearchContentViewCore.scrollTo(0, 0);
1173 }
1174 }
1175
1176 @Override
1177 public float getSearchContentViewVerticalScroll() {
1178 return mSearchContentViewCore != null
1179 ? mSearchContentViewCore.computeVerticalScrollOffset() : -1.f;
1180 }
1181
1182 @Override
1183 public void setSearchContentViewVisibility(boolean isVisible) {
1184 if (mIsSearchContentViewShowing == isVisible) return;
1185
1186 mIsSearchContentViewShowing = isVisible;
1187 if (isVisible) {
1188 mWereSearchResultsSeen = true;
1189 // If there's no current request, then either a search term resoluti on
1190 // is in progress or we should do a verbatim search now.
1191 if (mSearchRequest == null
1192 && mPolicy.shouldCreateVerbatimRequest(mSelectionController,
1193 mNetworkCommunicator.getBasePageUrl())) {
1194 mSearchRequest = new ContextualSearchRequest(
1195 mSelectionController.getSelectedText());
1196 mDidLoadResolvedSearchRequest = false;
1197 }
1198 if (mSearchRequest != null && !mDidLoadResolvedSearchRequest) {
1199 mSearchRequest.setNormalPriority();
1200 loadSearchUrl();
1201 }
1202 // The CVC is created with the search request, but if none was made we'll need
1203 // one in order to display an empty panel.
1204 createNewSearchContentViewCoreIfNeeded();
1205 if (mSearchContentViewCore != null) mSearchContentViewCore.onShow();
1206 mSearchPanelDelegate.setWasSearchContentViewSeen();
1207 mPolicy.resetTapCounters();
1208 } else {
1209 if (mSearchContentViewCore != null) mSearchContentViewCore.onHide();
1210 }
1211 }
1212
1213 @Override
1214 public void preserveBasePageSelectionOnNextLossOfFocus() {
1215 ContentViewCore basePageContentView = getBaseContentView();
1216 if (basePageContentView != null) {
1217 basePageContentView.preserveSelectionOnNextLossOfFocus();
1218 }
1219 }
1220
1221 @Override
1222 public void dismissContextualSearchBar() {
1223 hideContextualSearch(StateChangeReason.UNKNOWN);
1224 }
1225
1226 // Used to intercept intent navigations.
1227 // TODO(jeremycho): Consider creating a Tab with the Panel's ContentViewCore ,
1228 // which would also handle functionality like long-press-to-paste.
1229 private class InterceptNavigationDelegateImpl implements InterceptNavigation Delegate {
1230 final ExternalNavigationHandler mExternalNavHandler = new ExternalNaviga tionHandler(
1231 mActivity);
1232 @Override
1233 public boolean shouldIgnoreNavigation(NavigationParams navigationParams) {
1234 mTabRedirectHandler.updateNewUrlLoading(navigationParams.pageTransit ionType,
1235 navigationParams.isRedirect,
1236 navigationParams.hasUserGesture || navigationParams.hasUserG estureCarryover,
1237 mActivity.getLastUserInteractionTime(), TabRedirectHandler.I NVALID_ENTRY_INDEX);
1238
1239 ExternalNavigationParams params = new ExternalNavigationParams.Build er(
1240 navigationParams.url, false, getReferrerUrl(),
1241 navigationParams.pageTransitionType, navigationParams.isRedi rect)
1242 .setApplicationMustBeInForeground(true)
1243 .setRedirectHandler(mTabRedirectHandler)
1244 .setIsMainFrame(navigationParams.isMainFrame)
1245 .build();
1246 if (mExternalNavHandler.shouldOverrideUrlLoading(params)
1247 != OverrideUrlLoadingResult.NO_OVERRIDE) {
1248 mSearchPanelDelegate.maximizePanelThenPromoteToTab(
1249 StateChangeReason.TAB_PROMOTION,
1250 INTERCEPT_NAVIGATION_PROMOTION_ANIMATION_DURATION_MS);
1251 return true;
1252 }
1253 return false;
1254 }
1255
1256 private String getReferrerUrl() {
1257 if (mSearchContentViewCore != null && mSearchContentViewCore.getWebC ontents() != null) {
1258 return mSearchContentViewCore.getWebContents().getNavigationCont roller()
1259 .getOriginalUrlForVisibleNavigationEntry();
1260 } else {
1261 return null;
1262 }
1263 }
1264 }
1265
1266 // ------------------------------------------------------------------------- -------------------
1267 // ContextualSearchClient -- interface used by ContentViewCore.
1268 // ------------------------------------------------------------------------- -------------------
1269
1270 @Override
1271 public void onSelectionChanged(String selection) {
1272 mSelectionController.handleSelectionChanged(selection);
1273 updateTopControlsState(TopControlsState.BOTH, true);
1274 }
1275
1276 @Override
1277 public void onSelectionEvent(int eventType, float posXPix, float posYPix) {
1278 mSelectionController.handleSelectionEvent(eventType, posXPix, posYPix);
1279 }
1280
1281 @Override
1282 public void showUnhandledTapUIIfNeeded(final int x, final int y) {
1283 mDidBasePageLoadJustStart = false;
1284 mSelectionController.handleShowUnhandledTapUIIfNeeded(x, y);
1285 }
1286
1287 // ------------------------------------------------------------------------- -------------------
1288 // Selection
1289 // ------------------------------------------------------------------------- -------------------
1290
1291 /**
1292 * Returns a new {@code GestureStateListener} that will listen for events in the Base Page.
1293 * This listener will handle all Contextual Search-related interactions that go through the
1294 * listener.
1295 */
1296 public GestureStateListener getGestureStateListener() {
1297 return mSelectionController.getGestureStateListener();
1298 }
1299
1300 @Override
1301 public void handleScroll() {
1302 hideContextualSearch(StateChangeReason.BASE_PAGE_SCROLL);
1303 }
1304
1305 @Override
1306 public void handleInvalidTap() {
1307 hideContextualSearch(StateChangeReason.BASE_PAGE_TAP);
1308 }
1309
1310 @Override
1311 public void handleValidTap() {
1312 if (isTapSupported()) {
1313 // Here we are starting a new Contextual Search with a Tap gesture, therefore
1314 // we need to clear to properly reflect that a search just started a nd we don't
1315 // have the resolved search term yet.
1316 mSearchRequest = null;
1317
1318 // Let the policy know that a tap gesture has been received.
1319 mPolicy.registerTap();
1320
1321 ContentViewCore baseContentView = getBaseContentView();
1322 if (baseContentView != null) baseContentView.getWebContents().select WordAroundCaret();
1323 }
1324 }
1325
1326 @Override
1327 public void handleSelection(String selection, SelectionType type, float x, f loat y) {
1328 if (!selection.isEmpty()) {
1329 boolean isSelectionValid = isValidSelection(selection);
1330
1331 StateChangeReason stateChangeReason = type == SelectionType.TAP
1332 ? StateChangeReason.TEXT_SELECT_TAP : StateChangeReason.TEXT _SELECT_LONG_PRESS;
1333 ContextualSearchUma.logSelectionIsValid(isSelectionValid);
1334
1335 if (isSelectionValid) {
1336 mSearchPanelDelegate.updateBasePageSelectionYPx(y);
1337 showContextualSearch(stateChangeReason);
1338 } else {
1339 hideContextualSearch(stateChangeReason);
1340 }
1341 }
1342 }
1343
1344 @Override
1345 public void handleSelectionModification(String selection, float x, float y) {
1346 if (mSearchPanelDelegate.isShowing()) {
1347 getContextualSearchControl().setCentralText(selection);
1348 }
1349 }
1350
1351 @Override
1352 public void onClearSelection() {
1353 notifyHideContextualSearch();
1354 }
1355
1356 // ------------------------------------------------------------------------- -------------------
1357 // Native calls
1358 // ------------------------------------------------------------------------- -------------------
1359
1360 private native long nativeInit();
1361 private native void nativeDestroy(long nativeContextualSearchManager);
1362 private native void nativeStartSearchTermResolutionRequest(
1363 long nativeContextualSearchManager, String selection, boolean useRes olvedSearchTerm,
1364 ContentViewCore baseContentViewCore);
1365 private native void nativeGatherSurroundingText(
1366 long nativeContextualSearchManager, String selection, boolean useRes olvedSearchTerm,
1367 ContentViewCore baseContentViewCore);
1368 private native void nativeContinueSearchTermResolutionRequest(
1369 long nativeContextualSearchManager);
1370 private native void nativeRemoveLastSearchVisit(
1371 long nativeContextualSearchManager, String searchUrl, long searchUrl TimeMs);
1372 private native void nativeSetWebContents(long nativeContextualSearchManager,
1373 ContentViewCore searchContentViewCore, WebContentsDelegateAndroid de legate);
1374 private native void nativeDestroyWebContents(long nativeContextualSearchMana ger);
1375 private native void nativeReleaseWebContents(long nativeContextualSearchMana ger);
1376 private native void nativeDestroyWebContentsFromContentViewCore(
1377 long nativeContextualSearchManager, ContentViewCore contentViewCore) ;
1378 private native boolean nativeShouldHidePromoHeader(long nativeContextualSear chManager);
1379 private native void nativeSetInterceptNavigationDelegate(long nativeContextu alSearchManager,
1380 InterceptNavigationDelegate delegate, WebContents webContents);
1381 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698