From 625500212d3c94bed6255d6c95878bf0963559c7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 18 Jun 2019 15:44:11 +0200 Subject: [PATCH] Manage display name disambiguation (Fixes #172) --- .../android/api/session/events/model/Event.kt | 1 + .../api/session/room/timeline/TimelineEvent.kt | 13 +++++++++++++ .../internal/session/room/membership/RoomMembers.kt | 2 +- .../session/room/timeline/TimelineEventFactory.kt | 10 ++++++++-- .../features/home/room/detail/RoomDetailFragment.kt | 2 +- .../home/room/detail/RoomDetailViewModel.kt | 2 +- .../detail/timeline/action/ViewReactionViewModel.kt | 2 +- .../detail/timeline/format/NoticeEventFormatter.kt | 8 ++++---- .../timeline/helper/TimelineDisplayableEvents.kt | 11 +++++------ .../room/detail/timeline/item/AbsMessageItem.kt | 1 - .../timeline/util/MessageInformationDataFactory.kt | 9 ++++----- 11 files changed, 39 insertions(+), 22 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt index fe68e9a275..e7f1364cd8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt @@ -71,6 +71,7 @@ data class Event( @Json(name = "content") val content: Content? = null, @Json(name = "prev_content") val prevContent: Content? = null, @Json(name = "origin_server_ts") val originServerTs: Long? = null, + // MatrixId of the sender (TODO rename) @Json(name = "sender") val sender: String? = null, @Json(name = "state_key") val stateKey: String? = null, @Json(name = "room_id") val roomId: String? = null, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineEvent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineEvent.kt index 76217027c9..f231f28f95 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineEvent.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineEvent.kt @@ -31,6 +31,7 @@ data class TimelineEvent( val localId: String, val displayIndex: Int, val senderName: String?, + val isUniqueDisplayName: Boolean, val senderAvatar: String?, val sendState: SendState, val annotations: EventAnnotationsSummary? = null @@ -53,6 +54,18 @@ data class TimelineEvent( } } + fun getDisambiguatedDisplayName(): String { + return if (isUniqueDisplayName) { + senderName ?: root.sender + } else { + senderName?.let { + it + " (" + root.sender + ")" + } + ?: root.sender + } + ?: "" + } + /** * Get the metadata associated with a key. * @param key the key to get the metadata diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMembers.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMembers.kt index ff8b8ce416..41d5879ca3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMembers.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/membership/RoomMembers.kt @@ -54,7 +54,7 @@ internal class RoomMembers(private val realm: Realm, } return EventEntity .where(realm, roomId, EventType.STATE_ROOM_MEMBER) - .contains(EventEntityFields.CONTENT, displayName) + .contains(EventEntityFields.CONTENT, "\"displayname\":\"$displayName\"") .distinct(EventEntityFields.STATE_KEY) .findAll() .size == 1 diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt index 7c4cfff940..fe3bdcd4e3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineEventFactory.kt @@ -26,6 +26,7 @@ import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.session.room.EventRelationExtractor +import im.vector.matrix.android.internal.session.room.membership.RoomMembers import im.vector.matrix.android.internal.session.room.membership.SenderRoomMemberExtractor import io.realm.Realm import timber.log.Timber @@ -49,7 +50,11 @@ internal class TimelineEventFactory( val cacheKey = sender + eventEntity.localId val senderData = senderCache.getOrPut(cacheKey) { val senderRoomMember = roomMemberExtractor.extractFrom(eventEntity, realm) - SenderData(senderRoomMember?.displayName, senderRoomMember?.avatarUrl) + val isUniqueDisplayName = RoomMembers(realm, eventEntity.roomId).isUniqueDisplayName(senderRoomMember?.displayName) + + SenderData(senderRoomMember?.displayName, + isUniqueDisplayName, + senderRoomMember?.avatarUrl) } val event = eventEntity.asDomain() if (event.getClearType() == EventType.ENCRYPTED) { @@ -62,6 +67,7 @@ internal class TimelineEventFactory( eventEntity.localId, eventEntity.displayIndex, senderData.senderName, + senderData.isUniqueDisplayName, senderData.senderAvatar, eventEntity.sendState, relations @@ -96,7 +102,7 @@ internal class TimelineEventFactory( private data class SenderData( val senderName: String?, + val isUniqueDisplayName: Boolean, val senderAvatar: String? ) - } \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt index 3bf3453411..c6c0021789 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt @@ -222,7 +222,7 @@ class RoomDetailFragment : } //switch to expanded bar composerLayout.composerRelatedMessageTitle.apply { - text = event.senderName + text = event.getDisambiguatedDisplayName() setTextColor(ContextCompat.getColor(requireContext(), getColorFromUserId(event.root.sender))) } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt index 476ff99b86..c687e9ff89 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt @@ -282,7 +282,7 @@ class RoomDetailViewModel(initialState: RoomDetailViewState, val dateFormat = SimpleDateFormat("EEE, d MMM yyyy HH:mm", Locale.getDefault()) _nonBlockingPopAlert.postValue(LiveEvent( Pair(R.string.last_edited_info_message, listOf( - lastReplace.senderName ?: "?", + lastReplace.getDisambiguatedDisplayName(), dateFormat.format(Date(lastReplace.root.originServerTs ?: 0))) )) ) diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionViewModel.kt index 8854886512..d550aa0c5a 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionViewModel.kt @@ -50,7 +50,7 @@ class ViewReactionViewModel(private val session: Session, sum.sourceEvents.mapNotNull { room.getTimeLineEvent(it) }.forEach { val localDate = it.root.localDateTime() results.add(ReactionInfo(it.root.eventId!!, sum.key, it.root.sender - ?: "", it.senderName, timelineDateFormatter.formatMessageHour(localDate))) + ?: "", it.getDisambiguatedDisplayName(), timelineDateFormatter.formatMessageHour(localDate))) } } setState { diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/format/NoticeEventFormatter.kt index 70d0f68e44..27e1c8c5f8 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/format/NoticeEventFormatter.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/format/NoticeEventFormatter.kt @@ -32,13 +32,13 @@ class NoticeEventFormatter(private val stringProvider: StringProvider) { fun format(timelineEvent: TimelineEvent): CharSequence? { return when (val type = timelineEvent.root.getClearType()) { - EventType.STATE_ROOM_NAME -> formatRoomNameEvent(timelineEvent.root, timelineEvent.senderName) - EventType.STATE_ROOM_TOPIC -> formatRoomTopicEvent(timelineEvent.root, timelineEvent.senderName) + EventType.STATE_ROOM_NAME -> formatRoomNameEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName()) + EventType.STATE_ROOM_TOPIC -> formatRoomTopicEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName()) EventType.STATE_ROOM_MEMBER -> formatRoomMemberEvent(timelineEvent.root, timelineEvent.senderName()) - EventType.STATE_HISTORY_VISIBILITY -> formatRoomHistoryVisibilityEvent(timelineEvent.root, timelineEvent.senderName) + EventType.STATE_HISTORY_VISIBILITY -> formatRoomHistoryVisibilityEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName()) EventType.CALL_INVITE, EventType.CALL_HANGUP, - EventType.CALL_ANSWER -> formatCallEvent(timelineEvent.root, timelineEvent.senderName) + EventType.CALL_ANSWER -> formatCallEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName()) else -> { Timber.v("Type $type not handled by this formatter") null diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt index 858a8abc1a..1523ee6a36 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt @@ -85,12 +85,11 @@ fun TimelineEvent.senderAvatar(): String? { fun TimelineEvent.senderName(): String? { // We might have no senderName when user leave, so we try to get it from prevContent - return senderName - ?: if (root.type == EventType.STATE_ROOM_MEMBER) { - root.prevContent.toModel()?.displayName - } else { - null - } + return when { + senderName != null -> getDisambiguatedDisplayName() + root.type == EventType.STATE_ROOM_MEMBER -> root.prevContent.toModel()?.displayName + else -> null + } } fun TimelineEvent.canBeMerged(): Boolean { diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/AbsMessageItem.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/AbsMessageItem.kt index 8a32396801..26b3090bbc 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/AbsMessageItem.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/AbsMessageItem.kt @@ -83,7 +83,6 @@ abstract class AbsMessageItem : BaseEventItem() { override fun bind(holder: H) { super.bind(holder) if (informationData.showInformation) { - holder.avatarImageView.layoutParams = holder.avatarImageView.layoutParams?.apply { val size = dpToPx(avatarStyle.avatarSizeDP, holder.view.context) height = size 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 index 0ee5cd2ffb..bdc28f1f65 100644 --- 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 @@ -22,7 +22,6 @@ 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 @@ -38,21 +37,21 @@ class MessageInformationDataFactory(private val timelineDateFormatter: TimelineD 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 + || event.getDisambiguatedDisplayName() != nextEvent?.getDisambiguatedDisplayName() || (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 avatarUrl = event.senderAvatar + val memberName = event.getDisambiguatedDisplayName() val formattedMemberName = span(memberName) { textColor = colorProvider.getColor(getColorFromUserId(event.root.sender ?: ""))