diff --git a/changelog.d/2105.bugfix b/changelog.d/2105.bugfix new file mode 100644 index 0000000000..337b192aad --- /dev/null +++ b/changelog.d/2105.bugfix @@ -0,0 +1 @@ +Fix crashes in room list when the last message for a room was an extremely long one (several thousands of characters) with no line breaks. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt index 589ecaede0..77842fa7cd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt @@ -42,6 +42,12 @@ import javax.inject.Inject class MessageSummaryFormatterImpl @Inject constructor( @ApplicationContext private val context: Context, ) : MessageSummaryFormatter { + + companion object { + // Max characters to display in the summary message. This works around https://github.com/element-hq/element-x-android/issues/2105 + private const val MAX_SAFE_LENGTH = 500 + } + override fun format(event: TimelineItem.Event): String { return when (event.content) { is TimelineItemTextBasedContent -> event.content.plainText @@ -58,6 +64,6 @@ class MessageSummaryFormatterImpl @Inject constructor( is TimelineItemVideoContent -> context.getString(CommonStrings.common_video) is TimelineItemFileContent -> context.getString(CommonStrings.common_file) is TimelineItemAudioContent -> context.getString(CommonStrings.common_audio) - } + }.take(MAX_SAFE_LENGTH) } } diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt index e79b0ed199..6119fa00d9 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt @@ -63,47 +63,52 @@ class DefaultRoomLastMessageFormatter @Inject constructor( private val stateContentFormatter: StateContentFormatter, ) : RoomLastMessageFormatter { + companion object { + // Max characters to display in the last message. This works around https://github.com/element-hq/element-x-android/issues/2105 + private const val MAX_SAFE_LENGTH = 500 + } + override fun format(event: EventTimelineItem, isDmRoom: Boolean): CharSequence? { val isOutgoing = event.isOwn val senderDisplayName = (event.senderProfile as? ProfileTimelineDetails.Ready)?.displayName ?: event.sender.value return when (val content = event.content) { - is MessageContent -> processMessageContents(content, senderDisplayName, isDmRoom) - RedactedContent -> { - val message = sp.getString(CommonStrings.common_message_removed) - if (!isDmRoom) { - prefix(message, senderDisplayName) - } else { - message + is MessageContent -> processMessageContents(content, senderDisplayName, isDmRoom) + RedactedContent -> { + val message = sp.getString(CommonStrings.common_message_removed) + if (!isDmRoom) { + prefix(message, senderDisplayName) + } else { + message + } } - } - is StickerContent -> { - content.body - } - is UnableToDecryptContent -> { - val message = sp.getString(CommonStrings.common_waiting_for_decryption_key) - if (!isDmRoom) { - prefix(message, senderDisplayName) - } else { - message + is StickerContent -> { + content.body } - } - is RoomMembershipContent -> { - roomMembershipContentFormatter.format(content, senderDisplayName, isOutgoing) - } - is ProfileChangeContent -> { - profileChangeContentFormatter.format(content, senderDisplayName, isOutgoing) - } - is StateContent -> { - stateContentFormatter.format(content, senderDisplayName, isOutgoing, RenderingMode.RoomList) - } - is PollContent -> { - val message = sp.getString(CommonStrings.common_poll_summary, content.question) - prefixIfNeeded(message, senderDisplayName, isDmRoom) - } - is FailedToParseMessageLikeContent, is FailedToParseStateContent, is UnknownContent -> { - prefixIfNeeded(sp.getString(CommonStrings.common_unsupported_event), senderDisplayName, isDmRoom) - } - } + is UnableToDecryptContent -> { + val message = sp.getString(CommonStrings.common_waiting_for_decryption_key) + if (!isDmRoom) { + prefix(message, senderDisplayName) + } else { + message + } + } + is RoomMembershipContent -> { + roomMembershipContentFormatter.format(content, senderDisplayName, isOutgoing) + } + is ProfileChangeContent -> { + profileChangeContentFormatter.format(content, senderDisplayName, isOutgoing) + } + is StateContent -> { + stateContentFormatter.format(content, senderDisplayName, isOutgoing, RenderingMode.RoomList) + } + is PollContent -> { + val message = sp.getString(CommonStrings.common_poll_summary, content.question) + prefixIfNeeded(message, senderDisplayName, isDmRoom) + } + is FailedToParseMessageLikeContent, is FailedToParseStateContent, is UnknownContent -> { + prefixIfNeeded(sp.getString(CommonStrings.common_unsupported_event), senderDisplayName, isDmRoom) + } + }?.take(MAX_SAFE_LENGTH) } private fun processMessageContents(messageContent: MessageContent, senderDisplayName: String, isDmRoom: Boolean): CharSequence? {