Create VoiceBroadcast model with roomId and eventId

This commit is contained in:
Florian Renaud 2022-11-03 16:17:55 +01:00
parent 20d62b14de
commit 6d850b3030
11 changed files with 93 additions and 65 deletions

View File

@ -20,6 +20,7 @@ import android.net.Uri
import android.view.View import android.view.View
import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.features.call.conference.ConferenceEvent import im.vector.app.features.call.conference.ConferenceEvent
import im.vector.app.features.voicebroadcast.model.VoiceBroadcast
import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.content.ContentAttachmentData
import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
@ -129,10 +130,10 @@ sealed class RoomDetailAction : VectorViewModelAction {
} }
sealed class Listening : VoiceBroadcastAction() { sealed class Listening : VoiceBroadcastAction() {
data class PlayOrResume(val voiceBroadcastId: String) : Listening() data class PlayOrResume(val voiceBroadcast: VoiceBroadcast) : Listening()
object Pause : Listening() object Pause : Listening()
object Stop : Listening() object Stop : Listening()
data class SeekTo(val voiceBroadcastId: String, val positionMillis: Int) : Listening() data class SeekTo(val voiceBroadcast: VoiceBroadcast, val positionMillis: Int) : Listening()
} }
} }
} }

View File

@ -634,10 +634,10 @@ class TimelineViewModel @AssistedInject constructor(
VoiceBroadcastAction.Recording.Pause -> voiceBroadcastHelper.pauseVoiceBroadcast(room.roomId) VoiceBroadcastAction.Recording.Pause -> voiceBroadcastHelper.pauseVoiceBroadcast(room.roomId)
VoiceBroadcastAction.Recording.Resume -> voiceBroadcastHelper.resumeVoiceBroadcast(room.roomId) VoiceBroadcastAction.Recording.Resume -> voiceBroadcastHelper.resumeVoiceBroadcast(room.roomId)
VoiceBroadcastAction.Recording.Stop -> voiceBroadcastHelper.stopVoiceBroadcast(room.roomId) VoiceBroadcastAction.Recording.Stop -> voiceBroadcastHelper.stopVoiceBroadcast(room.roomId)
is VoiceBroadcastAction.Listening.PlayOrResume -> voiceBroadcastHelper.playOrResumePlayback(room.roomId, action.voiceBroadcastId) is VoiceBroadcastAction.Listening.PlayOrResume -> voiceBroadcastHelper.playOrResumePlayback(action.voiceBroadcast)
VoiceBroadcastAction.Listening.Pause -> voiceBroadcastHelper.pausePlayback() VoiceBroadcastAction.Listening.Pause -> voiceBroadcastHelper.pausePlayback()
VoiceBroadcastAction.Listening.Stop -> voiceBroadcastHelper.stopPlayback() VoiceBroadcastAction.Listening.Stop -> voiceBroadcastHelper.stopPlayback()
is VoiceBroadcastAction.Listening.SeekTo -> voiceBroadcastHelper.seekTo(action.voiceBroadcastId, action.positionMillis) is VoiceBroadcastAction.Listening.SeekTo -> voiceBroadcastHelper.seekTo(action.voiceBroadcast, action.positionMillis)
} }
} }
} }

View File

