Index: content/public/android/java/src/org/chromium/content/browser/ContextSelectionClient.java |
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContextSelectionClient.java b/content/public/android/java/src/org/chromium/content/browser/ContextSelectionClient.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..63d5a6161b39fcdec62b31c4f734ad5506b61ce6 |
--- /dev/null |
+++ b/content/public/android/java/src/org/chromium/content/browser/ContextSelectionClient.java |
@@ -0,0 +1,157 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.content.browser; |
+ |
+import android.support.annotation.IntDef; |
+import android.text.TextUtils; |
+ |
+import org.chromium.base.annotations.CalledByNative; |
+import org.chromium.base.annotations.JNINamespace; |
+import org.chromium.content_public.browser.WebContents; |
+import org.chromium.ui.base.WindowAndroid; |
+import org.chromium.ui.touch_selection.SelectionEventType; |
+ |
+import java.lang.annotation.Retention; |
+import java.lang.annotation.RetentionPolicy; |
+ |
+/** |
+ * A class that controls the classification of the textual selection. |
+ * It requests the selection together with its surrounding text from the focused frame and sends it |
+ * to ContextSelectionProvider which does the classification itself. |
+ */ |
+@JNINamespace("content") |
+public class ContextSelectionClient implements SelectionClient { |
+ @IntDef({CLASSIFY, SUGGEST_AND_CLASSIFY}) |
+ @Retention(RetentionPolicy.SOURCE) |
+ private @interface RequestType {} |
+ |
+ // Request to obtain the type (e.g. phone number, e-mail address) and the most |
+ // appropriate operation for the selected text. |
+ private static final int CLASSIFY = 0; |
+ |
+ // Request to obtain the type (e.g. phone number, e-mail address), the most |
+ // appropriate operation for the selected text and a better selection boundaries. |
+ private static final int SUGGEST_AND_CLASSIFY = 1; |
+ |
+ // The maximal number of characters on the left and on the right from the current selection. |
+ // Used for surrounding text request. |
+ private static final int NUM_EXTRA_CHARS = 100; |
+ |
+ private long mNativeContextSelectionClient; |
+ private ContextSelectionProvider mProvider; |
+ private ContextSelectionProvider.ResultCallback mCallback; |
+ |
+ /** |
+ * Creates the ContextSelectionClient. Returns null in case ContextSelectionProvider |
+ * does not exist in the system. |
+ */ |
+ public static ContextSelectionClient create(ContextSelectionProvider.ResultCallback callback, |
+ WindowAndroid windowAndroid, WebContents webContents) { |
+ ContextSelectionProvider provider = |
+ ContentClassFactory.get().createContextSelectionProvider(callback, windowAndroid); |
+ |
+ // ContextSelectionProvider might not exist. |
+ if (provider == null) return null; |
+ |
+ return new ContextSelectionClient(provider, callback, webContents); |
+ } |
+ |
+ private ContextSelectionClient(ContextSelectionProvider provider, |
+ ContextSelectionProvider.ResultCallback callback, WebContents webContents) { |
+ mProvider = provider; |
+ mCallback = callback; |
+ mNativeContextSelectionClient = nativeInit(webContents); |
+ } |
+ |
+ @CalledByNative |
+ private void onNativeSideDestroyed(long nativeContextSelectionClient) { |
+ assert nativeContextSelectionClient == mNativeContextSelectionClient; |
+ mNativeContextSelectionClient = 0; |
+ mProvider.cancelAllRequests(); |
+ } |
+ |
+ // SelectionClient implementation |
+ @Override |
+ public void onSelectionChanged(String selection) {} |
+ |
+ @Override |
+ public void onSelectionEvent(int eventType, float posXPix, float posYPix) { |
+ switch (eventType) { |
+ case SelectionEventType.SELECTION_HANDLES_SHOWN: |
+ // This event is sent when the long press is detected which causes |
+ // selection to appear for the first time. Temporarily hiding the |
+ // handles that happens e.g. during scroll does not affect this event. |
+ requestSurroundingText(SUGGEST_AND_CLASSIFY); |
+ break; |
+ |
+ case SelectionEventType.SELECTION_HANDLES_CLEARED: |
+ // The ActionMode should be stopped when this event comes. |
+ cancelAllRequests(); |
+ break; |
+ |
+ case SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED: |
+ // This event is sent after a user stopped dragging one of the |
+ // selection handles, i.e. stopped modifying the selection. |
+ requestSurroundingText(CLASSIFY); |
+ break; |
+ |
+ default: |
+ break; // ignore |
+ } |
+ } |
+ |
+ @Override |
+ public void showUnhandledTapUIIfNeeded(int x, int y) {} |
+ |
+ @Override |
+ public boolean sendsSelectionPopupUpdates() { |
+ return true; |
+ } |
+ |
+ private void cancelAllRequests() { |
+ if (mNativeContextSelectionClient != 0) { |
+ nativeCancelAllRequests(mNativeContextSelectionClient); |
+ } |
+ |
+ mProvider.cancelAllRequests(); |
+ } |
+ |
+ private void requestSurroundingText(@RequestType int callbackData) { |
+ if (mNativeContextSelectionClient == 0) { |
+ onSurroundingTextReceived(callbackData, "", 0, 0); |
+ return; |
+ } |
+ |
+ nativeRequestSurroundingText(mNativeContextSelectionClient, NUM_EXTRA_CHARS, callbackData); |
+ } |
+ |
+ @CalledByNative |
+ private void onSurroundingTextReceived( |
+ @RequestType int callbackData, String text, int start, int end) { |
+ if (TextUtils.isEmpty(text)) { |
+ mCallback.onClassified(new ContextSelectionProvider.Result()); |
+ return; |
+ } |
+ |
+ switch (callbackData) { |
+ case SUGGEST_AND_CLASSIFY: |
+ mProvider.sendSuggestAndClassifyRequest(text, start, end); |
+ break; |
+ |
+ case CLASSIFY: |
+ mProvider.sendClassifyRequest(text, start, end); |
+ break; |
+ |
+ default: |
+ assert false : "Unexpected callback data"; |
+ break; |
+ } |
+ } |
+ |
+ private native long nativeInit(WebContents webContents); |
+ private native void nativeRequestSurroundingText( |
+ long nativeContextSelectionClient, int numExtraCharacters, int callbackData); |
+ private native void nativeCancelAllRequests(long nativeContextSelectionClient); |
+} |