Skip to content

Commit 6f1e0b5

Browse files
authored
Merge pull request #7448 from vector-im/feature/fre/voice_broadcast_timeline_improvements
Voice Broadcast - Improve timeline rendering code
2 parents 2a977f3 + 1554d79 commit 6f1e0b5

18 files changed

+466
-340
lines changed

changelog.d/7448.wip

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[Voice Broadcast] Improve timeline items factory and handle bad recording state display

library/ui-strings/src/main/res/values/donottranslate.xml

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<resources>
33

44
<string name="ellipsis" translatable="false">…</string>
5+
<string name="no_value_placeholder" translatable="false">–</string>
56

67
<!-- Temporary string -->
78
<string name="not_implemented" translatable="false">Not implemented yet in ${app_name}</string>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources>
3+
4+
<declare-styleable name="VoiceBroadcastMetadataView">
5+
<attr name="metadataIcon" format="reference" />
6+
<attr name="metadataValue" format="string" />
7+
</declare-styleable>
8+
9+
</resources>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources>
3+
4+
<style name="VoiceBroadcastLiveIndicator" parent="Widget.AppCompat.TextView">
5+
<item name="android:layout_width">wrap_content</item>
6+
<item name="android:layout_height">20dp</item>
7+
<item name="android:backgroundTint">?colorError</item>
8+
<item name="android:drawablePadding">4dp</item>
9+
<item name="android:ellipsize">end</item>
10+
<item name="android:gravity">center_vertical</item>
11+
<item name="android:maxWidth">100dp</item>
12+
<item name="android:paddingEnd">4dp</item>
13+
<item name="android:paddingStart">4dp</item>
14+
<item name="android:singleLine">true</item>
15+
<item name="android:textColor">?colorOnError</item>
16+
<item name="drawableTint">?colorOnError</item>
17+
</style>
18+
19+
</resources>

vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ class MessageItemFactory @Inject constructor(
201201
is MessagePollContent -> buildPollItem(messageContent, informationData, highlight, callback, attributes)
202202
is MessageLocationContent -> buildLocationItem(messageContent, informationData, highlight, attributes)
203203
is MessageBeaconInfoContent -> liveLocationShareMessageItemFactory.create(params.event, highlight, attributes)
204-
is MessageVoiceBroadcastInfoContent -> voiceBroadcastItemFactory.create(params, messageContent, highlight, callback, attributes)
204+
is MessageVoiceBroadcastInfoContent -> voiceBroadcastItemFactory.create(params, messageContent, highlight, attributes)
205205
else -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes)
206206
}
207207
return messageItem?.apply {

vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt

+31-54
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,13 @@
1515
*/
1616
package im.vector.app.features.home.room.detail.timeline.factory
1717

18-
import im.vector.app.core.epoxy.VectorEpoxyHolder
19-
import im.vector.app.core.epoxy.VectorEpoxyModel
2018
import im.vector.app.core.resources.ColorProvider
2119
import im.vector.app.core.resources.DrawableProvider
22-
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
20+
import im.vector.app.features.displayname.getBestName
2321
import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
2422
import im.vector.app.features.home.room.detail.timeline.helper.VoiceBroadcastEventsGroup
2523
import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem
24+
import im.vector.app.features.home.room.detail.timeline.item.AbsMessageVoiceBroadcastItem
2625
import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastListeningItem
2726
import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastListeningItem_
2827
import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastRecordingItem
@@ -34,7 +33,7 @@ import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
3433
import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
3534
import org.matrix.android.sdk.api.session.Session
3635
import org.matrix.android.sdk.api.session.getRoom
37-
import org.matrix.android.sdk.api.session.getUser
36+
import org.matrix.android.sdk.api.session.getUserOrDefault
3837
import org.matrix.android.sdk.api.util.toMatrixItem
3938
import javax.inject.Inject
4039

@@ -51,81 +50,59 @@ class VoiceBroadcastItemFactory @Inject constructor(
5150
params: TimelineItemFactoryParams,
5251
messageContent: MessageVoiceBroadcastInfoContent,
5352
highlight: Boolean,
54-
callback: TimelineEventController.Callback?,
5553
attributes: AbsMessageItem.Attributes,
56-
): VectorEpoxyModel<out VectorEpoxyHolder>? {
54+
): AbsMessageVoiceBroadcastItem<*>? {
5755
// Only display item of the initial event with updated data
5856
if (messageContent.voiceBroadcastState != VoiceBroadcastState.STARTED) return null
59-
val eventsGroup = params.eventsGroup ?: return null
60-
val voiceBroadcastEventsGroup = VoiceBroadcastEventsGroup(eventsGroup)
61-
val mostRecentTimelineEvent = voiceBroadcastEventsGroup.getLastDisplayableEvent()
62-
val mostRecentEvent = mostRecentTimelineEvent.root.asVoiceBroadcastEvent()
63-
val mostRecentMessageContent = mostRecentEvent?.content ?: return null
64-
val isRecording = mostRecentMessageContent.voiceBroadcastState != VoiceBroadcastState.STOPPED && mostRecentEvent.root.stateKey == session.myUserId
65-
val recorderName = mostRecentTimelineEvent.root.stateKey?.let { session.getUser(it) }?.displayName ?: mostRecentTimelineEvent.root.stateKey
57+
58+
val voiceBroadcastEventsGroup = params.eventsGroup?.let { VoiceBroadcastEventsGroup(it) } ?: return null
59+
val voiceBroadcastEvent = voiceBroadcastEventsGroup.getLastDisplayableEvent().root.asVoiceBroadcastEvent() ?: return null
60+
val voiceBroadcastContent = voiceBroadcastEvent.content ?: return null
61+
val voiceBroadcastId = voiceBroadcastEventsGroup.voiceBroadcastId
62+
63+
val isRecording = voiceBroadcastContent.voiceBroadcastState != VoiceBroadcastState.STOPPED && voiceBroadcastEvent.root.stateKey == session.myUserId
64+
65+
val voiceBroadcastAttributes = AbsMessageVoiceBroadcastItem.Attributes(
66+
voiceBroadcastId = voiceBroadcastId,
67+
voiceBroadcastState = voiceBroadcastContent.voiceBroadcastState,
68+
recorderName = params.event.root.stateKey?.let { session.getUserOrDefault(it) }?.toMatrixItem()?.getBestName().orEmpty(),
69+
recorder = voiceBroadcastRecorder,
70+
player = voiceBroadcastPlayer,
71+
roomItem = session.getRoom(params.event.roomId)?.roomSummary()?.toMatrixItem(),
72+
colorProvider = colorProvider,
73+
drawableProvider = drawableProvider,
74+
)
75+
6676
return if (isRecording) {
67-
createRecordingItem(
68-
params.event.roomId,
69-
eventsGroup.groupId,
70-
highlight,
71-
callback,
72-
attributes
73-
)
77+
createRecordingItem(highlight, attributes, voiceBroadcastAttributes)
7478
} else {
75-
createListeningItem(
76-
params.event.roomId,
77-
eventsGroup.groupId,
78-
mostRecentMessageContent.voiceBroadcastState,
79-
recorderName,
80-
highlight,
81-
callback,
82-
attributes
83-
)
79+
createListeningItem(highlight, attributes, voiceBroadcastAttributes)
8480
}
8581
}
8682

8783
private fun createRecordingItem(
88-
roomId: String,
89-
voiceBroadcastId: String,
9084
highlight: Boolean,
91-
callback: TimelineEventController.Callback?,
9285
attributes: AbsMessageItem.Attributes,
86+
voiceBroadcastAttributes: AbsMessageVoiceBroadcastItem.Attributes,
9387
): MessageVoiceBroadcastRecordingItem {
94-
val roomSummary = session.getRoom(roomId)?.roomSummary()
9588
return MessageVoiceBroadcastRecordingItem_()
96-
.id("voice_broadcast_$voiceBroadcastId")
89+
.id("voice_broadcast_${voiceBroadcastAttributes.voiceBroadcastId}")
9790
.attributes(attributes)
91+
.voiceBroadcastAttributes(voiceBroadcastAttributes)
9892
.highlighted(highlight)
99-
.roomItem(roomSummary?.toMatrixItem())
100-
.colorProvider(colorProvider)
101-
.drawableProvider(drawableProvider)
102-
.voiceBroadcastRecorder(voiceBroadcastRecorder)
10393
.leftGuideline(avatarSizeProvider.leftGuideline)
104-
.callback(callback)
10594
}
10695

10796
private fun createListeningItem(
108-
roomId: String,
109-
voiceBroadcastId: String,
110-
voiceBroadcastState: VoiceBroadcastState?,
111-
broadcasterName: String?,
11297
highlight: Boolean,
113-
callback: TimelineEventController.Callback?,
11498
attributes: AbsMessageItem.Attributes,
99+
voiceBroadcastAttributes: AbsMessageVoiceBroadcastItem.Attributes,
115100
): MessageVoiceBroadcastListeningItem {
116-
val roomSummary = session.getRoom(roomId)?.roomSummary()
117101
return MessageVoiceBroadcastListeningItem_()
118-
.id("voice_broadcast_$voiceBroadcastId")
102+
.id("voice_broadcast_${voiceBroadcastAttributes.voiceBroadcastId}")
119103
.attributes(attributes)
104+
.voiceBroadcastAttributes(voiceBroadcastAttributes)
120105
.highlighted(highlight)
121-
.roomItem(roomSummary?.toMatrixItem())
122-
.colorProvider(colorProvider)
123-
.drawableProvider(drawableProvider)
124-
.voiceBroadcastPlayer(voiceBroadcastPlayer)
125-
.voiceBroadcastId(voiceBroadcastId)
126-
.voiceBroadcastState(voiceBroadcastState)
127-
.broadcasterName(broadcasterName)
128106
.leftGuideline(avatarSizeProvider.leftGuideline)
129-
.callback(callback)
130107
}
131108
}

vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventsGroups.kt

+3
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,9 @@ class CallSignalingEventsGroup(private val group: TimelineEventsGroup) {
141141
}
142142

143143
class VoiceBroadcastEventsGroup(private val group: TimelineEventsGroup) {
144+
145+
val voiceBroadcastId = group.groupId
146+
144147
fun getLastDisplayableEvent(): TimelineEvent {
145148
return group.events.find { it.root.asVoiceBroadcastEvent()?.content?.voiceBroadcastState == VoiceBroadcastState.STOPPED }
146149
?: group.events.filter { it.root.type == VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO }.maxBy { it.root.originServerTs ?: 0L }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright (c) 2022 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package im.vector.app.features.home.room.detail.timeline.item
18+
19+
import android.widget.ImageView
20+
import android.widget.TextView
21+
import androidx.annotation.IdRes
22+
import androidx.core.view.isVisible
23+
import com.airbnb.epoxy.EpoxyAttribute
24+
import im.vector.app.R
25+
import im.vector.app.core.extensions.tintBackground
26+
import im.vector.app.core.resources.ColorProvider
27+
import im.vector.app.core.resources.DrawableProvider
28+
import im.vector.app.features.voicebroadcast.VoiceBroadcastPlayer
29+
import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
30+
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
31+
import org.matrix.android.sdk.api.util.MatrixItem
32+
33+
abstract class AbsMessageVoiceBroadcastItem<H : AbsMessageVoiceBroadcastItem.Holder> : AbsMessageItem<H>() {
34+
35+
@EpoxyAttribute
36+
lateinit var voiceBroadcastAttributes: Attributes
37+
38+
protected val voiceBroadcastId get() = voiceBroadcastAttributes.voiceBroadcastId
39+
protected val voiceBroadcastState get() = voiceBroadcastAttributes.voiceBroadcastState
40+
protected val recorderName get() = voiceBroadcastAttributes.recorderName
41+
protected val recorder get() = voiceBroadcastAttributes.recorder
42+
protected val player get() = voiceBroadcastAttributes.player
43+
protected val roomItem get() = voiceBroadcastAttributes.roomItem
44+
protected val colorProvider get() = voiceBroadcastAttributes.colorProvider
45+
protected val drawableProvider get() = voiceBroadcastAttributes.drawableProvider
46+
protected val avatarRenderer get() = attributes.avatarRenderer
47+
protected val callback get() = attributes.callback
48+
49+
override fun isCacheable(): Boolean = false
50+
51+
override fun bind(holder: H) {
52+
super.bind(holder)
53+
renderHeader(holder)
54+
}
55+
56+
private fun renderHeader(holder: H) {
57+
with(holder) {
58+
roomItem?.let {
59+
avatarRenderer.render(it, roomAvatarImageView)
60+
titleText.text = it.displayName
61+
}
62+
}
63+
renderLiveIndicator(holder)
64+
renderMetadata(holder)
65+
}
66+
67+
private fun renderLiveIndicator(holder: H) {
68+
with(holder) {
69+
when (voiceBroadcastState) {
70+
VoiceBroadcastState.STARTED,
71+
VoiceBroadcastState.RESUMED -> {
72+
liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.colorError))
73+
liveIndicator.isVisible = true
74+
}
75+
VoiceBroadcastState.PAUSED -> {
76+
liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.vctr_content_quaternary))
77+
liveIndicator.isVisible = true
78+
}
79+
VoiceBroadcastState.STOPPED, null -> {
80+
liveIndicator.isVisible = false
81+
}
82+
}
83+
}
84+
}
85+
86+
abstract fun renderMetadata(holder: H)
87+
88+
abstract class Holder(@IdRes stubId: Int) : AbsMessageItem.Holder(stubId) {
89+
val liveIndicator by bind<TextView>(R.id.liveIndicator)
90+
val roomAvatarImageView by bind<ImageView>(R.id.roomAvatarImageView)
91+
val titleText by bind<TextView>(R.id.titleText)
92+
}
93+
94+
data class Attributes(
95+
val voiceBroadcastId: String,
96+
val voiceBroadcastState: VoiceBroadcastState?,
97+
val recorderName: String,
98+
val recorder: VoiceBroadcastRecorder?,
99+
val player: VoiceBroadcastPlayer,
100+
val roomItem: MatrixItem?,
101+
val colorProvider: ColorProvider,
102+
val drawableProvider: DrawableProvider,
103+
)
104+
}

0 commit comments

Comments
 (0)