@ -29,6 +29,7 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadca
import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastRecordingItem_ import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastRecordingItem_
import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayer import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayer
import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
import im.vector.app.features.voicebroadcast.model.VoiceBroadcast
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState 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 im.vector.app.features.voicebroadcast.recording.VoiceBroadcastRecorder import im.vector.app.features.voicebroadcast.recording.VoiceBroadcastRecorder
@ -60,14 +61,14 @@ class VoiceBroadcastItemFactory @Inject constructor(
val voiceBroadcastEventsGroup = params.eventsGroup?.let { VoiceBroadcastEventsGroup(it) } ?: return null val voiceBroadcastEventsGroup = params.eventsGroup?.let { VoiceBroadcastEventsGroup(it) } ?: return null
val voiceBroadcastEvent = voiceBroadcastEventsGroup.getLastDisplayableEvent().root.asVoiceBroadcastEvent() ?: return null val voiceBroadcastEvent = voiceBroadcastEventsGroup.getLastDisplayableEvent().root.asVoiceBroadcastEvent() ?: return null
val voiceBroadcastContent = voiceBroadcastEvent.content ?: return null val voiceBroadcastContent = voiceBroadcastEvent.content ?: return null
val voiceBroadcastId = voiceBroadcastEventsGroup.voiceBroadcastId val voiceBroadcast = VoiceBroadcast(voiceBroadcastId = voiceBroadcastEventsGroup.voiceBroadcastId, roomId = params.event.roomId)
val isRecording = voiceBroadcastContent.voiceBroadcastState != VoiceBroadcastState.STOPPED && val isRecording = voiceBroadcastContent.voiceBroadcastState != VoiceBroadcastState.STOPPED &&
voiceBroadcastEvent.root.stateKey == session.myUserId && voiceBroadcastEvent.root.stateKey == session.myUserId &&
messageContent.deviceId == session.sessionParams.deviceId messageContent.deviceId == session.sessionParams.deviceId
val voiceBroadcastAttributes = AbsMessageVoiceBroadcastItem.Attributes( val voiceBroadcastAttributes = AbsMessageVoiceBroadcastItem.Attributes(
voiceBroadcastId = voiceBroadcastId, voiceBroadcast = voiceBroadcast,
voiceBroadcastState = voiceBroadcastContent.voiceBroadcastState, voiceBroadcastState = voiceBroadcastContent.voiceBroadcastState,
duration = voiceBroadcastEventsGroup.getDuration(), duration = voiceBroadcastEventsGroup.getDuration(),
recorderName = params.event.root.stateKey?.let { session.getUserOrDefault(it) }?.toMatrixItem()?.getBestName().orEmpty(), recorderName = params.event.root.stateKey?.let { session.getUserOrDefault(it) }?.toMatrixItem()?.getBestName().orEmpty(),
@ -92,7 +93,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
voiceBroadcastAttributes: AbsMessageVoiceBroadcastItem.Attributes, voiceBroadcastAttributes: AbsMessageVoiceBroadcastItem.Attributes,
): MessageVoiceBroadcastRecordingItem { ): MessageVoiceBroadcastRecordingItem {
return MessageVoiceBroadcastRecordingItem_() return MessageVoiceBroadcastRecordingItem_()
.id("voice_broadcast_${voiceBroadcastAttributes.voiceBroadcastId}") .id("voice_broadcast_${voiceBroadcastAttributes.voiceBroadcast.voiceBroadcastId}")
.attributes(attributes) .attributes(attributes)
.voiceBroadcastAttributes(voiceBroadcastAttributes) .voiceBroadcastAttributes(voiceBroadcastAttributes)
.highlighted(highlight) .highlighted(highlight)
@ -105,7 +106,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
voiceBroadcastAttributes: AbsMessageVoiceBroadcastItem.Attributes, voiceBroadcastAttributes: AbsMessageVoiceBroadcastItem.Attributes,
): MessageVoiceBroadcastListeningItem { ): MessageVoiceBroadcastListeningItem {
return MessageVoiceBroadcastListeningItem_() return MessageVoiceBroadcastListeningItem_()
.id("voice_broadcast_${voiceBroadcastAttributes.voiceBroadcastId}") .id("voice_broadcast_${voiceBroadcastAttributes.voiceBroadcast.voiceBroadcastId}")
.attributes(attributes) .attributes(attributes)
.voiceBroadcastAttributes(voiceBroadcastAttributes) .voiceBroadcastAttributes(voiceBroadcastAttributes)
.highlighted(highlight) .highlighted(highlight)

View File

@ -27,6 +27,7 @@ import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.DrawableProvider import im.vector.app.core.resources.DrawableProvider
import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker
import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayer import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayer
import im.vector.app.features.voicebroadcast.model.VoiceBroadcast
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
import im.vector.app.features.voicebroadcast.recording.VoiceBroadcastRecorder import im.vector.app.features.voicebroadcast.recording.VoiceBroadcastRecorder
import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.MatrixItem
@ -36,7 +37,7 @@ abstract class AbsMessageVoiceBroadcastItem<H : AbsMessageVoiceBroadcastItem.Hol
@EpoxyAttribute @EpoxyAttribute
lateinit var voiceBroadcastAttributes: Attributes lateinit var voiceBroadcastAttributes: Attributes
protected val voiceBroadcastId get() = voiceBroadcastAttributes.voiceBroadcastId protected val voiceBroadcast get() = voiceBroadcastAttributes.voiceBroadcast
protected val voiceBroadcastState get() = voiceBroadcastAttributes.voiceBroadcastState protected val voiceBroadcastState get() = voiceBroadcastAttributes.voiceBroadcastState
protected val recorderName get() = voiceBroadcastAttributes.recorderName protected val recorderName get() = voiceBroadcastAttributes.recorderName
protected val recorder get() = voiceBroadcastAttributes.recorder protected val recorder get() = voiceBroadcastAttributes.recorder
@ -95,7 +96,7 @@ abstract class AbsMessageVoiceBroadcastItem<H : AbsMessageVoiceBroadcastItem.Hol
} }
data class Attributes( data class Attributes(
val voiceBroadcastId: String, val voiceBroadcast: VoiceBroadcast,
val voiceBroadcastState: VoiceBroadcastState?, val voiceBroadcastState: VoiceBroadcastState?,
val duration: Int, val duration: Int,
val recorderName: String, val recorderName: String,

View File

@ -46,7 +46,7 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageVoiceBroadcastItem
playerListener = VoiceBroadcastPlayer.Listener { state -> playerListener = VoiceBroadcastPlayer.Listener { state ->
renderPlayingState(holder, state) renderPlayingState(holder, state)
} }
player.addListener(voiceBroadcastId, playerListener) player.addListener(voiceBroadcast, playerListener)
bindSeekBar(holder) bindSeekBar(holder)
} }
@ -77,7 +77,7 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageVoiceBroadcastItem
VoiceBroadcastPlayer.State.PAUSED -> { VoiceBroadcastPlayer.State.PAUSED -> {
playPauseButton.setImageResource(R.drawable.ic_play_pause_play) playPauseButton.setImageResource(R.drawable.ic_play_pause_play)
playPauseButton.contentDescription = view.resources.getString(R.string.a11y_play_voice_broadcast) playPauseButton.contentDescription = view.resources.getString(R.string.a11y_play_voice_broadcast)
playPauseButton.onClick { callback?.onTimelineItemAction(VoiceBroadcastAction.Listening.PlayOrResume(voiceBroadcastId)) } playPauseButton.onClick { callback?.onTimelineItemAction(VoiceBroadcastAction.Listening.PlayOrResume(voiceBroadcast)) }
seekBar.isEnabled = false seekBar.isEnabled = false
} }
VoiceBroadcastPlayer.State.BUFFERING -> { VoiceBroadcastPlayer.State.BUFFERING -> {
@ -98,11 +98,11 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageVoiceBroadcastItem
} }
override fun onStopTrackingTouch(seekBar: SeekBar) { override fun onStopTrackingTouch(seekBar: SeekBar) {
callback?.onTimelineItemAction(VoiceBroadcastAction.Listening.SeekTo(voiceBroadcastId, seekBar.progress)) callback?.onTimelineItemAction(VoiceBroadcastAction.Listening.SeekTo(voiceBroadcast, seekBar.progress))
isUserSeeking = false isUserSeeking = false
} }
}) })
playbackTracker.track(voiceBroadcastId, object : AudioMessagePlaybackTracker.Listener { playbackTracker.track(voiceBroadcast.voiceBroadcastId, object : AudioMessagePlaybackTracker.Listener {
override fun onUpdate(state: AudioMessagePlaybackTracker.Listener.State) { override fun onUpdate(state: AudioMessagePlaybackTracker.Listener.State) {
when (state) { when (state) {
is AudioMessagePlaybackTracker.Listener.State.Paused -> { is AudioMessagePlaybackTracker.Listener.State.Paused -> {
@ -126,9 +126,9 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageVoiceBroadcastItem
override fun unbind(holder: Holder) { override fun unbind(holder: Holder) {
super.unbind(holder) super.unbind(holder)
player.removeListener(voiceBroadcastId, playerListener) player.removeListener(voiceBroadcast, playerListener)
holder.seekBar.setOnSeekBarChangeListener(null) holder.seekBar.setOnSeekBarChangeListener(null)
playbackTracker.untrack(voiceBroadcastId) playbackTracker.untrack(voiceBroadcast.voiceBroadcastId)
} }
override fun getViewStubId() = STUB_ID override fun getViewStubId() = STUB_ID

View File

@ -17,6 +17,7 @@
package im.vector.app.features.voicebroadcast package im.vector.app.features.voicebroadcast
import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayer import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayer
import im.vector.app.features.voicebroadcast.model.VoiceBroadcast
import im.vector.app.features.voicebroadcast.recording.usecase.PauseVoiceBroadcastUseCase import im.vector.app.features.voicebroadcast.recording.usecase.PauseVoiceBroadcastUseCase
import im.vector.app.features.voicebroadcast.recording.usecase.ResumeVoiceBroadcastUseCase import im.vector.app.features.voicebroadcast.recording.usecase.ResumeVoiceBroadcastUseCase
import im.vector.app.features.voicebroadcast.recording.usecase.StartVoiceBroadcastUseCase import im.vector.app.features.voicebroadcast.recording.usecase.StartVoiceBroadcastUseCase
@ -41,14 +42,14 @@ class VoiceBroadcastHelper @Inject constructor(
suspend fun stopVoiceBroadcast(roomId: String) = stopVoiceBroadcastUseCase.execute(roomId) suspend fun stopVoiceBroadcast(roomId: String) = stopVoiceBroadcastUseCase.execute(roomId)
fun playOrResumePlayback(roomId: String, voiceBroadcastId: String) = voiceBroadcastPlayer.playOrResume(roomId, voiceBroadcastId) fun playOrResumePlayback(voiceBroadcast: VoiceBroadcast) = voiceBroadcastPlayer.playOrResume(voiceBroadcast)
fun pausePlayback() = voiceBroadcastPlayer.pause() fun pausePlayback() = voiceBroadcastPlayer.pause()
fun stopPlayback() = voiceBroadcastPlayer.stop() fun stopPlayback() = voiceBroadcastPlayer.stop()
fun seekTo(voiceBroadcastId: String, positionMillis: Int) { fun seekTo(voiceBroadcast: VoiceBroadcast, positionMillis: Int) {
if (voiceBroadcastPlayer.currentVoiceBroadcastId == voiceBroadcastId) { if (voiceBroadcastPlayer.currentVoiceBroadcast == voiceBroadcast) {
voiceBroadcastPlayer.seekTo(positionMillis) voiceBroadcastPlayer.seekTo(positionMillis)
} }
} }

View File

@ -16,12 +16,14 @@
package im.vector.app.features.voicebroadcast.listening package im.vector.app.features.voicebroadcast.listening
import im.vector.app.features.voicebroadcast.model.VoiceBroadcast
interface VoiceBroadcastPlayer { interface VoiceBroadcastPlayer {
/** /**
* The current playing voice broadcast identifier, if any. * The current playing voice broadcast, if any.
*/ */
val currentVoiceBroadcastId: String? val currentVoiceBroadcast: VoiceBroadcast?
/** /**
* The current playing [State], [State.IDLE] by default. * The current playing [State], [State.IDLE] by default.
@ -31,7 +33,7 @@ interface VoiceBroadcastPlayer {
/** /**
* Start playback of the given voice broadcast. * Start playback of the given voice broadcast.
*/ */
fun playOrResume(roomId: String, voiceBroadcastId: String) fun playOrResume(voiceBroadcast: VoiceBroadcast)
/** /**
* Pause playback of the current voice broadcast, if any. * Pause playback of the current voice broadcast, if any.
@ -49,14 +51,14 @@ interface VoiceBroadcastPlayer {
fun seekTo(positionMillis: Int) fun seekTo(positionMillis: Int)
/** /**
* Add a [Listener] to the given voice broadcast id. * Add a [Listener] to the given voice broadcast.
*/ */
fun addListener(voiceBroadcastId: String, listener: Listener) fun addListener(voiceBroadcast: VoiceBroadcast, listener: Listener)
/** /**
* Remove a [Listener] from the given voice broadcast id. * Remove a [Listener] from the given voice broadcast.
*/ */
fun removeListener(voiceBroadcastId: String, listener: Listener) fun removeListener(voiceBroadcast: VoiceBroadcast, listener: Listener)
/** /**
* Player states. * Player states.

View File

@ -26,9 +26,10 @@ import im.vector.app.features.voicebroadcast.duration
import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayer.Listener import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayer.Listener
import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayer.State import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayer.State
import im.vector.app.features.voicebroadcast.listening.usecase.GetLiveVoiceBroadcastChunksUseCase import im.vector.app.features.voicebroadcast.listening.usecase.GetLiveVoiceBroadcastChunksUseCase
import im.vector.app.features.voicebroadcast.model.VoiceBroadcast
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
import im.vector.app.features.voicebroadcast.sequence import im.vector.app.features.voicebroadcast.sequence
import im.vector.app.features.voicebroadcast.usecase.GetVoiceBroadcastUseCase import im.vector.app.features.voicebroadcast.usecase.GetVoiceBroadcastEventUseCase
import im.vector.lib.core.utils.timer.CountUpTimer import im.vector.lib.core.utils.timer.CountUpTimer
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -51,7 +52,7 @@ import javax.inject.Singleton
class VoiceBroadcastPlayerImpl @Inject constructor( class VoiceBroadcastPlayerImpl @Inject constructor(
private val sessionHolder: ActiveSessionHolder, private val sessionHolder: ActiveSessionHolder,
private val playbackTracker: AudioMessagePlaybackTracker, private val playbackTracker: AudioMessagePlaybackTracker,
private val getVoiceBroadcastUseCase: GetVoiceBroadcastUseCase, private val getVoiceBroadcastEventUseCase: GetVoiceBroadcastEventUseCase,
private val getLiveVoiceBroadcastChunksUseCase: GetLiveVoiceBroadcastChunksUseCase private val getLiveVoiceBroadcastChunksUseCase: GetLiveVoiceBroadcastChunksUseCase
) : VoiceBroadcastPlayer { ) : VoiceBroadcastPlayer {
@ -73,7 +74,7 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
private var isLive: Boolean = false private var isLive: Boolean = false
override var currentVoiceBroadcastId: String? = null override var currentVoiceBroadcast: VoiceBroadcast? = null
override var playingState = State.IDLE override var playingState = State.IDLE
@MainThread @MainThread
@ -81,7 +82,7 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
Timber.w("## VoiceBroadcastPlayer state: $field -> $value") Timber.w("## VoiceBroadcastPlayer state: $field -> $value")
field = value field = value
// Notify state change to all the listeners attached to the current voice broadcast id // Notify state change to all the listeners attached to the current voice broadcast id
currentVoiceBroadcastId?.let { voiceBroadcastId -> currentVoiceBroadcast?.voiceBroadcastId?.let { voiceBroadcastId ->
when (value) { when (value) {
State.PLAYING -> { State.PLAYING -> {
playbackTracker.startPlayback(voiceBroadcastId) playbackTracker.startPlayback(voiceBroadcastId)
@ -103,17 +104,16 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
listeners[voiceBroadcastId]?.forEach { listener -> listener.onStateChanged(value) } listeners[voiceBroadcastId]?.forEach { listener -> listener.onStateChanged(value) }
} }
} }
private var currentRoomId: String? = null
/** /**
* Map voiceBroadcastId to listeners. * Map voiceBroadcastId to listeners.
*/ */
private val listeners: MutableMap<String, CopyOnWriteArrayList<Listener>> = mutableMapOf() private val listeners: MutableMap<String, CopyOnWriteArrayList<Listener>> = mutableMapOf()
override fun playOrResume(roomId: String, voiceBroadcastId: String) { override fun playOrResume(voiceBroadcast: VoiceBroadcast) {
val hasChanged = currentVoiceBroadcastId != voiceBroadcastId val hasChanged = currentVoiceBroadcast != voiceBroadcast
when { when {
hasChanged -> startPlayback(roomId, voiceBroadcastId) hasChanged -> startPlayback(voiceBroadcast)
playingState == State.PAUSED -> resumePlayback() playingState == State.PAUSED -> resumePlayback()
else -> Unit else -> Unit
} }
@ -152,37 +152,35 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
playlist = emptyList() playlist = emptyList()
currentSequence = null currentSequence = null
currentRoomId = null currentVoiceBroadcast = null
currentVoiceBroadcastId = null
} }
override fun addListener(voiceBroadcastId: String, listener: Listener) { override fun addListener(voiceBroadcast: VoiceBroadcast, listener: Listener) {
listeners[voiceBroadcastId]?.add(listener) ?: run { listeners[voiceBroadcast.voiceBroadcastId]?.add(listener) ?: run {
listeners[voiceBroadcastId] = CopyOnWriteArrayList<Listener>().apply { add(listener) } listeners[voiceBroadcast.voiceBroadcastId] = CopyOnWriteArrayList<Listener>().apply { add(listener) }
} }
if (voiceBroadcastId == currentVoiceBroadcastId) listener.onStateChanged(playingState) else listener.onStateChanged(State.IDLE) listener.onStateChanged(if (voiceBroadcast == currentVoiceBroadcast) playingState else State.IDLE)
} }
override fun removeListener(voiceBroadcastId: String, listener: Listener) { override fun removeListener(voiceBroadcast: VoiceBroadcast, listener: Listener) {
listeners[voiceBroadcastId]?.remove(listener) listeners[voiceBroadcast.voiceBroadcastId]?.remove(listener)
} }
private fun startPlayback(roomId: String, eventId: String) { private fun startPlayback(voiceBroadcast: VoiceBroadcast) {
// Stop listening previous voice broadcast if any // Stop listening previous voice broadcast if any
if (playingState != State.IDLE) stop() if (playingState != State.IDLE) stop()
currentRoomId = roomId currentVoiceBroadcast = voiceBroadcast
currentVoiceBroadcastId = eventId
playingState = State.BUFFERING playingState = State.BUFFERING
val voiceBroadcastState = getVoiceBroadcastUseCase.execute(roomId, eventId)?.content?.voiceBroadcastState val voiceBroadcastState = getVoiceBroadcastEventUseCase.execute(voiceBroadcast)?.content?.voiceBroadcastState
isLive = voiceBroadcastState != null && voiceBroadcastState != VoiceBroadcastState.STOPPED isLive = voiceBroadcastState != null && voiceBroadcastState != VoiceBroadcastState.STOPPED
fetchPlaylistAndStartPlayback(roomId, eventId) fetchPlaylistAndStartPlayback(voiceBroadcast)
} }
private fun fetchPlaylistAndStartPlayback(roomId: String, voiceBroadcastId: String) { private fun fetchPlaylistAndStartPlayback(voiceBroadcast: VoiceBroadcast) {
fetchPlaylistJob = getLiveVoiceBroadcastChunksUseCase.execute(roomId, voiceBroadcastId) fetchPlaylistJob = getLiveVoiceBroadcastChunksUseCase.execute(voiceBroadcast)
.onEach(this::updatePlaylist) .onEach(this::updatePlaylist)
.launchIn(coroutineScope) .launchIn(coroutineScope)
} }
@ -347,9 +345,8 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
override fun onCompletion(mp: MediaPlayer) { override fun onCompletion(mp: MediaPlayer) {
if (nextMediaPlayer != null) return if (nextMediaPlayer != null) return
val roomId = currentRoomId ?: return val voiceBroadcast = currentVoiceBroadcast ?: return
val voiceBroadcastId = currentVoiceBroadcastId ?: return val voiceBroadcastEventContent = getVoiceBroadcastEventUseCase.execute(voiceBroadcast)?.content ?: return
val voiceBroadcastEventContent = getVoiceBroadcastUseCase.execute(roomId, voiceBroadcastId)?.content ?: return
isLive = voiceBroadcastEventContent.voiceBroadcastState != null && voiceBroadcastEventContent.voiceBroadcastState != VoiceBroadcastState.STOPPED isLive = voiceBroadcastEventContent.voiceBroadcastState != null && voiceBroadcastEventContent.voiceBroadcastState != VoiceBroadcastState.STOPPED
if (!isLive && voiceBroadcastEventContent.lastChunkSequence == currentSequence) { if (!isLive && voiceBroadcastEventContent.lastChunkSequence == currentSequence) {

View File

@ -19,11 +19,12 @@ package im.vector.app.features.voicebroadcast.listening.usecase
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.features.voicebroadcast.getVoiceBroadcastEventId import im.vector.app.features.voicebroadcast.getVoiceBroadcastEventId
import im.vector.app.features.voicebroadcast.isVoiceBroadcast import im.vector.app.features.voicebroadcast.isVoiceBroadcast
import im.vector.app.features.voicebroadcast.model.VoiceBroadcast
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastEvent import im.vector.app.features.voicebroadcast.model.VoiceBroadcastEvent
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState 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 im.vector.app.features.voicebroadcast.sequence import im.vector.app.features.voicebroadcast.sequence
import im.vector.app.features.voicebroadcast.usecase.GetVoiceBroadcastUseCase import im.vector.app.features.voicebroadcast.usecase.GetVoiceBroadcastEventUseCase
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.callbackFlow
@ -44,19 +45,19 @@ import javax.inject.Inject
*/ */
class GetLiveVoiceBroadcastChunksUseCase @Inject constructor( class GetLiveVoiceBroadcastChunksUseCase @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder, private val activeSessionHolder: ActiveSessionHolder,
private val getVoiceBroadcastUseCase: GetVoiceBroadcastUseCase, private val getVoiceBroadcastEventUseCase: GetVoiceBroadcastEventUseCase,
) { ) {
fun execute(roomId: String, voiceBroadcastId: String): Flow<List<MessageAudioEvent>> { fun execute(voiceBroadcast: VoiceBroadcast): Flow<List<MessageAudioEvent>> {
val session = activeSessionHolder.getSafeActiveSession() ?: return emptyFlow() val session = activeSessionHolder.getSafeActiveSession() ?: return emptyFlow()
val room = session.roomService().getRoom(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, voiceBroadcastId) val existingChunks = room.timelineService().getTimelineEventsRelatedTo(RelationType.REFERENCE, voiceBroadcast.voiceBroadcastId)
.mapNotNull { timelineEvent -> timelineEvent.root.asMessageAudioEvent().takeIf { it.isVoiceBroadcast() } } .mapNotNull { timelineEvent -> timelineEvent.root.asMessageAudioEvent().takeIf { it.isVoiceBroadcast() } }
val voiceBroadcastEvent = getVoiceBroadcastUseCase.execute(roomId, voiceBroadcastId) val voiceBroadcastEvent = getVoiceBroadcastEventUseCase.execute(voiceBroadcast)
val voiceBroadcastState = voiceBroadcastEvent?.content?.voiceBroadcastState val voiceBroadcastState = voiceBroadcastEvent?.content?.voiceBroadcastState
return if (voiceBroadcastState == null || voiceBroadcastState == VoiceBroadcastState.STOPPED) { return if (voiceBroadcastState == null || voiceBroadcastState == VoiceBroadcastState.STOPPED) {
@ -82,7 +83,7 @@ class GetLiveVoiceBroadcastChunksUseCase @Inject constructor(
lastSequence = stopEvent.content?.lastChunkSequence lastSequence = stopEvent.content?.lastChunkSequence
} }
val newChunks = newEvents.mapToChunkEvents(voiceBroadcastId, voiceBroadcastEvent.root.senderId) val newChunks = newEvents.mapToChunkEvents(voiceBroadcast.voiceBroadcastId, voiceBroadcastEvent.root.senderId)
// Notify about new chunks // Notify about new chunks
if (newChunks.isNotEmpty()) { if (newChunks.isNotEmpty()) {

View File

@ -0,0 +1,22 @@
/*
* 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.voicebroadcast.model
data class VoiceBroadcast(
val voiceBroadcastId: String,
val roomId: String,
)

View File

@ -16,6 +16,7 @@
package im.vector.app.features.voicebroadcast.usecase package im.vector.app.features.voicebroadcast.usecase
import im.vector.app.features.voicebroadcast.model.VoiceBroadcast
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastEvent import im.vector.app.features.voicebroadcast.model.VoiceBroadcastEvent
import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
@ -24,17 +25,18 @@ import org.matrix.android.sdk.api.session.getRoom
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class GetVoiceBroadcastUseCase @Inject constructor( class GetVoiceBroadcastEventUseCase @Inject constructor(
private val session: Session, private val session: Session,
) { ) {
fun execute(roomId: String, eventId: String): VoiceBroadcastEvent? { fun execute(voiceBroadcast: VoiceBroadcast): VoiceBroadcastEvent? {
val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId") val room = session.getRoom(voiceBroadcast.roomId) ?: error("Unknown roomId: ${voiceBroadcast.roomId}")
Timber.d("## GetVoiceBroadcastUseCase: get voice broadcast $eventId") Timber.d("## GetVoiceBroadcastUseCase: get voice broadcast $voiceBroadcast")
val initialEvent = room.timelineService().getTimelineEvent(eventId)?.root?.asVoiceBroadcastEvent() // Fallback to initial event val initialEvent = room.timelineService().getTimelineEvent(voiceBroadcast.voiceBroadcastId)?.root?.asVoiceBroadcastEvent()
val relatedEvents = room.timelineService().getTimelineEventsRelatedTo(RelationType.REFERENCE, eventId).sortedBy { it.root.originServerTs } val relatedEvents = room.timelineService().getTimelineEventsRelatedTo(RelationType.REFERENCE, voiceBroadcast.voiceBroadcastId)
.sortedBy { it.root.originServerTs }
return relatedEvents.mapNotNull { it.root.asVoiceBroadcastEvent() }.lastOrNull() ?: initialEvent return relatedEvents.mapNotNull { it.root.asVoiceBroadcastEvent() }.lastOrNull() ?: initialEvent
} }
} }