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

Side by Side Diff: content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java

Issue 2740103006: Implement SmartText selection. (Closed)
Patch Set: Do not invalidate the menu and always recreate it; use Android resource id for Assist Created 3 years, 9 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.content.browser; 5 package org.chromium.content.browser;
6 6
7 import android.annotation.TargetApi; 7 import android.annotation.TargetApi;
8 import android.app.Activity; 8 import android.app.Activity;
9 import android.app.SearchManager; 9 import android.app.SearchManager;
10 import android.content.ClipboardManager; 10 import android.content.ClipboardManager;
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
47 * A class that handles input-related web content selection UI like action mode 47 * A class that handles input-related web content selection UI like action mode
48 * and paste popup view. It wraps an {@link ActionMode} created by the associate d view, 48 * and paste popup view. It wraps an {@link ActionMode} created by the associate d view,
49 * providing modified interaction with it. 49 * providing modified interaction with it.
50 * 50 *
51 * Embedders can use {@link ActionModeCallbackHelper} implemented by this class 51 * Embedders can use {@link ActionModeCallbackHelper} implemented by this class
52 * to create {@link ActionMode.Callback} instance and configure the selection ac tion 52 * to create {@link ActionMode.Callback} instance and configure the selection ac tion
53 * mode tasks to their requirements. 53 * mode tasks to their requirements.
54 */ 54 */
55 @TargetApi(Build.VERSION_CODES.M) 55 @TargetApi(Build.VERSION_CODES.M)
56 public class SelectionPopupController extends ActionModeCallbackHelper { 56 public class SelectionPopupController extends ActionModeCallbackHelper {
57 private static final String TAG = "cr.SelectionPopCtlr"; // 20 char limit 57 private static final String TAG = "SelectionPopupCtlr"; // 20 char limit
58 58
59 /** 59 /**
60 * Android Intent size limitations prevent sending over a megabyte of data. Limit 60 * Android Intent size limitations prevent sending over a megabyte of data. Limit
61 * query lengths to 100kB because other things may be added to the Intent. 61 * query lengths to 100kB because other things may be added to the Intent.
62 */ 62 */
63 private static final int MAX_SHARE_QUERY_LENGTH = 100000; 63 private static final int MAX_SHARE_QUERY_LENGTH = 100000;
64 64
65 // Default delay for reshowing the {@link ActionMode} after it has been 65 // Default delay for reshowing the {@link ActionMode} after it has been
66 // hidden. This avoids flickering issues if there are trailing rect 66 // hidden. This avoids flickering issues if there are trailing rect
67 // invalidations after the ActionMode is shown. For example, after the user 67 // invalidations after the ActionMode is shown. For example, after the user
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
108 private boolean mHasSelection; 108 private boolean mHasSelection;
109 109
110 // Lazily created paste popup menu, triggered either via long press in an 110 // Lazily created paste popup menu, triggered either via long press in an
111 // editable region or from tapping the insertion handle. 111 // editable region or from tapping the insertion handle.
112 private PastePopupMenu mPastePopupMenu; 112 private PastePopupMenu mPastePopupMenu;
113 private boolean mWasPastePopupShowingOnInsertionDragStart; 113 private boolean mWasPastePopupShowingOnInsertionDragStart;
114 114
115 // The client that processes textual selection, or null if none exists. 115 // The client that processes textual selection, or null if none exists.
116 private SelectionClient mSelectionClient; 116 private SelectionClient mSelectionClient;
117 117
118 // The classificaton result of the selected text if the selection exists and
119 // ContextSelectionProvider was able to classify it, otherwise null.
120 private ContextSelectionProvider.Result mClassificationResult;
121
122 // The resource ID for Assist menu item.
123 private int mAssistMenuItemId;
124
125 // This variable is set to true when the classification request is in progre ss.
126 private boolean mPendingClassificationRequest;
127
118 /** 128 /**
119 * Create {@link SelectionPopupController} instance. 129 * Create {@link SelectionPopupController} instance.
120 * @param context Context for action mode. 130 * @param context Context for action mode.
121 * @param window WindowAndroid instance. 131 * @param window WindowAndroid instance.
122 * @param webContents WebContents instance. 132 * @param webContents WebContents instance.
123 * @param view Container view. 133 * @param view Container view.
124 * @param renderCoordinates Coordinates info used to position elements. 134 * @param renderCoordinates Coordinates info used to position elements.
125 * @param imeAdapter ImeAdapter instance to handle cursor position. 135 * @param imeAdapter ImeAdapter instance to handle cursor position.
126 */ 136 */
127 public SelectionPopupController(Context context, WindowAndroid window, WebCo ntents webContents, 137 public SelectionPopupController(Context context, WindowAndroid window, WebCo ntents webContents,
(...skipping 10 matching lines...) Expand all
138 mRepeatingHideRunnable = new Runnable() { 148 mRepeatingHideRunnable = new Runnable() {
139 @Override 149 @Override
140 public void run() { 150 public void run() {
141 assert mHidden; 151 assert mHidden;
142 final long hideDuration = getDefaultHideDuration(); 152 final long hideDuration = getDefaultHideDuration();
143 // Ensure the next hide call occurs before the ActionMode reappe ars. 153 // Ensure the next hide call occurs before the ActionMode reappe ars.
144 mView.postDelayed(mRepeatingHideRunnable, hideDuration - 1); 154 mView.postDelayed(mRepeatingHideRunnable, hideDuration - 1);
145 hideActionModeTemporarily(hideDuration); 155 hideActionModeTemporarily(hideDuration);
146 } 156 }
147 }; 157 };
158
159 mSelectionClient =
160 ContextSelectionClient.create(new ContextSelectionCallback(), wi ndow, webContents);
161
162 // TODO(timav): Use android.R.id.textAssist for the Assist item id once we switch to
163 // Android O SDK and remove |mAssistMenuItemId|.
164 mAssistMenuItemId = mContext.getResources().getIdentifier("textAssist", "id", "android");
148 } 165 }
149 166
150 /** 167 /**
151 * Update the container view. 168 * Update the container view.
152 */ 169 */
153 void setContainerView(View view) { 170 void setContainerView(View view) {
154 assert view != null; 171 assert view != null;
155 172
156 // Cleans up action mode before switching to a new container view. 173 // Cleans up action mode before switching to a new container view.
157 if (isActionModeValid()) finishActionMode(); 174 if (isActionModeValid()) finishActionMode();
(...skipping 24 matching lines...) Expand all
182 @Override 199 @Override
183 public void setAllowedMenuItems(int allowedMenuItems) { 200 public void setAllowedMenuItems(int allowedMenuItems) {
184 mAllowedMenuItems = allowedMenuItems; 201 mAllowedMenuItems = allowedMenuItems;
185 } 202 }
186 203
187 /** 204 /**
188 * Show (activate) android action mode by starting it. 205 * Show (activate) android action mode by starting it.
189 * 206 *
190 * <p>Action mode in floating mode is tried first, and then falls back to 207 * <p>Action mode in floating mode is tried first, and then falls back to
191 * a normal one. 208 * a normal one.
192 * @return {@code true} if the action mode started successfully or is alread y on. 209 * <p> If the action mode cannot be created the selection is cleared.
193 */ 210 */
194 public boolean showActionMode() { 211 public void showActionModeOrClearOnFailure() {
195 if (isEmpty()) return false; 212 if (isEmpty()) return;
amaralp 2017/03/25 00:52:27 Wouldn't you also want to clear the selection here
Tima Vaisburd 2017/03/25 02:57:16 I assumed no real action happens before setCallbac
boliu 2017/03/25 21:19:10 It is true. Should rename isEmpty to something lik
Tima Vaisburd 2017/03/27 06:33:04 Does isReady() sound good?
boliu 2017/03/27 17:25:07 Not really. isReady implies it might not be ready.
Tima Vaisburd 2017/03/27 18:19:45 I renamed to isActionModeSupported(), if you like
196 213
197 destroyActionModeAndKeepSelection(); 214 destroyActionModeAndKeepSelection();
198 215
199 if (mView.getParent() != null) { 216 if (mView.getParent() != null) {
200 // On ICS, startActionMode throws an NPE when getParent() is null. 217 // On ICS, startActionMode throws an NPE when getParent() is null.
201 assert mWebContents != null; 218 assert mWebContents != null;
202 ActionMode actionMode = supportsFloatingActionMode() 219 ActionMode actionMode = supportsFloatingActionMode()
203 ? startFloatingActionMode() 220 ? startFloatingActionMode()
204 : mView.startActionMode(mCallback); 221 : mView.startActionMode(mCallback);
205 if (actionMode != null) { 222 if (actionMode != null) {
206 // This is to work around an LGE email issue. See crbug.com/6517 06 for more details. 223 // This is to work around an LGE email issue. See crbug.com/6517 06 for more details.
207 LGEmailActionModeWorkaround.runIfNecessary(mContext, actionMode) ; 224 LGEmailActionModeWorkaround.runIfNecessary(mContext, actionMode) ;
208 } 225 }
209 mActionMode = actionMode; 226 mActionMode = actionMode;
210 } 227 }
211 mUnselectAllOnDismiss = true; 228 mUnselectAllOnDismiss = true;
212 return isActionModeValid(); 229 if (!isActionModeValid()) clearSelection();
213 } 230 }
214 231
215 @TargetApi(Build.VERSION_CODES.M) 232 @TargetApi(Build.VERSION_CODES.M)
216 private ActionMode startFloatingActionMode() { 233 private ActionMode startFloatingActionMode() {
217 ActionMode actionMode = mView.startActionMode( 234 ActionMode actionMode = mView.startActionMode(
218 new FloatingActionModeCallback(this, mCallback), ActionMode.TYPE _FLOATING); 235 new FloatingActionModeCallback(this, mCallback), ActionMode.TYPE _FLOATING);
219 return actionMode; 236 return actionMode;
220 } 237 }
221 238
222 void createAndShowPastePopup(int x, int y) { 239 void createAndShowPastePopup(int x, int y) {
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
277 return mPastePopupMenu != null; 294 return mPastePopupMenu != null;
278 } 295 }
279 296
280 // Composition methods for android.view.ActionMode 297 // Composition methods for android.view.ActionMode
281 298
282 /** 299 /**
283 * @see ActionMode#finish() 300 * @see ActionMode#finish()
284 */ 301 */
285 @Override 302 @Override
286 public void finishActionMode() { 303 public void finishActionMode() {
304 mPendingClassificationRequest = false;
305 mHidden = false;
amaralp 2017/03/25 00:52:27 My CL crrev.com/2777663002 should make this and th
Tima Vaisburd 2017/03/27 06:33:04 I kept all the checks since I thought invalidate()
306 mView.removeCallbacks(mRepeatingHideRunnable);
307
287 if (isActionModeValid()) { 308 if (isActionModeValid()) {
288 mActionMode.finish(); 309 mActionMode.finish();
289 310
290 // Should be nulled out in case #onDestroyActionMode() is not invoke d in response. 311 // Should be nulled out in case #onDestroyActionMode() is not invoke d in response.
291 mActionMode = null; 312 mActionMode = null;
292 } 313 }
293 } 314 }
294 315
295 /** 316 /**
296 * @see ActionMode#invalidateContentRect() 317 * @see ActionMode#invalidateContentRect()
297 */ 318 */
298 public void invalidateContentRect() { 319 private void invalidateContentRect() {
299 if (supportsFloatingActionMode()) { 320 if (supportsFloatingActionMode()) {
300 if (mHidden) { 321 if (mHidden) {
301 mPendingInvalidateContentRect = true; 322 mPendingInvalidateContentRect = true;
302 } else { 323 } else {
303 mPendingInvalidateContentRect = false; 324 mPendingInvalidateContentRect = false;
304 if (isActionModeValid()) mActionMode.invalidateContentRect(); 325 if (isActionModeValid()) mActionMode.invalidateContentRect();
305 } 326 }
306 } 327 }
307 } 328 }
308 329
(...skipping 13 matching lines...) Expand all
322 */ 343 */
323 void hideActionMode(boolean hide) { 344 void hideActionMode(boolean hide) {
324 if (!canHideActionMode()) return; 345 if (!canHideActionMode()) return;
325 if (mHidden == hide) return; 346 if (mHidden == hide) return;
326 mHidden = hide; 347 mHidden = hide;
327 if (mHidden) { 348 if (mHidden) {
328 mRepeatingHideRunnable.run(); 349 mRepeatingHideRunnable.run();
329 } else { 350 } else {
330 mHidden = false; 351 mHidden = false;
331 mView.removeCallbacks(mRepeatingHideRunnable); 352 mView.removeCallbacks(mRepeatingHideRunnable);
353 // To show the action mode that is being hidden call hide() again wi th a short delay.
332 hideActionModeTemporarily(SHOW_DELAY_MS); 354 hideActionModeTemporarily(SHOW_DELAY_MS);
333 if (mPendingInvalidateContentRect) { 355 if (mPendingInvalidateContentRect) {
334 mPendingInvalidateContentRect = false; 356 mPendingInvalidateContentRect = false;
335 invalidateContentRect(); 357 invalidateContentRect();
336 } 358 }
337 } 359 }
338 } 360 }
339 361
340 /** 362 /**
341 * @see ActionMode#hide(long) 363 * @see ActionMode#hide(long)
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
391 // caused a resource loading failure to be logged. WebView 413 // caused a resource loading failure to be logged. WebView
392 // resource access needs to be improved so that this 414 // resource access needs to be improved so that this
393 // logspam can be avoided. 415 // logspam can be avoided.
394 new MenuInflater(context).inflate(R.menu.select_action_menu, menu); 416 new MenuInflater(context).inflate(R.menu.select_action_menu, menu);
395 } 417 }
396 } 418 }
397 419
398 private void createActionMenu(ActionMode mode, Menu menu) { 420 private void createActionMenu(ActionMode mode, Menu menu) {
399 mNeedsPrepare = false; 421 mNeedsPrepare = false;
400 initializeMenu(mContext, mode, menu); 422 initializeMenu(mContext, mode, menu);
423 updateAssistMenuItem(menu);
401 424
402 if (!isSelectionEditable() || !canPaste()) { 425 if (!isSelectionEditable() || !canPaste()) {
403 menu.removeItem(R.id.select_action_menu_paste); 426 menu.removeItem(R.id.select_action_menu_paste);
404 } 427 }
405 428
406 if (isInsertion()) { 429 if (isInsertion()) {
407 menu.removeItem(R.id.select_action_menu_select_all); 430 menu.removeItem(R.id.select_action_menu_select_all);
408 menu.removeItem(R.id.select_action_menu_cut); 431 menu.removeItem(R.id.select_action_menu_cut);
409 menu.removeItem(R.id.select_action_menu_copy); 432 menu.removeItem(R.id.select_action_menu_copy);
410 menu.removeItem(R.id.select_action_menu_share); 433 menu.removeItem(R.id.select_action_menu_share);
(...skipping 22 matching lines...) Expand all
433 456
434 initializeTextProcessingMenu(menu); 457 initializeTextProcessingMenu(menu);
435 } 458 }
436 459
437 private boolean canPaste() { 460 private boolean canPaste() {
438 ClipboardManager clipMgr = (ClipboardManager) 461 ClipboardManager clipMgr = (ClipboardManager)
439 mContext.getSystemService(Context.CLIPBOARD_SERVICE); 462 mContext.getSystemService(Context.CLIPBOARD_SERVICE);
440 return clipMgr.hasPrimaryClip(); 463 return clipMgr.hasPrimaryClip();
441 } 464 }
442 465
466 private void updateAssistMenuItem(Menu menu) {
467 // The assist menu item ID has to be equal to android.R.id.textAssist. U ntil we compile
468 // with Android O SDK where this ID is defined we replace the correspond ing inflated
469 // item with an item with the proper ID.
470 // TODO(timav): Use android.R.id.textAssist for the Assist item id once we switch to
471 // Android O SDK and remove |mAssistMenuItemId|.
472 menu.removeItem(R.id.select_action_menu_assist);
473 if (mAssistMenuItemId == 0) return;
474
475 if (mClassificationResult != null && mClassificationResult.hasNamedActio n()) {
476 menu.add(mAssistMenuItemId, mAssistMenuItemId, 1, mClassificationRes ult.label)
477 .setIcon(mClassificationResult.icon);
478 }
479 }
480
443 /** 481 /**
444 * Intialize the menu items for processing text, if there is any. 482 * Intialize the menu items for processing text, if there is any.
445 */ 483 */
446 private void initializeTextProcessingMenu(Menu menu) { 484 private void initializeTextProcessingMenu(Menu menu) {
447 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M 485 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M
448 || !isSelectActionModeAllowed(MENU_ITEM_PROCESS_TEXT)) { 486 || !isSelectActionModeAllowed(MENU_ITEM_PROCESS_TEXT)) {
449 return; 487 return;
450 } 488 }
451 489
452 PackageManager packageManager = mContext.getPackageManager(); 490 PackageManager packageManager = mContext.getPackageManager();
453 List<ResolveInfo> supportedActivities = 491 List<ResolveInfo> supportedActivities =
454 packageManager.queryIntentActivities(createProcessTextIntent(), 0); 492 packageManager.queryIntentActivities(createProcessTextIntent(), 0);
493 final int order = menu.size();
455 for (int i = 0; i < supportedActivities.size(); i++) { 494 for (int i = 0; i < supportedActivities.size(); i++) {
456 ResolveInfo resolveInfo = supportedActivities.get(i); 495 ResolveInfo resolveInfo = supportedActivities.get(i);
457 CharSequence label = resolveInfo.loadLabel(mContext.getPackageManage r()); 496 CharSequence label = resolveInfo.loadLabel(mContext.getPackageManage r());
458 menu.add(R.id.select_action_menu_text_processing_menus, Menu.NONE, i , label) 497 menu.add(R.id.select_action_menu_text_processing_menus, Menu.NONE, o rder, label)
459 .setIntent(createProcessTextIntentForResolveInfo(resolveInfo )) 498 .setIntent(createProcessTextIntentForResolveInfo(resolveInfo ))
460 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 499 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
461 } 500 }
462 } 501 }
463 502
464 @TargetApi(Build.VERSION_CODES.M) 503 @TargetApi(Build.VERSION_CODES.M)
465 private static Intent createProcessTextIntent() { 504 private static Intent createProcessTextIntent() {
466 return new Intent().setAction(Intent.ACTION_PROCESS_TEXT).setType("text/ plain"); 505 return new Intent().setAction(Intent.ACTION_PROCESS_TEXT).setType("text/ plain");
467 } 506 }
468 507
469 @TargetApi(Build.VERSION_CODES.M) 508 @TargetApi(Build.VERSION_CODES.M)
470 private Intent createProcessTextIntentForResolveInfo(ResolveInfo info) { 509 private Intent createProcessTextIntentForResolveInfo(ResolveInfo info) {
471 boolean isReadOnly = !isSelectionEditable(); 510 boolean isReadOnly = !isSelectionEditable();
472 return createProcessTextIntent() 511 return createProcessTextIntent()
473 .putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, isReadOnly) 512 .putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, isReadOnly)
474 .setClassName(info.activityInfo.packageName, info.activityInfo.n ame); 513 .setClassName(info.activityInfo.packageName, info.activityInfo.n ame);
475 } 514 }
476 515
477 @Override 516 @Override
478 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 517 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
479 if (!isActionModeValid()) return true; 518 if (!isActionModeValid()) return true;
480 519
481 int id = item.getItemId(); 520 int id = item.getItemId();
482 int groupId = item.getGroupId(); 521 int groupId = item.getGroupId();
483 522
484 if (id == R.id.select_action_menu_select_all) { 523 if (id == mAssistMenuItemId) {
524 doAssistAction();
525 mode.finish();
526 } else if (id == R.id.select_action_menu_select_all) {
485 selectAll(); 527 selectAll();
486 } else if (id == R.id.select_action_menu_cut) { 528 } else if (id == R.id.select_action_menu_cut) {
487 cut(); 529 cut();
488 mode.finish(); 530 mode.finish();
489 } else if (id == R.id.select_action_menu_copy) { 531 } else if (id == R.id.select_action_menu_copy) {
490 copy(); 532 copy();
491 mode.finish(); 533 mode.finish();
492 } else if (id == R.id.select_action_menu_paste) { 534 } else if (id == R.id.select_action_menu_paste) {
493 paste(); 535 paste();
494 mode.finish(); 536 mode.finish();
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
533 (int) (mSelectionRect.top * deviceScale), 575 (int) (mSelectionRect.top * deviceScale),
534 (int) (mSelectionRect.right * deviceScale), 576 (int) (mSelectionRect.right * deviceScale),
535 (int) (mSelectionRect.bottom * deviceScale)); 577 (int) (mSelectionRect.bottom * deviceScale));
536 578
537 // The selection coordinates are relative to the content viewport, but w e need 579 // The selection coordinates are relative to the content viewport, but w e need
538 // coordinates relative to the containing View. 580 // coordinates relative to the containing View.
539 outRect.offset(0, (int) mRenderCoordinates.getContentOffsetYPix()); 581 outRect.offset(0, (int) mRenderCoordinates.getContentOffsetYPix());
540 } 582 }
541 583
542 /** 584 /**
585 * Perform an action that depends on the semantics of the selected text.
586 */
587 @VisibleForTesting
588 void doAssistAction() {
589 if (mClassificationResult == null || !mClassificationResult.hasNamedActi on()) return;
590
591 assert mClassificationResult.onClickListener != null
592 || mClassificationResult.intent != null;
593
594 if (mClassificationResult.onClickListener != null) {
595 mClassificationResult.onClickListener.onClick(mView);
596 return;
597 }
598
599 if (mClassificationResult.intent != null) {
600 Context context = mWindowAndroid.getContext().get();
601 if (context == null) return;
602
603 context.startActivity(mClassificationResult.intent);
604 return;
605 }
606 }
607
608 /**
543 * Perform a select all action. 609 * Perform a select all action.
544 */ 610 */
545 @VisibleForTesting 611 @VisibleForTesting
546 void selectAll() { 612 void selectAll() {
547 mWebContents.selectAll(); 613 mWebContents.selectAll();
548 // Even though the above statement logged a SelectAll user action, we wa nt to 614 // Even though the above statement logged a SelectAll user action, we wa nt to
549 // track whether the focus was in an editable field, so log that too. 615 // track whether the focus was in an editable field, so log that too.
550 if (isSelectionEditable()) { 616 if (isSelectionEditable()) {
551 RecordUserAction.record("MobileActionMode.SelectAllWasEditable"); 617 RecordUserAction.record("MobileActionMode.SelectAllWasEditable");
552 } else { 618 } else {
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after
710 CharSequence result = data.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEX T); 776 CharSequence result = data.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEX T);
711 if (result != null) { 777 if (result != null) {
712 // TODO(hush): Use a variant of replace that re-selects the replaced text. 778 // TODO(hush): Use a variant of replace that re-selects the replaced text.
713 // crbug.com/546710 779 // crbug.com/546710
714 mWebContents.replace(result.toString()); 780 mWebContents.replace(result.toString());
715 } 781 }
716 } 782 }
717 783
718 void restoreSelectionPopupsIfNecessary() { 784 void restoreSelectionPopupsIfNecessary() {
719 if (mHasSelection && !isActionModeValid()) { 785 if (mHasSelection && !isActionModeValid()) {
720 if (!showActionMode()) clearSelection(); 786 showActionModeOrClearOnFailure();
721 } 787 }
722 } 788 }
723 789
724 // All coordinates are in DIP. 790 // All coordinates are in DIP.
725 void onSelectionEvent(int eventType, int xAnchor, int yAnchor, 791 void onSelectionEvent(int eventType, int xAnchor, int yAnchor,
726 int left, int top, int right, int bottom, boolean isScrollInProgress , 792 int left, int top, int right, int bottom, boolean isScrollInProgress ,
727 boolean touchScrollInProgress) { 793 boolean touchScrollInProgress) {
728 // Ensure the provided selection coordinates form a non-empty rect, as r equired by 794 // Ensure the provided selection coordinates form a non-empty rect, as r equired by
729 // the selection action mode. 795 // the selection action mode.
730 if (left == right) ++right; 796 if (left == right) ++right;
731 if (top == bottom) ++bottom; 797 if (top == bottom) ++bottom;
732 switch (eventType) { 798 switch (eventType) {
733 case SelectionEventType.SELECTION_HANDLES_SHOWN: 799 case SelectionEventType.SELECTION_HANDLES_SHOWN:
734 mSelectionRect.set(left, top, right, bottom); 800 mSelectionRect.set(left, top, right, bottom);
735 mHasSelection = true; 801 mHasSelection = true;
736 mUnselectAllOnDismiss = true; 802 mUnselectAllOnDismiss = true;
737 if (!showActionMode()) clearSelection(); 803 if (mSelectionClient != null && mSelectionClient.sendsSelectionP opupUpdates()) {
804 // Rely on |mSelectionClient| sending a classification reque st and the request
805 // always calling onClassified() callback.
806 mPendingClassificationRequest = true;
807 } else {
808 showActionModeOrClearOnFailure();
809 }
738 break; 810 break;
739 811
740 case SelectionEventType.SELECTION_HANDLES_MOVED: 812 case SelectionEventType.SELECTION_HANDLES_MOVED:
741 mSelectionRect.set(left, top, right, bottom); 813 mSelectionRect.set(left, top, right, bottom);
742 invalidateContentRect(); 814 invalidateContentRect();
743 break; 815 break;
744 816
745 case SelectionEventType.SELECTION_HANDLES_CLEARED: 817 case SelectionEventType.SELECTION_HANDLES_CLEARED:
746 mHasSelection = false; 818 mHasSelection = false;
747 mUnselectAllOnDismiss = false; 819 mUnselectAllOnDismiss = false;
748 mSelectionRect.setEmpty(); 820 mSelectionRect.setEmpty();
749 finishActionMode(); 821 finishActionMode();
750 break; 822 break;
751 823
752 case SelectionEventType.SELECTION_HANDLE_DRAG_STARTED: 824 case SelectionEventType.SELECTION_HANDLE_DRAG_STARTED:
753 hideActionMode(true); 825 hideActionMode(true);
754 break; 826 break;
755 827
756 case SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED: 828 case SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED:
757 hideActionMode(false); 829 if (mSelectionClient != null && mSelectionClient.sendsSelectionP opupUpdates()) {
830 // Rely on |mSelectionClient| sending a classification reque st and the request
831 // always calling onClassified() callback.
832 mPendingClassificationRequest = true;
833 } else {
834 hideActionMode(false);
835 }
758 break; 836 break;
759 837
760 case SelectionEventType.INSERTION_HANDLE_SHOWN: 838 case SelectionEventType.INSERTION_HANDLE_SHOWN:
761 mSelectionRect.set(left, top, right, bottom); 839 mSelectionRect.set(left, top, right, bottom);
762 setIsInsertion(true); 840 setIsInsertion(true);
763 break; 841 break;
764 842
765 case SelectionEventType.INSERTION_HANDLE_MOVED: 843 case SelectionEventType.INSERTION_HANDLE_MOVED:
766 mSelectionRect.set(left, top, right, bottom); 844 mSelectionRect.set(left, top, right, bottom);
767 if (!isScrollInProgress && isPastePopupShowing()) { 845 if (!isScrollInProgress && isPastePopupShowing()) {
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
810 } 888 }
811 } 889 }
812 890
813 /** 891 /**
814 * Clears the current text selection. Note that we will try to move cursor t o selection 892 * Clears the current text selection. Note that we will try to move cursor t o selection
815 * end if applicable. 893 * end if applicable.
816 */ 894 */
817 void clearSelection() { 895 void clearSelection() {
818 if (mWebContents == null || isEmpty()) return; 896 if (mWebContents == null || isEmpty()) return;
819 mWebContents.collapseSelection(); 897 mWebContents.collapseSelection();
898 mClassificationResult = null;
820 } 899 }
821 900
822 void onSelectionChanged(String text) { 901 void onSelectionChanged(String text) {
823 mLastSelectedText = text; 902 mLastSelectedText = text;
824 if (mSelectionClient != null) { 903 if (mSelectionClient != null) {
825 mSelectionClient.onSelectionChanged(text); 904 mSelectionClient.onSelectionChanged(text);
826 } 905 }
827 } 906 }
828 907
829 // The client that implements selection augmenting functionality, or null if none exists. 908 // The client that implements selection augmenting functionality, or null if none exists.
830 void setSelectionClient(SelectionClient selectionClient) { 909 void setSelectionClient(SelectionClient selectionClient) {
831 mSelectionClient = selectionClient; 910 mSelectionClient = selectionClient;
911
912 mClassificationResult = null;
913
914 assert !mPendingClassificationRequest;
915 assert !mHidden;
832 } 916 }
833 917
834 void onShowUnhandledTapUIIfNeeded(int x, int y) { 918 void onShowUnhandledTapUIIfNeeded(int x, int y) {
835 if (mSelectionClient != null) { 919 if (mSelectionClient != null) {
836 mSelectionClient.showUnhandledTapUIIfNeeded(x, y); 920 mSelectionClient.showUnhandledTapUIIfNeeded(x, y);
837 } 921 }
838 } 922 }
839 923
840 void destroyActionModeAndUnselect() { 924 void destroyActionModeAndUnselect() {
841 mUnselectAllOnDismiss = true; 925 mUnselectAllOnDismiss = true;
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
876 if (isActionModeValid() && mIsInsertion != insertion) mNeedsPrepare = tr ue; 960 if (isActionModeValid() && mIsInsertion != insertion) mNeedsPrepare = tr ue;
877 mIsInsertion = insertion; 961 mIsInsertion = insertion;
878 } 962 }
879 963
880 private boolean isShareAvailable() { 964 private boolean isShareAvailable() {
881 Intent intent = new Intent(Intent.ACTION_SEND); 965 Intent intent = new Intent(Intent.ACTION_SEND);
882 intent.setType("text/plain"); 966 intent.setType("text/plain");
883 return mContext.getPackageManager().queryIntentActivities(intent, 967 return mContext.getPackageManager().queryIntentActivities(intent,
884 PackageManager.MATCH_DEFAULT_ONLY).size() > 0; 968 PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
885 } 969 }
970
971 // The callback class that delivers result from a ContextSelectionClient.
972 private class ContextSelectionCallback implements ContextSelectionProvider.R esultCallback {
973 @Override
974 public void onClassified(ContextSelectionProvider.Result result) {
975 boolean pendingClassificationRequest = mPendingClassificationRequest ;
976 mPendingClassificationRequest = false;
977
978 // If the selection does not exist any more, discard |result|.
979 if (!mHasSelection) {
980 assert !mHidden;
981 assert mClassificationResult == null;
982 return;
983 }
984
985 mClassificationResult = result;
986
987 // The request might be cancelled by finishActionMode(). Keep the re sult and quit.
amaralp 2017/03/25 00:52:27 Why do you want to keep the result if the request
Tima Vaisburd 2017/03/25 02:57:16 This onClassified() may come after finish and recr
Tima Vaisburd 2017/03/27 06:33:04 Updated.
988 if (!pendingClassificationRequest) {
989 assert !mHidden;
990 return;
991 }
992
993 // Update the selection range if needed.
994 if (!(result.startAdjust == 0 && result.endAdjust == 0)) {
995 // This call causes SELECTION_HANDLES_MOVED event
996 mWebContents.adjustSelectionByCharacterOffset(result.startAdjust , result.endAdjust);
997 }
998
999 // Rely on this method to clear mHidden and unhide the action mode.
1000 showActionModeOrClearOnFailure();
1001 }
1002 };
886 } 1003 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698