From fc5a41ec9893cf6851158546ac1845f1081fdd5f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 15 Dec 2023 15:05:35 +0100 Subject: [PATCH] Ensure link are clickable on simple body (#2038) --- .../TimelineItemContentMessageFactory.kt | 12 ++++++- .../TimelineItemContentMessageFactoryTest.kt | 34 ++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index 77e514fa60..7b690505ab 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -21,6 +21,7 @@ import android.text.style.URLSpan import android.text.util.Linkify import androidx.core.text.buildSpannedString import androidx.core.text.getSpans +import androidx.core.text.toSpannable import androidx.core.text.util.LinkifyCompat import io.element.android.features.location.api.Location import io.element.android.features.messages.api.timeline.HtmlConverterProvider @@ -178,7 +179,7 @@ class TimelineItemContentMessageFactory @Inject constructor( TimelineItemTextContent( body = messageType.body, htmlDocument = messageType.formatted?.toHtmlDocument(), - formattedBody = parseHtml(messageType.formatted), + formattedBody = parseHtml(messageType.formatted) ?: messageType.body.withLinks(), isEdited = content.isEdited, ) } @@ -237,3 +238,12 @@ class TimelineItemContentMessageFactory @Inject constructor( return this } } + +private fun String.withLinks(): CharSequence? { + val spannable = toSpannable() + LinkifyCompat.addLinks(spannable, Linkify.WEB_URLS or Linkify.PHONE_NUMBERS) + if (spannable.getSpans().isEmpty()) { + return null + } + return spannable +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt index 5945ccbee2..40c0c7b8ca 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactoryTest.kt @@ -17,8 +17,9 @@ package io.element.android.features.messages.impl.timeline.factories.event import android.text.SpannableString -import android.text.SpannableStringBuilder +import android.text.Spanned import android.text.style.URLSpan +import androidx.core.text.buildSpannedString import androidx.core.text.inSpans import com.google.common.truth.Truth.assertThat import io.element.android.features.location.api.Location @@ -144,6 +145,37 @@ class TimelineItemContentMessageFactoryTest { assertThat(result).isEqualTo(expected) } + @Test + fun `test create TextMessageType with simple link`() = runTest { + val sut = createTimelineItemContentMessageFactory() + val result = sut.create( + content = createMessageContent(type = TextMessageType("https://www.example.org", null)), + senderDisplayName = "Bob", + eventId = AN_EVENT_ID, + ) as TimelineItemTextContent + val expected = TimelineItemTextContent( + body = "https://www.example.org", + htmlDocument = null, + plainText = "https://www.example.org", + isEdited = false, + formattedBody = buildSpannedString { + inSpans(URLSpan("https://www.example.org")) { + append("https://www.example.org") + } + } + ) + assertThat(result.body).isEqualTo(expected.body) + assertThat(result.htmlDocument).isEqualTo(expected.htmlDocument) + assertThat(result.plainText).isEqualTo(expected.plainText) + assertThat(result.isEdited).isEqualTo(expected.isEdited) + assertThat(result.formattedBody).isInstanceOf(Spanned::class.java) + val spanned = result.formattedBody as Spanned + assertThat(spanned.toString()).isEqualTo("https://www.example.org") + val urlSpans = spanned.getSpans(0, spanned.length, URLSpan::class.java) + assertThat(urlSpans).hasLength(1) + assertThat(urlSpans[0].url).isEqualTo("https://www.example.org") + } + @Test fun `test create TextMessageType with HTML formatted body`() = runTest { val expected = SpannableStringBuilder().apply {