When linkifying, adjust the URLSpan's url too (#6188)

This commit is contained in:
Jorge Martin Espinosa 2026-02-12 13:12:57 +01:00 committed by GitHub
parent 35a5ae27e2
commit edd747327b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 29 additions and 5 deletions

View file

@ -16,8 +16,6 @@ import androidx.core.text.toSpannable
import androidx.core.text.util.LinkifyCompat
import io.element.android.libraries.core.extensions.runCatchingExceptions
import timber.log.Timber
import kotlin.collections.component1
import kotlin.collections.component2
/**
* Helper class to linkify text while preserving existing URL spans.
@ -59,7 +57,8 @@ object LinkifyHelper {
// Adapt the url in the URL span to the new end index too if needed
if (end != newEnd) {
val url = spannable.subSequence(start, newEnd).toString()
val diff = end - newEnd
val url = urlSpan.url.substring(0, urlSpan.url.length - diff)
spannable.removeSpan(urlSpan)
spannable.setSpan(URLSpan(url), start, newEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
} else {
@ -87,12 +86,12 @@ object LinkifyHelper {
var end = end
// Trailing punctuation found, adjust the end index
while (spannable[end - 1] in sequenceOf('.', ',', ';', ':', '!', '?', '…') && end > start) {
while (end > start && spannable[end - 1] in sequenceOf('.', ',', ';', ':', '!', '?', '…')) {
end--
}
// If the last character is a closing parenthesis, check if it's part of a pair
if (spannable[end - 1] == ')' && end > start) {
if (end > start && spannable[end - 1] == ')') {
val linkifiedTextLastPath = spannable.substring(start, end).substringAfterLast('/')
val closingParenthesisCount = linkifiedTextLastPath.count { it == ')' }
val openingParenthesisCount = linkifiedTextLastPath.count { it == '(' }

View file

@ -10,7 +10,9 @@ package io.element.android.libraries.androidutils.text
import android.telephony.TelephonyManager
import android.text.style.URLSpan
import androidx.core.text.buildSpannedString
import androidx.core.text.getSpans
import androidx.core.text.inSpans
import androidx.core.text.toSpannable
import com.google.common.truth.Truth.assertThat
import io.element.android.tests.testutils.WarmUpRule
@ -140,4 +142,27 @@ class LinkifierHelperTest {
assertThat(urlSpans.size).isEqualTo(1)
assertThat(urlSpans.first().url).isEqualTo("https://github.com/element-hq/element-android/READ(ME)")
}
@Test
fun `linkification handles trailing question marks`() {
val text = "A url: https://github.com/element-hq/element-android?"
val result = LinkifyHelper.linkify(text)
val urlSpans = result.toSpannable().getSpans<URLSpan>()
assertThat(urlSpans.size).isEqualTo(1)
assertThat(urlSpans.first().url).isEqualTo("https://github.com/element-hq/element-android")
}
@Test
fun `linkification doesn't modify existing URLSpan`() {
val text = buildSpannedString {
append("A url: ")
inSpans(URLSpan("https://github.com/element-hq/element-android?")) {
append("here")
}
}
val result = LinkifyHelper.linkify(text)
val urlSpans = result.toSpannable().getSpans<URLSpan>()
assertThat(urlSpans.size).isEqualTo(1)
assertThat(urlSpans.first().url).isEqualTo("https://github.com/element-hq/element-android?")
}
}