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

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

Issue 2211353002: [TTS] Gather surrounding text on Tap before any UX. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Split usage of the Tapped text from the SearchAction into a separate CL. Created 4 years, 4 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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698