Skip to content

Commit 9d32975

Browse files
authored
fix: android links detection after pasting text (#376)
# Summary Fixes issue that links were not properly detected on Android when pasting raw text. ## Test Plan 1. Enter some text 2. Paste text containing url that should be tected 3. Ensure that link is detected, styled and included in HTML output ## Screenshots / Videos https://github.com/user-attachments/assets/c366b473-03ad-4030-9572-6fbc0ff4df78 ## Compatibility | OS | Implemented | | ------- | :---------: | | iOS | ❌ | | Android | ✅ |
1 parent d908a24 commit 9d32975

2 files changed

Lines changed: 43 additions & 52 deletions

File tree

android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,13 @@ class EnrichedTextInputView : AppCompatEditText {
270270

271271
// Currently, we do not support pasting images
272272
if (item?.text == null) return
273+
val lengthBefore = currentText.length
273274
val finalText = currentText.mergeSpannables(start, end, item.text.toString())
274275
setValue(finalText)
275-
parametrizedStyles?.detectAllLinks()
276+
277+
// Detect links in the newly pasted range
278+
val finalEndIndex = start + finalText.length - lengthBefore
279+
parametrizedStyles?.detectLinksInRange(finalText, start, finalEndIndex)
276280
}
277281

278282
fun requestFocusProgrammatically() {

android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt

Lines changed: 38 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -77,23 +77,49 @@ class ParametrizedStyles(
7777
afterTextChangedMentions(s, startCursorPosition)
7878
}
7979

80-
fun detectAllLinks() {
80+
fun detectLinksInRange(
81+
spannable: Spannable,
82+
start: Int,
83+
end: Int,
84+
) {
8185
val regex = view.linkRegex ?: return
82-
val spannable = view.text as Spannable
83-
val urlPattern = regex.matcher(spannable)
86+
val contextText = spannable.subSequence(start, end).toString()
8487

85-
val spans = spannable.getSpans(0, spannable.length, EnrichedLinkSpan::class.java)
88+
val spans = spannable.getSpans(start, end, EnrichedLinkSpan::class.java)
8689
for (span in spans) {
8790
spannable.removeSpan(span)
8891
}
8992

90-
while (urlPattern.find()) {
91-
val word = urlPattern.group()
92-
val start = urlPattern.start()
93-
val end = urlPattern.end()
94-
val span = EnrichedLinkSpan(word, view.htmlStyle)
95-
val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(start, end)
96-
spannable.setSpan(span, safeStart, safeEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
93+
val wordsRegex = Regex("\\S+")
94+
for (wordMatch in wordsRegex.findAll(contextText)) {
95+
var word = wordMatch.value
96+
var wordStart = wordMatch.range.first
97+
98+
// Do not include zero-width space in link detection
99+
if (word.startsWith("\u200B")) {
100+
word = word.substring(1)
101+
wordStart += 1
102+
}
103+
104+
// Loop over words and detect links
105+
val matcher = regex.matcher(word)
106+
while (matcher.find()) {
107+
val linkStart = matcher.start()
108+
val linkEnd = matcher.end()
109+
110+
val spanStart = start + wordStart + linkStart
111+
val spanEnd = start + wordStart + linkEnd
112+
113+
val span = EnrichedLinkSpan(matcher.group(), view.htmlStyle)
114+
val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(spanStart, spanEnd)
115+
116+
spannable.setSpan(
117+
span,
118+
safeStart,
119+
safeEnd,
120+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
121+
)
122+
}
97123
}
98124
}
99125

@@ -162,51 +188,12 @@ class ParametrizedStyles(
162188
editStart: Int,
163189
editEnd: Int,
164190
) {
165-
val regex = view.linkRegex ?: return
166-
167191
// Do not detect link if it's applied manually
168192
if (isSettingLinkSpan || !canLinkBeApplied()) return
169193

170194
val spannable = view.text as? Spannable ?: return
171195
val affectedRange = getLinksAffectedRange(spannable, editStart, editEnd)
172-
val contextText = spannable.subSequence(affectedRange.first, affectedRange.last).toString()
173-
174-
val spans = spannable.getSpans(affectedRange.first, affectedRange.last, EnrichedLinkSpan::class.java)
175-
for (span in spans) {
176-
spannable.removeSpan(span)
177-
}
178-
179-
val wordsRegex = Regex("\\S+")
180-
for (wordMatch in wordsRegex.findAll(contextText)) {
181-
var word = wordMatch.value
182-
var wordStart = wordMatch.range.first
183-
184-
// Do not include zero-width space in link detection
185-
if (word.startsWith("\u200B")) {
186-
word = word.substring(1)
187-
wordStart += 1
188-
}
189-
190-
// Loop over words and detect links
191-
val matcher = regex.matcher(word)
192-
while (matcher.find()) {
193-
val linkStart = matcher.start()
194-
val linkEnd = matcher.end()
195-
196-
val spanStart = affectedRange.first + wordStart + linkStart
197-
val spanEnd = affectedRange.first + wordStart + linkEnd
198-
199-
val span = EnrichedLinkSpan(matcher.group(), view.htmlStyle)
200-
val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(spanStart, spanEnd)
201-
202-
spannable.setSpan(
203-
span,
204-
safeStart,
205-
safeEnd,
206-
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
207-
)
208-
}
209-
}
196+
detectLinksInRange(spannable, affectedRange.first, affectedRange.last)
210197
}
211198

212199
private fun afterTextChangedMentions(

0 commit comments

Comments
 (0)