diff --git a/CHANGES.md b/CHANGES.md
index e08c36d406..3918318b45 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -5,6 +5,7 @@ Features ✨:
-
Improvements 🙌:
+ - Create a WidgetItemFactory and use it for better rendering of Jitsi widget change (video conference)
- Open image from URL Preview (#2705)
Bugfix 🐛:
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/sender/SenderInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/sender/SenderInfo.kt
index 1b4368e3da..9b73136fc3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/sender/SenderInfo.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/sender/SenderInfo.kt
@@ -16,6 +16,8 @@
package org.matrix.android.sdk.api.session.room.sender
+import org.matrix.android.sdk.internal.util.replaceSpaceChars
+
data class SenderInfo(
val userId: String,
/**
@@ -27,8 +29,9 @@ data class SenderInfo(
) {
val disambiguatedDisplayName: String
get() = when {
- displayName.isNullOrBlank() -> userId
- isUniqueDisplayName -> displayName
- else -> "$displayName ($userId)"
+ displayName == null -> userId
+ displayName.replaceSpaceChars().isBlank() -> "$displayName ($userId)"
+ isUniqueDisplayName -> displayName
+ else -> "$displayName ($userId)"
}
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt
index ecfbe311f1..2fabca4be8 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt
@@ -71,3 +71,10 @@ fun String.caseInsensitiveFind(subString: String): Boolean {
return false
}
+
+internal val spaceChars = "[\u00A0\u2000-\u200B\u2800\u3000]".toRegex()
+
+/**
+ * Strip all the UTF-8 chars which are actually spaces
+ */
+internal fun String.replaceSpaceChars() = replace(spaceChars, "")
diff --git a/matrix-sdk-android/src/main/res/values/strings.xml b/matrix-sdk-android/src/main/res/values/strings.xml
index 7a0fe1d735..26b9bc19d9 100644
--- a/matrix-sdk-android/src/main/res/values/strings.xml
+++ b/matrix-sdk-android/src/main/res/values/strings.xml
@@ -126,6 +126,13 @@
%1$s modified %2$s widget
You modified %1$s widget
+ Video conference started by %1$s
+ You started video conference
+ Video conference ended by %1$s
+ You ended video conference
+ Video conference modified by %1$s
+ You modified video conference
+
Admin
Moderator
Default
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt
index 243cbbd0e6..943e78ae35 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt
@@ -32,6 +32,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
private val defaultItemFactory: DefaultItemFactory,
private val encryptionItemFactory: EncryptionItemFactory,
private val roomCreateItemFactory: RoomCreateItemFactory,
+ private val widgetItemFactory: WidgetItemFactory,
private val roomSummaryHolder: RoomSummaryHolder,
private val verificationConclusionItemFactory: VerificationItemFactory,
private val userPreferencesProvider: UserPreferencesProvider) {
@@ -58,14 +59,14 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
EventType.STATE_ROOM_HISTORY_VISIBILITY,
EventType.STATE_ROOM_SERVER_ACL,
EventType.STATE_ROOM_GUEST_ACCESS,
- EventType.STATE_ROOM_WIDGET_LEGACY,
- EventType.STATE_ROOM_WIDGET,
EventType.CALL_INVITE,
EventType.CALL_HANGUP,
EventType.CALL_ANSWER,
EventType.STATE_ROOM_POWER_LEVELS,
EventType.REACTION,
EventType.REDACTION -> noticeItemFactory.create(event, highlight, roomSummaryHolder.roomSummary, callback)
+ EventType.STATE_ROOM_WIDGET_LEGACY,
+ EventType.STATE_ROOM_WIDGET -> widgetItemFactory.create(event, highlight, callback)
EventType.STATE_ROOM_ENCRYPTION -> encryptionItemFactory.create(event, highlight, callback)
// State room create
EventType.STATE_ROOM_CREATE -> roomCreateItemFactory.create(event, callback)
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/WidgetItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/WidgetItemFactory.kt
new file mode 100644
index 0000000000..8d8f42b2d1
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/WidgetItemFactory.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2021 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.app.features.home.room.detail.timeline.factory
+
+import im.vector.app.ActiveSessionDataSource
+import im.vector.app.R
+import im.vector.app.core.epoxy.VectorEpoxyModel
+import im.vector.app.core.resources.StringProvider
+import im.vector.app.features.home.room.detail.timeline.TimelineEventController
+import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
+import im.vector.app.features.home.room.detail.timeline.helper.MessageInformationDataFactory
+import im.vector.app.features.home.room.detail.timeline.helper.MessageItemAttributesFactory
+import im.vector.app.features.home.room.detail.timeline.helper.RoomSummaryHolder
+import im.vector.app.features.home.room.detail.timeline.item.WidgetTileTimelineItem
+import im.vector.app.features.home.room.detail.timeline.item.WidgetTileTimelineItem_
+import org.matrix.android.sdk.api.extensions.orFalse
+import org.matrix.android.sdk.api.session.events.model.Event
+import org.matrix.android.sdk.api.session.events.model.toModel
+import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
+import org.matrix.android.sdk.api.session.widgets.model.WidgetContent
+import org.matrix.android.sdk.api.session.widgets.model.WidgetType
+import javax.inject.Inject
+
+class WidgetItemFactory @Inject constructor(
+ private val sp: StringProvider,
+ private val roomSummaryHolder: RoomSummaryHolder,
+ private val messageItemAttributesFactory: MessageItemAttributesFactory,
+ private val informationDataFactory: MessageInformationDataFactory,
+ private val noticeItemFactory: NoticeItemFactory,
+ private val avatarSizeProvider: AvatarSizeProvider,
+ private val activeSessionDataSource: ActiveSessionDataSource
+) {
+ private val currentUserId: String?
+ get() = activeSessionDataSource.currentValue?.orNull()?.myUserId
+
+ private fun Event.isSentByCurrentUser() = senderId != null && senderId == currentUserId
+
+ fun create(event: TimelineEvent,
+ highlight: Boolean,
+ callback: TimelineEventController.Callback?): VectorEpoxyModel<*>? {
+ val widgetContent: WidgetContent = event.root.getClearContent().toModel() ?: return null
+ val previousWidgetContent: WidgetContent? = event.root.resolvedPrevContent().toModel()
+
+ return when (WidgetType.fromString(widgetContent.type ?: previousWidgetContent?.type ?: "")) {
+ WidgetType.Jitsi -> createJitsiItem(event, callback, widgetContent, previousWidgetContent)
+ // There is lot of other widget types we could improve here
+ else -> noticeItemFactory.create(event, highlight, roomSummaryHolder.roomSummary, callback)
+ }
+ }
+
+ private fun createJitsiItem(timelineEvent: TimelineEvent,
+ callback: TimelineEventController.Callback?,
+ widgetContent: WidgetContent,
+ previousWidgetContent: WidgetContent?): VectorEpoxyModel<*> {
+ val informationData = informationDataFactory.create(timelineEvent, null)
+ val attributes = messageItemAttributesFactory.create(null, informationData, callback)
+
+ val disambiguatedDisplayName = timelineEvent.senderInfo.disambiguatedDisplayName
+ val message = if (widgetContent.isActive()) {
+ val widgetName = widgetContent.getHumanName()
+ if (previousWidgetContent?.isActive().orFalse()) {
+ // Widget has been modified
+ if (timelineEvent.root.isSentByCurrentUser()) {
+ sp.getString(R.string.notice_widget_jitsi_modified_by_you, widgetName)
+ } else {
+ sp.getString(R.string.notice_widget_jitsi_modified, disambiguatedDisplayName, widgetName)
+ }
+ } else {
+ // Widget has been added
+ if (timelineEvent.root.isSentByCurrentUser()) {
+ sp.getString(R.string.notice_widget_jitsi_added_by_you, widgetName)
+ } else {
+ sp.getString(R.string.notice_widget_jitsi_added, disambiguatedDisplayName, widgetName)
+ }
+ }
+ } else {
+ // Widget has been removed
+ val widgetName = previousWidgetContent?.getHumanName()
+ if (timelineEvent.root.isSentByCurrentUser()) {
+ sp.getString(R.string.notice_widget_jitsi_removed_by_you, widgetName)
+ } else {
+ sp.getString(R.string.notice_widget_jitsi_removed, disambiguatedDisplayName, widgetName)
+ }
+ }
+
+ return WidgetTileTimelineItem_()
+ .attributes(
+ WidgetTileTimelineItem.Attributes(
+ title = message,
+ drawableStart = R.drawable.ic_video,
+ informationData = informationData,
+ avatarRenderer = attributes.avatarRenderer,
+ messageColorProvider = attributes.messageColorProvider,
+ itemLongClickListener = attributes.itemLongClickListener,
+ itemClickListener = attributes.itemClickListener,
+ reactionPillCallback = attributes.reactionPillCallback,
+ readReceiptsCallback = attributes.readReceiptsCallback,
+ emojiTypeFace = attributes.emojiTypeFace
+ )
+ )
+ .leftGuideline(avatarSizeProvider.leftGuideline)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt
index c725f5b7dc..8204ce39ec 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt
@@ -140,12 +140,14 @@ class NoticeEventFormatter @Inject constructor(
return if (widgetContent.isActive()) {
val widgetName = widgetContent.getHumanName()
if (previousWidgetContent?.isActive().orFalse()) {
+ // Widget has been modified
if (event.isSentByCurrentUser()) {
sp.getString(R.string.notice_widget_modified_by_you, widgetName)
} else {
sp.getString(R.string.notice_widget_modified, disambiguatedDisplayName, widgetName)
}
} else {
+ // Widget has been added
if (event.isSentByCurrentUser()) {
sp.getString(R.string.notice_widget_added_by_you, widgetName)
} else {
@@ -153,6 +155,7 @@ class NoticeEventFormatter @Inject constructor(
}
}
} else {
+ // Widget has been removed
val widgetName = previousWidgetContent?.getHumanName()
if (event.isSentByCurrentUser()) {
sp.getString(R.string.notice_widget_removed_by_you, widgetName)
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/WidgetTileTimelineItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/WidgetTileTimelineItem.kt
new file mode 100644
index 0000000000..33f59ca22a
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/WidgetTileTimelineItem.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2021 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.app.features.home.room.detail.timeline.item
+
+import android.annotation.SuppressLint
+import android.graphics.Typeface
+import android.view.View
+import android.widget.ImageView
+import android.widget.RelativeLayout
+import android.widget.TextView
+import androidx.annotation.DrawableRes
+import androidx.core.content.ContextCompat
+import androidx.core.view.updateLayoutParams
+import com.airbnb.epoxy.EpoxyAttribute
+import com.airbnb.epoxy.EpoxyModelClass
+import im.vector.app.R
+import im.vector.app.features.home.AvatarRenderer
+import im.vector.app.features.home.room.detail.timeline.MessageColorProvider
+import im.vector.app.features.home.room.detail.timeline.TimelineEventController
+
+@EpoxyModelClass(layout = R.layout.item_timeline_event_base_state)
+abstract class WidgetTileTimelineItem : AbsBaseMessageItem() {
+
+ override val baseAttributes: AbsBaseMessageItem.Attributes
+ get() = attributes
+
+ @EpoxyAttribute
+ lateinit var attributes: Attributes
+
+ override fun getViewType() = STUB_ID
+
+ @SuppressLint("SetTextI18n")
+ override fun bind(holder: Holder) {
+ super.bind(holder)
+ holder.endGuideline.updateLayoutParams {
+ this.marginEnd = leftGuideline
+ }
+
+ holder.titleView.text = attributes.title
+ holder.titleView.setCompoundDrawablesWithIntrinsicBounds(
+ ContextCompat.getDrawable(holder.view.context, attributes.drawableStart),
+ null, null, null
+ )
+
+ renderSendState(holder.view, null, holder.failedToSendIndicator)
+ }
+
+ class Holder : AbsBaseMessageItem.Holder(STUB_ID) {
+ val titleView by bind(R.id.itemWidgetTitle)
+ val endGuideline by bind(R.id.messageEndGuideline)
+ val failedToSendIndicator by bind(R.id.messageFailToSendIndicator)
+ }
+
+ companion object {
+ private const val STUB_ID = R.id.messageWidgetStub
+ }
+
+ /**
+ * This class holds all the common attributes for timeline items.
+ */
+ data class Attributes(
+ val title: CharSequence,
+ @DrawableRes
+ val drawableStart: Int,
+ override val informationData: MessageInformationData,
+ override val avatarRenderer: AvatarRenderer,
+ override val messageColorProvider: MessageColorProvider,
+ override val itemLongClickListener: View.OnLongClickListener? = null,
+ override val itemClickListener: View.OnClickListener? = null,
+ override val reactionPillCallback: TimelineEventController.ReactionPillCallback? = null,
+ override val readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null,
+ val emojiTypeFace: Typeface? = null
+ ) : AbsBaseMessageItem.Attributes
+}
diff --git a/vector/src/main/java/im/vector/app/features/media/BaseAttachmentProvider.kt b/vector/src/main/java/im/vector/app/features/media/BaseAttachmentProvider.kt
index 4ca93898cd..11b8832c94 100644
--- a/vector/src/main/java/im/vector/app/features/media/BaseAttachmentProvider.kt
+++ b/vector/src/main/java/im/vector/app/features/media/BaseAttachmentProvider.kt
@@ -83,7 +83,7 @@ abstract class BaseAttachmentProvider(
val dateString = dateFormatter.format(timelineEvent.root.originServerTs, DateFormatKind.DEFAULT_DATE_AND_TIME)
overlayView?.updateWith(
counter = stringProvider.getString(R.string.attachment_viewer_item_x_of_y, position + 1, getItemCount()),
- senderInfo = "${timelineEvent.senderInfo.displayName} $dateString"
+ senderInfo = "${timelineEvent.senderInfo.disambiguatedDisplayName} $dateString"
)
overlayView?.views?.overlayVideoControlsGroup?.isVisible = timelineEvent.root.isVideoMessage()
} else {
diff --git a/vector/src/main/res/layout/item_timeline_event_base_state.xml b/vector/src/main/res/layout/item_timeline_event_base_state.xml
index 38fb3af07b..3f44b11aa7 100644
--- a/vector/src/main/res/layout/item_timeline_event_base_state.xml
+++ b/vector/src/main/res/layout/item_timeline_event_base_state.xml
@@ -53,6 +53,13 @@
tools:layout_marginTop="180dp"
tools:visibility="visible" />
+
+
diff --git a/vector/src/main/res/layout/item_timeline_event_widget_stub.xml b/vector/src/main/res/layout/item_timeline_event_widget_stub.xml
new file mode 100644
index 0000000000..573fecc4e8
--- /dev/null
+++ b/vector/src/main/res/layout/item_timeline_event_widget_stub.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+