mirror of
https://github.com/element-hq/element-android
synced 2024-11-23 18:05:36 +03:00
VoiceBroadcast - Listening view
This commit is contained in:
parent
f1b4ebbc37
commit
f711a0ea74
5 changed files with 98 additions and 62 deletions
|
@ -3082,6 +3082,9 @@
|
|||
<string name="a11y_resume_voice_broadcast_record">Resume voice broadcast record</string>
|
||||
<string name="a11y_pause_voice_broadcast_record">Pause voice broadcast record</string>
|
||||
<string name="a11y_stop_voice_broadcast_record">Stop voice broadcast record</string>
|
||||
<string name="a11y_play_voice_broadcast">Play or resume voice broadcast</string>
|
||||
<string name="a11y_pause_voice_broadcast">Pause voice broadcast</string>
|
||||
<string name="a11y_voice_broadcast_buffering">Buffering</string>
|
||||
|
||||
<string name="upgrade_room_for_restricted">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.</string>
|
||||
<string name="upgrade_room_for_restricted_no_param">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.</string>
|
||||
|
|
|
@ -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<out VectorEpoxyHolder>? {
|
||||
// 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)
|
||||
}
|
||||
|
|
|
@ -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<MessageVoiceB
|
|||
var callback: TimelineEventController.Callback? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var voiceBroadcastRecorder: VoiceBroadcastRecorder? = null
|
||||
var voiceBroadcastPlayer: VoiceBroadcastPlayer? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
lateinit var voiceBroadcastId: String
|
||||
|
||||
@EpoxyAttribute
|
||||
lateinit var colorProvider: ColorProvider
|
||||
|
@ -52,7 +55,7 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
|
|||
@EpoxyAttribute
|
||||
var title: String? = null
|
||||
|
||||
private lateinit var recorderListener: VoiceBroadcastRecorder.Listener
|
||||
private lateinit var playerListener: VoiceBroadcastPlayer.Listener
|
||||
|
||||
override fun isCacheable(): Boolean = false
|
||||
|
||||
|
@ -62,12 +65,10 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
|
|||
}
|
||||
|
||||
private fun bindVoiceBroadcastItem(holder: Holder) {
|
||||
recorderListener = object : VoiceBroadcastRecorder.Listener {
|
||||
override fun onStateUpdated(state: VoiceBroadcastRecorder.State) {
|
||||
renderState(holder, state)
|
||||
}
|
||||
playerListener = VoiceBroadcastPlayer.Listener { state ->
|
||||
renderState(holder, state)
|
||||
}
|
||||
voiceBroadcastRecorder?.addListener(recorderListener)
|
||||
voiceBroadcastPlayer?.addListener(playerListener)
|
||||
renderHeader(holder)
|
||||
}
|
||||
|
||||
|
@ -80,45 +81,59 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
|
|||
}
|
||||
}
|
||||
|
||||
private fun renderState(holder: Holder, state: VoiceBroadcastRecorder.State) {
|
||||
private fun renderState(holder: Holder, state: VoiceBroadcastPlayer.State) {
|
||||
if (isCurrentMediaActive()) {
|
||||
renderActiveMedia(holder, state)
|
||||
} else {
|
||||
renderInactiveMedia(holder)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun renderActiveMedia(holder: Holder, state: VoiceBroadcastPlayer.State) {
|
||||
with(holder) {
|
||||
bufferingView.isVisible = state == VoiceBroadcastPlayer.State.BUFFERING
|
||||
playPauseButton.isVisible = state != VoiceBroadcastPlayer.State.BUFFERING
|
||||
liveIndicator.isVisible = false
|
||||
// liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.colorOnError))
|
||||
|
||||
when (state) {
|
||||
VoiceBroadcastRecorder.State.Recording -> {
|
||||
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<MessageVoiceB
|
|||
val liveIndicator by bind<TextView>(R.id.liveIndicator)
|
||||
val roomAvatarImageView by bind<ImageView>(R.id.roomAvatarImageView)
|
||||
val titleText by bind<TextView>(R.id.titleText)
|
||||
val recordButton by bind<ImageButton>(R.id.recordButton)
|
||||
val stopRecordButton by bind<ImageButton>(R.id.stopRecordButton)
|
||||
val playPauseButton by bind<ImageButton>(R.id.playPauseButton)
|
||||
val bufferingView by bind<View>(R.id.bufferingView)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -73,15 +73,17 @@ class VoiceBroadcastPlayer @Inject constructor(
|
|||
private var currentSequence: Int? = null
|
||||
|
||||
private var playlist = emptyList<MessageAudioEvent>()
|
||||
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<Listener>()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,29 +65,27 @@
|
|||
app:constraint_referenced_ids="roomAvatarImageView,titleText" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/recordButton"
|
||||
android:id="@+id/playPauseButton"
|
||||
android:layout_width="@dimen/voice_broadcast_controller_button_size"
|
||||
android:layout_height="@dimen/voice_broadcast_controller_button_size"
|
||||
android:background="@drawable/bg_rounded_button"
|
||||
android:backgroundTint="?vctr_system"
|
||||
android:contentDescription="@string/a11y_resume_voice_broadcast_record"
|
||||
android:src="@drawable/ic_recording_dot"
|
||||
android:contentDescription="@string/a11y_play_voice_broadcast"
|
||||
android:src="@drawable/ic_play_pause_play"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/stopRecordButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/headerBottomBarrier"
|
||||
app:tint="?vctr_content_secondary" />
|
||||
|
||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
android:id="@+id/bufferingView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/a11y_voice_broadcast_buffering"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/playPauseButton"
|
||||
app:layout_constraintTop_toBottomOf="@id/headerBottomBarrier" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/stopRecordButton"
|
||||
android:layout_width="@dimen/voice_broadcast_controller_button_size"
|
||||
android:layout_height="@dimen/voice_broadcast_controller_button_size"
|
||||
android:background="@drawable/bg_rounded_button"
|
||||
android:backgroundTint="?vctr_system"
|
||||
android:contentDescription="@string/a11y_stop_voice_broadcast_record"
|
||||
android:src="@drawable/ic_stop"
|
||||
app:layout_constraintBottom_toBottomOf="@id/recordButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/recordButton"
|
||||
app:layout_constraintTop_toTopOf="@id/recordButton" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
Loading…
Reference in a new issue