mirror of
https://github.com/element-hq/element-android
synced 2024-11-23 18:05:36 +03:00
Improve live indicator
This commit is contained in:
parent
0d3c779455
commit
6ee1e86951
6 changed files with 68 additions and 21 deletions
|
@ -68,25 +68,26 @@ abstract class AbsMessageVoiceBroadcastItem<H : AbsMessageVoiceBroadcastItem.Hol
|
||||||
renderMetadata(holder)
|
renderMetadata(holder)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderLiveIndicator(holder: H) {
|
abstract fun renderLiveIndicator(holder: H)
|
||||||
|
|
||||||
|
protected fun renderPlayingLiveIndicator(holder: H) {
|
||||||
with(holder) {
|
with(holder) {
|
||||||
when (voiceBroadcastState) {
|
liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.colorError))
|
||||||
VoiceBroadcastState.STARTED,
|
liveIndicator.isVisible = true
|
||||||
VoiceBroadcastState.RESUMED -> {
|
|
||||||
liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.colorError))
|
|
||||||
liveIndicator.isVisible = true
|
|
||||||
}
|
|
||||||
VoiceBroadcastState.PAUSED -> {
|
|
||||||
liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.vctr_content_quaternary))
|
|
||||||
liveIndicator.isVisible = true
|
|
||||||
}
|
|
||||||
VoiceBroadcastState.STOPPED, null -> {
|
|
||||||
liveIndicator.isVisible = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun renderPausedLiveIndicator(holder: H) {
|
||||||
|
with(holder) {
|
||||||
|
liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.vctr_content_quaternary))
|
||||||
|
liveIndicator.isVisible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun renderNoLiveIndicator(holder: H) {
|
||||||
|
holder.liveIndicator.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
abstract fun renderMetadata(holder: H)
|
abstract fun renderMetadata(holder: H)
|
||||||
|
|
||||||
abstract class Holder(@IdRes stubId: Int) : AbsMessageItem.Holder(stubId) {
|
abstract class Holder(@IdRes stubId: Int) : AbsMessageItem.Holder(stubId) {
|
||||||
|
|
|
@ -29,6 +29,7 @@ import im.vector.app.core.epoxy.onClick
|
||||||
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.listening.VoiceBroadcastPlayer
|
import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayer
|
||||||
|
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
|
||||||
import im.vector.app.features.voicebroadcast.views.VoiceBroadcastMetadataView
|
import im.vector.app.features.voicebroadcast.views.VoiceBroadcastMetadataView
|
||||||
|
|
||||||
@EpoxyModelClass
|
@EpoxyModelClass
|
||||||
|
@ -82,6 +83,14 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageVoiceBroadcastItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun renderLiveIndicator(holder: Holder) {
|
||||||
|
when {
|
||||||
|
voiceBroadcastState == null || voiceBroadcastState == VoiceBroadcastState.STOPPED -> renderNoLiveIndicator(holder)
|
||||||
|
voiceBroadcastState == VoiceBroadcastState.PAUSED || !player.isLiveListening -> renderPausedLiveIndicator(holder)
|
||||||
|
else -> renderPlayingLiveIndicator(holder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun renderPlayingState(holder: Holder, state: VoiceBroadcastPlayer.State) {
|
private fun renderPlayingState(holder: Holder, state: VoiceBroadcastPlayer.State) {
|
||||||
with(holder) {
|
with(holder) {
|
||||||
bufferingView.isVisible = state == VoiceBroadcastPlayer.State.BUFFERING
|
bufferingView.isVisible = state == VoiceBroadcastPlayer.State.BUFFERING
|
||||||
|
@ -99,6 +108,8 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageVoiceBroadcastItem
|
||||||
}
|
}
|
||||||
VoiceBroadcastPlayer.State.BUFFERING -> Unit
|
VoiceBroadcastPlayer.State.BUFFERING -> Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderLiveIndicator(holder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,6 +132,7 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageVoiceBroadcastItem
|
||||||
}
|
}
|
||||||
playbackTracker.track(voiceBroadcast.voiceBroadcastId) { playbackState ->
|
playbackTracker.track(voiceBroadcast.voiceBroadcastId) { playbackState ->
|
||||||
renderBackwardForwardButtons(holder, playbackState)
|
renderBackwardForwardButtons(holder, playbackState)
|
||||||
|
renderLiveIndicator(holder)
|
||||||
if (!isUserSeeking) {
|
if (!isUserSeeking) {
|
||||||
holder.seekBar.progress = playbackTracker.getPlaybackTime(voiceBroadcast.voiceBroadcastId)
|
holder.seekBar.progress = playbackTracker.getPlaybackTime(voiceBroadcast.voiceBroadcastId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,15 @@ abstract class MessageVoiceBroadcastRecordingItem : AbsMessageVoiceBroadcastItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun renderLiveIndicator(holder: Holder) {
|
||||||
|
when (voiceBroadcastState) {
|
||||||
|
VoiceBroadcastState.STARTED,
|
||||||
|
VoiceBroadcastState.RESUMED -> renderPlayingLiveIndicator(holder)
|
||||||
|
VoiceBroadcastState.PAUSED -> renderPausedLiveIndicator(holder)
|
||||||
|
VoiceBroadcastState.STOPPED, null -> renderNoLiveIndicator(holder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun renderMetadata(holder: Holder) {
|
override fun renderMetadata(holder: Holder) {
|
||||||
with(holder) {
|
with(holder) {
|
||||||
listenersCountMetadata.isVisible = false
|
listenersCountMetadata.isVisible = false
|
||||||
|
|
|
@ -30,6 +30,11 @@ interface VoiceBroadcastPlayer {
|
||||||
*/
|
*/
|
||||||
val playingState: State
|
val playingState: State
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells whether the player is listening a live voice broadcast in "live" position.
|
||||||
|
*/
|
||||||
|
val isLiveListening: Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start playback of the given voice broadcast.
|
* Start playback of the given voice broadcast.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -30,7 +30,6 @@ import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayer.Stat
|
||||||
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.VoiceBroadcast
|
||||||
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastEvent
|
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastEvent
|
||||||
import im.vector.app.features.voicebroadcast.sequence
|
|
||||||
import im.vector.app.features.voicebroadcast.usecase.GetVoiceBroadcastEventUseCase
|
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.Job
|
import kotlinx.coroutines.Job
|
||||||
|
@ -70,6 +69,7 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
|
||||||
private var currentVoiceBroadcastEvent: VoiceBroadcastEvent? = null
|
private var currentVoiceBroadcastEvent: VoiceBroadcastEvent? = null
|
||||||
|
|
||||||
override var currentVoiceBroadcast: VoiceBroadcast? = null
|
override var currentVoiceBroadcast: VoiceBroadcast? = null
|
||||||
|
override var isLiveListening: Boolean = false
|
||||||
|
|
||||||
override var playingState = State.IDLE
|
override var playingState = State.IDLE
|
||||||
@MainThread
|
@MainThread
|
||||||
|
@ -142,7 +142,10 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
|
||||||
|
|
||||||
private fun observeVoiceBroadcastLiveState(voiceBroadcast: VoiceBroadcast) {
|
private fun observeVoiceBroadcastLiveState(voiceBroadcast: VoiceBroadcast) {
|
||||||
voiceBroadcastStateObserver = getVoiceBroadcastEventUseCase.execute(voiceBroadcast)
|
voiceBroadcastStateObserver = getVoiceBroadcastEventUseCase.execute(voiceBroadcast)
|
||||||
.onEach { currentVoiceBroadcastEvent = it.getOrNull() }
|
.onEach {
|
||||||
|
currentVoiceBroadcastEvent = it.getOrNull()
|
||||||
|
updateLiveListeningMode()
|
||||||
|
}
|
||||||
.launchIn(sessionScope)
|
.launchIn(sessionScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +193,7 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
|
||||||
else -> playlist.firstOrNull()
|
else -> playlist.firstOrNull()
|
||||||
}
|
}
|
||||||
val content = playlistItem?.audioEvent?.content ?: run { Timber.w("## VoiceBroadcastPlayer: No content to play"); return }
|
val content = playlistItem?.audioEvent?.content ?: run { Timber.w("## VoiceBroadcastPlayer: No content to play"); return }
|
||||||
val sequence = playlistItem.audioEvent.sequence ?: run { Timber.w("## VoiceBroadcastPlayer: playlist item has no sequence"); return }
|
val sequence = playlistItem.sequence ?: run { Timber.w("## VoiceBroadcastPlayer: playlist item has no sequence"); return }
|
||||||
val sequencePosition = position?.let { it - playlistItem.startTime } ?: 0
|
val sequencePosition = position?.let { it - playlistItem.startTime } ?: 0
|
||||||
sessionScope.launch {
|
sessionScope.launch {
|
||||||
try {
|
try {
|
||||||
|
@ -241,6 +244,7 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
|
||||||
playbackTracker.updatePausedAtPlaybackTime(voiceBroadcast.voiceBroadcastId, positionMillis, positionMillis.toFloat() / duration)
|
playbackTracker.updatePausedAtPlaybackTime(voiceBroadcast.voiceBroadcastId, positionMillis, positionMillis.toFloat() / duration)
|
||||||
}
|
}
|
||||||
playingState == State.PLAYING || playingState == State.BUFFERING -> {
|
playingState == State.PLAYING || playingState == State.BUFFERING -> {
|
||||||
|
updateLiveListeningMode(positionMillis)
|
||||||
startPlayback(positionMillis)
|
startPlayback(positionMillis)
|
||||||
}
|
}
|
||||||
playingState == State.IDLE || playingState == State.PAUSED -> {
|
playingState == State.IDLE || playingState == State.PAUSED -> {
|
||||||
|
@ -302,18 +306,31 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onPlayingStateChanged(playingState: State) {
|
private fun onPlayingStateChanged(playingState: State) {
|
||||||
// Notify state change to all the listeners attached to the current voice broadcast id
|
// Update live playback flag
|
||||||
|
updateLiveListeningMode()
|
||||||
|
|
||||||
currentVoiceBroadcast?.voiceBroadcastId?.let { voiceBroadcastId ->
|
currentVoiceBroadcast?.voiceBroadcastId?.let { voiceBroadcastId ->
|
||||||
|
// Start or stop playback ticker
|
||||||
when (playingState) {
|
when (playingState) {
|
||||||
State.PLAYING -> playbackTicker.startPlaybackTicker(voiceBroadcastId)
|
State.PLAYING -> playbackTicker.startPlaybackTicker(voiceBroadcastId)
|
||||||
State.PAUSED,
|
State.PAUSED,
|
||||||
State.BUFFERING,
|
State.BUFFERING,
|
||||||
State.IDLE -> playbackTicker.stopPlaybackTicker(voiceBroadcastId)
|
State.IDLE -> playbackTicker.stopPlaybackTicker(voiceBroadcastId)
|
||||||
}
|
}
|
||||||
|
// Notify state change to all the listeners attached to the current voice broadcast id
|
||||||
listeners[voiceBroadcastId]?.forEach { listener -> listener.onStateChanged(playingState) }
|
listeners[voiceBroadcastId]?.forEach { listener -> listener.onStateChanged(playingState) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateLiveListeningMode(playbackPosition: Int? = null) {
|
||||||
|
isLiveListening = when {
|
||||||
|
!currentVoiceBroadcastEvent?.isLive.orFalse() -> false
|
||||||
|
playingState == State.IDLE || playingState == State.PAUSED -> false
|
||||||
|
playbackPosition != null -> playlist.findByPosition(playbackPosition)?.sequence == playlist.lastOrNull()?.sequence
|
||||||
|
else -> isLiveListening || playlist.currentSequence == playlist.lastOrNull()?.sequence
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getCurrentPlaybackPosition(): Int? {
|
private fun getCurrentPlaybackPosition(): Int? {
|
||||||
val playlistPosition = playlist.currentItem?.startTime
|
val playlistPosition = playlist.currentItem?.startTime
|
||||||
val computedPosition = currentMediaPlayer?.currentPosition?.let { playlistPosition?.plus(it) } ?: playlistPosition
|
val computedPosition = currentMediaPlayer?.currentPosition?.let { playlistPosition?.plus(it) } ?: playlistPosition
|
||||||
|
|
|
@ -56,7 +56,7 @@ class VoiceBroadcastPlaylist(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findBySequence(sequenceNumber: Int): PlaylistItem? {
|
fun findBySequence(sequenceNumber: Int): PlaylistItem? {
|
||||||
return items.find { it.audioEvent.sequence == sequenceNumber }
|
return items.find { it.sequence == sequenceNumber }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getNextItem() = findBySequence(currentSequence?.plus(1) ?: 1)
|
fun getNextItem() = findBySequence(currentSequence?.plus(1) ?: 1)
|
||||||
|
@ -64,4 +64,7 @@ class VoiceBroadcastPlaylist(
|
||||||
fun firstOrNull() = findBySequence(1)
|
fun firstOrNull() = findBySequence(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class PlaylistItem(val audioEvent: MessageAudioEvent, val startTime: Int)
|
data class PlaylistItem(val audioEvent: MessageAudioEvent, val startTime: Int) {
|
||||||
|
val sequence: Int?
|
||||||
|
get() = audioEvent.sequence
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue