diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 0d0b7a160..eb1899ca2 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -668,12 +668,16 @@ public final class RichInputConnection { } } + final boolean hasUrlSpans = + SpannableStringUtils.hasUrlSpans(before, startIndexInBefore, before.length()) + || SpannableStringUtils.hasUrlSpans(after, 0, endIndexInAfter); // We don't use TextUtils#concat because it copies all spans without respect to their // nature. If the text includes a PARAGRAPH span and it has been split, then // TextUtils#concat will crash when it tries to concat both sides of it. return new TextRange( SpannableStringUtils.concatWithNonParagraphSuggestionSpansOnly(before, after), - startIndexInBefore, before.length() + endIndexInAfter, before.length()); + startIndexInBefore, before.length() + endIndexInAfter, before.length(), + hasUrlSpans); } public boolean isCursorTouchingWord(final SpacingAndPunctuations spacingAndPunctuations) { diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 6f73859e8..9bf9d1f45 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -1270,6 +1270,10 @@ public final class InputLogic { if (range.length() <= 0) return; // Race condition. No text to resume on, so bail out. // If for some strange reason (editor bug or so) we measure the text before the cursor as // longer than what the entire text is supposed to be, the safe thing to do is bail out. + if (range.mHasUrlSpans) return; // If there are links, we don't resume suggestions. Making + // edits to a linkified text through batch commands would ruin the URL spans, and unless + // we take very complicated steps to preserve the whole link, we can't do things right so + // we just do not resume because it's safer. final int numberOfCharsInWordBeforeCursor = range.getNumberOfCharsInWordBeforeCursor(); if (numberOfCharsInWordBeforeCursor > expectedCursorPosition) return; final ArrayList suggestions = CollectionUtils.newArrayList(); diff --git a/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java b/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java index be0955456..38164cb36 100644 --- a/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/SpannableStringUtils.java @@ -22,6 +22,7 @@ import android.text.Spanned; import android.text.SpannedString; import android.text.TextUtils; import android.text.style.SuggestionSpan; +import android.text.style.URLSpan; public final class SpannableStringUtils { /** @@ -112,4 +113,16 @@ public final class SpannableStringUtils { return new SpannedString(ss); } + + public static boolean hasUrlSpans(final CharSequence text, + final int startIndex, final int endIndex) { + if (!(text instanceof Spanned)) { + return false; // Not spanned, so no link + } + final Spanned spanned = (Spanned)text; + // getSpans(x, y) does not return spans that start on x or end on y. x-1, y+1 does the + // trick, and works in all cases even if startIndex <= 0 or endIndex >= text.length(). + final URLSpan[] spans = spanned.getSpans(startIndex - 1, endIndex + 1, URLSpan.class); + return null != spans && spans.length > 0; + } } diff --git a/java/src/com/android/inputmethod/latin/utils/TextRange.java b/java/src/com/android/inputmethod/latin/utils/TextRange.java index 48b443ddd..dbf3b5060 100644 --- a/java/src/com/android/inputmethod/latin/utils/TextRange.java +++ b/java/src/com/android/inputmethod/latin/utils/TextRange.java @@ -31,6 +31,7 @@ public final class TextRange { private final int mCursorIndex; public final CharSequence mWord; + public final boolean mHasUrlSpans; public int getNumberOfCharsInWordBeforeCursor() { return mCursorIndex - mWordAtCursorStartIndex; @@ -95,7 +96,7 @@ public final class TextRange { } } if (spanStart == mWordAtCursorStartIndex && spanEnd == mWordAtCursorEndIndex) { - // If the span does not start and stop here, we ignore it. It probably extends + // If the span does not start and stop here, ignore it. It probably extends // past the start or end of the word, as happens in missing space correction // or EasyEditSpans put by voice input. spans[writeIndex++] = spans[readIndex]; @@ -105,7 +106,7 @@ public final class TextRange { } public TextRange(final CharSequence textAtCursor, final int wordAtCursorStartIndex, - final int wordAtCursorEndIndex, final int cursorIndex) { + final int wordAtCursorEndIndex, final int cursorIndex, final boolean hasUrlSpans) { if (wordAtCursorStartIndex < 0 || cursorIndex < wordAtCursorStartIndex || cursorIndex > wordAtCursorEndIndex || wordAtCursorEndIndex > textAtCursor.length()) { @@ -115,6 +116,7 @@ public final class TextRange { mWordAtCursorStartIndex = wordAtCursorStartIndex; mWordAtCursorEndIndex = wordAtCursorEndIndex; mCursorIndex = cursorIndex; + mHasUrlSpans = hasUrlSpans; mWord = mTextAtCursor.subSequence(mWordAtCursorStartIndex, mWordAtCursorEndIndex); } } \ No newline at end of file