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

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

Issue 2785853002: Selection Action mode triggered like a context menu (Closed)
Patch Set: fix rebase Created 3 years, 6 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.ClipData; 10 import android.content.ClipData;
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698