diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 69b4d57e28..ea9b4b5999 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3082,6 +3082,9 @@
Resume voice broadcast record
Pause voice broadcast record
Stop voice broadcast record
+ Play or resume voice broadcast
+ Pause voice broadcast
+ Buffering
Anyone in %s will be able to find and join this room - no need to manually invite everyone. You’ll be able to change this in room settings anytime.
Anyone in a parent space will be able to find and join this room - no need to manually invite everyone. You’ll be able to change this in room settings anytime.
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
index 13a38ac4be..af14d532c8 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
@@ -27,6 +27,7 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadca
import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastListeningItem_
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.VoiceBroadcastPlayer
import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
@@ -42,6 +43,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
private val colorProvider: ColorProvider,
private val drawableProvider: DrawableProvider,
private val voiceBroadcastRecorder: VoiceBroadcastRecorder?,
+ private val voiceBroadcastPlayer: VoiceBroadcastPlayer,
) {
fun create(
@@ -53,7 +55,8 @@ class VoiceBroadcastItemFactory @Inject constructor(
): VectorEpoxyModel? {
// Only display item of the initial event with updated data
if (messageContent.voiceBroadcastState != VoiceBroadcastState.STARTED) return null
- val voiceBroadcastEventsGroup = params.eventsGroup?.let { VoiceBroadcastEventsGroup(it) } ?: return null
+ val eventsGroup = params.eventsGroup ?: return null
+ val voiceBroadcastEventsGroup = VoiceBroadcastEventsGroup(eventsGroup)
val mostRecentTimelineEvent = voiceBroadcastEventsGroup.getLastDisplayableEvent()
val mostRecentEvent = mostRecentTimelineEvent.root.asVoiceBroadcastEvent()
val mostRecentMessageContent = mostRecentEvent?.content ?: return null
@@ -61,7 +64,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
return if (isRecording) {
createRecordingItem(params.event.roomId, highlight, callback, attributes)
} else {
- createListeningItem(params.event.roomId, highlight, callback, attributes)
+ createListeningItem(params.event.roomId, eventsGroup.groupId, highlight, callback, attributes)
}
}
@@ -85,6 +88,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
private fun createListeningItem(
roomId: String,
+ voiceBroadcastId: String,
highlight: Boolean,
callback: TimelineEventController.Callback?,
attributes: AbsMessageItem.Attributes,
@@ -96,7 +100,8 @@ class VoiceBroadcastItemFactory @Inject constructor(
.roomItem(roomSummary?.toMatrixItem())
.colorProvider(colorProvider)
.drawableProvider(drawableProvider)
- .voiceBroadcastRecorder(voiceBroadcastRecorder)
+ .voiceBroadcastPlayer(voiceBroadcastPlayer)
+ .voiceBroadcastId(voiceBroadcastId)
.leftGuideline(avatarSizeProvider.leftGuideline)
.callback(callback)
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
index e5d0fd6c30..3a090b0eb6 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
@@ -16,6 +16,7 @@
package im.vector.app.features.home.room.detail.timeline.item
+import android.view.View
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.TextView
@@ -23,12 +24,11 @@ import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
-import im.vector.app.core.extensions.tintBackground
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.DrawableProvider
-import im.vector.app.features.home.room.detail.RoomDetailAction.VoiceBroadcastAction
+import im.vector.app.features.home.room.detail.RoomDetailAction
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
-import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
+import im.vector.app.features.voicebroadcast.VoiceBroadcastPlayer
import org.matrix.android.sdk.api.util.MatrixItem
@EpoxyModelClass
@@ -38,7 +38,10 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem
+ renderState(holder, state)
}
- voiceBroadcastRecorder?.addListener(recorderListener)
+ voiceBroadcastPlayer?.addListener(playerListener)
renderHeader(holder)
}
@@ -80,45 +81,59 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem {
- stopRecordButton.isEnabled = true
-
- liveIndicator.isVisible = true
- liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.colorOnError))
-
- val drawableColor = colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
- val drawable = drawableProvider.getDrawable(R.drawable.ic_play_pause_pause, drawableColor)
- recordButton.setImageDrawable(drawable)
- recordButton.contentDescription = holder.view.resources.getString(R.string.a11y_pause_voice_broadcast_record)
- recordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Pause) }
- stopRecordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Stop) }
+ VoiceBroadcastPlayer.State.PLAYING -> {
+ playPauseButton.setImageResource(R.drawable.ic_play_pause_pause)
+ playPauseButton.contentDescription = view.resources.getString(R.string.a11y_play_voice_broadcast)
+ playPauseButton.setOnClickListener { attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Listening.Pause) }
}
- VoiceBroadcastRecorder.State.Paused -> {
- stopRecordButton.isEnabled = true
-
- liveIndicator.isVisible = true
- liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.vctr_content_quaternary))
-
- recordButton.setImageResource(R.drawable.ic_recording_dot)
- recordButton.contentDescription = holder.view.resources.getString(R.string.a11y_resume_voice_broadcast_record)
- recordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Resume) }
- stopRecordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Stop) }
- }
- VoiceBroadcastRecorder.State.Idle -> {
- recordButton.isEnabled = false
- stopRecordButton.isEnabled = false
- liveIndicator.isVisible = false
+ VoiceBroadcastPlayer.State.IDLE,
+ VoiceBroadcastPlayer.State.PAUSED -> {
+ playPauseButton.setImageResource(R.drawable.ic_play_pause_play)
+ playPauseButton.contentDescription = view.resources.getString(R.string.a11y_pause_voice_broadcast)
+ playPauseButton.setOnClickListener {
+ attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Listening.PlayOrResume(voiceBroadcastId))
+ }
}
+ VoiceBroadcastPlayer.State.BUFFERING -> Unit
}
}
}
+ private fun renderInactiveMedia(holder: Holder) {
+ with(holder) {
+ liveIndicator.isVisible = false
+ bufferingView.isVisible = false
+ playPauseButton.isVisible = true
+ playPauseButton.setImageResource(R.drawable.ic_play_pause_play)
+ playPauseButton.contentDescription = view.resources.getString(R.string.a11y_pause_voice_broadcast)
+ playPauseButton.setOnClickListener {
+ attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Listening.PlayOrResume(voiceBroadcastId))
+ }
+ }
+ }
+
+ private fun isCurrentMediaActive() = voiceBroadcastPlayer?.currentVoiceBroadcastId == voiceBroadcastId
+
override fun unbind(holder: Holder) {
super.unbind(holder)
- voiceBroadcastRecorder?.removeListener(recorderListener)
+ voiceBroadcastPlayer?.removeListener(playerListener)
}
override fun getViewStubId() = STUB_ID
@@ -127,8 +142,8 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem(R.id.liveIndicator)
val roomAvatarImageView by bind(R.id.roomAvatarImageView)
val titleText by bind(R.id.titleText)
- val recordButton by bind(R.id.recordButton)
- val stopRecordButton by bind(R.id.stopRecordButton)
+ val playPauseButton by bind(R.id.playPauseButton)
+ val bufferingView by bind(R.id.bufferingView)
}
companion object {
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
index 62252570c6..9c118f957d 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
@@ -73,15 +73,17 @@ class VoiceBroadcastPlayer @Inject constructor(
private var currentSequence: Int? = null
private var playlist = emptyList()
- private val currentVoiceBroadcastId
+ val currentVoiceBroadcastId
get() = playlist.firstOrNull()?.root?.getRelationContent()?.eventId
private var state: State = State.IDLE
set(value) {
Timber.w("## VoiceBroadcastPlayer state: $field -> $value")
field = value
+ listeners.forEach { it.onStateChanged(value) }
}
private var currentRoomId: String? = null
+ private var listeners = mutableListOf()
fun playOrResume(roomId: String, eventId: String) {
val hasChanged = currentVoiceBroadcastId != eventId
@@ -128,6 +130,15 @@ class VoiceBroadcastPlayer @Inject constructor(
currentRoomId = null
}
+ fun addListener(listener: Listener) {
+ listeners.add(listener)
+ listener.onStateChanged(state)
+ }
+
+ fun removeListener(listener: Listener) {
+ listeners.remove(listener)
+ }
+
private fun startPlayback(roomId: String, eventId: String) {
val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId")
currentRoomId = roomId
@@ -316,4 +327,8 @@ class VoiceBroadcastPlayer @Inject constructor(
BUFFERING,
IDLE
}
+
+ fun interface Listener {
+ fun onStateChanged(state: State)
+ }
}
diff --git a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml
index 6773280ba5..506ca4ff47 100644
--- a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml
@@ -65,29 +65,27 @@
app:constraint_referenced_ids="roomAvatarImageView,titleText" />
+
+
-
-