OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.chrome.browser.contextualsearch; | 5 package org.chromium.chrome.browser.contextualsearch; |
6 | 6 |
7 import android.os.Handler; | 7 import android.os.Handler; |
8 | 8 |
9 import org.chromium.base.VisibleForTesting; | 9 import org.chromium.base.VisibleForTesting; |
10 import org.chromium.chrome.browser.ChromeActivity; | 10 import org.chromium.chrome.browser.ChromeActivity; |
11 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel; | 11 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel; |
12 import org.chromium.chrome.browser.contextualsearch.ContextualSearchBlacklist.Bl acklistReason; | 12 import org.chromium.chrome.browser.contextualsearch.ContextualSearchBlacklist.Bl acklistReason; |
13 import org.chromium.chrome.browser.contextualsearch.action.ResolvedSearchAction; | |
14 import org.chromium.chrome.browser.contextualsearch.action.SearchAction; | |
15 import org.chromium.chrome.browser.contextualsearch.action.SearchActionListener; | |
16 import org.chromium.chrome.browser.contextualsearch.gesture.SearchGestureHost; | |
13 import org.chromium.chrome.browser.preferences.ChromePreferenceManager; | 17 import org.chromium.chrome.browser.preferences.ChromePreferenceManager; |
14 import org.chromium.chrome.browser.tab.Tab; | 18 import org.chromium.chrome.browser.tab.Tab; |
15 import org.chromium.content.browser.ContentViewCore; | 19 import org.chromium.content.browser.ContentViewCore; |
16 import org.chromium.content_public.browser.GestureStateListener; | 20 import org.chromium.content_public.browser.GestureStateListener; |
21 import org.chromium.content_public.browser.WebContents; | |
17 import org.chromium.ui.touch_selection.SelectionEventType; | 22 import org.chromium.ui.touch_selection.SelectionEventType; |
18 | 23 |
19 import java.util.regex.Matcher; | 24 import java.util.regex.Matcher; |
20 import java.util.regex.Pattern; | 25 import java.util.regex.Pattern; |
21 | 26 |
22 /** | 27 /** |
23 * Controls selection gesture interaction for Contextual Search. | 28 * Controls selection gesture interaction for Contextual Search. |
24 */ | 29 */ |
25 public class ContextualSearchSelectionController { | 30 public class ContextualSearchSelectionController implements SearchGestureHost { |
Theresa
2016/08/16 15:41:49
Longer term this class will become SearchGestureCo
Donn Denman
2016/08/17 04:35:21
I believe this class becomes the SearchGestureReco
pedro (no code reviews)
2016/08/22 20:54:17
Yes, this class is doing lots of things, and ideal
Donn Denman
2016/08/23 23:21:46
Acknowledged.
| |
26 | 31 |
27 /** | 32 /** |
28 * The type of selection made by the user. | 33 * The type of selection made by the user. |
29 */ | 34 */ |
30 public enum SelectionType { | 35 public enum SelectionType { |
31 UNDETERMINED, | 36 UNDETERMINED, |
32 TAP, | 37 TAP, |
33 LONG_PRESS | 38 LONG_PRESS |
34 } | 39 } |
35 | 40 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
72 private boolean mShouldHandleSelectionModification; | 77 private boolean mShouldHandleSelectionModification; |
73 private boolean mDidExpandSelection; | 78 private boolean mDidExpandSelection; |
74 | 79 |
75 // Position of the selection. | 80 // Position of the selection. |
76 private float mX; | 81 private float mX; |
77 private float mY; | 82 private float mY; |
78 | 83 |
79 // The time of the most last scroll activity, or 0 if none. | 84 // The time of the most last scroll activity, or 0 if none. |
80 private long mLastScrollTimeNs; | 85 private long mLastScrollTimeNs; |
81 | 86 |
87 // When the last tap gesture happened. | |
88 private long mTapTimeNanoseconds; | |
89 | |
82 // Tracks whether a Context Menu has just been shown and the UX has been dis missed. | 90 // Tracks whether a Context Menu has just been shown and the UX has been dis missed. |
83 // The selection may be unreliable until the next reset. See crbug.com/6284 36. | 91 // The selection may be unreliable until the next reset. See crbug.com/6284 36. |
84 private boolean mIsContextMenuShown; | 92 private boolean mIsContextMenuShown; |
85 | 93 |
94 // Set to true when we have identified that a pending tap has not been handl ed by Blink. | |
95 private boolean mHasIdentifiedUnhandledTap; | |
96 | |
97 // The current Search action. | |
98 private SearchAction mSearchAction; | |
99 | |
86 private class ContextualSearchGestureStateListener extends GestureStateListe ner { | 100 private class ContextualSearchGestureStateListener extends GestureStateListe ner { |
87 @Override | 101 @Override |
88 public void onScrollStarted(int scrollOffsetY, int scrollExtentY) { | 102 public void onScrollStarted(int scrollOffsetY, int scrollExtentY) { |
89 mHandler.handleScroll(); | 103 mHandler.handleScroll(); |
90 } | 104 } |
91 | 105 |
92 @Override | 106 @Override |
93 public void onScrollEnded(int scrollOffsetY, int scrollExtentY) { | 107 public void onScrollEnded(int scrollOffsetY, int scrollExtentY) { |
94 mLastScrollTimeNs = System.nanoTime(); | 108 mLastScrollTimeNs = System.nanoTime(); |
95 } | 109 } |
(...skipping 10 matching lines...) Expand all Loading... | |
106 // notification in this case. | 120 // notification in this case. |
107 // See crbug.com/444114. | 121 // See crbug.com/444114. |
108 @Override | 122 @Override |
109 public void onSingleTap(boolean consumed, int x, int y) { | 123 public void onSingleTap(boolean consumed, int x, int y) { |
110 // We may be notified that a tap has happened even when the system c onsumed the event. | 124 // We may be notified that a tap has happened even when the system c onsumed the event. |
111 // This is being used to support tapping on an existing selection to show the selection | 125 // This is being used to support tapping on an existing selection to show the selection |
112 // handles. We should process this tap unless we have already shown the selection | 126 // handles. We should process this tap unless we have already shown the selection |
113 // handles (have a long-press selection) and the tap was consumed. | 127 // handles (have a long-press selection) and the tap was consumed. |
114 if (!(consumed && mSelectionType == SelectionType.LONG_PRESS)) { | 128 if (!(consumed && mSelectionType == SelectionType.LONG_PRESS)) { |
115 scheduleInvalidTapNotification(); | 129 scheduleInvalidTapNotification(); |
130 | |
131 if (mHasIdentifiedUnhandledTap) createSearchAction(); | |
Theresa
2016/08/16 15:41:49
This is happening before the selection is establis
Donn Denman
2016/08/17 04:35:21
That's right.
pedro (no code reviews)
2016/08/22 20:54:17
Not sure this is the right place to call createAct
Donn Denman
2016/08/23 23:21:46
In this initial integration of the refactoring we'
pedro (no code reviews)
2016/08/31 22:38:22
I think it should also be destroyed when a long pr
| |
116 } | 132 } |
117 } | 133 } |
118 } | 134 } |
119 | 135 |
120 /** | 136 /** |
121 * Constructs a new Selection controller for the given activity. Callbacks will be issued | 137 * Constructs a new Selection controller for the given activity. Callbacks will be issued |
122 * through the given selection handler. | 138 * through the given selection handler. |
123 * @param activity The {@link ChromeActivity} to control. | 139 * @param activity The {@link ChromeActivity} to control. |
124 * @param handler The handler for callbacks. | 140 * @param handler The handler for callbacks. |
125 */ | 141 */ |
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
331 } | 347 } |
332 | 348 |
333 /** | 349 /** |
334 * Resets all internal state of this class, including the tap state. | 350 * Resets all internal state of this class, including the tap state. |
335 */ | 351 */ |
336 private void resetAllStates() { | 352 private void resetAllStates() { |
337 resetSelectionStates(); | 353 resetSelectionStates(); |
338 mLastTapState = null; | 354 mLastTapState = null; |
339 mLastScrollTimeNs = 0; | 355 mLastScrollTimeNs = 0; |
340 mIsContextMenuShown = false; | 356 mIsContextMenuShown = false; |
357 mHasIdentifiedUnhandledTap = false; | |
Theresa
2016/08/16 15:41:49
nit: reset mTapTimeNanos here too?
Donn Denman
2016/08/17 04:35:21
Good idea, done.
| |
341 } | 358 } |
342 | 359 |
343 /** | 360 /** |
344 * Resets all of the internal state of this class that handles the selection . | 361 * Resets all of the internal state of this class that handles the selection . |
345 */ | 362 */ |
346 private void resetSelectionStates() { | 363 private void resetSelectionStates() { |
347 mSelectionType = SelectionType.UNDETERMINED; | 364 mSelectionType = SelectionType.UNDETERMINED; |
348 mSelectedText = null; | 365 mSelectedText = null; |
349 | 366 |
350 mWasTapGestureDetected = false; | 367 mWasTapGestureDetected = false; |
351 } | 368 } |
352 | 369 |
353 /** | 370 /** |
354 * Should be called when a new Tab is selected. | 371 * Should be called when a new Tab is selected. |
355 * Resets all of the internal state of this class. | 372 * Resets all of the internal state of this class. |
356 */ | 373 */ |
357 void onTabSelected() { | 374 void onTabSelected() { |
358 resetAllStates(); | 375 resetAllStates(); |
359 } | 376 } |
360 | 377 |
361 /** | 378 /** |
362 * Handles an unhandled tap gesture. | 379 * Handles an unhandled tap gesture. |
380 * @param x The x coordinate. | |
381 * @param y The y coordinate. | |
363 */ | 382 */ |
364 void handleShowUnhandledTapUIIfNeeded(int x, int y) { | 383 void handleShowUnhandledTapUIIfNeeded(int x, int y) { |
365 mWasTapGestureDetected = false; | 384 mWasTapGestureDetected = false; |
366 // TODO(donnd): shouldn't we check == TAP here instead of LONG_PRESS? | 385 // TODO(donnd): shouldn't we check == TAP here instead of LONG_PRESS? |
367 // TODO(donnd): refactor to avoid needing a new handler API method as su ggested by Pedro. | 386 // TODO(donnd): refactor to avoid needing a new handler API method as su ggested by Pedro. |
368 if (mSelectionType != SelectionType.LONG_PRESS) { | 387 if (mSelectionType != SelectionType.LONG_PRESS) { |
369 mWasTapGestureDetected = true; | 388 mWasTapGestureDetected = true; |
370 long tapTimeNanoseconds = System.nanoTime(); | 389 mTapTimeNanoseconds = System.nanoTime(); |
371 // TODO(donnd): add a policy method to get adjusted tap count. | 390 |
372 ChromePreferenceManager prefs = ChromePreferenceManager.getInstance( mActivity); | 391 // NOTE(donnd): We first acknowledge that a unhandled tap was identi fied, but |
373 int adjustedTapsSinceOpen = prefs.getContextualSearchTapCount() | 392 // we don't do anything now. Instead we'll wait for the onSingleTap( ) event to fire, |
374 - prefs.getContextualSearchTapQuickAnswerCount(); | 393 // and only do something when an unhandled tap was identified. onSin gleTap() will |
375 TapSuppressionHeuristics tapHeuristics = | 394 // always get fired, as opposed to showUnhandledTapUIIfNeeded(). |
376 new TapSuppressionHeuristics(this, mLastTapState, x, y, adju stedTapsSinceOpen); | 395 mHasIdentifiedUnhandledTap = true; |
377 // TODO(donnd): Move to be called when the panel closes to work with states that change. | 396 |
378 tapHeuristics.logConditionState(); | |
379 // Tell the manager what it needs in order to log metrics on whether the tap would have | |
380 // been suppressed if each of the heuristics were satisfied. | |
381 mHandler.handleMetricsForWouldSuppressTap(tapHeuristics); | |
382 mX = x; | 397 mX = x; |
383 mY = y; | 398 mY = y; |
384 boolean shouldSuppressTap = tapHeuristics.shouldSuppressTap(); | |
385 if (shouldSuppressTap) { | |
386 mHandler.handleSuppressedTap(); | |
387 } else { | |
388 // TODO(donnd): Find a better way to determine that a navigation will be triggered | |
389 // by the tap, or merge with other time-consuming actions like g athering surrounding | |
390 // text or detecting page mutations. | |
391 new Handler().postDelayed(new Runnable() { | |
392 @Override | |
393 public void run() { | |
394 mHandler.handleValidTap(); | |
395 } | |
396 }, TAP_NAVIGATION_DETECTION_DELAY); | |
397 } | |
398 // Remember the tap state for subsequent tap evaluation. | |
399 mLastTapState = | |
400 new ContextualSearchTapState(x, y, tapTimeNanoseconds, shoul dSuppressTap); | |
401 } else { | 399 } else { |
402 // Long press; reset last tap state. | 400 // Long press; reset last tap state. |
403 mLastTapState = null; | 401 mLastTapState = null; |
404 mHandler.handleInvalidTap(); | 402 mHandler.handleInvalidTap(); |
405 } | 403 } |
406 } | 404 } |
407 | 405 |
408 /** | 406 /** |
407 * Processes a {@link SearchAction}. | |
408 * This should be called when the associated {@code SearchAction} has built its context (by | |
409 * gathering surrounding text if needed, etc). | |
410 * @param searchAction The {@link SearchAction} for this Tap gesture. | |
411 * @param x The x coordinate. | |
412 * @param y The y coordinate. | |
413 */ | |
414 void processSearchAction(SearchAction searchAction, int x, int y) { | |
415 // TODO(donnd): use the supplied searchAction! | |
Theresa
2016/08/16 15:41:49
The plan is to move these suppressions into the se
Donn Denman
2016/08/17 04:35:21
That's right.
| |
416 // TODO(donnd): add a policy method to get adjusted tap count. | |
417 ChromePreferenceManager prefs = ChromePreferenceManager.getInstance(mAct ivity); | |
418 int adjustedTapsSinceOpen = prefs.getContextualSearchTapCount() | |
419 - prefs.getContextualSearchTapQuickAnswerCount(); | |
420 TapSuppressionHeuristics tapHeuristics = new TapSuppressionHeuristics( | |
421 this, mLastTapState, x, y, adjustedTapsSinceOpen); | |
422 // TODO(donnd): Move to be called when the panel closes to work with sta tes that change. | |
423 tapHeuristics.logConditionState(); | |
424 // Tell the manager what it needs in order to log metrics on whether the tap would have | |
425 // been suppressed if each of the heuristics were satisfied. | |
426 mHandler.handleMetricsForWouldSuppressTap(tapHeuristics); | |
427 | |
428 boolean shouldSuppressTap = tapHeuristics.shouldSuppressTap(); | |
429 if (shouldSuppressTap) { | |
430 mHandler.handleSuppressedTap(); | |
pedro (no code reviews)
2016/08/22 20:54:17
Should a suppressed gesture cause the action to be
Donn Denman
2016/08/23 23:21:46
I think it's simplest to just destroy the search a
| |
431 } else { | |
432 // TODO(donnd): Find a better way to determine that a navigation wil l be triggered | |
433 // by the tap, or merge with other time-consuming actions like gathe ring surrounding | |
434 // text or detecting page mutations. | |
435 new Handler().postDelayed(new Runnable() { | |
436 @Override | |
437 public void run() { | |
438 mHandler.handleValidTap(); | |
439 } | |
440 }, TAP_NAVIGATION_DETECTION_DELAY); | |
441 } | |
442 // Remember the tap state for subsequent tap evaluation. | |
443 mLastTapState = new ContextualSearchTapState(x, y, mTapTimeNanoseconds, shouldSuppressTap); | |
444 } | |
445 | |
446 /** | |
447 * Creates the current {@link SearchAction}. | |
448 */ | |
449 void createSearchAction() { | |
450 System.out.println("ctxs createSearchAction"); | |
451 mSearchAction = new ResolvedSearchAction(new SearchActionListener() { | |
452 | |
453 @Override | |
454 protected void onContextReady(SearchAction action) { | |
455 System.out.println("ctxs onContextReady"); | |
456 processSearchAction(action, (int) mX, (int) mY); | |
457 } | |
458 | |
459 @Override | |
460 protected void onActionAccepted(SearchAction action) { | |
461 System.out.println("ctxs onActionAccepted"); | |
462 destroySearchAction(); | |
pedro (no code reviews)
2016/08/22 20:54:17
Why are we destroying the action here?
Donn Denman
2016/08/23 23:21:46
I guess we don't need to destroy the action, so re
| |
463 } | |
464 }); | |
465 | |
466 mSearchAction.extractContext(this); | |
467 } | |
468 | |
469 /** | |
470 * Destroys the current {@link SearchAction}. | |
471 */ | |
472 private void destroySearchAction() { | |
473 System.out.println("ctxs destroySearchAction"); | |
474 if (mSearchAction == null) return; | |
475 | |
476 mSearchAction.destroyAction(); | |
477 mSearchAction = null; | |
478 } | |
479 | |
480 // ========================================================================= =================== | |
481 // SearchGestureHost | |
482 // ========================================================================= =================== | |
483 | |
484 @Override | |
485 public WebContents getTabWebContents() { | |
486 ContentViewCore baseCvc = getBaseContentView(); | |
Theresa
2016/08/16 15:41:49
I believe we're trying to remove CVC dependencies
Donn Denman
2016/08/17 04:35:21
Reworked this to not use a CVC, and added a TODO (
pedro (no code reviews)
2016/08/22 20:54:17
I'd call this baseContentViewCore or simply conten
Donn Denman
2016/08/23 23:21:46
Done -- code changed to not use CVC at all.
| |
487 if (baseCvc == null) { | |
488 return null; | |
489 } | |
490 return baseCvc.getWebContents(); | |
491 } | |
492 | |
493 @Override | |
494 public void dismissGesture() { | |
495 System.out.println("ctxs dismissGesture"); | |
496 destroySearchAction(); | |
497 } | |
498 | |
499 /** | |
409 * @return The Base Page's {@link ContentViewCore}, or {@code null} if there is no current tab. | 500 * @return The Base Page's {@link ContentViewCore}, or {@code null} if there is no current tab. |
410 */ | 501 */ |
411 ContentViewCore getBaseContentView() { | 502 ContentViewCore getBaseContentView() { |
Donn Denman
2016/08/17 04:35:21
Added a TODO here to stop using CVC.
| |
412 Tab currentTab = mActivity.getActivityTab(); | 503 Tab currentTab = mActivity.getActivityTab(); |
413 return currentTab != null ? currentTab.getContentViewCore() : null; | 504 return currentTab != null ? currentTab.getContentViewCore() : null; |
414 } | 505 } |
415 | 506 |
416 /** | 507 /** |
417 * Expands the current selection by the specified amounts. | 508 * Expands the current selection by the specified amounts. |
418 * @param selectionStartAdjust The start offset adjustment of the selection to use to highlight | 509 * @param selectionStartAdjust The start offset adjustment of the selection to use to highlight |
419 * the search term. | 510 * the search term. |
420 * @param selectionEndAdjust The end offset adjustment of the selection to u se to highlight | 511 * @param selectionEndAdjust The end offset adjustment of the selection to u se to highlight |
421 * the search term. | 512 * the search term. |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
597 // Starts are inclusive and ends are non-inclusive for both GSAContext & matcher. | 688 // Starts are inclusive and ends are non-inclusive for both GSAContext & matcher. |
598 while (matcher.find()) { | 689 while (matcher.find()) { |
599 if (startOffset >= matcher.start() && endOffset <= matcher.end()) { | 690 if (startOffset >= matcher.start() && endOffset <= matcher.end()) { |
600 return true; | 691 return true; |
601 } | 692 } |
602 } | 693 } |
603 | 694 |
604 return false; | 695 return false; |
605 } | 696 } |
606 } | 697 } |
OLD | NEW |