OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.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 Loading... | |
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 |
68 // stops dragging a selection handle, in turn showing the ActionMode, the | 68 // stops dragging a selection handle, in turn showing the ActionMode, the |
69 // selection change response will be asynchronous. 300ms should accomodate | 69 // selection change response will be asynchronous. 300ms should accomodate |
70 // most such trailing, async delays. | 70 // most such trailing, async delays. |
71 private static final int SHOW_DELAY_MS = 300; | 71 private static final int SHOW_DELAY_MS = 300; |
72 | 72 |
73 // Display operation for ActionMode (show or invalidate) that is delayed bec ause | |
74 // the selected text needs to be classified before. See comment to mPostpone dDisplayOp. | |
75 private static final int POSTPONED_NONE = 0; | |
76 private static final int POSTPONED_SHOW = 1; | |
77 private static final int POSTPONED_INVALIDATE = 2; | |
78 | |
73 private final Context mContext; | 79 private final Context mContext; |
74 private final WindowAndroid mWindowAndroid; | 80 private final WindowAndroid mWindowAndroid; |
75 private final WebContents mWebContents; | 81 private final WebContents mWebContents; |
76 private final RenderCoordinates mRenderCoordinates; | 82 private final RenderCoordinates mRenderCoordinates; |
77 private final ImeAdapter mImeAdapter; | 83 private final ImeAdapter mImeAdapter; |
78 private ActionMode.Callback mCallback; | 84 private ActionMode.Callback mCallback; |
79 | 85 |
80 // Selection rectangle in DIP. | 86 // Selection rectangle in DIP. |
81 private final Rect mSelectionRect = new Rect(); | 87 private final Rect mSelectionRect = new Rect(); |
82 | 88 |
(...skipping 25 matching lines...) Expand all Loading... | |
108 private boolean mHasSelection; | 114 private boolean mHasSelection; |
109 | 115 |
110 // Lazily created paste popup menu, triggered either via long press in an | 116 // Lazily created paste popup menu, triggered either via long press in an |
111 // editable region or from tapping the insertion handle. | 117 // editable region or from tapping the insertion handle. |
112 private PastePopupMenu mPastePopupMenu; | 118 private PastePopupMenu mPastePopupMenu; |
113 private boolean mWasPastePopupShowingOnInsertionDragStart; | 119 private boolean mWasPastePopupShowingOnInsertionDragStart; |
114 | 120 |
115 // The client that processes textual selection, or null if none exists. | 121 // The client that processes textual selection, or null if none exists. |
116 private SelectionClient mSelectionClient; | 122 private SelectionClient mSelectionClient; |
117 | 123 |
124 // The classificaton result of the selected text if the selection exists and | |
125 // ContextSelectionProvider was able to classify it, otherwise null. | |
126 private ContextSelectionProvider.Result mClassificationResult; | |
127 | |
128 // Postponed display operation for ActionMode. | |
129 // | |
130 // The |mSelectionClient| might perform a recognition of the selected texts that | |
131 // can affect the ActionMode menu. In this case its method sendsPopupMenuUpd ates() | |
132 // returns true and the client itself sends the results back asynchronously. | |
133 // | |
134 // With this kind of SelectionClient we postpone the display of ActionMode m enu | |
135 // until the result comes, so we can update the menu before showing it and a void the flicker. | |
136 // This variable holds the type of operation we have postponed, i.e. show or invalidate. | |
137 private int mPostponedDisplayOp = POSTPONED_NONE; | |
138 | |
139 // The callback object that delivers result from SelectionClient. | |
140 private ContextSelectionProvider.ResultCallback mSelectonCallback = | |
141 new ContextSelectionProvider.ResultCallback() { | |
142 @Override | |
143 public void onClassified(ContextSelectionProvider.Result result) { | |
144 // If the selection does not exist any more, discard |result |. | |
145 if (!mHasSelection) { | |
146 mPostponedDisplayOp = POSTPONED_NONE; | |
147 return; | |
148 } | |
149 | |
150 // Update the selection range if needed. | |
151 if (!(result.startAdjust == 0 && result.endAdjust == 0)) { | |
152 // This call causes SELECTION_HANDLES_MOVED event | |
153 mWebContents.adjustSelectionByCharacterOffset( | |
154 result.startAdjust, result.endAdjust); | |
155 } | |
156 | |
157 final boolean hadPriorResult = mClassificationResult != null ; | |
158 mClassificationResult = result.hasNamedAction() ? result : n ull; | |
159 | |
160 // For simplicity always update the menu if we need to show assist item. | |
161 // Also we need to update the menu if the assist item is goi ng to disappear. | |
162 if (mClassificationResult != null || hadPriorResult) mNeedsP repare = true; | |
163 | |
164 switch (mPostponedDisplayOp) { | |
165 case POSTPONED_NONE: | |
166 break; | |
167 | |
168 case POSTPONED_SHOW: | |
169 // TODO(timav): if the |result| contained the select ion adjustment and | |
170 // we called adjustSelectionByCharacterOffset() abov e, there is a | |
171 // flicker if SELECTION_HANDLES_MOVED comes later an d invalidates the | |
172 // selection rectangle. Consider postponing till SEL ECTION_HANDLES_MOVED | |
173 // or introduce delay. | |
174 showActionModeOrClearOnFailure(); | |
175 mPostponedDisplayOp = POSTPONED_NONE; | |
176 break; | |
177 | |
178 case POSTPONED_INVALIDATE: | |
179 invalidateActionMode(); | |
180 mPostponedDisplayOp = POSTPONED_NONE; | |
181 break; | |
182 | |
183 default: | |
184 assert false : "Invalid postponed display operation type."; | |
185 break; | |
186 } | |
187 } | |
188 }; | |
189 | |
118 /** | 190 /** |
119 * Create {@link SelectionPopupController} instance. | 191 * Create {@link SelectionPopupController} instance. |
120 * @param context Context for action mode. | 192 * @param context Context for action mode. |
121 * @param window WindowAndroid instance. | 193 * @param window WindowAndroid instance. |
122 * @param webContents WebContents instance. | 194 * @param webContents WebContents instance. |
123 * @param view Container view. | 195 * @param view Container view. |
124 * @param renderCoordinates Coordinates info used to position elements. | 196 * @param renderCoordinates Coordinates info used to position elements. |
125 * @param imeAdapter ImeAdapter instance to handle cursor position. | 197 * @param imeAdapter ImeAdapter instance to handle cursor position. |
126 */ | 198 */ |
127 public SelectionPopupController(Context context, WindowAndroid window, WebCo ntents webContents, | 199 public SelectionPopupController(Context context, WindowAndroid window, WebCo ntents webContents, |
(...skipping 10 matching lines...) Expand all Loading... | |
138 mRepeatingHideRunnable = new Runnable() { | 210 mRepeatingHideRunnable = new Runnable() { |
139 @Override | 211 @Override |
140 public void run() { | 212 public void run() { |
141 assert mHidden; | 213 assert mHidden; |
142 final long hideDuration = getDefaultHideDuration(); | 214 final long hideDuration = getDefaultHideDuration(); |
143 // Ensure the next hide call occurs before the ActionMode reappe ars. | 215 // Ensure the next hide call occurs before the ActionMode reappe ars. |
144 mView.postDelayed(mRepeatingHideRunnable, hideDuration - 1); | 216 mView.postDelayed(mRepeatingHideRunnable, hideDuration - 1); |
145 hideActionModeTemporarily(hideDuration); | 217 hideActionModeTemporarily(hideDuration); |
146 } | 218 } |
147 }; | 219 }; |
220 | |
221 mSelectionClient = ContextSelectionClient.create(mSelectonCallback, wind ow, webContents); | |
148 } | 222 } |
149 | 223 |
150 /** | 224 /** |
151 * Update the container view. | 225 * Update the container view. |
152 */ | 226 */ |
153 void setContainerView(View view) { | 227 void setContainerView(View view) { |
154 assert view != null; | 228 assert view != null; |
155 | 229 |
156 // Cleans up action mode before switching to a new container view. | 230 // Cleans up action mode before switching to a new container view. |
157 if (isActionModeValid()) finishActionMode(); | 231 if (isActionModeValid()) finishActionMode(); |
(...skipping 24 matching lines...) Expand all Loading... | |
182 @Override | 256 @Override |
183 public void setAllowedMenuItems(int allowedMenuItems) { | 257 public void setAllowedMenuItems(int allowedMenuItems) { |
184 mAllowedMenuItems = allowedMenuItems; | 258 mAllowedMenuItems = allowedMenuItems; |
185 } | 259 } |
186 | 260 |
187 /** | 261 /** |
188 * Show (activate) android action mode by starting it. | 262 * Show (activate) android action mode by starting it. |
189 * | 263 * |
190 * <p>Action mode in floating mode is tried first, and then falls back to | 264 * <p>Action mode in floating mode is tried first, and then falls back to |
191 * a normal one. | 265 * a normal one. |
192 * @return {@code true} if the action mode started successfully or is alread y on. | 266 * <p> If the action mode cannot be created the selection is cleared. |
193 */ | 267 */ |
194 public boolean showActionMode() { | 268 public void showActionModeOrClearOnFailure() { |
195 if (isEmpty()) return false; | 269 if (isEmpty()) { |
270 clearSelection(); | |
271 return; | |
272 } | |
196 | 273 |
197 // Just refreshes the view if it is already showing. | 274 // Just refreshes the view if it is already showing. |
198 if (isActionModeValid()) { | 275 if (isActionModeValid()) { |
199 invalidateActionMode(); | 276 invalidateActionMode(); |
200 return true; | 277 return; |
201 } | 278 } |
202 | 279 |
203 if (mView.getParent() != null) { | 280 if (mView.getParent() != null) { |
204 // On ICS, startActionMode throws an NPE when getParent() is null. | 281 // On ICS, startActionMode throws an NPE when getParent() is null. |
205 assert mWebContents != null; | 282 assert mWebContents != null; |
206 ActionMode actionMode = supportsFloatingActionMode() | 283 ActionMode actionMode = supportsFloatingActionMode() |
207 ? startFloatingActionMode() | 284 ? startFloatingActionMode() |
208 : mView.startActionMode(mCallback); | 285 : mView.startActionMode(mCallback); |
209 if (actionMode != null) { | 286 if (actionMode != null) { |
210 // This is to work around an LGE email issue. See crbug.com/6517 06 for more details. | 287 // This is to work around an LGE email issue. See crbug.com/6517 06 for more details. |
211 LGEmailActionModeWorkaround.runIfNecessary(mContext, actionMode) ; | 288 LGEmailActionModeWorkaround.runIfNecessary(mContext, actionMode) ; |
212 } | 289 } |
213 mActionMode = actionMode; | 290 mActionMode = actionMode; |
214 } | 291 } |
215 mUnselectAllOnDismiss = true; | 292 mUnselectAllOnDismiss = true; |
216 return isActionModeValid(); | 293 if (!isActionModeValid()) clearSelection(); |
217 } | 294 } |
218 | 295 |
219 @TargetApi(Build.VERSION_CODES.M) | 296 @TargetApi(Build.VERSION_CODES.M) |
220 private ActionMode startFloatingActionMode() { | 297 private ActionMode startFloatingActionMode() { |
221 ActionMode actionMode = mView.startActionMode( | 298 ActionMode actionMode = mView.startActionMode( |
222 new FloatingActionModeCallback(this, mCallback), ActionMode.TYPE _FLOATING); | 299 new FloatingActionModeCallback(this, mCallback), ActionMode.TYPE _FLOATING); |
223 return actionMode; | 300 return actionMode; |
224 } | 301 } |
225 | 302 |
226 void showPastePopup(int x, int y) { | 303 void showPastePopup(int x, int y) { |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
288 return mPastePopupMenu != null && mPastePopupMenu.isShowing(); | 365 return mPastePopupMenu != null && mPastePopupMenu.isShowing(); |
289 } | 366 } |
290 | 367 |
291 // Composition methods for android.view.ActionMode | 368 // Composition methods for android.view.ActionMode |
292 | 369 |
293 /** | 370 /** |
294 * @see ActionMode#finish() | 371 * @see ActionMode#finish() |
295 */ | 372 */ |
296 @Override | 373 @Override |
297 public void finishActionMode() { | 374 public void finishActionMode() { |
375 mPostponedDisplayOp = POSTPONED_NONE; | |
298 if (isActionModeValid()) { | 376 if (isActionModeValid()) { |
299 mActionMode.finish(); | 377 mActionMode.finish(); |
300 | 378 |
301 // Should be nulled out in case #onDestroyActionMode() is not invoke d in response. | 379 // Should be nulled out in case #onDestroyActionMode() is not invoke d in response. |
302 mActionMode = null; | 380 mActionMode = null; |
303 } | 381 } |
304 } | 382 } |
305 | 383 |
306 /** | 384 /** |
307 * @see ActionMode#invalidate() | 385 * @see ActionMode#invalidate() |
308 * Note that invalidation will also reset visibility state. The caller | 386 * Note that invalidation will also reset visibility state. The caller |
309 * should account for this when making subsequent visibility updates. | 387 * should account for this when making subsequent visibility updates. |
310 */ | 388 */ |
311 private void invalidateActionMode() { | 389 private void invalidateActionMode() { |
312 if (!isActionModeValid()) return; | 390 if (!isActionModeValid()) return; |
313 if (mHidden) { | 391 if (mHidden) { |
314 assert canHideActionMode(); | 392 assert canHideActionMode(); |
315 mHidden = false; | 393 mHidden = false; |
316 mView.removeCallbacks(mRepeatingHideRunnable); | 394 mView.removeCallbacks(mRepeatingHideRunnable); |
395 hideActionModeTemporarily(SHOW_DELAY_MS); | |
317 mPendingInvalidateContentRect = false; | 396 mPendingInvalidateContentRect = false; |
318 } | 397 } |
319 | 398 |
320 // Try/catch necessary for framework bug, crbug.com/446717. | 399 // Try/catch necessary for framework bug, crbug.com/446717. |
321 try { | 400 try { |
322 mActionMode.invalidate(); | 401 mActionMode.invalidate(); |
323 } catch (NullPointerException e) { | 402 } catch (NullPointerException e) { |
324 Log.w(TAG, "Ignoring NPE from ActionMode.invalidate() as workaround for L", e); | 403 Log.w(TAG, "Ignoring NPE from ActionMode.invalidate() as workaround for L", e); |
325 } | 404 } |
326 } | 405 } |
327 | 406 |
328 /** | 407 /** |
329 * @see ActionMode#invalidateContentRect() | 408 * @see ActionMode#invalidateContentRect() |
330 */ | 409 */ |
331 public void invalidateContentRect() { | 410 private void invalidateContentRect() { |
332 if (supportsFloatingActionMode()) { | 411 if (supportsFloatingActionMode()) { |
333 if (mHidden) { | 412 if (mHidden) { |
334 mPendingInvalidateContentRect = true; | 413 mPendingInvalidateContentRect = true; |
335 } else { | 414 } else { |
336 mPendingInvalidateContentRect = false; | 415 mPendingInvalidateContentRect = false; |
337 if (isActionModeValid()) mActionMode.invalidateContentRect(); | 416 if (isActionModeValid()) mActionMode.invalidateContentRect(); |
338 } | 417 } |
339 } | 418 } |
340 } | 419 } |
341 | 420 |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
424 // caused a resource loading failure to be logged. WebView | 503 // caused a resource loading failure to be logged. WebView |
425 // resource access needs to be improved so that this | 504 // resource access needs to be improved so that this |
426 // logspam can be avoided. | 505 // logspam can be avoided. |
427 new MenuInflater(context).inflate(R.menu.select_action_menu, menu); | 506 new MenuInflater(context).inflate(R.menu.select_action_menu, menu); |
428 } | 507 } |
429 } | 508 } |
430 | 509 |
431 private void createActionMenu(ActionMode mode, Menu menu) { | 510 private void createActionMenu(ActionMode mode, Menu menu) { |
432 mNeedsPrepare = false; | 511 mNeedsPrepare = false; |
433 initializeMenu(mContext, mode, menu); | 512 initializeMenu(mContext, mode, menu); |
513 updateContextDependentMenuItem(menu); | |
434 | 514 |
435 if (!isSelectionEditable() || !canPaste()) { | 515 if (!isSelectionEditable() || !canPaste()) { |
436 menu.removeItem(R.id.select_action_menu_paste); | 516 menu.removeItem(R.id.select_action_menu_paste); |
437 } | 517 } |
438 | 518 |
439 if (isInsertion()) { | 519 if (isInsertion()) { |
440 menu.removeItem(R.id.select_action_menu_select_all); | 520 menu.removeItem(R.id.select_action_menu_select_all); |
441 menu.removeItem(R.id.select_action_menu_cut); | 521 menu.removeItem(R.id.select_action_menu_cut); |
442 menu.removeItem(R.id.select_action_menu_copy); | 522 menu.removeItem(R.id.select_action_menu_copy); |
443 menu.removeItem(R.id.select_action_menu_share); | 523 menu.removeItem(R.id.select_action_menu_share); |
(...skipping 22 matching lines...) Expand all Loading... | |
466 | 546 |
467 initializeTextProcessingMenu(menu); | 547 initializeTextProcessingMenu(menu); |
468 } | 548 } |
469 | 549 |
470 private boolean canPaste() { | 550 private boolean canPaste() { |
471 ClipboardManager clipMgr = (ClipboardManager) | 551 ClipboardManager clipMgr = (ClipboardManager) |
472 mContext.getSystemService(Context.CLIPBOARD_SERVICE); | 552 mContext.getSystemService(Context.CLIPBOARD_SERVICE); |
473 return clipMgr.hasPrimaryClip(); | 553 return clipMgr.hasPrimaryClip(); |
474 } | 554 } |
475 | 555 |
556 private void updateContextDependentMenuItem(Menu menu) { | |
557 MenuItem assistItem = menu.findItem(R.id.select_action_menu_context_dep) ; | |
558 if (assistItem == null) return; | |
559 | |
560 if (mClassificationResult == null) { | |
561 assistItem.setVisible(false).setEnabled(false); | |
562 } else { | |
563 assistItem.setVisible(true) | |
564 .setEnabled(true) | |
565 .setTitle(mClassificationResult.label) | |
566 .setIcon(mClassificationResult.icon); | |
567 } | |
568 } | |
569 | |
476 /** | 570 /** |
477 * Intialize the menu items for processing text, if there is any. | 571 * Intialize the menu items for processing text, if there is any. |
478 */ | 572 */ |
479 private void initializeTextProcessingMenu(Menu menu) { | 573 private void initializeTextProcessingMenu(Menu menu) { |
480 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M | 574 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M |
481 || !isSelectActionModeAllowed(MENU_ITEM_PROCESS_TEXT)) { | 575 || !isSelectActionModeAllowed(MENU_ITEM_PROCESS_TEXT)) { |
482 return; | 576 return; |
483 } | 577 } |
484 | 578 |
485 PackageManager packageManager = mContext.getPackageManager(); | 579 PackageManager packageManager = mContext.getPackageManager(); |
486 List<ResolveInfo> supportedActivities = | 580 List<ResolveInfo> supportedActivities = |
487 packageManager.queryIntentActivities(createProcessTextIntent(), 0); | 581 packageManager.queryIntentActivities(createProcessTextIntent(), 0); |
488 for (int i = 0; i < supportedActivities.size(); i++) { | 582 for (int i = 0; i < supportedActivities.size(); i++) { |
489 ResolveInfo resolveInfo = supportedActivities.get(i); | 583 ResolveInfo resolveInfo = supportedActivities.get(i); |
490 CharSequence label = resolveInfo.loadLabel(mContext.getPackageManage r()); | 584 CharSequence label = resolveInfo.loadLabel(mContext.getPackageManage r()); |
491 menu.add(R.id.select_action_menu_text_processing_menus, Menu.NONE, i , label) | 585 menu.add(R.id.select_action_menu_text_processing_menus, Menu.NONE, M enu.NONE, label) |
Tima Vaisburd
2017/03/21 02:07:02
Here i==0 and i==1 for the order have different se
| |
492 .setIntent(createProcessTextIntentForResolveInfo(resolveInfo )) | 586 .setIntent(createProcessTextIntentForResolveInfo(resolveInfo )) |
493 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); | 587 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); |
494 } | 588 } |
495 } | 589 } |
496 | 590 |
497 @TargetApi(Build.VERSION_CODES.M) | 591 @TargetApi(Build.VERSION_CODES.M) |
498 private static Intent createProcessTextIntent() { | 592 private static Intent createProcessTextIntent() { |
499 return new Intent().setAction(Intent.ACTION_PROCESS_TEXT).setType("text/ plain"); | 593 return new Intent().setAction(Intent.ACTION_PROCESS_TEXT).setType("text/ plain"); |
500 } | 594 } |
501 | 595 |
502 @TargetApi(Build.VERSION_CODES.M) | 596 @TargetApi(Build.VERSION_CODES.M) |
503 private Intent createProcessTextIntentForResolveInfo(ResolveInfo info) { | 597 private Intent createProcessTextIntentForResolveInfo(ResolveInfo info) { |
504 boolean isReadOnly = !isSelectionEditable(); | 598 boolean isReadOnly = !isSelectionEditable(); |
505 return createProcessTextIntent() | 599 return createProcessTextIntent() |
506 .putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, isReadOnly) | 600 .putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, isReadOnly) |
507 .setClassName(info.activityInfo.packageName, info.activityInfo.n ame); | 601 .setClassName(info.activityInfo.packageName, info.activityInfo.n ame); |
508 } | 602 } |
509 | 603 |
510 @Override | 604 @Override |
511 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { | 605 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { |
512 if (!isActionModeValid()) return true; | 606 if (!isActionModeValid()) return true; |
513 | 607 |
514 int id = item.getItemId(); | 608 int id = item.getItemId(); |
515 int groupId = item.getGroupId(); | 609 int groupId = item.getGroupId(); |
516 | 610 |
517 if (id == R.id.select_action_menu_select_all) { | 611 if (id == R.id.select_action_menu_context_dep) { |
612 doContextDependentAction(); | |
613 mode.finish(); | |
614 } else if (id == R.id.select_action_menu_select_all) { | |
518 selectAll(); | 615 selectAll(); |
519 } else if (id == R.id.select_action_menu_cut) { | 616 } else if (id == R.id.select_action_menu_cut) { |
520 cut(); | 617 cut(); |
521 mode.finish(); | 618 mode.finish(); |
522 } else if (id == R.id.select_action_menu_copy) { | 619 } else if (id == R.id.select_action_menu_copy) { |
523 copy(); | 620 copy(); |
524 mode.finish(); | 621 mode.finish(); |
525 } else if (id == R.id.select_action_menu_paste) { | 622 } else if (id == R.id.select_action_menu_paste) { |
526 paste(); | 623 paste(); |
527 mode.finish(); | 624 mode.finish(); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
566 (int) (mSelectionRect.top * deviceScale), | 663 (int) (mSelectionRect.top * deviceScale), |
567 (int) (mSelectionRect.right * deviceScale), | 664 (int) (mSelectionRect.right * deviceScale), |
568 (int) (mSelectionRect.bottom * deviceScale)); | 665 (int) (mSelectionRect.bottom * deviceScale)); |
569 | 666 |
570 // The selection coordinates are relative to the content viewport, but w e need | 667 // The selection coordinates are relative to the content viewport, but w e need |
571 // coordinates relative to the containing View. | 668 // coordinates relative to the containing View. |
572 outRect.offset(0, (int) mRenderCoordinates.getContentOffsetYPix()); | 669 outRect.offset(0, (int) mRenderCoordinates.getContentOffsetYPix()); |
573 } | 670 } |
574 | 671 |
575 /** | 672 /** |
673 * Perform an action that depends on the semantics of the selected text. | |
674 */ | |
675 @VisibleForTesting | |
676 void doContextDependentAction() { | |
677 if (mClassificationResult == null) return; | |
678 | |
679 assert mClassificationResult.onClickListener != null | |
680 || mClassificationResult.intent != null; | |
681 | |
682 if (mClassificationResult.onClickListener != null) { | |
683 mClassificationResult.onClickListener.onClick(mView); | |
684 return; | |
685 } | |
686 | |
687 if (mClassificationResult.intent != null) { | |
688 Context context = mWindowAndroid.getContext().get(); | |
689 if (context == null) return; | |
690 | |
691 context.startActivity(mClassificationResult.intent); | |
692 return; | |
693 } | |
694 } | |
695 | |
696 /** | |
576 * Perform a select all action. | 697 * Perform a select all action. |
577 */ | 698 */ |
578 @VisibleForTesting | 699 @VisibleForTesting |
579 void selectAll() { | 700 void selectAll() { |
580 mWebContents.selectAll(); | 701 mWebContents.selectAll(); |
581 // Even though the above statement logged a SelectAll user action, we wa nt to | 702 // Even though the above statement logged a SelectAll user action, we wa nt to |
582 // track whether the focus was in an editable field, so log that too. | 703 // track whether the focus was in an editable field, so log that too. |
583 if (isSelectionEditable()) { | 704 if (isSelectionEditable()) { |
584 RecordUserAction.record("MobileActionMode.SelectAllWasEditable"); | 705 RecordUserAction.record("MobileActionMode.SelectAllWasEditable"); |
585 } else { | 706 } else { |
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
743 CharSequence result = data.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEX T); | 864 CharSequence result = data.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEX T); |
744 if (result != null) { | 865 if (result != null) { |
745 // TODO(hush): Use a variant of replace that re-selects the replaced text. | 866 // TODO(hush): Use a variant of replace that re-selects the replaced text. |
746 // crbug.com/546710 | 867 // crbug.com/546710 |
747 mWebContents.replace(result.toString()); | 868 mWebContents.replace(result.toString()); |
748 } | 869 } |
749 } | 870 } |
750 | 871 |
751 void restoreSelectionPopupsIfNecessary() { | 872 void restoreSelectionPopupsIfNecessary() { |
752 if (mHasSelection && !isActionModeValid()) { | 873 if (mHasSelection && !isActionModeValid()) { |
753 if (!showActionMode()) clearSelection(); | 874 showActionModeOrClearOnFailure(); |
754 } | 875 } |
755 } | 876 } |
756 | 877 |
757 // All coordinates are in DIP. | 878 // All coordinates are in DIP. |
758 void onSelectionEvent(int eventType, int xAnchor, int yAnchor, | 879 void onSelectionEvent(int eventType, int xAnchor, int yAnchor, |
759 int left, int top, int right, int bottom, boolean isScrollInProgress , | 880 int left, int top, int right, int bottom, boolean isScrollInProgress , |
760 boolean touchScrollInProgress) { | 881 boolean touchScrollInProgress) { |
761 // Ensure the provided selection coordinates form a non-empty rect, as r equired by | 882 // Ensure the provided selection coordinates form a non-empty rect, as r equired by |
762 // the selection action mode. | 883 // the selection action mode. |
763 if (left == right) ++right; | 884 if (left == right) ++right; |
764 if (top == bottom) ++bottom; | 885 if (top == bottom) ++bottom; |
765 switch (eventType) { | 886 switch (eventType) { |
766 case SelectionEventType.SELECTION_HANDLES_SHOWN: | 887 case SelectionEventType.SELECTION_HANDLES_SHOWN: |
767 mSelectionRect.set(left, top, right, bottom); | 888 mSelectionRect.set(left, top, right, bottom); |
768 mHasSelection = true; | 889 mHasSelection = true; |
769 mUnselectAllOnDismiss = true; | 890 mUnselectAllOnDismiss = true; |
770 if (!showActionMode()) clearSelection(); | 891 if (mSelectionClient != null && mSelectionClient.sendsSelectionP opupUpdates()) { |
892 mPostponedDisplayOp = POSTPONED_SHOW; | |
893 } else { | |
894 showActionModeOrClearOnFailure(); | |
895 } | |
771 break; | 896 break; |
772 | 897 |
773 case SelectionEventType.SELECTION_HANDLES_MOVED: | 898 case SelectionEventType.SELECTION_HANDLES_MOVED: |
774 mSelectionRect.set(left, top, right, bottom); | 899 mSelectionRect.set(left, top, right, bottom); |
775 invalidateContentRect(); | 900 invalidateContentRect(); |
776 break; | 901 break; |
777 | 902 |
778 case SelectionEventType.SELECTION_HANDLES_CLEARED: | 903 case SelectionEventType.SELECTION_HANDLES_CLEARED: |
779 mHasSelection = false; | 904 mHasSelection = false; |
780 mUnselectAllOnDismiss = false; | 905 mUnselectAllOnDismiss = false; |
781 mSelectionRect.setEmpty(); | 906 mSelectionRect.setEmpty(); |
782 finishActionMode(); | 907 finishActionMode(); |
783 break; | 908 break; |
784 | 909 |
785 case SelectionEventType.SELECTION_HANDLE_DRAG_STARTED: | 910 case SelectionEventType.SELECTION_HANDLE_DRAG_STARTED: |
786 hideActionMode(true); | 911 hideActionMode(true); |
787 break; | 912 break; |
788 | 913 |
789 case SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED: | 914 case SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED: |
790 hideActionMode(false); | 915 if (mSelectionClient != null && mSelectionClient.sendsSelectionP opupUpdates()) { |
916 mPostponedDisplayOp = POSTPONED_INVALIDATE; | |
917 } else { | |
918 hideActionMode(false); | |
919 } | |
791 break; | 920 break; |
792 | 921 |
793 case SelectionEventType.INSERTION_HANDLE_SHOWN: | 922 case SelectionEventType.INSERTION_HANDLE_SHOWN: |
794 mSelectionRect.set(left, top, right, bottom); | 923 mSelectionRect.set(left, top, right, bottom); |
795 setIsInsertion(true); | 924 setIsInsertion(true); |
796 break; | 925 break; |
797 | 926 |
798 case SelectionEventType.INSERTION_HANDLE_MOVED: | 927 case SelectionEventType.INSERTION_HANDLE_MOVED: |
799 mSelectionRect.set(left, top, right, bottom); | 928 mSelectionRect.set(left, top, right, bottom); |
800 if (!isScrollInProgress && isPastePopupShowing()) { | 929 if (!isScrollInProgress && isPastePopupShowing()) { |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
841 int yAnchorPix = (int) (yAnchor * deviceScale); | 970 int yAnchorPix = (int) (yAnchor * deviceScale); |
842 mSelectionClient.onSelectionEvent(eventType, xAnchorPix, yAnchorPix) ; | 971 mSelectionClient.onSelectionEvent(eventType, xAnchorPix, yAnchorPix) ; |
843 } | 972 } |
844 } | 973 } |
845 | 974 |
846 /** | 975 /** |
847 * Clears the current text selection. Note that we will try to move cursor t o selection | 976 * Clears the current text selection. Note that we will try to move cursor t o selection |
848 * end if applicable. | 977 * end if applicable. |
849 */ | 978 */ |
850 void clearSelection() { | 979 void clearSelection() { |
980 mClassificationResult = null; | |
851 if (mWebContents == null || isEmpty()) return; | 981 if (mWebContents == null || isEmpty()) return; |
852 mWebContents.collapseSelection(); | 982 mWebContents.collapseSelection(); |
853 } | 983 } |
854 | 984 |
855 void onSelectionChanged(String text) { | 985 void onSelectionChanged(String text) { |
856 mLastSelectedText = text; | 986 mLastSelectedText = text; |
857 if (mSelectionClient != null) { | 987 if (mSelectionClient != null) { |
858 mSelectionClient.onSelectionChanged(text); | 988 mSelectionClient.onSelectionChanged(text); |
859 } | 989 } |
860 } | 990 } |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
910 mIsInsertion = insertion; | 1040 mIsInsertion = insertion; |
911 } | 1041 } |
912 | 1042 |
913 private boolean isShareAvailable() { | 1043 private boolean isShareAvailable() { |
914 Intent intent = new Intent(Intent.ACTION_SEND); | 1044 Intent intent = new Intent(Intent.ACTION_SEND); |
915 intent.setType("text/plain"); | 1045 intent.setType("text/plain"); |
916 return mContext.getPackageManager().queryIntentActivities(intent, | 1046 return mContext.getPackageManager().queryIntentActivities(intent, |
917 PackageManager.MATCH_DEFAULT_ONLY).size() > 0; | 1047 PackageManager.MATCH_DEFAULT_ONLY).size() > 0; |
918 } | 1048 } |
919 } | 1049 } |
OLD | NEW |