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

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

Issue 2740103006: Implement SmartText selection. (Closed)
Patch Set: Rebased again Created 3 years, 8 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 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
107 private boolean mHasSelection; 107 private boolean mHasSelection;
108 108
109 // Lazily created paste popup menu, triggered either via long press in an 109 // Lazily created paste popup menu, triggered either via long press in an
110 // editable region or from tapping the insertion handle. 110 // editable region or from tapping the insertion handle.
111 private PastePopupMenu mPastePopupMenu; 111 private PastePopupMenu mPastePopupMenu;
112 private boolean mWasPastePopupShowingOnInsertionDragStart; 112 private boolean mWasPastePopupShowingOnInsertionDragStart;
113 113
114 // The client that processes textual selection, or null if none exists. 114 // The client that processes textual selection, or null if none exists.
115 private SelectionClient mSelectionClient; 115 private SelectionClient mSelectionClient;
116 116
117 // The classificaton result of the selected text if the selection exists and
118 // ContextSelectionProvider was able to classify it, otherwise null.
119 private ContextSelectionProvider.Result mClassificationResult;
120
121 // The resource ID for Assist menu item.
122 private int mAssistMenuItemId;
123
124 // This variable is set to true when the classification request is in progre ss.
125 private boolean mPendingClassificationRequest;
126
117 /** 127 /**
118 * Create {@link SelectionPopupController} instance. 128 * Create {@link SelectionPopupController} instance.
119 * @param context Context for action mode. 129 * @param context Context for action mode.
120 * @param window WindowAndroid instance. 130 * @param window WindowAndroid instance.
121 * @param webContents WebContents instance. 131 * @param webContents WebContents instance.
122 * @param view Container view. 132 * @param view Container view.
123 * @param renderCoordinates Coordinates info used to position elements. 133 * @param renderCoordinates Coordinates info used to position elements.
124 * @param imeAdapter ImeAdapter instance to handle cursor position. 134 * @param imeAdapter ImeAdapter instance to handle cursor position.
125 */ 135 */
126 public SelectionPopupController(Context context, WindowAndroid window, WebCo ntents webContents, 136 public SelectionPopupController(Context context, WindowAndroid window, WebCo ntents webContents,
(...skipping 10 matching lines...) Expand all
137 mRepeatingHideRunnable = new Runnable() { 147 mRepeatingHideRunnable = new Runnable() {
138 @Override 148 @Override
139 public void run() { 149 public void run() {
140 assert mHidden; 150 assert mHidden;
141 final long hideDuration = getDefaultHideDuration(); 151 final long hideDuration = getDefaultHideDuration();
142 // Ensure the next hide call occurs before the ActionMode reappe ars. 152 // Ensure the next hide call occurs before the ActionMode reappe ars.
143 mView.postDelayed(mRepeatingHideRunnable, hideDuration - 1); 153 mView.postDelayed(mRepeatingHideRunnable, hideDuration - 1);
144 hideActionModeTemporarily(hideDuration); 154 hideActionModeTemporarily(hideDuration);
145 } 155 }
146 }; 156 };
157
158 mSelectionClient =
159 ContextSelectionClient.create(new ContextSelectionCallback(), wi ndow, webContents);
160
161 // TODO(timav): Use android.R.id.textAssist for the Assist item id once we switch to
162 // Android O SDK and remove |mAssistMenuItemId|.
163 mAssistMenuItemId = mContext.getResources().getIdentifier("textAssist", "id", "android");
147 } 164 }
148 165
149 /** 166 /**
150 * Update the container view. 167 * Update the container view.
151 */ 168 */
152 void setContainerView(View view) { 169 void setContainerView(View view) {
153 assert view != null; 170 assert view != null;
154 171
155 // Cleans up action mode before switching to a new container view. 172 // Cleans up action mode before switching to a new container view.
156 if (isActionModeValid()) finishActionMode(); 173 if (isActionModeValid()) finishActionMode();
157 mUnselectAllOnDismiss = true; 174 mUnselectAllOnDismiss = true;
158 destroyPastePopup(); 175 destroyPastePopup();
159 176
160 mView = view; 177 mView = view;
161 } 178 }
162 179
163 /** 180 /**
164 * Set the action mode callback. 181 * Set the action mode callback.
165 * @param callback ActionMode.Callback handling the callbacks from action mo de. 182 * @param callback ActionMode.Callback handling the callbacks from action mo de.
166 */ 183 */
167 void setCallback(ActionMode.Callback callback) { 184 void setCallback(ActionMode.Callback callback) {
168 mCallback = callback; 185 mCallback = callback;
169 } 186 }
170 187
171 @Override 188 @Override
172 public boolean isActionModeValid() { 189 public boolean isActionModeValid() {
173 return mActionMode != null; 190 return mActionMode != null;
174 } 191 }
175 192
176 // True if action mode is not yet initialized or set to no-op mode. 193 // True if action mode is initialized to a working (not a no-op) mode.
177 private boolean isEmpty() { 194 private boolean isActionModeSupported() {
178 return mCallback == EMPTY_CALLBACK; 195 return mCallback != EMPTY_CALLBACK;
179 } 196 }
180 197
181 @Override 198 @Override
182 public void setAllowedMenuItems(int allowedMenuItems) { 199 public void setAllowedMenuItems(int allowedMenuItems) {
183 mAllowedMenuItems = allowedMenuItems; 200 mAllowedMenuItems = allowedMenuItems;
184 } 201 }
185 202
186 /** 203 /**
187 * Show (activate) android action mode by starting it. 204 * Show (activate) android action mode by starting it.
188 * 205 *
189 * <p>Action mode in floating mode is tried first, and then falls back to 206 * <p>Action mode in floating mode is tried first, and then falls back to
190 * a normal one. 207 * a normal one.
191 * @return {@code true} if the action mode started successfully or is alread y on. 208 * <p> If the action mode cannot be created the selection is cleared.
192 */ 209 */
193 public boolean showActionMode() { 210 public void showActionModeOrClearOnFailure() {
194 if (isEmpty()) return false; 211 if (!isActionModeSupported()) return;
195 212
196 // Just refreshes the view if it is already showing. 213 // Just refresh the view if action mode already exists.
197 if (isActionModeValid()) { 214 if (isActionModeValid()) {
198 invalidateActionMode(); 215 invalidateActionMode();
199 return true; 216 return;
200 } 217 }
201 218
202 if (mView.getParent() != null) { 219 if (mView.getParent() != null) {
203 // On ICS, startActionMode throws an NPE when getParent() is null. 220 // On ICS, startActionMode throws an NPE when getParent() is null.
204 assert mWebContents != null; 221 assert mWebContents != null;
205 ActionMode actionMode = supportsFloatingActionMode() 222 ActionMode actionMode = supportsFloatingActionMode()
206 ? startFloatingActionMode() 223 ? startFloatingActionMode()
207 : mView.startActionMode(mCallback); 224 : mView.startActionMode(mCallback);
208 if (actionMode != null) { 225 if (actionMode != null) {
209 // This is to work around an LGE email issue. See crbug.com/6517 06 for more details. 226 // This is to work around an LGE email issue. See crbug.com/6517 06 for more details.
210 LGEmailActionModeWorkaround.runIfNecessary(mContext, actionMode) ; 227 LGEmailActionModeWorkaround.runIfNecessary(mContext, actionMode) ;
211 } 228 }
212 mActionMode = actionMode; 229 mActionMode = actionMode;
213 } 230 }
214 mUnselectAllOnDismiss = true; 231 mUnselectAllOnDismiss = true;
215 return isActionModeValid(); 232 if (!isActionModeValid()) clearSelection();
216 } 233 }
217 234
218 @TargetApi(Build.VERSION_CODES.M) 235 @TargetApi(Build.VERSION_CODES.M)
219 private ActionMode startFloatingActionMode() { 236 private ActionMode startFloatingActionMode() {
220 ActionMode actionMode = mView.startActionMode( 237 ActionMode actionMode = mView.startActionMode(
221 new FloatingActionModeCallback(this, mCallback), ActionMode.TYPE _FLOATING); 238 new FloatingActionModeCallback(this, mCallback), ActionMode.TYPE _FLOATING);
222 return actionMode; 239 return actionMode;
223 } 240 }
224 241
225 void createAndShowPastePopup(int x, int y) { 242 void createAndShowPastePopup(int x, int y) {
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
280 return mPastePopupMenu != null; 297 return mPastePopupMenu != null;
281 } 298 }
282 299
283 // Composition methods for android.view.ActionMode 300 // Composition methods for android.view.ActionMode
284 301
285 /** 302 /**
286 * @see ActionMode#finish() 303 * @see ActionMode#finish()
287 */ 304 */
288 @Override 305 @Override
289 public void finishActionMode() { 306 public void finishActionMode() {
307 mPendingClassificationRequest = false;
308 mHidden = false;
309 if (mView != null) mView.removeCallbacks(mRepeatingHideRunnable);
310
290 if (isActionModeValid()) { 311 if (isActionModeValid()) {
291 mActionMode.finish(); 312 mActionMode.finish();
292 313
293 // Should be nulled out in case #onDestroyActionMode() is not invoke d in response. 314 // Should be nulled out in case #onDestroyActionMode() is not invoke d in response.
294 mActionMode = null; 315 mActionMode = null;
295 } 316 }
296 } 317 }
297 318
298 /** 319 /**
299 * @see ActionMode#invalidate() 320 * @see ActionMode#invalidate()
300 * Note that invalidation will also reset visibility state. The caller 321 * Note that invalidation will also reset visibility state. The caller
301 * should account for this when making subsequent visibility updates. 322 * should account for this when making subsequent visibility updates.
302 */ 323 */
303 private void invalidateActionMode() { 324 private void invalidateActionMode() {
304 if (!isActionModeValid()) return; 325 if (!isActionModeValid()) return;
305 if (mHidden) { 326 if (mHidden) {
306 assert canHideActionMode(); 327 assert canHideActionMode();
307 mHidden = false; 328 mHidden = false;
308 mView.removeCallbacks(mRepeatingHideRunnable); 329 unhideActionMode();
309 } 330 }
310 331
311 // Try/catch necessary for framework bug, crbug.com/446717. 332 // Try/catch necessary for framework bug, crbug.com/446717.
312 try { 333 try {
313 mActionMode.invalidate(); 334 mActionMode.invalidate();
314 } catch (NullPointerException e) { 335 } catch (NullPointerException e) {
315 Log.w(TAG, "Ignoring NPE from ActionMode.invalidate() as workaround for L", e); 336 Log.w(TAG, "Ignoring NPE from ActionMode.invalidate() as workaround for L", e);
316 } 337 }
317 } 338 }
318 339
(...skipping 21 matching lines...) Expand all
340 * @param hide whether to hide or show the ActionMode. 361 * @param hide whether to hide or show the ActionMode.
341 */ 362 */
342 void hideActionMode(boolean hide) { 363 void hideActionMode(boolean hide) {
343 if (!canHideActionMode()) return; 364 if (!canHideActionMode()) return;
344 if (mHidden == hide) return; 365 if (mHidden == hide) return;
345 mHidden = hide; 366 mHidden = hide;
346 if (mHidden) { 367 if (mHidden) {
347 mRepeatingHideRunnable.run(); 368 mRepeatingHideRunnable.run();
348 } else { 369 } else {
349 mHidden = false; 370 mHidden = false;
350 mView.removeCallbacks(mRepeatingHideRunnable); 371 unhideActionMode();
351 hideActionModeTemporarily(SHOW_DELAY_MS);
352 } 372 }
353 } 373 }
354 374
375 private void unhideActionMode() {
376 mView.removeCallbacks(mRepeatingHideRunnable);
377 // To show the action mode that is being hidden call hide() again with a short delay.
378 hideActionModeTemporarily(SHOW_DELAY_MS);
379 }
380
355 /** 381 /**
356 * @see ActionMode#hide(long) 382 * @see ActionMode#hide(long)
357 */ 383 */
358 private void hideActionModeTemporarily(long duration) { 384 private void hideActionModeTemporarily(long duration) {
359 assert canHideActionMode(); 385 assert canHideActionMode();
360 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 386 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
361 if (isActionModeValid()) mActionMode.hide(duration); 387 if (isActionModeValid()) mActionMode.hide(duration);
362 } 388 }
363 } 389 }
364 390
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
406 // caused a resource loading failure to be logged. WebView 432 // caused a resource loading failure to be logged. WebView
407 // resource access needs to be improved so that this 433 // resource access needs to be improved so that this
408 // logspam can be avoided. 434 // logspam can be avoided.
409 new MenuInflater(context).inflate(R.menu.select_action_menu, menu); 435 new MenuInflater(context).inflate(R.menu.select_action_menu, menu);
410 } 436 }
411 } 437 }
412 438
413 private void createActionMenu(ActionMode mode, Menu menu) { 439 private void createActionMenu(ActionMode mode, Menu menu) {
414 mNeedsPrepare = false; 440 mNeedsPrepare = false;
415 initializeMenu(mContext, mode, menu); 441 initializeMenu(mContext, mode, menu);
442 updateAssistMenuItem(menu);
416 443
417 if (!isSelectionEditable() || !canPaste()) { 444 if (!isSelectionEditable() || !canPaste()) {
418 menu.removeItem(R.id.select_action_menu_paste); 445 menu.removeItem(R.id.select_action_menu_paste);
419 } 446 }
420 447
421 if (isInsertion()) { 448 if (isInsertion()) {
422 menu.removeItem(R.id.select_action_menu_select_all); 449 menu.removeItem(R.id.select_action_menu_select_all);
423 menu.removeItem(R.id.select_action_menu_cut); 450 menu.removeItem(R.id.select_action_menu_cut);
424 menu.removeItem(R.id.select_action_menu_copy); 451 menu.removeItem(R.id.select_action_menu_copy);
425 menu.removeItem(R.id.select_action_menu_share); 452 menu.removeItem(R.id.select_action_menu_share);
(...skipping 22 matching lines...) Expand all
448 475
449 initializeTextProcessingMenu(menu); 476 initializeTextProcessingMenu(menu);
450 } 477 }
451 478
452 private boolean canPaste() { 479 private boolean canPaste() {
453 ClipboardManager clipMgr = (ClipboardManager) 480 ClipboardManager clipMgr = (ClipboardManager)
454 mContext.getSystemService(Context.CLIPBOARD_SERVICE); 481 mContext.getSystemService(Context.CLIPBOARD_SERVICE);
455 return clipMgr.hasPrimaryClip(); 482 return clipMgr.hasPrimaryClip();
456 } 483 }
457 484
485 private void updateAssistMenuItem(Menu menu) {
486 // The assist menu item ID has to be equal to android.R.id.textAssist. U ntil we compile
487 // with Android O SDK where this ID is defined we replace the correspond ing inflated
488 // item with an item with the proper ID.
489 // TODO(timav): Use android.R.id.textAssist for the Assist item id once we switch to
490 // Android O SDK and remove |mAssistMenuItemId|.
491 menu.removeItem(R.id.select_action_menu_assist);
492 if (mAssistMenuItemId == 0) return;
493
494 if (mClassificationResult != null && mClassificationResult.hasNamedActio n()) {
495 menu.add(mAssistMenuItemId, mAssistMenuItemId, 1, mClassificationRes ult.label)
496 .setIcon(mClassificationResult.icon);
497 }
498 }
499
458 /** 500 /**
459 * Intialize the menu items for processing text, if there is any. 501 * Intialize the menu items for processing text, if there is any.
460 */ 502 */
461 private void initializeTextProcessingMenu(Menu menu) { 503 private void initializeTextProcessingMenu(Menu menu) {
462 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M 504 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M
463 || !isSelectActionModeAllowed(MENU_ITEM_PROCESS_TEXT)) { 505 || !isSelectActionModeAllowed(MENU_ITEM_PROCESS_TEXT)) {
464 return; 506 return;
465 } 507 }
466 508
467 PackageManager packageManager = mContext.getPackageManager(); 509 PackageManager packageManager = mContext.getPackageManager();
468 List<ResolveInfo> supportedActivities = 510 List<ResolveInfo> supportedActivities =
469 packageManager.queryIntentActivities(createProcessTextIntent(), 0); 511 packageManager.queryIntentActivities(createProcessTextIntent(), 0);
512 // Force text processing menu at the end.
513 final int order = 100;
boliu 2017/03/28 16:49:13 that's random and hacky. can you do this in a more
Tima Vaisburd 2017/03/28 16:57:51 I followed Android, maybe not so good example... I
Tima Vaisburd 2017/03/28 23:16:01 Done, see my other comment.
470 for (int i = 0; i < supportedActivities.size(); i++) { 514 for (int i = 0; i < supportedActivities.size(); i++) {
471 ResolveInfo resolveInfo = supportedActivities.get(i); 515 ResolveInfo resolveInfo = supportedActivities.get(i);
472 CharSequence label = resolveInfo.loadLabel(mContext.getPackageManage r()); 516 CharSequence label = resolveInfo.loadLabel(mContext.getPackageManage r());
473 menu.add(R.id.select_action_menu_text_processing_menus, Menu.NONE, i , label) 517 menu.add(R.id.select_action_menu_text_processing_menus, Menu.NONE, o rder + i, label)
474 .setIntent(createProcessTextIntentForResolveInfo(resolveInfo )) 518 .setIntent(createProcessTextIntentForResolveInfo(resolveInfo ))
475 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 519 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
476 } 520 }
477 } 521 }
478 522
479 @TargetApi(Build.VERSION_CODES.M) 523 @TargetApi(Build.VERSION_CODES.M)
480 private static Intent createProcessTextIntent() { 524 private static Intent createProcessTextIntent() {
481 return new Intent().setAction(Intent.ACTION_PROCESS_TEXT).setType("text/ plain"); 525 return new Intent().setAction(Intent.ACTION_PROCESS_TEXT).setType("text/ plain");
482 } 526 }
483 527
484 @TargetApi(Build.VERSION_CODES.M) 528 @TargetApi(Build.VERSION_CODES.M)
485 private Intent createProcessTextIntentForResolveInfo(ResolveInfo info) { 529 private Intent createProcessTextIntentForResolveInfo(ResolveInfo info) {
486 boolean isReadOnly = !isSelectionEditable(); 530 boolean isReadOnly = !isSelectionEditable();
487 return createProcessTextIntent() 531 return createProcessTextIntent()
488 .putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, isReadOnly) 532 .putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, isReadOnly)
489 .setClassName(info.activityInfo.packageName, info.activityInfo.n ame); 533 .setClassName(info.activityInfo.packageName, info.activityInfo.n ame);
490 } 534 }
491 535
492 @Override 536 @Override
493 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 537 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
494 if (!isActionModeValid()) return true; 538 if (!isActionModeValid()) return true;
495 539
496 int id = item.getItemId(); 540 int id = item.getItemId();
497 int groupId = item.getGroupId(); 541 int groupId = item.getGroupId();
498 542
499 if (id == R.id.select_action_menu_select_all) { 543 if (id == mAssistMenuItemId) {
544 doAssistAction();
545 mode.finish();
546 } else if (id == R.id.select_action_menu_select_all) {
500 selectAll(); 547 selectAll();
501 } else if (id == R.id.select_action_menu_cut) { 548 } else if (id == R.id.select_action_menu_cut) {
502 cut(); 549 cut();
503 mode.finish(); 550 mode.finish();
504 } else if (id == R.id.select_action_menu_copy) { 551 } else if (id == R.id.select_action_menu_copy) {
505 copy(); 552 copy();
506 mode.finish(); 553 mode.finish();
507 } else if (id == R.id.select_action_menu_paste) { 554 } else if (id == R.id.select_action_menu_paste) {
508 paste(); 555 paste();
509 mode.finish(); 556 mode.finish();
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
548 (int) (mSelectionRect.top * deviceScale), 595 (int) (mSelectionRect.top * deviceScale),
549 (int) (mSelectionRect.right * deviceScale), 596 (int) (mSelectionRect.right * deviceScale),
550 (int) (mSelectionRect.bottom * deviceScale)); 597 (int) (mSelectionRect.bottom * deviceScale));
551 598
552 // The selection coordinates are relative to the content viewport, but w e need 599 // The selection coordinates are relative to the content viewport, but w e need
553 // coordinates relative to the containing View. 600 // coordinates relative to the containing View.
554 outRect.offset(0, (int) mRenderCoordinates.getContentOffsetYPix()); 601 outRect.offset(0, (int) mRenderCoordinates.getContentOffsetYPix());
555 } 602 }
556 603
557 /** 604 /**
605 * Perform an action that depends on the semantics of the selected text.
606 */
607 @VisibleForTesting
608 void doAssistAction() {
609 if (mClassificationResult == null || !mClassificationResult.hasNamedActi on()) return;
610
611 assert mClassificationResult.onClickListener != null
612 || mClassificationResult.intent != null;
613
614 if (mClassificationResult.onClickListener != null) {
615 mClassificationResult.onClickListener.onClick(mView);
616 return;
617 }
618
619 if (mClassificationResult.intent != null) {
620 Context context = mWindowAndroid.getContext().get();
621 if (context == null) return;
622
623 context.startActivity(mClassificationResult.intent);
624 return;
625 }
626 }
627
628 /**
558 * Perform a select all action. 629 * Perform a select all action.
559 */ 630 */
560 @VisibleForTesting 631 @VisibleForTesting
561 void selectAll() { 632 void selectAll() {
562 mWebContents.selectAll(); 633 mWebContents.selectAll();
634 mClassificationResult = null;
635 mNeedsPrepare = true;
636 invalidateActionMode();
563 // Even though the above statement logged a SelectAll user action, we wa nt to 637 // Even though the above statement logged a SelectAll user action, we wa nt to
564 // track whether the focus was in an editable field, so log that too. 638 // track whether the focus was in an editable field, so log that too.
565 if (isSelectionEditable()) { 639 if (isSelectionEditable()) {
566 RecordUserAction.record("MobileActionMode.SelectAllWasEditable"); 640 RecordUserAction.record("MobileActionMode.SelectAllWasEditable");
567 } else { 641 } else {
568 RecordUserAction.record("MobileActionMode.SelectAllWasNonEditable"); 642 RecordUserAction.record("MobileActionMode.SelectAllWasNonEditable");
569 } 643 }
570 } 644 }
571 645
572 /** 646 /**
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after
725 CharSequence result = data.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEX T); 799 CharSequence result = data.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEX T);
726 if (result != null) { 800 if (result != null) {
727 // TODO(hush): Use a variant of replace that re-selects the replaced text. 801 // TODO(hush): Use a variant of replace that re-selects the replaced text.
728 // crbug.com/546710 802 // crbug.com/546710
729 mWebContents.replace(result.toString()); 803 mWebContents.replace(result.toString());
730 } 804 }
731 } 805 }
732 806
733 void restoreSelectionPopupsIfNecessary() { 807 void restoreSelectionPopupsIfNecessary() {
734 if (mHasSelection && !isActionModeValid()) { 808 if (mHasSelection && !isActionModeValid()) {
735 if (!showActionMode()) clearSelection(); 809 showActionModeOrClearOnFailure();
736 } 810 }
737 } 811 }
738 812
739 // All coordinates are in DIP. 813 // All coordinates are in DIP.
740 void onSelectionEvent(int eventType, int xAnchor, int yAnchor, 814 void onSelectionEvent(int eventType, int xAnchor, int yAnchor,
741 int left, int top, int right, int bottom, boolean isScrollInProgress , 815 int left, int top, int right, int bottom, boolean isScrollInProgress ,
742 boolean touchScrollInProgress) { 816 boolean touchScrollInProgress) {
743 // Ensure the provided selection coordinates form a non-empty rect, as r equired by 817 // Ensure the provided selection coordinates form a non-empty rect, as r equired by
744 // the selection action mode. 818 // the selection action mode.
745 if (left == right) ++right; 819 if (left == right) ++right;
746 if (top == bottom) ++bottom; 820 if (top == bottom) ++bottom;
747 switch (eventType) { 821 switch (eventType) {
748 case SelectionEventType.SELECTION_HANDLES_SHOWN: 822 case SelectionEventType.SELECTION_HANDLES_SHOWN:
749 mSelectionRect.set(left, top, right, bottom); 823 mSelectionRect.set(left, top, right, bottom);
750 mHasSelection = true; 824 mHasSelection = true;
751 mUnselectAllOnDismiss = true; 825 mUnselectAllOnDismiss = true;
752 if (!showActionMode()) clearSelection(); 826 if (mSelectionClient != null && mSelectionClient.sendsSelectionP opupUpdates()) {
827 // Rely on |mSelectionClient| sending a classification reque st and the request
828 // always calling onClassified() callback.
829 mPendingClassificationRequest = true;
830 } else {
831 showActionModeOrClearOnFailure();
832 }
753 break; 833 break;
754 834
755 case SelectionEventType.SELECTION_HANDLES_MOVED: 835 case SelectionEventType.SELECTION_HANDLES_MOVED:
756 mSelectionRect.set(left, top, right, bottom); 836 mSelectionRect.set(left, top, right, bottom);
757 invalidateContentRect(); 837 invalidateContentRect();
758 break; 838 break;
759 839
760 case SelectionEventType.SELECTION_HANDLES_CLEARED: 840 case SelectionEventType.SELECTION_HANDLES_CLEARED:
761 mHasSelection = false; 841 mHasSelection = false;
762 mUnselectAllOnDismiss = false; 842 mUnselectAllOnDismiss = false;
763 mSelectionRect.setEmpty(); 843 mSelectionRect.setEmpty();
764 finishActionMode(); 844 finishActionMode();
765 break; 845 break;
766 846
767 case SelectionEventType.SELECTION_HANDLE_DRAG_STARTED: 847 case SelectionEventType.SELECTION_HANDLE_DRAG_STARTED:
768 hideActionMode(true); 848 hideActionMode(true);
769 break; 849 break;
770 850
771 case SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED: 851 case SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED:
772 hideActionMode(false); 852 if (mSelectionClient != null && mSelectionClient.sendsSelectionP opupUpdates()) {
853 // Rely on |mSelectionClient| sending a classification reque st and the request
854 // always calling onClassified() callback.
855 mPendingClassificationRequest = true;
856 } else {
857 hideActionMode(false);
858 }
773 break; 859 break;
774 860
775 case SelectionEventType.INSERTION_HANDLE_SHOWN: 861 case SelectionEventType.INSERTION_HANDLE_SHOWN:
776 mSelectionRect.set(left, top, right, bottom); 862 mSelectionRect.set(left, top, right, bottom);
777 setIsInsertion(true); 863 setIsInsertion(true);
778 break; 864 break;
779 865
780 case SelectionEventType.INSERTION_HANDLE_MOVED: 866 case SelectionEventType.INSERTION_HANDLE_MOVED:
781 mSelectionRect.set(left, top, right, bottom); 867 mSelectionRect.set(left, top, right, bottom);
782 if (!isScrollInProgress && isPastePopupShowing()) { 868 if (!isScrollInProgress && isPastePopupShowing()) {
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
823 int yAnchorPix = (int) (yAnchor * deviceScale); 909 int yAnchorPix = (int) (yAnchor * deviceScale);
824 mSelectionClient.onSelectionEvent(eventType, xAnchorPix, yAnchorPix) ; 910 mSelectionClient.onSelectionEvent(eventType, xAnchorPix, yAnchorPix) ;
825 } 911 }
826 } 912 }
827 913
828 /** 914 /**
829 * Clears the current text selection. Note that we will try to move cursor t o selection 915 * Clears the current text selection. Note that we will try to move cursor t o selection
830 * end if applicable. 916 * end if applicable.
831 */ 917 */
832 void clearSelection() { 918 void clearSelection() {
833 if (mWebContents == null || isEmpty()) return; 919 if (mWebContents == null || !isActionModeSupported()) return;
834 mWebContents.collapseSelection(); 920 mWebContents.collapseSelection();
921 mClassificationResult = null;
835 } 922 }
836 923
837 void onSelectionChanged(String text) { 924 void onSelectionChanged(String text) {
838 mLastSelectedText = text; 925 mLastSelectedText = text;
839 if (mSelectionClient != null) { 926 if (mSelectionClient != null) {
840 mSelectionClient.onSelectionChanged(text); 927 mSelectionClient.onSelectionChanged(text);
841 } 928 }
842 } 929 }
843 930
844 // The client that implements selection augmenting functionality, or null if none exists. 931 // The client that implements selection augmenting functionality, or null if none exists.
845 void setSelectionClient(SelectionClient selectionClient) { 932 void setSelectionClient(SelectionClient selectionClient) {
846 mSelectionClient = selectionClient; 933 mSelectionClient = selectionClient;
934
935 mClassificationResult = null;
936
937 assert !mPendingClassificationRequest;
938 assert !mHidden;
847 } 939 }
848 940
849 void onShowUnhandledTapUIIfNeeded(int x, int y) { 941 void onShowUnhandledTapUIIfNeeded(int x, int y) {
850 if (mSelectionClient != null) { 942 if (mSelectionClient != null) {
851 mSelectionClient.showUnhandledTapUIIfNeeded(x, y); 943 mSelectionClient.showUnhandledTapUIIfNeeded(x, y);
852 } 944 }
853 } 945 }
854 946
855 void destroyActionModeAndUnselect() { 947 void destroyActionModeAndUnselect() {
856 mUnselectAllOnDismiss = true; 948 mUnselectAllOnDismiss = true;
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
891 if (isActionModeValid() && mIsInsertion != insertion) mNeedsPrepare = tr ue; 983 if (isActionModeValid() && mIsInsertion != insertion) mNeedsPrepare = tr ue;
892 mIsInsertion = insertion; 984 mIsInsertion = insertion;
893 } 985 }
894 986
895 private boolean isShareAvailable() { 987 private boolean isShareAvailable() {
896 Intent intent = new Intent(Intent.ACTION_SEND); 988 Intent intent = new Intent(Intent.ACTION_SEND);
897 intent.setType("text/plain"); 989 intent.setType("text/plain");
898 return mContext.getPackageManager().queryIntentActivities(intent, 990 return mContext.getPackageManager().queryIntentActivities(intent,
899 PackageManager.MATCH_DEFAULT_ONLY).size() > 0; 991 PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
900 } 992 }
993
994 // The callback class that delivers result from a ContextSelectionClient.
995 private class ContextSelectionCallback implements ContextSelectionProvider.R esultCallback {
996 @Override
997 public void onClassified(ContextSelectionProvider.Result result) {
998 boolean pendingClassificationRequest = mPendingClassificationRequest ;
999 mPendingClassificationRequest = false;
1000
1001 // If the selection does not exist any more, discard |result|.
1002 if (!mHasSelection) {
1003 assert !mHidden;
1004 assert mClassificationResult == null;
1005 return;
1006 }
1007
1008 // Determine whether we need to recreate the menu in case we are doi ng invalidate().
1009 final boolean hadOldResult =
1010 mClassificationResult != null && mClassificationResult.hasNa medAction();
1011 final boolean hasNewResult = result != null && result.hasNamedAction ();
1012
1013 mClassificationResult = result;
1014
1015 // Do not recreate the action mode if it has been cancelled (by Acti onMode.finish())
1016 // and not recreated after that.
1017 if (!pendingClassificationRequest && !isActionModeValid()) {
1018 assert !mHidden;
1019 return;
1020 }
1021
1022 // Update the selection range if needed.
1023 if (!(result.startAdjust == 0 && result.endAdjust == 0)) {
1024 // This call causes SELECTION_HANDLES_MOVED event
1025 mWebContents.adjustSelectionByCharacterOffset(result.startAdjust , result.endAdjust);
1026 }
1027
1028 // For simplicity always recreate the menu if the new result exists.
1029 mNeedsPrepare = hasNewResult || hadOldResult;
1030
1031 // Rely on this method to clear mHidden and unhide the action mode.
1032 showActionModeOrClearOnFailure();
1033 }
1034 };
901 } 1035 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698