| 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.ClipData; | 10 import android.content.ClipData; |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 111 | 111 |
| 112 private boolean mEditable; | 112 private boolean mEditable; |
| 113 private boolean mIsPasswordType; | 113 private boolean mIsPasswordType; |
| 114 private boolean mIsInsertion; | 114 private boolean mIsInsertion; |
| 115 private boolean mCanSelectAllForPastePopup; | 115 private boolean mCanSelectAllForPastePopup; |
| 116 private boolean mCanEditRichlyForPastePopup; | 116 private boolean mCanEditRichlyForPastePopup; |
| 117 | 117 |
| 118 private boolean mUnselectAllOnDismiss; | 118 private boolean mUnselectAllOnDismiss; |
| 119 private String mLastSelectedText; | 119 private String mLastSelectedText; |
| 120 | 120 |
| 121 // Tracks whether a selection is currently active. When applied to selected
text, indicates | |
| 122 // whether the last selected text is still highlighted. | |
| 123 private boolean mHasSelection; | |
| 124 | |
| 125 // Lazily created paste popup menu, triggered either via long press in an | 121 // Lazily created paste popup menu, triggered either via long press in an |
| 126 // editable region or from tapping the insertion handle. | 122 // editable region or from tapping the insertion handle. |
| 127 private PastePopupMenu mPastePopupMenu; | 123 private PastePopupMenu mPastePopupMenu; |
| 128 private boolean mWasPastePopupShowingOnInsertionDragStart; | 124 private boolean mWasPastePopupShowingOnInsertionDragStart; |
| 129 | 125 |
| 130 // The client that processes textual selection, or null if none exists. | 126 // The client that processes textual selection, or null if none exists. |
| 131 private SelectionClient mSelectionClient; | 127 private SelectionClient mSelectionClient; |
| 132 | 128 |
| 133 // The classificaton result of the selected text if the selection exists and | 129 // The classificaton result of the selected text if the selection exists and |
| 134 // SmartSelectionProvider was able to classify it, otherwise null. | 130 // SmartSelectionProvider was able to classify it, otherwise null. |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 169 final long hideDuration = getDefaultHideDuration(); | 165 final long hideDuration = getDefaultHideDuration(); |
| 170 // Ensure the next hide call occurs before the ActionMode reappe
ars. | 166 // Ensure the next hide call occurs before the ActionMode reappe
ars. |
| 171 mView.postDelayed(mRepeatingHideRunnable, hideDuration - 1); | 167 mView.postDelayed(mRepeatingHideRunnable, hideDuration - 1); |
| 172 hideActionModeTemporarily(hideDuration); | 168 hideActionModeTemporarily(hideDuration); |
| 173 } | 169 } |
| 174 }; | 170 }; |
| 175 | 171 |
| 176 mSelectionClient = | 172 mSelectionClient = |
| 177 SmartSelectionClient.create(new SmartSelectionCallback(), window
, webContents); | 173 SmartSelectionClient.create(new SmartSelectionCallback(), window
, webContents); |
| 178 | 174 |
| 175 mLastSelectedText = ""; |
| 179 // TODO(timav): Use android.R.id.textAssist for the Assist item id once
we switch to | 176 // TODO(timav): Use android.R.id.textAssist for the Assist item id once
we switch to |
| 180 // Android O SDK and remove |mAssistMenuItemId|. | 177 // Android O SDK and remove |mAssistMenuItemId|. |
| 181 if (BuildInfo.isAtLeastO()) { | 178 if (BuildInfo.isAtLeastO()) { |
| 182 mAssistMenuItemId = | 179 mAssistMenuItemId = |
| 183 mContext.getResources().getIdentifier("textAssist", "id", "a
ndroid"); | 180 mContext.getResources().getIdentifier("textAssist", "id", "a
ndroid"); |
| 184 } | 181 } |
| 185 | 182 |
| 186 nativeInit(webContents); | 183 nativeInit(webContents); |
| 187 } | 184 } |
| 188 | 185 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 216 // True if action mode is initialized to a working (not a no-op) mode. | 213 // True if action mode is initialized to a working (not a no-op) mode. |
| 217 private boolean isActionModeSupported() { | 214 private boolean isActionModeSupported() { |
| 218 return mCallback != EMPTY_CALLBACK; | 215 return mCallback != EMPTY_CALLBACK; |
| 219 } | 216 } |
| 220 | 217 |
| 221 @Override | 218 @Override |
| 222 public void setAllowedMenuItems(int allowedMenuItems) { | 219 public void setAllowedMenuItems(int allowedMenuItems) { |
| 223 mAllowedMenuItems = allowedMenuItems; | 220 mAllowedMenuItems = allowedMenuItems; |
| 224 } | 221 } |
| 225 | 222 |
| 223 public void showSelectionMenu(int left, int top, int right, int bottom, bool
ean isEditable, |
| 224 boolean isPasswordType, String selectionText, boolean canSelectAll, |
| 225 boolean canRichlyEdit, boolean shouldSuggest) { |
| 226 mSelectionRect.set(left, top, right, bottom); |
| 227 mEditable = isEditable; |
| 228 mLastSelectedText = selectionText; |
| 229 mIsPasswordType = isPasswordType; |
| 230 mCanSelectAllForPastePopup = canSelectAll; |
| 231 mCanEditRichlyForPastePopup = canRichlyEdit; |
| 232 mUnselectAllOnDismiss = true; |
| 233 if (hasSelection()) { |
| 234 if (mSelectionClient != null |
| 235 && mSelectionClient.requestSelectionPopupUpdates(shouldSugge
st)) { |
| 236 // Rely on |mSelectionClient| sending a classification request a
nd the request |
| 237 // always calling onClassified() callback. |
| 238 mPendingShowActionMode = true; |
| 239 } else { |
| 240 showActionModeOrClearOnFailure(); |
| 241 } |
| 242 |
| 243 } else { |
| 244 createAndShowPastePopup(); |
| 245 } |
| 246 } |
| 247 |
| 226 /** | 248 /** |
| 227 * Show (activate) android action mode by starting it. | 249 * Show (activate) android action mode by starting it. |
| 228 * | 250 * |
| 229 * <p>Action mode in floating mode is tried first, and then falls back to | 251 * <p>Action mode in floating mode is tried first, and then falls back to |
| 230 * a normal one. | 252 * a normal one. |
| 231 * <p> If the action mode cannot be created the selection is cleared. | 253 * <p> If the action mode cannot be created the selection is cleared. |
| 232 */ | 254 */ |
| 233 public void showActionModeOrClearOnFailure() { | 255 public void showActionModeOrClearOnFailure() { |
| 234 mPendingShowActionMode = false; | 256 mPendingShowActionMode = false; |
| 235 | 257 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 261 if (!isActionModeValid()) clearSelection(); | 283 if (!isActionModeValid()) clearSelection(); |
| 262 } | 284 } |
| 263 | 285 |
| 264 @TargetApi(Build.VERSION_CODES.M) | 286 @TargetApi(Build.VERSION_CODES.M) |
| 265 private ActionMode startFloatingActionMode() { | 287 private ActionMode startFloatingActionMode() { |
| 266 ActionMode actionMode = mView.startActionMode( | 288 ActionMode actionMode = mView.startActionMode( |
| 267 new FloatingActionModeCallback(this, mCallback), ActionMode.TYPE
_FLOATING); | 289 new FloatingActionModeCallback(this, mCallback), ActionMode.TYPE
_FLOATING); |
| 268 return actionMode; | 290 return actionMode; |
| 269 } | 291 } |
| 270 | 292 |
| 271 void createAndShowPastePopup( | 293 private void createAndShowPastePopup() { |
| 272 int left, int top, int right, int bottom, boolean canSelectAll, bool
ean canEditRichly) { | |
| 273 if (mView.getParent() == null || mView.getVisibility() != View.VISIBLE)
{ | 294 if (mView.getParent() == null || mView.getVisibility() != View.VISIBLE)
{ |
| 274 return; | 295 return; |
| 275 } | 296 } |
| 276 | 297 |
| 277 if (!supportsFloatingActionMode() && !canPaste()) return; | 298 if (!supportsFloatingActionMode() && !canPaste()) return; |
| 278 destroyPastePopup(); | 299 destroyPastePopup(); |
| 279 mSelectionRect.set(left, top, right, bottom); | |
| 280 mCanSelectAllForPastePopup = canSelectAll; | |
| 281 mCanEditRichlyForPastePopup = canEditRichly; | |
| 282 PastePopupMenuDelegate delegate = new PastePopupMenuDelegate() { | 300 PastePopupMenuDelegate delegate = new PastePopupMenuDelegate() { |
| 283 @Override | 301 @Override |
| 284 public void paste() { | 302 public void paste() { |
| 285 mWebContents.paste(); | 303 mWebContents.paste(); |
| 286 mWebContents.dismissTextHandles(); | 304 mWebContents.dismissTextHandles(); |
| 287 } | 305 } |
| 288 | 306 |
| 289 @Override | 307 @Override |
| 290 public void pasteAsPlainText() { | 308 public void pasteAsPlainText() { |
| 291 mWebContents.pasteAsPlainText(); | 309 mWebContents.pasteAsPlainText(); |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 487 updateAssistMenuItem(descriptor); | 505 updateAssistMenuItem(descriptor); |
| 488 | 506 |
| 489 // TODO(ctzsm): Remove "paste as plain text" for now, need to add it bac
k when | 507 // TODO(ctzsm): Remove "paste as plain text" for now, need to add it bac
k when |
| 490 // crrev.com/2785853002 landed. | 508 // crrev.com/2785853002 landed. |
| 491 descriptor.removeItem(R.id.select_action_menu_paste_as_plain_text); | 509 descriptor.removeItem(R.id.select_action_menu_paste_as_plain_text); |
| 492 | 510 |
| 493 if (!isSelectionEditable() || !canPaste()) { | 511 if (!isSelectionEditable() || !canPaste()) { |
| 494 descriptor.removeItem(R.id.select_action_menu_paste); | 512 descriptor.removeItem(R.id.select_action_menu_paste); |
| 495 } | 513 } |
| 496 | 514 |
| 497 if (isInsertion()) { | 515 if (!hasSelection()) { |
| 498 descriptor.removeItem(R.id.select_action_menu_select_all); | 516 descriptor.removeItem(R.id.select_action_menu_select_all); |
| 499 descriptor.removeItem(R.id.select_action_menu_cut); | 517 descriptor.removeItem(R.id.select_action_menu_cut); |
| 500 descriptor.removeItem(R.id.select_action_menu_copy); | 518 descriptor.removeItem(R.id.select_action_menu_copy); |
| 501 descriptor.removeItem(R.id.select_action_menu_share); | 519 descriptor.removeItem(R.id.select_action_menu_share); |
| 502 descriptor.removeItem(R.id.select_action_menu_web_search); | 520 descriptor.removeItem(R.id.select_action_menu_web_search); |
| 503 return descriptor; | 521 return descriptor; |
| 504 } | 522 } |
| 505 | 523 |
| 506 if (!isSelectionEditable()) { | 524 if (!isSelectionEditable()) { |
| 507 descriptor.removeItem(R.id.select_action_menu_cut); | 525 descriptor.removeItem(R.id.select_action_menu_cut); |
| (...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 724 } | 742 } |
| 725 } | 743 } |
| 726 | 744 |
| 727 /** | 745 /** |
| 728 * Perform a select all action. | 746 * Perform a select all action. |
| 729 */ | 747 */ |
| 730 @VisibleForTesting | 748 @VisibleForTesting |
| 731 void selectAll() { | 749 void selectAll() { |
| 732 mWebContents.selectAll(); | 750 mWebContents.selectAll(); |
| 733 mClassificationResult = null; | 751 mClassificationResult = null; |
| 734 if (needsActionMenuUpdate()) showActionModeOrClearOnFailure(); | |
| 735 | |
| 736 // Even though the above statement logged a SelectAll user action, we wa
nt to | 752 // Even though the above statement logged a SelectAll user action, we wa
nt to |
| 737 // track whether the focus was in an editable field, so log that too. | 753 // track whether the focus was in an editable field, so log that too. |
| 738 if (isSelectionEditable()) { | 754 if (isSelectionEditable()) { |
| 739 RecordUserAction.record("MobileActionMode.SelectAllWasEditable"); | 755 RecordUserAction.record("MobileActionMode.SelectAllWasEditable"); |
| 740 } else { | 756 } else { |
| 741 RecordUserAction.record("MobileActionMode.SelectAllWasNonEditable"); | 757 RecordUserAction.record("MobileActionMode.SelectAllWasNonEditable"); |
| 742 } | 758 } |
| 743 } | 759 } |
| 744 | 760 |
| 745 /** | 761 /** |
| (...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 927 | 943 |
| 928 // All coordinates are in DIP. | 944 // All coordinates are in DIP. |
| 929 @CalledByNative | 945 @CalledByNative |
| 930 private void onSelectionEvent(int eventType, int left, int top, int right, i
nt bottom) { | 946 private void onSelectionEvent(int eventType, int left, int top, int right, i
nt bottom) { |
| 931 // Ensure the provided selection coordinates form a non-empty rect, as r
equired by | 947 // Ensure the provided selection coordinates form a non-empty rect, as r
equired by |
| 932 // the selection action mode. | 948 // the selection action mode. |
| 933 if (left == right) ++right; | 949 if (left == right) ++right; |
| 934 if (top == bottom) ++bottom; | 950 if (top == bottom) ++bottom; |
| 935 switch (eventType) { | 951 switch (eventType) { |
| 936 case SelectionEventType.SELECTION_HANDLES_SHOWN: | 952 case SelectionEventType.SELECTION_HANDLES_SHOWN: |
| 937 mSelectionRect.set(left, top, right, bottom); | |
| 938 mHasSelection = true; | |
| 939 mUnselectAllOnDismiss = true; | |
| 940 if (mSelectionClient != null | |
| 941 && mSelectionClient.requestSelectionPopupUpdates(true /*
suggest */)) { | |
| 942 // Rely on |mSelectionClient| sending a classification reque
st and the request | |
| 943 // always calling onClassified() callback. | |
| 944 mPendingShowActionMode = true; | |
| 945 } else { | |
| 946 showActionModeOrClearOnFailure(); | |
| 947 } | |
| 948 break; | 953 break; |
| 949 | 954 |
| 950 case SelectionEventType.SELECTION_HANDLES_MOVED: | 955 case SelectionEventType.SELECTION_HANDLES_MOVED: |
| 951 mSelectionRect.set(left, top, right, bottom); | 956 mSelectionRect.set(left, top, right, bottom); |
| 952 if (mPendingShowActionMode) { | 957 if (mPendingShowActionMode) { |
| 953 showActionModeOrClearOnFailure(); | 958 showActionModeOrClearOnFailure(); |
| 954 } else { | 959 } else { |
| 955 invalidateContentRect(); | 960 invalidateContentRect(); |
| 956 } | 961 } |
| 957 break; | 962 break; |
| 958 | 963 |
| 959 case SelectionEventType.SELECTION_HANDLES_CLEARED: | 964 case SelectionEventType.SELECTION_HANDLES_CLEARED: |
| 960 mHasSelection = false; | 965 mLastSelectedText = ""; |
| 961 mUnselectAllOnDismiss = false; | 966 mUnselectAllOnDismiss = false; |
| 962 mSelectionRect.setEmpty(); | 967 mSelectionRect.setEmpty(); |
| 963 if (mSelectionClient != null) mSelectionClient.cancelAllRequests
(); | 968 if (mSelectionClient != null) mSelectionClient.cancelAllRequests
(); |
| 964 finishActionMode(); | 969 finishActionMode(); |
| 965 break; | 970 break; |
| 966 | 971 |
| 967 case SelectionEventType.SELECTION_HANDLE_DRAG_STARTED: | 972 case SelectionEventType.SELECTION_HANDLE_DRAG_STARTED: |
| 968 hideActionMode(true); | 973 hideActionMode(true); |
| 969 break; | 974 break; |
| 970 | 975 |
| 971 case SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED: | 976 case SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED: |
| 972 if (mSelectionClient != null | 977 mWebContents.showContextMenuAtTouchHandle(left, bottom); |
| 973 && mSelectionClient.requestSelectionPopupUpdates(false /
* suggest */)) { | |
| 974 // Rely on |mSelectionClient| sending a classification reque
st and the request | |
| 975 // always calling onClassified() callback. | |
| 976 } else { | |
| 977 hideActionMode(false); | |
| 978 } | |
| 979 break; | 978 break; |
| 980 | 979 |
| 981 case SelectionEventType.INSERTION_HANDLE_SHOWN: | 980 case SelectionEventType.INSERTION_HANDLE_SHOWN: |
| 982 mSelectionRect.set(left, top, right, bottom); | 981 mSelectionRect.set(left, top, right, bottom); |
| 983 mIsInsertion = true; | 982 mIsInsertion = true; |
| 984 break; | 983 break; |
| 985 | 984 |
| 986 case SelectionEventType.INSERTION_HANDLE_MOVED: | 985 case SelectionEventType.INSERTION_HANDLE_MOVED: |
| 987 mSelectionRect.set(left, top, right, bottom); | 986 mSelectionRect.set(left, top, right, bottom); |
| 988 if (!mScrollInProgress && isPastePopupShowing()) { | 987 if (!mScrollInProgress && isPastePopupShowing()) { |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1090 mIsPasswordType = isPassword; | 1089 mIsPasswordType = isPassword; |
| 1091 if (isActionModeValid()) mActionMode.invalidate(); | 1090 if (isActionModeValid()) mActionMode.invalidate(); |
| 1092 } | 1091 } |
| 1093 } | 1092 } |
| 1094 | 1093 |
| 1095 /** | 1094 /** |
| 1096 * @return Whether the page has an active, touch-controlled selection region
. | 1095 * @return Whether the page has an active, touch-controlled selection region
. |
| 1097 */ | 1096 */ |
| 1098 @VisibleForTesting | 1097 @VisibleForTesting |
| 1099 public boolean hasSelection() { | 1098 public boolean hasSelection() { |
| 1100 return mHasSelection; | 1099 return mLastSelectedText.length() != 0; |
| 1101 } | 1100 } |
| 1102 | 1101 |
| 1103 @Override | 1102 @Override |
| 1104 public String getSelectedText() { | 1103 public String getSelectedText() { |
| 1105 return hasSelection() ? mLastSelectedText : ""; | 1104 return mLastSelectedText; |
| 1106 } | 1105 } |
| 1107 | 1106 |
| 1108 private boolean isShareAvailable() { | 1107 private boolean isShareAvailable() { |
| 1109 Intent intent = new Intent(Intent.ACTION_SEND); | 1108 Intent intent = new Intent(Intent.ACTION_SEND); |
| 1110 intent.setType("text/plain"); | 1109 intent.setType("text/plain"); |
| 1111 return mContext.getPackageManager().queryIntentActivities(intent, | 1110 return mContext.getPackageManager().queryIntentActivities(intent, |
| 1112 PackageManager.MATCH_DEFAULT_ONLY).size() > 0; | 1111 PackageManager.MATCH_DEFAULT_ONLY).size() > 0; |
| 1113 } | 1112 } |
| 1114 | 1113 |
| 1115 // TODO(timav): Use |TextClassifier| instead of |Object| after we switch to
Android SDK 26. | 1114 // TODO(timav): Use |TextClassifier| instead of |Object| after we switch to
Android SDK 26. |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1182 if (mPendingShowActionMode) return; | 1181 if (mPendingShowActionMode) return; |
| 1183 } | 1182 } |
| 1184 | 1183 |
| 1185 // Rely on this method to clear |mHidden| and unhide the action mode
. | 1184 // Rely on this method to clear |mHidden| and unhide the action mode
. |
| 1186 showActionModeOrClearOnFailure(); | 1185 showActionModeOrClearOnFailure(); |
| 1187 } | 1186 } |
| 1188 }; | 1187 }; |
| 1189 | 1188 |
| 1190 private native void nativeInit(WebContents webContents); | 1189 private native void nativeInit(WebContents webContents); |
| 1191 } | 1190 } |
| OLD | NEW |