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

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

Issue 2740103006: Implement SmartText selection. (Closed)
Patch Set: ResultCallback is not a member of SelectionPopupController any more Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/public/android/java/src/org/chromium/content/browser/SelectionClient.java ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
diff --git a/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java b/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
index c45ec5aca4667aeb45a3f0e9dd621e122ef2d4ee..0315d165df2091c14a1981f38bab97161f030b69 100644
--- a/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
+++ b/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
@@ -54,7 +54,7 @@ import java.util.List;
*/
@TargetApi(Build.VERSION_CODES.M)
public class SelectionPopupController extends ActionModeCallbackHelper {
- private static final String TAG = "cr.SelectionPopCtlr"; // 20 char limit
+ private static final String TAG = "SelectionPopupCtlr"; // 20 char limit
/**
* Android Intent size limitations prevent sending over a megabyte of data. Limit
@@ -70,6 +70,12 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
// most such trailing, async delays.
private static final int SHOW_DELAY_MS = 300;
+ // Display operation for ActionMode (show or invalidate) that is delayed because
+ // the selected text needs to be classified before. See comment to mPostponedDisplayOp.
+ private static final int POSTPONED_NONE = 0;
+ private static final int POSTPONED_SHOW = 1;
+ private static final int POSTPONED_INVALIDATE = 2;
+
private final Context mContext;
private final WindowAndroid mWindowAndroid;
private final WebContents mWebContents;
@@ -115,6 +121,21 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
// The client that processes textual selection, or null if none exists.
private SelectionClient mSelectionClient;
+ // The classificaton result of the selected text if the selection exists and
+ // ContextSelectionProvider was able to classify it, otherwise null.
+ private ContextSelectionProvider.Result mClassificationResult;
+
+ // Postponed display operation for ActionMode.
+ //
+ // The |mSelectionClient| might perform a recognition of the selected texts that
+ // can affect the ActionMode menu. In this case its method sendsPopupMenuUpdates()
+ // returns true and the client itself sends the results back asynchronously.
+ //
+ // With this kind of SelectionClient we postpone the display of ActionMode menu
+ // until the result comes, so we can update the menu before showing it and avoid the flicker.
+ // This variable holds the type of operation we have postponed, i.e. show or invalidate.
+ private int mPostponedDisplayOp = POSTPONED_NONE;
boliu 2017/03/22 02:32:12 should mention this is only valid and not NONE if
Tima Vaisburd 2017/03/24 22:13:55 I followed Pedro's and your advises and tried to s
+
/**
* Create {@link SelectionPopupController} instance.
* @param context Context for action mode.
@@ -145,6 +166,9 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
hideActionModeTemporarily(hideDuration);
}
};
+
+ mSelectionClient =
+ ContextSelectionClient.create(new ContextSelectionCallback(), window, webContents);
}
/**
@@ -189,15 +213,18 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
*
* <p>Action mode in floating mode is tried first, and then falls back to
* a normal one.
- * @return {@code true} if the action mode started successfully or is already on.
+ * <p> If the action mode cannot be created the selection is cleared.
*/
- public boolean showActionMode() {
- if (isEmpty()) return false;
+ public void showActionModeOrClearOnFailure() {
+ if (isEmpty()) {
+ clearSelection();
+ return;
+ }
// Just refreshes the view if it is already showing.
if (isActionModeValid()) {
invalidateActionMode();
- return true;
+ return;
}
if (mView.getParent() != null) {
@@ -213,7 +240,7 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
mActionMode = actionMode;
}
mUnselectAllOnDismiss = true;
- return isActionModeValid();
+ if (!isActionModeValid()) clearSelection();
}
@TargetApi(Build.VERSION_CODES.M)
@@ -295,6 +322,7 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
*/
@Override
public void finishActionMode() {
+ mPostponedDisplayOp = POSTPONED_NONE;
boliu 2017/03/22 02:32:12 reset mClassificationResult here too?
Tima Vaisburd 2017/03/24 22:13:55 I wanted to associate mClassificationResult with t
if (isActionModeValid()) {
mActionMode.finish();
@@ -314,6 +342,7 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
assert canHideActionMode();
mHidden = false;
boliu 2017/03/22 02:32:12 so... 343-345 is a repeated at 392-394, factor out
Tima Vaisburd 2017/03/24 22:13:55 This code changed. Since I do not try to invalidat
mView.removeCallbacks(mRepeatingHideRunnable);
+ hideActionModeTemporarily(SHOW_DELAY_MS);
mPendingInvalidateContentRect = false;
}
@@ -328,7 +357,7 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
/**
* @see ActionMode#invalidateContentRect()
*/
- public void invalidateContentRect() {
+ private void invalidateContentRect() {
if (supportsFloatingActionMode()) {
if (mHidden) {
mPendingInvalidateContentRect = true;
@@ -431,6 +460,7 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
private void createActionMenu(ActionMode mode, Menu menu) {
mNeedsPrepare = false;
initializeMenu(mContext, mode, menu);
+ updateContextDependentMenuItem(menu);
if (!isSelectionEditable() || !canPaste()) {
menu.removeItem(R.id.select_action_menu_paste);
@@ -473,6 +503,20 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
return clipMgr.hasPrimaryClip();
}
+ private void updateContextDependentMenuItem(Menu menu) {
+ MenuItem assistItem = menu.findItem(R.id.select_action_menu_context_dep);
+ if (assistItem == null) return;
+
+ if (mClassificationResult == null) {
+ assistItem.setVisible(false).setEnabled(false);
+ } else {
+ assistItem.setVisible(true)
+ .setEnabled(true)
+ .setTitle(mClassificationResult.label)
+ .setIcon(mClassificationResult.icon);
+ }
+ }
+
/**
* Intialize the menu items for processing text, if there is any.
*/
@@ -488,7 +532,7 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
for (int i = 0; i < supportedActivities.size(); i++) {
ResolveInfo resolveInfo = supportedActivities.get(i);
CharSequence label = resolveInfo.loadLabel(mContext.getPackageManager());
- menu.add(R.id.select_action_menu_text_processing_menus, Menu.NONE, i, label)
+ menu.add(R.id.select_action_menu_text_processing_menus, Menu.NONE, Menu.NONE, label)
.setIntent(createProcessTextIntentForResolveInfo(resolveInfo))
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
@@ -514,7 +558,10 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
int id = item.getItemId();
int groupId = item.getGroupId();
- if (id == R.id.select_action_menu_select_all) {
+ if (id == R.id.select_action_menu_context_dep) {
+ doContextDependentAction();
+ mode.finish();
+ } else if (id == R.id.select_action_menu_select_all) {
selectAll();
} else if (id == R.id.select_action_menu_cut) {
cut();
@@ -573,6 +620,30 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
}
/**
+ * Perform an action that depends on the semantics of the selected text.
+ */
+ @VisibleForTesting
+ void doContextDependentAction() {
+ if (mClassificationResult == null) return;
+
+ assert mClassificationResult.onClickListener != null
+ || mClassificationResult.intent != null;
+
+ if (mClassificationResult.onClickListener != null) {
+ mClassificationResult.onClickListener.onClick(mView);
+ return;
+ }
+
+ if (mClassificationResult.intent != null) {
+ Context context = mWindowAndroid.getContext().get();
+ if (context == null) return;
+
+ context.startActivity(mClassificationResult.intent);
+ return;
+ }
+ }
+
+ /**
* Perform a select all action.
*/
@VisibleForTesting
@@ -750,7 +821,7 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
void restoreSelectionPopupsIfNecessary() {
if (mHasSelection && !isActionModeValid()) {
- if (!showActionMode()) clearSelection();
+ showActionModeOrClearOnFailure();
}
}
@@ -767,7 +838,11 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
mSelectionRect.set(left, top, right, bottom);
mHasSelection = true;
mUnselectAllOnDismiss = true;
- if (!showActionMode()) clearSelection();
+ if (mSelectionClient != null && mSelectionClient.sendsSelectionPopupUpdates()) {
+ mPostponedDisplayOp = POSTPONED_SHOW;
+ } else {
+ showActionModeOrClearOnFailure();
+ }
break;
case SelectionEventType.SELECTION_HANDLES_MOVED:
@@ -787,7 +862,11 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
break;
case SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED:
- hideActionMode(false);
+ if (mSelectionClient != null && mSelectionClient.sendsSelectionPopupUpdates()) {
+ mPostponedDisplayOp = POSTPONED_INVALIDATE;
+ } else {
+ hideActionMode(false);
+ }
break;
case SelectionEventType.INSERTION_HANDLE_SHOWN:
@@ -848,6 +927,7 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
* end if applicable.
*/
void clearSelection() {
+ mClassificationResult = null;
if (mWebContents == null || isEmpty()) return;
mWebContents.collapseSelection();
}
@@ -916,4 +996,53 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
return mContext.getPackageManager().queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
}
+
+ // The callback class that delivers result from a ContextSelectionClient.
+ private class ContextSelectionCallback implements ContextSelectionProvider.ResultCallback {
+ @Override
+ public void onClassified(ContextSelectionProvider.Result result) {
+ // If the selection does not exist any more, discard |result|.
+ if (!mHasSelection) {
+ mPostponedDisplayOp = POSTPONED_NONE;
+ return;
+ }
+
+ // Update the selection range if needed.
+ if (!(result.startAdjust == 0 && result.endAdjust == 0)) {
+ // This call causes SELECTION_HANDLES_MOVED event
+ mWebContents.adjustSelectionByCharacterOffset(result.startAdjust, result.endAdjust);
+ }
+
+ final boolean hadPriorResult = mClassificationResult != null;
+ mClassificationResult = result.hasNamedAction() ? result : null;
+
+ // For simplicity always update the menu if we need to show assist item.
+ // Also we need to update the menu if the assist item is going to disappear.
+ if (mClassificationResult != null || hadPriorResult) mNeedsPrepare = true;
+
+ switch (mPostponedDisplayOp) {
+ case POSTPONED_NONE:
+ break;
+
+ case POSTPONED_SHOW:
+ // TODO(timav): if the |result| contained the selection adjustment and
+ // we called adjustSelectionByCharacterOffset() above, there is a
+ // flicker if SELECTION_HANDLES_MOVED comes later and invalidates the
+ // selection rectangle. Consider postponing till SELECTION_HANDLES_MOVED
+ // or introduce delay.
+ showActionModeOrClearOnFailure();
+ mPostponedDisplayOp = POSTPONED_NONE;
+ break;
+
+ case POSTPONED_INVALIDATE:
+ invalidateActionMode();
+ mPostponedDisplayOp = POSTPONED_NONE;
+ break;
+
+ default:
+ assert false : "Invalid postponed display operation type.";
+ break;
+ }
+ }
+ };
}
« no previous file with comments | « content/public/android/java/src/org/chromium/content/browser/SelectionClient.java ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698