| Index: chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java
|
| index 5d80817b268f82afdf521f8da31c4176b6902342..9ed354309497ac4474c036ed8379b1f7f9ad1329 100644
|
| --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchContext.java
|
| @@ -4,6 +4,7 @@
|
|
|
| package org.chromium.chrome.browser.contextualsearch;
|
|
|
| +import android.annotation.SuppressLint;
|
| import android.text.TextUtils;
|
|
|
| import org.chromium.base.annotations.CalledByNative;
|
| @@ -17,7 +18,10 @@ import javax.annotation.Nullable;
|
| * or changed.
|
| */
|
| public abstract class ContextualSearchContext {
|
| - static final int INVALID_SELECTION_OFFSET = -1;
|
| + static final int INVALID_OFFSET = -1;
|
| +
|
| + // non-visible word-break marker present in German Wikipedia pages.
|
| + static final int SOFT_HYPHEN = 173;
|
|
|
| // Pointer to the native instance of this class.
|
| private long mNativePointer;
|
| @@ -30,8 +34,12 @@ public abstract class ContextualSearchContext {
|
| private String mSurroundingText;
|
|
|
| // The start and end offsets of the selection within the text content.
|
| - private int mSelectionStartOffset = INVALID_SELECTION_OFFSET;
|
| - private int mSelectionEndOffset = INVALID_SELECTION_OFFSET;
|
| + private int mSelectionStartOffset = INVALID_OFFSET;
|
| + private int mSelectionEndOffset = INVALID_OFFSET;
|
| +
|
| + // The start and end offsets of an initial Tap gesture within the text content.
|
| + private int mTapStartOffset = INVALID_OFFSET;
|
| + private int mTapEndOffset = INVALID_OFFSET;
|
|
|
| // The initial word selected by a Tap, or null.
|
| private String mInitialSelectedWord;
|
| @@ -39,6 +47,11 @@ public abstract class ContextualSearchContext {
|
| // The original encoding of the base page.
|
| private String mEncoding;
|
|
|
| + // The tapped word as analyzed internally before selection takes place, or {@code null} if no
|
| + // analysis has been done yet.
|
| + private String mTappedWord;
|
| + private int mTappedWordStartOffset = INVALID_OFFSET;
|
| +
|
| /**
|
| * Constructs a context that tracks the selection and some amount of page content.
|
| */
|
| @@ -85,6 +98,11 @@ public abstract class ContextualSearchContext {
|
| mSurroundingText = surroundingText;
|
| mSelectionStartOffset = startOffset;
|
| mSelectionEndOffset = endOffset;
|
| + if (startOffset == endOffset && mTapStartOffset == INVALID_OFFSET
|
| + && mTapEndOffset == INVALID_OFFSET) {
|
| + mTapStartOffset = startOffset;
|
| + mTapEndOffset = endOffset;
|
| + }
|
| // Notify of an initial selection if it's not empty.
|
| if (endOffset > startOffset) onSelectionChanged();
|
| }
|
| @@ -99,7 +117,7 @@ public abstract class ContextualSearchContext {
|
|
|
| /**
|
| * @return The offset into the surrounding text of the start of the selection, or
|
| - * {@link #INVALID_SELECTION_OFFSET} if not yet established.
|
| + * {@link #INVALID_OFFSET} if not yet established.
|
| */
|
| int getSelectionStartOffset() {
|
| return mSelectionStartOffset;
|
| @@ -107,7 +125,7 @@ public abstract class ContextualSearchContext {
|
|
|
| /**
|
| * @return The offset into the surrounding text of the end of the selection, or
|
| - * {@link #INVALID_SELECTION_OFFSET} if not yet established.
|
| + * {@link #INVALID_OFFSET} if not yet established.
|
| */
|
| int getSelectionEndOffset() {
|
| return mSelectionEndOffset;
|
| @@ -143,9 +161,7 @@ public abstract class ContextualSearchContext {
|
| * @return Whether this context can Resolve the Search Term.
|
| */
|
| boolean canResolve() {
|
| - return mHasSetResolveProperties && mSelectionStartOffset != INVALID_SELECTION_OFFSET
|
| - && mSelectionEndOffset != INVALID_SELECTION_OFFSET
|
| - && mSelectionEndOffset > mSelectionStartOffset;
|
| + return mHasSetResolveProperties && haveValidSelection();
|
| }
|
|
|
| /**
|
| @@ -173,7 +189,107 @@ public abstract class ContextualSearchContext {
|
| */
|
| abstract void onSelectionChanged();
|
|
|
| - // TODO(donnd): Add a test for this class!
|
| + // ============================================================================================
|
| + // Content Analysis.
|
| + // ============================================================================================
|
| +
|
| + /**
|
| + * @return Whether we have valid Surrounding text and initial Tap offset.
|
| + */
|
| + private boolean haveValidTappedText() {
|
| + return !TextUtils.isEmpty(mSurroundingText) && mTapStartOffset != INVALID_OFFSET
|
| + && mTapEndOffset != INVALID_OFFSET && mTapEndOffset <= mSurroundingText.length();
|
| + }
|
| +
|
| + /**
|
| + * @return Whether we have a valid selection.
|
| + */
|
| + private boolean haveValidSelection() {
|
| + if (!haveValidTappedText()) return false;
|
| +
|
| + return mSelectionStartOffset != INVALID_OFFSET && mSelectionEndOffset != INVALID_OFFSET
|
| + && mSelectionStartOffset < mSelectionEndOffset
|
| + && mSelectionEndOffset < mSurroundingText.length();
|
| + }
|
| +
|
| + /**
|
| + * @return The tapped word, or {@code null} if the tapped word cannot be identified by the
|
| + * current parsing capability.
|
| + */
|
| + String getTappedWord() {
|
| + if (mTappedWord != null) return mTappedWord;
|
| +
|
| + assert haveValidTappedText();
|
| + int wordStartOffset = findWordStartOffset(mSurroundingText, mTapStartOffset);
|
| + int wordEndOffset = findWordEndOffset(mSurroundingText, mTapEndOffset);
|
| + if (wordStartOffset == INVALID_OFFSET || wordEndOffset == INVALID_OFFSET) return null;
|
| +
|
| + mTappedWord = mSurroundingText.substring(wordStartOffset, wordEndOffset);
|
| + mTappedWordStartOffset = wordStartOffset;
|
| + return mTappedWord;
|
| + }
|
| +
|
| + /**
|
| + * @return The offset of the tap within the tapped word, or {@code -1} if the tapped word cannot
|
| + * be identified by the current parsing capability.
|
| + */
|
| + int getTappedWordOffset() {
|
| + getTappedWord();
|
| + if (mTappedWordStartOffset != INVALID_OFFSET && mTapStartOffset == mTapEndOffset) {
|
| + return mTapStartOffset - mTappedWordStartOffset;
|
| + }
|
| +
|
| + return INVALID_OFFSET;
|
| + }
|
| +
|
| + /**
|
| + * @return The start of the word that contains the given initial offset, within {@code text}.
|
| + */
|
| + private int findWordStartOffset(String text, int initial) {
|
| + // Scan before, aborting if we hit any CJKN letter.
|
| + for (int offset = initial - 1; offset >= 0; offset--) {
|
| + if (isIdeographicAtIndex(text, offset)) return INVALID_OFFSET;
|
| +
|
| + if (isWordBreakAtIndex(text, offset)) {
|
| + // The start of the word is after this word break.
|
| + return offset + 1;
|
| + }
|
| + }
|
| + return INVALID_OFFSET;
|
| + }
|
| +
|
| + /**
|
| + * @return The end of the word that contains the given initial offset, within {@code text}.
|
| + */
|
| + private int findWordEndOffset(String text, int initial) {
|
| + // Scan after, aborting if we hit any CJKN letter.
|
| + for (int offset = initial; offset <= text.length(); offset++) {
|
| + if (isIdeographicAtIndex(text, offset)) return INVALID_OFFSET;
|
| +
|
| + if (isWordBreakAtIndex(text, offset)) {
|
| + // The end of the word is the offset of this word break.
|
| + return offset;
|
| + }
|
| + }
|
| + return INVALID_OFFSET;
|
| + }
|
| +
|
| + /**
|
| + * @return Whether the character at the given index in the text is "Ideographic", which means
|
| + * we probably won't see reliable word breaks.
|
| + */
|
| + @SuppressLint("NewApi")
|
| + private boolean isIdeographicAtIndex(String text, int index) {
|
| + return Character.isIdeographic(text.charAt(index));
|
| + }
|
| +
|
| + /**
|
| + * @return Whether the character at the given index is a word-break.
|
| + */
|
| + private boolean isWordBreakAtIndex(String text, int index) {
|
| + return !Character.isLetterOrDigit(text.charAt(index))
|
| + && text.codePointAt(index) != SOFT_HYPHEN;
|
| + }
|
|
|
| // ============================================================================================
|
| // Native callback support.
|
|
|