mirror of
https://github.com/element-hq/element-android
synced 2024-11-26 11:25:40 +03:00
Merge pull request #8057 from vector-im/yostyle/vb_utd
Let the user know when we are not able to decrypt the voice broadcast…
This commit is contained in:
commit
0971a28532
14 changed files with 119 additions and 26 deletions
1
changelog.d/7820.misc
Normal file
1
changelog.d/7820.misc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Let the user know when we are not able to decrypt the voice broadcast chunks
|
|
@ -3120,6 +3120,7 @@
|
||||||
<string name="error_voice_broadcast_already_in_progress_message">You are already recording a voice broadcast. Please end your current voice broadcast to start a new one.</string>
|
<string name="error_voice_broadcast_already_in_progress_message">You are already recording a voice broadcast. Please end your current voice broadcast to start a new one.</string>
|
||||||
<string name="error_voice_broadcast_unable_to_play">Unable to play this voice broadcast.</string>
|
<string name="error_voice_broadcast_unable_to_play">Unable to play this voice broadcast.</string>
|
||||||
<string name="error_voice_broadcast_no_connection_recording">Connection error - Recording paused</string>
|
<string name="error_voice_broadcast_no_connection_recording">Connection error - Recording paused</string>
|
||||||
|
<string name="error_voice_broadcast_unable_to_decrypt">Unable to decrypt this voice broadcast.</string>
|
||||||
<!-- Examples of usage: 6h 15min 30sec left / 15min 30sec left / 30sec left -->
|
<!-- Examples of usage: 6h 15min 30sec left / 15min 30sec left / 30sec left -->
|
||||||
<string name="voice_broadcast_recording_time_left">%1$s left</string>
|
<string name="voice_broadcast_recording_time_left">%1$s left</string>
|
||||||
<string name="stop_voice_broadcast_dialog_title">Stop live broadcasting?</string>
|
<string name="stop_voice_broadcast_dialog_title">Stop live broadcasting?</string>
|
||||||
|
|
|
@ -28,4 +28,12 @@ interface EventService {
|
||||||
roomId: String,
|
roomId: String,
|
||||||
eventId: String
|
eventId: String
|
||||||
): Event
|
): Event
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an Event from cache. Return null if not found.
|
||||||
|
*/
|
||||||
|
fun getEventFromCache(
|
||||||
|
roomId: String,
|
||||||
|
eventId: String
|
||||||
|
): Event?
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,12 @@ internal fun EventEntity.Companion.where(realm: Realm, eventId: String): RealmQu
|
||||||
.equalTo(EventEntityFields.EVENT_ID, eventId)
|
.equalTo(EventEntityFields.EVENT_ID, eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun EventEntity.Companion.where(realm: Realm, roomId: String, eventId: String): RealmQuery<EventEntity> {
|
||||||
|
return realm.where<EventEntity>()
|
||||||
|
.equalTo(EventEntityFields.ROOM_ID, roomId)
|
||||||
|
.equalTo(EventEntityFields.EVENT_ID, eventId)
|
||||||
|
}
|
||||||
|
|
||||||
internal fun EventEntity.Companion.whereRoomId(realm: Realm, roomId: String): RealmQuery<EventEntity> {
|
internal fun EventEntity.Companion.whereRoomId(realm: Realm, roomId: String): RealmQuery<EventEntity> {
|
||||||
return realm.where<EventEntity>()
|
return realm.where<EventEntity>()
|
||||||
.equalTo(EventEntityFields.ROOM_ID, roomId)
|
.equalTo(EventEntityFields.ROOM_ID, roomId)
|
||||||
|
|
|
@ -18,13 +18,18 @@ package org.matrix.android.sdk.internal.session.events
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.session.events.EventService
|
import org.matrix.android.sdk.api.session.events.EventService
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
|
import org.matrix.android.sdk.internal.database.RealmSessionProvider
|
||||||
|
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
||||||
|
import org.matrix.android.sdk.internal.database.model.EventEntity
|
||||||
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
import org.matrix.android.sdk.internal.session.call.CallEventProcessor
|
import org.matrix.android.sdk.internal.session.call.CallEventProcessor
|
||||||
import org.matrix.android.sdk.internal.session.room.timeline.GetEventTask
|
import org.matrix.android.sdk.internal.session.room.timeline.GetEventTask
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class DefaultEventService @Inject constructor(
|
internal class DefaultEventService @Inject constructor(
|
||||||
private val getEventTask: GetEventTask,
|
private val getEventTask: GetEventTask,
|
||||||
private val callEventProcessor: CallEventProcessor
|
private val callEventProcessor: CallEventProcessor,
|
||||||
|
private val realmSessionProvider: RealmSessionProvider,
|
||||||
) : EventService {
|
) : EventService {
|
||||||
|
|
||||||
override suspend fun getEvent(roomId: String, eventId: String): Event {
|
override suspend fun getEvent(roomId: String, eventId: String): Event {
|
||||||
|
@ -36,4 +41,16 @@ internal class DefaultEventService @Inject constructor(
|
||||||
|
|
||||||
return event
|
return event
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getEventFromCache(roomId: String, eventId: String): Event? {
|
||||||
|
return realmSessionProvider.withRealm { realm ->
|
||||||
|
EventEntity.where(
|
||||||
|
realm = realm,
|
||||||
|
roomId = roomId,
|
||||||
|
eventId = eventId
|
||||||
|
)
|
||||||
|
.findFirst()
|
||||||
|
?.asDomain()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,7 +160,9 @@ class DefaultErrorFormatter @Inject constructor(
|
||||||
RecordingError.BlockedBySomeoneElse -> stringProvider.getString(R.string.error_voice_broadcast_blocked_by_someone_else_message)
|
RecordingError.BlockedBySomeoneElse -> stringProvider.getString(R.string.error_voice_broadcast_blocked_by_someone_else_message)
|
||||||
RecordingError.NoPermission -> stringProvider.getString(R.string.error_voice_broadcast_permission_denied_message)
|
RecordingError.NoPermission -> stringProvider.getString(R.string.error_voice_broadcast_permission_denied_message)
|
||||||
RecordingError.UserAlreadyBroadcasting -> stringProvider.getString(R.string.error_voice_broadcast_already_in_progress_message)
|
RecordingError.UserAlreadyBroadcasting -> stringProvider.getString(R.string.error_voice_broadcast_already_in_progress_message)
|
||||||
is VoiceBroadcastFailure.ListeningError -> stringProvider.getString(R.string.error_voice_broadcast_unable_to_play)
|
is VoiceBroadcastFailure.ListeningError.UnableToPlay,
|
||||||
|
is VoiceBroadcastFailure.ListeningError.PrepareMediaPlayerError -> stringProvider.getString(R.string.error_voice_broadcast_unable_to_play)
|
||||||
|
is VoiceBroadcastFailure.ListeningError.UnableToDecrypt -> stringProvider.getString(R.string.error_voice_broadcast_unable_to_decrypt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,12 @@ import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||||
import im.vector.app.features.analytics.DecryptionFailureTracker
|
import im.vector.app.features.analytics.DecryptionFailureTracker
|
||||||
import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventVisibilityHelper
|
import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventVisibilityHelper
|
||||||
import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
|
import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
|
||||||
|
import im.vector.app.features.voicebroadcast.model.isVoiceBroadcast
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.RelationType
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.getRelationContent
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -39,6 +43,7 @@ class TimelineItemFactory @Inject constructor(
|
||||||
private val callItemFactory: CallItemFactory,
|
private val callItemFactory: CallItemFactory,
|
||||||
private val decryptionFailureTracker: DecryptionFailureTracker,
|
private val decryptionFailureTracker: DecryptionFailureTracker,
|
||||||
private val timelineEventVisibilityHelper: TimelineEventVisibilityHelper,
|
private val timelineEventVisibilityHelper: TimelineEventVisibilityHelper,
|
||||||
|
private val session: Session,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -130,11 +135,16 @@ class TimelineItemFactory @Inject constructor(
|
||||||
EventType.CALL_ANSWER -> callItemFactory.create(params)
|
EventType.CALL_ANSWER -> callItemFactory.create(params)
|
||||||
// Crypto
|
// Crypto
|
||||||
EventType.ENCRYPTED -> {
|
EventType.ENCRYPTED -> {
|
||||||
if (event.root.isRedacted()) {
|
val relationContent = event.getRelationContent()
|
||||||
|
when {
|
||||||
// Redacted event, let the MessageItemFactory handle it
|
// Redacted event, let the MessageItemFactory handle it
|
||||||
messageItemFactory.create(params)
|
event.root.isRedacted() -> messageItemFactory.create(params)
|
||||||
} else {
|
relationContent?.type == RelationType.REFERENCE -> {
|
||||||
encryptedItemFactory.create(params)
|
// Hide the decryption error for VoiceBroadcast chunks
|
||||||
|
val relatedEvent = relationContent.eventId?.let { session.eventService().getEventFromCache(event.roomId, it) }
|
||||||
|
if (relatedEvent?.isVoiceBroadcast() != true) encryptedItemFactory.create(params) else null
|
||||||
|
}
|
||||||
|
else -> encryptedItemFactory.create(params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EventType.KEY_VERIFICATION_CANCEL,
|
EventType.KEY_VERIFICATION_CANCEL,
|
||||||
|
|
|
@ -75,6 +75,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
|
||||||
voiceBroadcast = voiceBroadcast,
|
voiceBroadcast = voiceBroadcast,
|
||||||
voiceBroadcastState = voiceBroadcastContent.voiceBroadcastState,
|
voiceBroadcastState = voiceBroadcastContent.voiceBroadcastState,
|
||||||
duration = voiceBroadcastEventsGroup.getDuration(),
|
duration = voiceBroadcastEventsGroup.getDuration(),
|
||||||
|
hasUnableToDecryptEvent = voiceBroadcastEventsGroup.hasUnableToDecryptEvent(),
|
||||||
recorderName = params.event.senderInfo.disambiguatedDisplayName,
|
recorderName = params.event.senderInfo.disambiguatedDisplayName,
|
||||||
recorder = voiceBroadcastRecorder,
|
recorder = voiceBroadcastRecorder,
|
||||||
player = voiceBroadcastPlayer,
|
player = voiceBroadcastPlayer,
|
||||||
|
|
|
@ -25,6 +25,8 @@ import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
|
||||||
import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
|
import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.RelationType
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.getRelationContent
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.asMessageAudioEvent
|
import org.matrix.android.sdk.api.session.room.model.message.asMessageAudioEvent
|
||||||
|
@ -61,6 +63,7 @@ class TimelineEventsGroups {
|
||||||
private fun TimelineEvent.getGroupIdOrNull(): String? {
|
private fun TimelineEvent.getGroupIdOrNull(): String? {
|
||||||
val type = root.getClearType()
|
val type = root.getClearType()
|
||||||
val content = root.getClearContent()
|
val content = root.getClearContent()
|
||||||
|
val relationContent = root.getRelationContent()
|
||||||
return when {
|
return when {
|
||||||
EventType.isCallEvent(type) -> (content?.get("call_id") as? String)
|
EventType.isCallEvent(type) -> (content?.get("call_id") as? String)
|
||||||
type == VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO -> root.asVoiceBroadcastEvent()?.reference?.eventId
|
type == VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO -> root.asVoiceBroadcastEvent()?.reference?.eventId
|
||||||
|
@ -69,6 +72,9 @@ class TimelineEventsGroups {
|
||||||
// Group voice messages with a reference to an eventId
|
// Group voice messages with a reference to an eventId
|
||||||
root.asMessageAudioEvent()?.getVoiceBroadcastEventId()
|
root.asMessageAudioEvent()?.getVoiceBroadcastEventId()
|
||||||
}
|
}
|
||||||
|
type == EventType.ENCRYPTED && relationContent?.type == RelationType.REFERENCE -> {
|
||||||
|
relationContent.eventId
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
@ -153,4 +159,8 @@ class VoiceBroadcastEventsGroup(private val group: TimelineEventsGroup) {
|
||||||
fun getDuration(): Int {
|
fun getDuration(): Int {
|
||||||
return group.events.mapNotNull { it.root.asMessageAudioEvent()?.duration }.sum()
|
return group.events.mapNotNull { it.root.asMessageAudioEvent()?.duration }.sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hasUnableToDecryptEvent(): Boolean {
|
||||||
|
return group.events.any { it.root.getClearType() == EventType.ENCRYPTED }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ abstract class AbsMessageVoiceBroadcastItem<H : AbsMessageVoiceBroadcastItem.Hol
|
||||||
protected val player get() = voiceBroadcastAttributes.player
|
protected val player get() = voiceBroadcastAttributes.player
|
||||||
protected val playbackTracker get() = voiceBroadcastAttributes.playbackTracker
|
protected val playbackTracker get() = voiceBroadcastAttributes.playbackTracker
|
||||||
protected val duration get() = voiceBroadcastAttributes.duration
|
protected val duration get() = voiceBroadcastAttributes.duration
|
||||||
|
protected val hasUnableToDecryptEvent get() = voiceBroadcastAttributes.hasUnableToDecryptEvent
|
||||||
protected val roomItem get() = voiceBroadcastAttributes.roomItem
|
protected val roomItem get() = voiceBroadcastAttributes.roomItem
|
||||||
protected val colorProvider get() = voiceBroadcastAttributes.colorProvider
|
protected val colorProvider get() = voiceBroadcastAttributes.colorProvider
|
||||||
protected val drawableProvider get() = voiceBroadcastAttributes.drawableProvider
|
protected val drawableProvider get() = voiceBroadcastAttributes.drawableProvider
|
||||||
|
@ -102,6 +103,7 @@ abstract class AbsMessageVoiceBroadcastItem<H : AbsMessageVoiceBroadcastItem.Hol
|
||||||
val voiceBroadcast: VoiceBroadcast,
|
val voiceBroadcast: VoiceBroadcast,
|
||||||
val voiceBroadcastState: VoiceBroadcastState?,
|
val voiceBroadcastState: VoiceBroadcastState?,
|
||||||
val duration: Int,
|
val duration: Int,
|
||||||
|
val hasUnableToDecryptEvent: Boolean,
|
||||||
val recorderName: String,
|
val recorderName: String,
|
||||||
val recorder: VoiceBroadcastRecorder?,
|
val recorder: VoiceBroadcastRecorder?,
|
||||||
val player: VoiceBroadcastPlayer,
|
val player: VoiceBroadcastPlayer,
|
||||||
|
|
|
@ -29,6 +29,7 @@ import im.vector.app.core.epoxy.onClick
|
||||||
import im.vector.app.core.extensions.setTextOrHide
|
import im.vector.app.core.extensions.setTextOrHide
|
||||||
import im.vector.app.features.home.room.detail.RoomDetailAction.VoiceBroadcastAction
|
import im.vector.app.features.home.room.detail.RoomDetailAction.VoiceBroadcastAction
|
||||||
import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker.Listener.State
|
import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker.Listener.State
|
||||||
|
import im.vector.app.features.voicebroadcast.VoiceBroadcastFailure
|
||||||
import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayer
|
import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayer
|
||||||
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
|
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
|
||||||
import im.vector.app.features.voicebroadcast.views.VoiceBroadcastBufferingView
|
import im.vector.app.features.voicebroadcast.views.VoiceBroadcastBufferingView
|
||||||
|
@ -136,12 +137,19 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageVoiceBroadcastItem
|
||||||
|
|
||||||
private fun renderPlaybackError(holder: Holder, playbackState: State) {
|
private fun renderPlaybackError(holder: Holder, playbackState: State) {
|
||||||
with(holder) {
|
with(holder) {
|
||||||
if (playbackState is State.Error) {
|
when {
|
||||||
controlsGroup.isVisible = false
|
playbackState is State.Error -> {
|
||||||
errorView.setTextOrHide(errorFormatter.toHumanReadable(playbackState.failure))
|
controlsGroup.isVisible = false
|
||||||
} else {
|
errorView.setTextOrHide(errorFormatter.toHumanReadable(playbackState.failure))
|
||||||
errorView.isVisible = false
|
}
|
||||||
controlsGroup.isVisible = true
|
playbackState is State.Idle && hasUnableToDecryptEvent -> {
|
||||||
|
controlsGroup.isVisible = false
|
||||||
|
errorView.setTextOrHide(errorFormatter.toHumanReadable(VoiceBroadcastFailure.ListeningError.UnableToDecrypt))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
errorView.isVisible = false
|
||||||
|
controlsGroup.isVisible = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,5 +32,6 @@ sealed class VoiceBroadcastFailure : Throwable() {
|
||||||
*/
|
*/
|
||||||
data class UnableToPlay(val what: Int, val extra: Int) : ListeningError()
|
data class UnableToPlay(val what: Int, val extra: Int) : ListeningError()
|
||||||
data class PrepareMediaPlayerError(override val cause: Throwable? = null) : ListeningError()
|
data class PrepareMediaPlayerError(override val cause: Throwable? = null) : ListeningError()
|
||||||
|
object UnableToDecrypt : ListeningError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,9 @@ import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.message.asMessageAudioEvent
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.concurrent.CopyOnWriteArrayList
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -189,9 +191,13 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
|
||||||
|
|
||||||
private fun fetchPlaylistAndStartPlayback(voiceBroadcast: VoiceBroadcast) {
|
private fun fetchPlaylistAndStartPlayback(voiceBroadcast: VoiceBroadcast) {
|
||||||
fetchPlaylistTask = getLiveVoiceBroadcastChunksUseCase.execute(voiceBroadcast)
|
fetchPlaylistTask = getLiveVoiceBroadcastChunksUseCase.execute(voiceBroadcast)
|
||||||
.onEach {
|
.onEach { events ->
|
||||||
playlist.setItems(it)
|
if (events.any { it.getClearType() == EventType.ENCRYPTED }) {
|
||||||
onPlaylistUpdated()
|
playingState = State.Error(VoiceBroadcastFailure.ListeningError.UnableToDecrypt)
|
||||||
|
} else {
|
||||||
|
playlist.setItems(events.mapNotNull { it.asMessageAudioEvent() })
|
||||||
|
onPlaylistUpdated()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.launchIn(sessionScope)
|
.launchIn(sessionScope)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,10 @@ import kotlinx.coroutines.flow.emptyFlow
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.runningReduce
|
import kotlinx.coroutines.flow.runningReduce
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.events.model.RelationType
|
import org.matrix.android.sdk.api.session.events.model.RelationType
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.getRelationContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioEvent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioEvent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.asMessageAudioEvent
|
import org.matrix.android.sdk.api.session.room.model.message.asMessageAudioEvent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||||
|
@ -49,14 +52,22 @@ class GetLiveVoiceBroadcastChunksUseCase @Inject constructor(
|
||||||
private val getVoiceBroadcastEventUseCase: GetVoiceBroadcastStateEventUseCase,
|
private val getVoiceBroadcastEventUseCase: GetVoiceBroadcastStateEventUseCase,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun execute(voiceBroadcast: VoiceBroadcast): Flow<List<MessageAudioEvent>> {
|
fun execute(voiceBroadcast: VoiceBroadcast): Flow<List<Event>> {
|
||||||
val session = activeSessionHolder.getSafeActiveSession() ?: return emptyFlow()
|
val session = activeSessionHolder.getSafeActiveSession() ?: return emptyFlow()
|
||||||
val room = session.roomService().getRoom(voiceBroadcast.roomId) ?: return emptyFlow()
|
val room = session.roomService().getRoom(voiceBroadcast.roomId) ?: return emptyFlow()
|
||||||
val timeline = room.timelineService().createTimeline(null, TimelineSettings(5))
|
val timeline = room.timelineService().createTimeline(null, TimelineSettings(5))
|
||||||
|
|
||||||
// Get initial chunks
|
// Get initial chunks
|
||||||
val existingChunks = room.timelineService().getTimelineEventsRelatedTo(RelationType.REFERENCE, voiceBroadcast.voiceBroadcastId)
|
val existingChunks = room.timelineService().getTimelineEventsRelatedTo(RelationType.REFERENCE, voiceBroadcast.voiceBroadcastId)
|
||||||
.mapNotNull { timelineEvent -> timelineEvent.root.asMessageAudioEvent().takeIf { it.isVoiceBroadcast() } }
|
.mapNotNull { timelineEvent ->
|
||||||
|
val event = timelineEvent.root
|
||||||
|
val relationContent = event.getRelationContent()
|
||||||
|
when {
|
||||||
|
event.getClearType() == EventType.MESSAGE -> event.takeIf { it.asMessageAudioEvent().isVoiceBroadcast() }
|
||||||
|
event.getClearType() == EventType.ENCRYPTED && relationContent?.type == RelationType.REFERENCE -> event
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val voiceBroadcastEvent = getVoiceBroadcastEventUseCase.execute(voiceBroadcast)
|
val voiceBroadcastEvent = getVoiceBroadcastEventUseCase.execute(voiceBroadcast)
|
||||||
val voiceBroadcastState = voiceBroadcastEvent?.content?.voiceBroadcastState
|
val voiceBroadcastState = voiceBroadcastEvent?.content?.voiceBroadcastState
|
||||||
|
@ -93,7 +104,7 @@ class GetLiveVoiceBroadcastChunksUseCase @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Automatically stop observing the timeline if the last chunk has been received
|
// Automatically stop observing the timeline if the last chunk has been received
|
||||||
if (lastSequence != null && newChunks.any { it.sequence == lastSequence }) {
|
if (lastSequence != null && newChunks.any { it.asMessageAudioEvent()?.sequence == lastSequence }) {
|
||||||
timeline.removeListener(this)
|
timeline.removeListener(this)
|
||||||
timeline.dispose()
|
timeline.dispose()
|
||||||
}
|
}
|
||||||
|
@ -109,8 +120,8 @@ class GetLiveVoiceBroadcastChunksUseCase @Inject constructor(
|
||||||
timeline.dispose()
|
timeline.dispose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.runningReduce { accumulator: List<MessageAudioEvent>, value: List<MessageAudioEvent> -> accumulator.plus(value) }
|
.runningReduce { accumulator: List<Event>, value: List<Event> -> accumulator.plus(value) }
|
||||||
.map { events -> events.distinctBy { it.sequence } }
|
.map { events -> events.distinctBy { it.eventId } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,12 +135,21 @@ class GetLiveVoiceBroadcastChunksUseCase @Inject constructor(
|
||||||
/**
|
/**
|
||||||
* Transform the list of [TimelineEvent] to a mapped list of [MessageAudioEvent] related to a given voice broadcast.
|
* Transform the list of [TimelineEvent] to a mapped list of [MessageAudioEvent] related to a given voice broadcast.
|
||||||
*/
|
*/
|
||||||
private fun List<TimelineEvent>.mapToChunkEvents(voiceBroadcastId: String, senderId: String?): List<MessageAudioEvent> =
|
private fun List<TimelineEvent>.mapToChunkEvents(voiceBroadcastId: String, senderId: String?): List<Event> =
|
||||||
this.mapNotNull { timelineEvent ->
|
this.mapNotNull { timelineEvent ->
|
||||||
timelineEvent.root.asMessageAudioEvent()
|
val event = timelineEvent.root
|
||||||
?.takeIf {
|
val relationContent = event.getRelationContent()
|
||||||
it.isVoiceBroadcast() && it.getVoiceBroadcastEventId() == voiceBroadcastId &&
|
when {
|
||||||
it.root.senderId == senderId
|
event.getClearType() == EventType.MESSAGE -> {
|
||||||
}
|
event.asMessageAudioEvent()
|
||||||
|
?.takeIf {
|
||||||
|
it.isVoiceBroadcast() && it.getVoiceBroadcastEventId() == voiceBroadcastId && it.root.senderId == senderId
|
||||||
|
}?.root
|
||||||
|
}
|
||||||
|
event.getClearType() == EventType.ENCRYPTED && relationContent?.type == RelationType.REFERENCE -> {
|
||||||
|
event.takeIf { relationContent.eventId == voiceBroadcastId }
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue