mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-10-31 16:22:49 +00:00
Do not linkify the server part of a fediverse handle as an email address
A fediverse handle such as `@name@server.tld` contains a substring that `Linkify.EMAIL_ADDRESSES` recognises as a valid address, so the timeline renders `name@server.tld` as a tappable `mailto:` link. Tapping it opens a mail composer addressed to an account that does not accept mail. Drop a newly added `mailto:` span when the character immediately before it is `@`. This mirrors what Android already does for web URLs, where `Linkify.sUrlMatchFilter` rejects a match preceded by `@` — which is why the handle currently produces exactly one stray mailto span and no partial link is left behind once it is removed. Author-supplied links are unaffected: spans that were already present before linkification are excluded from the check and restored as before. A genuine address written directly after an `@` — "mail me @support@element.io" — is no longer linkified, which is the same trade-off Android already makes for web URLs.
This commit is contained in:
+9
@@ -47,6 +47,11 @@ object LinkifyHelper {
|
||||
val start = spannable.getSpanStart(urlSpan)
|
||||
val end = spannable.getSpanEnd(urlSpan)
|
||||
|
||||
if (urlSpan !in oldURLSpans && spannable.isEmailInsideFediverseHandle(urlSpan, start)) {
|
||||
spannable.removeSpan(urlSpan)
|
||||
continue
|
||||
}
|
||||
|
||||
// Try to avoid including trailing punctuation in the link.
|
||||
// Since this might fail in some edge cases, we catch the exception and just use the original end index.
|
||||
val newEnd = runCatchingExceptions {
|
||||
@@ -82,6 +87,10 @@ object LinkifyHelper {
|
||||
return spannable
|
||||
}
|
||||
|
||||
private fun Spannable.isEmailInsideFediverseHandle(urlSpan: URLSpan, start: Int): Boolean {
|
||||
return urlSpan.url.startsWith("mailto:") && start > 0 && this[start - 1] == '@'
|
||||
}
|
||||
|
||||
private fun adjustLinkifiedUrlSpanEndIndex(spannable: Spannable, start: Int, end: Int): Int {
|
||||
var end = end
|
||||
|
||||
|
||||
+39
@@ -87,6 +87,45 @@ class LinkifierHelperTest : RobolectricTest() {
|
||||
assertThat(urlSpans.first().url).isEqualTo("tel:1234")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `linkification ignores fediverse handle`() {
|
||||
val text = "Follow me at @name@server.tld"
|
||||
val result = LinkifyHelper.linkify(text)
|
||||
val urlSpans = result.toSpannable().getSpans<URLSpan>()
|
||||
assertThat(urlSpans).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `linkification ignores fediverse handle at the start of the text`() {
|
||||
val text = "@name@server.tld posted this"
|
||||
val result = LinkifyHelper.linkify(text)
|
||||
val urlSpans = result.toSpannable().getSpans<URLSpan>()
|
||||
assertThat(urlSpans).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `linkification finds email next to a fediverse handle`() {
|
||||
val text = "@name@server.tld and john@doe.com"
|
||||
val result = LinkifyHelper.linkify(text)
|
||||
val urlSpans = result.toSpannable().getSpans<URLSpan>()
|
||||
assertThat(urlSpans.size).isEqualTo(1)
|
||||
assertThat(urlSpans.first().url).isEqualTo("mailto:john@doe.com")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `linkification keeps an existing mailto span preceded by an at sign`() {
|
||||
val text = buildSpannedString {
|
||||
append("Mail me @")
|
||||
inSpans(URLSpan("mailto:john@doe.com")) {
|
||||
append("john@doe.com")
|
||||
}
|
||||
}
|
||||
val result = LinkifyHelper.linkify(text)
|
||||
val urlSpans = result.toSpannable().getSpans<URLSpan>()
|
||||
assertThat(urlSpans.size).isEqualTo(1)
|
||||
assertThat(urlSpans.first().url).isEqualTo("mailto:john@doe.com")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `linkification handles trailing dot`() {
|
||||
val text = "A url https://matrix.org."
|
||||
|
||||
Reference in New Issue
Block a user