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

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: Simplified the results for the native accessors to the context. Removed print statements. Created 4 years, 3 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 {
26
27 /** 31 /**
28 * The type of selection made by the user. 32 * The type of selection made by the user.
29 */ 33 */
30 public enum SelectionType { 34 public enum SelectionType {
31 UNDETERMINED, 35 UNDETERMINED,
32 TAP, 36 TAP,
33 LONG_PRESS 37 LONG_PRESS
34 } 38 }
35 39
36 // The number of milliseconds to wait for a selection change after a tap bef ore considering 40 // The number of milliseconds to wait for a selection change after a tap bef ore considering
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
72 private boolean mShouldHandleSelectionModification; 76 private boolean mShouldHandleSelectionModification;
73 private boolean mDidExpandSelection; 77 private boolean mDidExpandSelection;
74 78
75 // Position of the selection. 79 // Position of the selection.
76 private float mX; 80 private float mX;
77 private float mY; 81 private float mY;
78 82
79 // The time of the most last scroll activity, or 0 if none. 83 // The time of the most last scroll activity, or 0 if none.
80 private long mLastScrollTimeNs; 84 private long mLastScrollTimeNs;
81 85
86 // When the last tap gesture happened.
87 private long mTapTimeNanoseconds;
88
82 // Tracks whether a Context Menu has just been shown and the UX has been dis missed. 89 // 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. 90 // The selection may be unreliable until the next reset. See crbug.com/6284 36.
84 private boolean mIsContextMenuShown; 91 private boolean mIsContextMenuShown;
85 92
93 // Set to true when we have identified that a pending tap has not been handl ed by Blink.
94 private boolean mHasIdentifiedUnhandledTap;
95
96 // The current Search action.
97 private SearchAction mSearchAction;
98
86 private class ContextualSearchGestureStateListener extends GestureStateListe ner { 99 private class ContextualSearchGestureStateListener extends GestureStateListe ner {
87 @Override 100 @Override
88 public void onScrollStarted(int scrollOffsetY, int scrollExtentY) { 101 public void onScrollStarted(int scrollOffsetY, int scrollExtentY) {
89 mHandler.handleScroll(); 102 mHandler.handleScroll();
90 } 103 }
91 104
92 @Override 105 @Override
93 public void onScrollEnded(int scrollOffsetY, int scrollExtentY) { 106 public void onScrollEnded(int scrollOffsetY, int scrollExtentY) {
94 mLastScrollTimeNs = System.nanoTime(); 107 mLastScrollTimeNs = System.nanoTime();
95 } 108 }
(...skipping 10 matching lines...) Expand all
106 // notification in this case. 119 // notification in this case.
107 // See crbug.com/444114. 120 // See crbug.com/444114.
108 @Override 121 @Override
109 public void onSingleTap(boolean consumed, int x, int y) { 122 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. 123 // 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 124 // 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 125 // 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. 126 // handles (have a long-press selection) and the tap was consumed.
114 if (!(consumed && mSelectionType == SelectionType.LONG_PRESS)) { 127 if (!(consumed && mSelectionType == SelectionType.LONG_PRESS)) {
115 scheduleInvalidTapNotification(); 128 scheduleInvalidTapNotification();
129
130 if (mHasIdentifiedUnhandledTap) createSearchAction();
116 } 131 }
117 } 132 }
118 } 133 }
119 134
120 /** 135 /**
121 * Constructs a new Selection controller for the given activity. Callbacks will be issued 136 * Constructs a new Selection controller for the given activity. Callbacks will be issued
122 * through the given selection handler. 137 * through the given selection handler.
123 * @param activity The {@link ChromeActivity} to control. 138 * @param activity The {@link ChromeActivity} to control.
124 * @param handler The handler for callbacks. 139 * @param handler The handler for callbacks.
125 */ 140 */
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after
331 } 346 }
332 347
333 /** 348 /**
334 * Resets all internal state of this class, including the tap state. 349 * Resets all internal state of this class, including the tap state.
335 */ 350 */
336 private void resetAllStates() { 351 private void resetAllStates() {
337 resetSelectionStates(); 352 resetSelectionStates();
338 mLastTapState = null; 353 mLastTapState = null;
339 mLastScrollTimeNs = 0; 354 mLastScrollTimeNs = 0;
340 mIsContextMenuShown = false; 355 mIsContextMenuShown = false;
356 mHasIdentifiedUnhandledTap = false;
357 mTapTimeNanoseconds = 0;
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).
Theresa 2016/09/01 16:49:36 nit: ..., etc) but before a selection has been est
Donn Denman 2016/09/02 16:40:43 Done. Now reads: "This should be called when the
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): consider using the supplied searchAction, or remove if u sed from native!
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 =
421 new TapSuppressionHeuristics(this, mLastTapState, x, y, adjusted TapsSinceOpen);
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();
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 if (mTapTimeNanoseconds == 0) throw new RuntimeException("Tap time not s et!");
443 // Remember the tap state for subsequent tap evaluation.
444 mLastTapState = new ContextualSearchTapState(x, y, mTapTimeNanoseconds, shouldSuppressTap);
445 }
446
447 /**
448 * Creates the current {@link SearchAction}.
449 */
450 void createSearchAction() {
pedro (no code reviews) 2016/08/31 22:38:22 The SearchAction should also be destroyed when a l
Donn Denman 2016/09/02 16:40:43 Done: Added a destroySearchAction to handleSelecti
451 if (mSearchAction != null) destroySearchAction();
Theresa 2016/09/01 16:49:36 nit: destroySearchAction() checks if mSearchAction
Donn Denman 2016/09/02 16:40:43 Done.
452 mSearchAction = new ResolvedSearchAction(new SearchActionListener() {
453
454 @Override
455 protected void onContextReady(SearchAction action) {
456 processSearchAction(action, (int) mX, (int) mY);
457 }
458 }, this);
459
460 mSearchAction.extractContext();
461 }
462
463 /**
464 * Destroys the current {@link SearchAction}.
465 */
466 private void destroySearchAction() {
467 if (mSearchAction == null) return;
468
469 mSearchAction.destroyAction();
470 mSearchAction = null;
471 }
472
473 // ========================================================================= ===================
474 // SearchGestureHost
475 // ========================================================================= ===================
476
477 @Override
478 public WebContents getTabWebContents() {
479 Tab currentTab = mActivity.getActivityTab();
480 return currentTab != null ? currentTab.getWebContents() : null;
481 }
482
483 @Override
484 public void dismissGesture() {
485 destroySearchAction();
486 }
487
488 // ========================================================================= ===================
489 // Utilities
490 // ========================================================================= ===================
491
492 /**
409 * @return The Base Page's {@link ContentViewCore}, or {@code null} if there is no current tab. 493 * @return The Base Page's {@link ContentViewCore}, or {@code null} if there is no current tab.
410 */ 494 */
411 ContentViewCore getBaseContentView() { 495 ContentViewCore getBaseContentView() {
496 // TODO(donnd): switch to using WebContents over ContentViewCore.
412 Tab currentTab = mActivity.getActivityTab(); 497 Tab currentTab = mActivity.getActivityTab();
413 return currentTab != null ? currentTab.getContentViewCore() : null; 498 return currentTab != null ? currentTab.getContentViewCore() : null;
414 } 499 }
415 500
416 /** 501 /**
417 * Expands the current selection by the specified amounts. 502 * Expands the current selection by the specified amounts.
418 * @param selectionStartAdjust The start offset adjustment of the selection to use to highlight 503 * @param selectionStartAdjust The start offset adjustment of the selection to use to highlight
419 * the search term. 504 * the search term.
420 * @param selectionEndAdjust The end offset adjustment of the selection to u se to highlight 505 * @param selectionEndAdjust The end offset adjustment of the selection to u se to highlight
421 * the search term. 506 * 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. 682 // Starts are inclusive and ends are non-inclusive for both GSAContext & matcher.
598 while (matcher.find()) { 683 while (matcher.find()) {
599 if (startOffset >= matcher.start() && endOffset <= matcher.end()) { 684 if (startOffset >= matcher.start() && endOffset <= matcher.end()) {
600 return true; 685 return true;
601 } 686 }
602 } 687 }
603 688
604 return false; 689 return false;
605 } 690 }
606 } 691 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698