From a53e40e1ee7c5ba779dd0f97f69531ac0c989bef Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 18 Jun 2019 12:25:50 +0200 Subject: [PATCH] Create MessageInformationDataFactory for reusability --- .../riotredesign/features/home/HomeModule.kt | 14 +++- .../timeline/factory/EncryptedItemFactory.kt | 53 +++++++------ .../timeline/factory/MessageItemFactory.kt | 51 ++---------- .../timeline/factory/TimelineItemFactory.kt | 2 +- .../util/MessageInformationDataFactory.kt | 77 +++++++++++++++++++ 5 files changed, 121 insertions(+), 76 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/util/MessageInformationDataFactory.kt diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt index 22d52787..7f85269a 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt @@ -29,6 +29,7 @@ import im.vector.riotredesign.features.home.room.detail.timeline.factory.* import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider +import im.vector.riotredesign.features.home.room.detail.timeline.util.MessageInformationDataFactory import im.vector.riotredesign.features.home.room.list.RoomSummaryController import im.vector.riotredesign.features.html.EventHtmlRenderer import org.koin.core.parameter.parametersOf @@ -69,15 +70,22 @@ class HomeModule { val eventHtmlRenderer = EventHtmlRenderer(GlideApp.with(fragment), fragment.requireContext(), get()) val noticeEventFormatter = get(parameters = { parametersOf(fragment) }) val timelineMediaSizeProvider = TimelineMediaSizeProvider() - val messageItemFactory = MessageItemFactory(colorProvider, timelineMediaSizeProvider, - timelineDateFormatter, eventHtmlRenderer, get(), get()) + val messageInformationDataFactory = MessageInformationDataFactory(timelineDateFormatter, colorProvider) + val messageItemFactory = MessageItemFactory(colorProvider, + timelineMediaSizeProvider, + eventHtmlRenderer, + get(), + messageInformationDataFactory, + get()) + + val encryptedItemFactory = EncryptedItemFactory(messageInformationDataFactory, colorProvider, get()) val timelineItemFactory = TimelineItemFactory( messageItemFactory = messageItemFactory, noticeItemFactory = NoticeItemFactory(noticeEventFormatter), defaultItemFactory = DefaultItemFactory(), encryptionItemFactory = EncryptionItemFactory(get()), - encryptedItemFactory = EncryptedItemFactory(get()) + encryptedItemFactory = encryptedItemFactory ) TimelineEventController(timelineDateFormatter, timelineItemFactory, timelineMediaSizeProvider) } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt index 4e892183..92294f19 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt @@ -16,28 +16,28 @@ package im.vector.riotredesign.features.home.room.detail.timeline.factory -import android.graphics.Typeface -import android.text.Spannable -import android.text.SpannableString -import android.text.style.StyleSpan import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.riotredesign.R import im.vector.riotredesign.core.epoxy.VectorEpoxyModel +import im.vector.riotredesign.core.resources.ColorProvider import im.vector.riotredesign.core.resources.StringProvider -import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderAvatar -import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderName -import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData -import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_ +import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageTextItem_ +import im.vector.riotredesign.features.home.room.detail.timeline.util.MessageInformationDataFactory +import me.gujun.android.span.span -// This class handles timeline event who haven't been successfully decrypted -class EncryptedItemFactory(private val stringProvider: StringProvider) { +// This class handles timeline events who haven't been successfully decrypted +class EncryptedItemFactory(private val messageInformationDataFactory: MessageInformationDataFactory, + private val colorProvider: ColorProvider, + private val stringProvider: StringProvider) { + + fun create(event: TimelineEvent, nextEvent: TimelineEvent?): VectorEpoxyModel<*>? { + event.root.eventId ?: return null - fun create(timelineEvent: TimelineEvent): VectorEpoxyModel<*>? { return when { - EventType.ENCRYPTED == timelineEvent.root.getClearType() -> { - val cryptoError = timelineEvent.root.mCryptoError + EventType.ENCRYPTED == event.root.getClearType() -> { + val cryptoError = event.root.mCryptoError val errorDescription = if (cryptoError?.code == MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) { stringProvider.getString(R.string.notice_crypto_error_unkwown_inbound_session_id) @@ -46,22 +46,21 @@ class EncryptedItemFactory(private val stringProvider: StringProvider) { } val message = stringProvider.getString(R.string.notice_crypto_unable_to_decrypt, errorDescription) - val spannableStr = SpannableString(message) - spannableStr.setSpan(StyleSpan(Typeface.ITALIC), 0, message.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + val spannableStr = span(message) { + textStyle = "italic" + textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary) + } + // TODO This is not correct format for error, change it - val informationData = MessageInformationData( - eventId = timelineEvent.root.eventId ?: "?", - senderId = timelineEvent.root.sender ?: "", - sendState = timelineEvent.sendState, - avatarUrl = timelineEvent.senderAvatar(), - memberName = timelineEvent.senderName(), - showInformation = false - ) - return NoticeItem_() - .noticeText(spannableStr) + val informationData = messageInformationDataFactory.create(event, nextEvent) + + return MessageTextItem_() + .message(spannableStr) .informationData(informationData) + + // TODO Handle click on this event } - else -> null + else -> null } } -} \ No newline at end of file +} diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/MessageItemFactory.kt index a4b31251..cc733382 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -25,7 +25,6 @@ import android.text.style.RelativeSizeSpan import android.view.View import im.vector.matrix.android.api.permalinks.MatrixLinkify import im.vector.matrix.android.api.permalinks.MatrixPermalinkSpan -import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.RelationType import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary @@ -35,16 +34,14 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.riotredesign.EmojiCompatFontProvider import im.vector.riotredesign.R import im.vector.riotredesign.core.epoxy.VectorEpoxyModel -import im.vector.riotredesign.core.extensions.localDateTime import im.vector.riotredesign.core.linkify.VectorLinkify import im.vector.riotredesign.core.resources.ColorProvider import im.vector.riotredesign.core.resources.StringProvider import im.vector.riotredesign.core.utils.DebouncedClickListener -import im.vector.riotredesign.features.home.getColorFromUserId import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController -import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider import im.vector.riotredesign.features.home.room.detail.timeline.item.* +import im.vector.riotredesign.features.home.room.detail.timeline.util.MessageInformationDataFactory import im.vector.riotredesign.features.html.EventHtmlRenderer import im.vector.riotredesign.features.media.ImageContentRenderer import im.vector.riotredesign.features.media.VideoContentRenderer @@ -52,50 +49,18 @@ import me.gujun.android.span.span class MessageItemFactory(private val colorProvider: ColorProvider, private val timelineMediaSizeProvider: TimelineMediaSizeProvider, - private val timelineDateFormatter: TimelineDateFormatter, private val htmlRenderer: EventHtmlRenderer, private val stringProvider: StringProvider, + private val messageInformationDataFactory: MessageInformationDataFactory, private val emojiCompatFontProvider: EmojiCompatFontProvider) { fun create(event: TimelineEvent, nextEvent: TimelineEvent?, callback: TimelineEventController.Callback? ): VectorEpoxyModel<*>? { + event.root.eventId ?: return null - val eventId = event.root.eventId ?: return null - - val date = event.root.localDateTime() - val nextDate = nextEvent?.root?.localDateTime() - val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate() - val isNextMessageReceivedMoreThanOneHourAgo = nextDate?.isBefore(date.minusMinutes(60)) - ?: false - - val showInformation = addDaySeparator - || event.senderAvatar != nextEvent?.senderAvatar - || event.senderName != nextEvent?.senderName - || nextEvent?.root?.getClearType() != EventType.MESSAGE - || isNextMessageReceivedMoreThanOneHourAgo - - val time = timelineDateFormatter.formatMessageHour(date) - val avatarUrl = event.senderAvatar - val memberName = event.senderName ?: event.root.sender ?: "" - val formattedMemberName = span(memberName) { - textColor = colorProvider.getColor(getColorFromUserId(event.root.sender - ?: "")) - } - val hasBeenEdited = event.annotations?.editSummary != null - val informationData = MessageInformationData(eventId = eventId, - senderId = event.root.sender ?: "", - sendState = event.sendState, - time = time, - avatarUrl = avatarUrl, - memberName = formattedMemberName, - showInformation = showInformation, - orderedReactionList = event.annotations?.reactionsSummary?.map { - ReactionInfoData(it.key, it.count, it.addedByMe, it.localEchoEvents.isEmpty()) - }, - hasBeenEdited = hasBeenEdited - ) + val informationData = messageInformationDataFactory.create(event, nextEvent) if (event.root.unsignedData?.redactedEvent != null) { //message is redacted @@ -117,13 +82,11 @@ class MessageItemFactory(private val colorProvider: ColorProvider, return when (messageContent) { is MessageEmoteContent -> buildEmoteMessageItem(messageContent, informationData, - hasBeenEdited, event.annotations?.editSummary, callback) is MessageTextContent -> buildTextMessageItem(event.sendState, messageContent, informationData, - hasBeenEdited, event.annotations?.editSummary, callback ) @@ -266,7 +229,6 @@ class MessageItemFactory(private val colorProvider: ColorProvider, private fun buildTextMessageItem(sendState: SendState, messageContent: MessageTextContent, informationData: MessageInformationData, - hasBeenEdited: Boolean, editSummary: EditAggregatedSummary?, callback: TimelineEventController.Callback?): MessageTextItem? { @@ -278,7 +240,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider, return MessageTextItem_() .apply { - if (hasBeenEdited) { + if (informationData.hasBeenEdited) { val spannable = annotateWithEdited(linkifiedBody, callback, informationData, editSummary) message(spannable) } else { @@ -368,7 +330,6 @@ class MessageItemFactory(private val colorProvider: ColorProvider, private fun buildEmoteMessageItem(messageContent: MessageEmoteContent, informationData: MessageInformationData, - hasBeenEdited: Boolean, editSummary: EditAggregatedSummary?, callback: TimelineEventController.Callback?): MessageTextItem? { @@ -378,7 +339,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider, } return MessageTextItem_() .apply { - if (hasBeenEdited) { + if (informationData.hasBeenEdited) { val spannable = annotateWithEdited(message, callback, informationData, editSummary) message(spannable) } else { diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/TimelineItemFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/TimelineItemFactory.kt index c4609f88..a39998f3 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/TimelineItemFactory.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/TimelineItemFactory.kt @@ -53,7 +53,7 @@ class TimelineItemFactory(private val messageItemFactory: MessageItemFactory, // Crypto EventType.ENCRYPTION -> encryptionItemFactory.create(event) - EventType.ENCRYPTED -> encryptedItemFactory.create(event) + EventType.ENCRYPTED -> encryptedItemFactory.create(event, nextEvent) // Unhandled event types (yet) EventType.STATE_ROOM_THIRD_PARTY_INVITE, diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/util/MessageInformationDataFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/util/MessageInformationDataFactory.kt new file mode 100644 index 00000000..0ee5cd2f --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/util/MessageInformationDataFactory.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotredesign.features.home.room.detail.timeline.util + +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.room.timeline.TimelineEvent +import im.vector.riotredesign.core.extensions.localDateTime +import im.vector.riotredesign.core.resources.ColorProvider +import im.vector.riotredesign.features.home.getColorFromUserId +import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter +import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderAvatar +import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData +import im.vector.riotredesign.features.home.room.detail.timeline.item.ReactionInfoData +import me.gujun.android.span.span + +/** + * This class compute if data of an event (such has avatar, display name, ...) should be displayed, depending on the previous event in the timeline + */ +class MessageInformationDataFactory(private val timelineDateFormatter: TimelineDateFormatter, + private val colorProvider: ColorProvider) { + + fun create(event: TimelineEvent, nextEvent: TimelineEvent?): MessageInformationData { + // Non nullability has been tested before + val eventId = event.root.eventId!! + + val date = event.root.localDateTime() + + val nextDate = nextEvent?.root?.localDateTime() + val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate() + val isNextMessageReceivedMoreThanOneHourAgo = nextDate?.isBefore(date.minusMinutes(60)) + ?: false + val showInformation = + addDaySeparator + || event.senderAvatar != nextEvent?.senderAvatar + || event.senderName != nextEvent?.senderName + || (nextEvent?.root?.getClearType() != EventType.MESSAGE && nextEvent?.root?.getClearType() != EventType.ENCRYPTED) + || isNextMessageReceivedMoreThanOneHourAgo + + val time = timelineDateFormatter.formatMessageHour(date) + val avatarUrl = event.senderAvatar() + val memberName = event.senderName ?: event.root.sender ?: "" + val formattedMemberName = span(memberName) { + textColor = colorProvider.getColor(getColorFromUserId(event.root.sender + ?: "")) + } + + val hasBeenEdited = event.annotations?.editSummary != null + + return MessageInformationData( + eventId = eventId, + senderId = event.root.sender ?: "", + sendState = event.sendState, + time = time, + avatarUrl = avatarUrl, + memberName = formattedMemberName, + showInformation = showInformation, + orderedReactionList = event.annotations?.reactionsSummary?.map { + ReactionInfoData(it.key, it.count, it.addedByMe, it.localEchoEvents.isEmpty()) + }, + hasBeenEdited = hasBeenEdited + ) + } +} \ No newline at end of file