diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 7f11e63469..2d289150c6 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -134,6 +134,8 @@
** Unable to decrypt: %s **
The sender\'s device has not sent us the keys for this message.
+ %1$s ended a voice broadcast.
+
@@ -3101,6 +3103,7 @@
(%1$s)
Live
+ Live broadcast
Buffering…
Resume voice broadcast record
diff --git a/vector-app/src/main/java/im/vector/app/core/di/SingletonModule.kt b/vector-app/src/main/java/im/vector/app/core/di/SingletonModule.kt
index 28ca761ace..7a3aa7cf8b 100644
--- a/vector-app/src/main/java/im/vector/app/core/di/SingletonModule.kt
+++ b/vector-app/src/main/java/im/vector/app/core/di/SingletonModule.kt
@@ -48,6 +48,7 @@ import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.VectorAnalytics
import im.vector.app.features.analytics.impl.DefaultVectorAnalytics
import im.vector.app.features.analytics.metrics.VectorPlugins
+import im.vector.app.features.configuration.VectorCustomEventTypesProvider
import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.invite.CompileTimeAutoAcceptInvites
import im.vector.app.features.navigation.DefaultNavigator
@@ -141,6 +142,7 @@ import javax.inject.Singleton
vectorRoomDisplayNameFallbackProvider: VectorRoomDisplayNameFallbackProvider,
flipperProxy: FlipperProxy,
vectorPlugins: VectorPlugins,
+ vectorCustomEventTypesProvider: VectorCustomEventTypesProvider,
): MatrixConfiguration {
return MatrixConfiguration(
applicationFlavor = BuildConfig.FLAVOR_DESCRIPTION,
@@ -150,6 +152,7 @@ import javax.inject.Singleton
flipperProxy.networkInterceptor(),
),
metricPlugins = vectorPlugins.plugins(),
+ customEventTypesProvider = vectorCustomEventTypesProvider,
)
}
diff --git a/vector/src/main/java/im/vector/app/features/configuration/VectorCustomEventTypesProvider.kt b/vector/src/main/java/im/vector/app/features/configuration/VectorCustomEventTypesProvider.kt
new file mode 100644
index 0000000000..55244685d7
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/configuration/VectorCustomEventTypesProvider.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022 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.configuration
+
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
+import org.matrix.android.sdk.api.provider.CustomEventTypesProvider
+import javax.inject.Inject
+
+class VectorCustomEventTypesProvider @Inject constructor() : CustomEventTypesProvider {
+
+ override val customPreviewableEventTypes = listOf(
+ VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO
+ )
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt
index aaa0fc10c9..c8af85db4f 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt
@@ -21,8 +21,14 @@ import im.vector.app.EmojiSpanify
import im.vector.app.R
import im.vector.app.core.extensions.getVectorLastMessageContent
import im.vector.app.core.resources.ColorProvider
+import im.vector.app.core.resources.DrawableProvider
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.html.EventHtmlRenderer
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
+import im.vector.app.features.voicebroadcast.isLive
+import im.vector.app.features.voicebroadcast.model.VoiceBroadcastEvent
+import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
+import me.gujun.android.span.image
import me.gujun.android.span.span
import org.commonmark.node.Document
import org.matrix.android.sdk.api.session.events.model.Event
@@ -41,6 +47,7 @@ import javax.inject.Inject
class DisplayableEventFormatter @Inject constructor(
private val stringProvider: StringProvider,
private val colorProvider: ColorProvider,
+ private val drawableProvider: DrawableProvider,
private val emojiSpanify: EmojiSpanify,
private val noticeEventFormatter: NoticeEventFormatter,
private val htmlRenderer: Lazy
@@ -135,6 +142,9 @@ class DisplayableEventFormatter @Inject constructor(
in EventType.STATE_ROOM_BEACON_INFO.values -> {
simpleFormat(senderName, stringProvider.getString(R.string.sent_live_location), appendAuthor)
}
+ VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO -> {
+ formatVoiceBroadcastEvent(timelineEvent.root.asVoiceBroadcastEvent(), senderName)
+ }
else -> {
span {
text = noticeEventFormatter.format(timelineEvent, isDm) ?: ""
@@ -252,4 +262,20 @@ class DisplayableEventFormatter @Inject constructor(
body
}
}
+
+ private fun formatVoiceBroadcastEvent(voiceBroadcastEvent: VoiceBroadcastEvent?, senderName: String): CharSequence {
+ return if (voiceBroadcastEvent?.isLive == true) {
+ span {
+ drawableProvider.getDrawable(R.drawable.ic_voice_broadcast, colorProvider.getColor(R.color.palette_vermilion))?.let {
+ image(it)
+ +" "
+ }
+ span(stringProvider.getString(R.string.voice_broadcast_live_broadcast)) {
+ textColor = colorProvider.getColor(R.color.palette_vermilion)
+ }
+ }
+ } else {
+ stringProvider.getString(R.string.notice_voice_broadcast_ended, senderName)
+ }
+ }
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt
index 638e3c185d..ca80530261 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItemFactory.kt
@@ -22,6 +22,7 @@ import com.airbnb.mvrx.Loading
import im.vector.app.R
import im.vector.app.core.date.DateFormatKind
import im.vector.app.core.date.VectorDateFormatter
+import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.resources.StringProvider
@@ -29,21 +30,30 @@ import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.RoomListDisplayMode
import im.vector.app.features.home.room.detail.timeline.format.DisplayableEventFormatter
import im.vector.app.features.home.room.typing.TypingHelper
+import im.vector.app.features.voicebroadcast.isVoiceBroadcast
+import im.vector.app.features.voicebroadcast.usecase.GetOngoingVoiceBroadcastsUseCase
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
+import org.matrix.android.sdk.api.extensions.orFalse
+import org.matrix.android.sdk.api.session.getRoom
+import org.matrix.android.sdk.api.session.room.getTimelineEvent
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
+import org.matrix.android.sdk.api.session.room.model.message.asMessageAudioEvent
+import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.util.toMatrixItem
import javax.inject.Inject
class RoomSummaryItemFactory @Inject constructor(
+ private val sessionHolder: ActiveSessionHolder,
private val displayableEventFormatter: DisplayableEventFormatter,
private val dateFormatter: VectorDateFormatter,
private val stringProvider: StringProvider,
private val typingHelper: TypingHelper,
private val avatarRenderer: AvatarRenderer,
- private val errorFormatter: ErrorFormatter
+ private val errorFormatter: ErrorFormatter,
+ private val getOngoingVoiceBroadcastsUseCase: GetOngoingVoiceBroadcastsUseCase,
) {
fun create(
@@ -129,7 +139,7 @@ class RoomSummaryItemFactory @Inject constructor(
val showSelected = selectedRoomIds.contains(roomSummary.roomId)
var latestFormattedEvent: CharSequence = ""
var latestEventTime = ""
- val latestEvent = roomSummary.latestPreviewableEvent
+ val latestEvent = roomSummary.getVectorLatestPreviewableEvent()
if (latestEvent != null) {
latestFormattedEvent = displayableEventFormatter.format(latestEvent, roomSummary.isDirect, roomSummary.isDirect.not())
latestEventTime = dateFormatter.format(latestEvent.root.originServerTs, DateFormatKind.ROOM_LIST)
@@ -225,4 +235,13 @@ class RoomSummaryItemFactory @Inject constructor(
else -> stringProvider.getQuantityString(R.plurals.search_space_multiple_parents, size - 1, directParentNames[0], size - 1)
}
}
+
+ private fun RoomSummary.getVectorLatestPreviewableEvent(): TimelineEvent? {
+ val room = sessionHolder.getSafeActiveSession()?.getRoom(roomId) ?: return latestPreviewableEvent
+ val liveVoiceBroadcastTimelineEvent = getOngoingVoiceBroadcastsUseCase.execute(roomId).lastOrNull()
+ ?.root?.eventId?.let { room.getTimelineEvent(it) }
+ return liveVoiceBroadcastTimelineEvent
+ ?: latestPreviewableEvent
+ ?.takeUnless { it.root.asMessageAudioEvent()?.isVoiceBroadcast().orFalse() } // Skip voice messages related to voice broadcast
+ }
}
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetOngoingVoiceBroadcastsUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetOngoingVoiceBroadcastsUseCase.kt
index ec50618969..9974db470f 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetOngoingVoiceBroadcastsUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetOngoingVoiceBroadcastsUseCase.kt
@@ -18,8 +18,8 @@ package im.vector.app.features.voicebroadcast.usecase
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
+import im.vector.app.features.voicebroadcast.isLive
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastEvent
-import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.getRoom
@@ -44,6 +44,6 @@ class GetOngoingVoiceBroadcastsUseCase @Inject constructor(
QueryStringValue.IsNotEmpty
)
.mapNotNull { it.asVoiceBroadcastEvent() }
- .filter { it.content?.voiceBroadcastState != null && it.content?.voiceBroadcastState != VoiceBroadcastState.STOPPED }
+ .filter { it.isLive }
}
}