From 02a798d66f67208f2d1c3a822fb8702985c27004 Mon Sep 17 00:00:00 2001 From: IvanIhnatsiuk Date: Fri, 27 Feb 2026 11:32:17 +0100 Subject: [PATCH 1/2] fix: span link when user types any text near the link --- .../com/swmansion/enriched/parser/HtmlToSpannedConverter.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android/src/main/java/com/swmansion/enriched/parser/HtmlToSpannedConverter.kt b/android/src/main/java/com/swmansion/enriched/parser/HtmlToSpannedConverter.kt index 5e10ca4b..315cf1d1 100644 --- a/android/src/main/java/com/swmansion/enriched/parser/HtmlToSpannedConverter.kt +++ b/android/src/main/java/com/swmansion/enriched/parser/HtmlToSpannedConverter.kt @@ -5,7 +5,6 @@ import android.text.Editable import android.text.Spannable import android.text.SpannableStringBuilder import android.text.Spanned -import android.util.Log import androidx.core.graphics.toColorInt import com.swmansion.enriched.EnrichedTextInputView import com.swmansion.enriched.constants.HtmlTags @@ -286,7 +285,7 @@ class HtmlToSpannedConverter( val href = ctx.attributes?.getValue("", "href") ?: return applyInline(ctx) { - EnrichedLinkSpan(href, mStyle) + EnrichedLinkSpan(href, mStyle, true) } return } From 997397369f2f150eee4dcbda21f2982a26e5828f Mon Sep 17 00:00:00 2001 From: IvanIhnatsiuk Date: Fri, 27 Feb 2026 12:53:05 +0100 Subject: [PATCH 2/2] fix: properly handle automatic links --- .../enriched/styles/ParametrizedStyles.kt | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt b/android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt index aa478ee1..3a7232fa 100644 --- a/android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt +++ b/android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt @@ -11,8 +11,10 @@ import com.swmansion.enriched.spans.EnrichedLinkSpan import com.swmansion.enriched.spans.EnrichedMentionSpan import com.swmansion.enriched.spans.EnrichedSpans import com.swmansion.enriched.spans.TextStyle +import com.swmansion.enriched.spans.interfaces.EnrichedSpan import com.swmansion.enriched.utils.getSafeSpanBoundaries import com.swmansion.enriched.utils.removeZWS +import kotlin.math.max class ParametrizedStyles( private val view: EnrichedTextInputView, @@ -172,6 +174,9 @@ class ParametrizedStyles( if (isSettingLinkSpan || !canLinkBeApplied()) return val spannable = view.text as? Spannable ?: return + // If user inserted a newline right after a link, don't touch spans. + if (isNewlineInsertedAtEndOfSpan(spannable, editStart, editEnd, EnrichedLinkSpan::class.java)) return + val affectedRange = getLinksAffectedRange(spannable, editStart, editEnd) val contextText = spannable @@ -227,12 +232,15 @@ class ParametrizedStyles( private fun afterTextChangedMentions( s: CharSequence, + startCursorPosition: Int, endCursorPosition: Int, ) { val mentionHandler = view.mentionHandler ?: return - val currentWord = getWordAtIndex(s, endCursorPosition) ?: return + val spannable = view.text as Spannable + if (isNewlineInsertedAtEndOfSpan(spannable, startCursorPosition, endCursorPosition, EnrichedMentionSpan::class.java)) return + val currentWord = getWordAtIndex(s, endCursorPosition) ?: return val indicatorsPattern = mentionIndicators.joinToString("|") { Regex.escape(it) } val mentionIndicatorRegex = Regex("^($indicatorsPattern)") val mentionRegex = Regex("^($indicatorsPattern)\\w*") @@ -282,13 +290,34 @@ class ParametrizedStyles( mentionHandler.onMention(indicator, text) } + private fun isNewlineInsertedAtEndOfSpan( + spannable: Spannable, + editStart: Int, + editEnd: Int, + clazz: Class, + ): Boolean { + // insertion of exactly one char + if (editEnd != editStart + 1) return false + if (editStart < 0 || editStart >= spannable.length) return false + + val insertedChar = spannable[editStart] + if (insertedChar != Strings.NEWLINE) return false + + // If there is any span span whose END is exactly at editStart, then newline was inserted + // at the boundary right after the link -> do nothing. + val lookupStart = max(0, editStart - 1) + val spans = spannable.getSpans(lookupStart, editStart, clazz) + + return spans.any { span -> spannable.getSpanEnd(span) == editStart } + } + fun afterTextChanged( s: Editable, startCursorPosition: Int, endCursorPosition: Int, ) { afterTextChangedLinks(startCursorPosition, endCursorPosition) - afterTextChangedMentions(s, startCursorPosition) + afterTextChangedMentions(s, startCursorPosition, endCursorPosition) } fun setImageSpan(