Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Voice Broadcast - Send state events #7273

Merged
merged 15 commits into from
Oct 7, 2022
Merged
1 change: 1 addition & 0 deletions changelog.d/7273.wip
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[Voice Broadcast] Add the "io.element.voice_broadcast_info" state event with a minimalist timeline widget
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,7 @@ object MessageType {
// Fake message types for live location events to be able to inherit them from MessageContent
const val MSGTYPE_BEACON_INFO = "org.matrix.android.sdk.beacon.info"
const val MSGTYPE_BEACON_LOCATION_DATA = "org.matrix.android.sdk.beacon.location.data"

// Fake message types for voice broadcast events to be able to inherit them from MessageContent
const val MSGTYPE_VOICE_BROADCAST_INFO = "io.element.voicebroadcast.info"
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,30 @@

package im.vector.app.core.extensions

import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent

fun TimelineEvent.canReact(): Boolean {
// Only event of type EventType.MESSAGE, EventType.STICKER and EventType.POLL_START are supported for the moment
return root.getClearType() in listOf(EventType.MESSAGE, EventType.STICKER) + EventType.POLL_START &&
root.sendState == SendState.SYNCED &&
!root.isRedacted()
}

/**
* Get last MessageContent, after a possible edition.
* This method iterate on the vector event types and fallback to [getLastMessageContent] from the matrix sdk for the other types.
*/
fun TimelineEvent.getVectorLastMessageContent(): MessageContent? {
// Iterate on event types which are not part of the matrix sdk, otherwise fallback to the sdk method
return when (root.getClearType()) {
STATE_ROOM_VOICE_BROADCAST_INFO -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel<MessageVoiceBroadcastInfoContent>()
else -> getLastMessageContent()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ sealed class RoomDetailAction : VectorViewModelAction {
data class ReRequestKeys(val eventId: String) : RoomDetailAction()

object SelectStickerAttachment : RoomDetailAction()
object StartVoiceBroadcast : RoomDetailAction()
object OpenIntegrationManager : RoomDetailAction()
object ManageIntegrations : RoomDetailAction()
data class AddJitsiWidget(val withVideo: Boolean) : RoomDetailAction()
Expand Down Expand Up @@ -120,4 +119,11 @@ sealed class RoomDetailAction : VectorViewModelAction {
object StopLiveLocationSharing : RoomDetailAction()

object OpenElementCallWidget : RoomDetailAction()

sealed class VoiceBroadcastAction : RoomDetailAction() {
object Start : VoiceBroadcastAction()
object Pause : VoiceBroadcastAction()
object Resume : VoiceBroadcastAction()
object Stop : VoiceBroadcastAction()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import im.vector.app.features.raw.wellknown.withElementWellKnown
import im.vector.app.features.session.coroutineScope
import im.vector.app.features.settings.VectorDataStore
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.voicebroadcast.VoiceBroadcastHelper
import im.vector.lib.core.utils.flow.chunk
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
Expand Down Expand Up @@ -149,6 +150,7 @@ class TimelineViewModel @AssistedInject constructor(
buildMeta: BuildMeta,
timelineFactory: TimelineFactory,
private val spaceStateHandler: SpaceStateHandler,
private val voiceBroadcastHelper: VoiceBroadcastHelper,
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
Timeline.Listener, ChatEffectManager.Delegate, CallProtocolsChecker.Listener, LocationSharingServiceConnection.Callback {

Expand Down Expand Up @@ -456,7 +458,7 @@ class TimelineViewModel @AssistedInject constructor(
is RoomDetailAction.ReRequestKeys -> handleReRequestKeys(action)
is RoomDetailAction.TapOnFailedToDecrypt -> handleTapOnFailedToDecrypt(action)
is RoomDetailAction.SelectStickerAttachment -> handleSelectStickerAttachment()
is RoomDetailAction.StartVoiceBroadcast -> handleStartVoiceBroadcast()
is RoomDetailAction.VoiceBroadcastAction -> handleVoiceBroadcastAction(action)
is RoomDetailAction.OpenIntegrationManager -> handleOpenIntegrationManager()
is RoomDetailAction.StartCall -> handleStartCall(action)
is RoomDetailAction.AcceptCall -> handleAcceptCall(action)
Expand Down Expand Up @@ -598,9 +600,16 @@ class TimelineViewModel @AssistedInject constructor(
}
}

private fun handleStartVoiceBroadcast() {
// Todo implement start voice broadcast action
Timber.d("Start voice broadcast clicked")
private fun handleVoiceBroadcastAction(action: RoomDetailAction.VoiceBroadcastAction) {
if (room == null) return
viewModelScope.launch {
when (action) {
RoomDetailAction.VoiceBroadcastAction.Start -> voiceBroadcastHelper.startVoiceBroadcast(room.roomId)
RoomDetailAction.VoiceBroadcastAction.Pause -> voiceBroadcastHelper.pauseVoiceBroadcast(room.roomId)
RoomDetailAction.VoiceBroadcastAction.Resume -> voiceBroadcastHelper.resumeVoiceBroadcast(room.roomId)
RoomDetailAction.VoiceBroadcastAction.Stop -> voiceBroadcastHelper.stopVoiceBroadcast(room.roomId)
}
giomfo marked this conversation as resolved.
Show resolved Hide resolved
}
}

private fun handleOpenIntegrationManager() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import com.vanniktech.emoji.EmojiPopup
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.error.fatalError
import im.vector.app.core.extensions.getVectorLastMessageContent
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.extensions.showKeyboard
import im.vector.app.core.glide.GlideApp
Expand All @@ -73,6 +74,7 @@ import im.vector.app.features.command.ParsedCommand
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.AutoCompleter
import im.vector.app.features.home.room.detail.RoomDetailAction
import im.vector.app.features.home.room.detail.RoomDetailAction.VoiceBroadcastAction
import im.vector.app.features.home.room.detail.TimelineViewModel
import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView
import im.vector.app.features.home.room.detail.timeline.action.MessageSharedActionViewModel
Expand Down Expand Up @@ -102,7 +104,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageFormat
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
import org.matrix.android.sdk.api.util.MatrixItem
import org.matrix.android.sdk.api.util.toMatrixItem
import reactivecircus.flowbinding.android.view.focusChanges
Expand Down Expand Up @@ -355,7 +356,7 @@ class MessageComposerFragment : VectorBaseFragment<FragmentComposerBinding>(), A
setTextColor(matrixItemColorProvider.getColor(MatrixItem.UserItem(event.root.senderId ?: "@")))
}

val messageContent: MessageContent? = event.getLastMessageContent()
val messageContent: MessageContent? = event.getVectorLastMessageContent()
val nonFormattedBody = when (messageContent) {
is MessageAudioContent -> getAudioContentBodyText(messageContent)
is MessagePollContent -> messageContent.getBestPollCreationInfo()?.question?.getBestQuestion()
Expand Down Expand Up @@ -653,7 +654,7 @@ class MessageComposerFragment : VectorBaseFragment<FragmentComposerBinding>(), A
locationOwnerId = session.myUserId
)
}
AttachmentTypeSelectorView.Type.VOICE_BROADCAST -> timelineViewModel.handle(RoomDetailAction.StartVoiceBroadcast)
AttachmentTypeSelectorView.Type.VOICE_BROADCAST -> timelineViewModel.handle(VoiceBroadcastAction.Start)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import dagger.assisted.AssistedInject
import im.vector.app.R
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.getVectorLastMessageContent
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.analytics.AnalyticsTracker
Expand Down Expand Up @@ -62,7 +63,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageType
import org.matrix.android.sdk.api.session.room.model.relation.shouldRenderInThread
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
import org.matrix.android.sdk.api.session.room.send.UserDraft
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
import org.matrix.android.sdk.api.session.room.timeline.getRelationContent
import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent
import org.matrix.android.sdk.api.session.space.CreateSpaceParams
Expand Down Expand Up @@ -513,7 +513,7 @@ class MessageComposerViewModel @AssistedInject constructor(
room.relationService().editReply(state.sendMode.timelineEvent, it, action.text.toString())
}
} else {
val messageContent = state.sendMode.timelineEvent.getLastMessageContent()
val messageContent = state.sendMode.timelineEvent.getVectorLastMessageContent()
val existingBody = messageContent?.body ?: ""
if (existingBody != action.text) {
room.relationService().editTextMessage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package im.vector.app.features.home.room.detail.timeline.action

import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import javax.inject.Inject
Expand All @@ -27,8 +28,13 @@ class CheckIfCanRedactEventUseCase @Inject constructor(

fun execute(event: TimelineEvent, actionPermissions: ActionPermissions): Boolean {
// Only some event types are supported for the moment
val canRedactEventTypes = listOf(EventType.MESSAGE, EventType.STICKER) +
EventType.POLL_START + EventType.STATE_ROOM_BEACON_INFO
val canRedactEventTypes: List<String> = listOf(
EventType.MESSAGE,
EventType.STICKER,
STATE_ROOM_VOICE_BROADCAST_INFO,
) +
EventType.POLL_START +
EventType.STATE_ROOM_BEACON_INFO

return event.root.getClearType() in canRedactEventTypes &&
// Message sent by the current user can always be redacted, else check permission for messages sent by other users
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.extensions.canReact
import im.vector.app.core.extensions.getVectorLastMessageContent
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
Expand Down Expand Up @@ -60,7 +61,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachme
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited
import org.matrix.android.sdk.api.session.room.timeline.isPoll
import org.matrix.android.sdk.api.session.room.timeline.isRootThread
Expand Down Expand Up @@ -187,7 +187,7 @@ class MessageActionsViewModel @AssistedInject constructor(
when (timelineEvent.root.getClearType()) {
EventType.MESSAGE,
EventType.STICKER -> {
val messageContent: MessageContent? = timelineEvent.getLastMessageContent()
val messageContent: MessageContent? = timelineEvent.getVectorLastMessageContent()
if (messageContent is MessageTextContent && messageContent.format == MessageFormat.FORMAT_MATRIX_HTML) {
val html = messageContent.formattedBody
?.takeIf { it.isNotBlank() }
Expand Down Expand Up @@ -253,7 +253,7 @@ class MessageActionsViewModel @AssistedInject constructor(
}

private fun actionsForEvent(timelineEvent: TimelineEvent, actionPermissions: ActionPermissions): List<EventSharedAction> {
val messageContent = timelineEvent.getLastMessageContent()
val messageContent = timelineEvent.getVectorLastMessageContent()
val msgType = messageContent?.msgType

return arrayListOf<EventSharedAction>().apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import dagger.Lazy
import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.extensions.getVectorLastMessageContent
import im.vector.app.core.files.LocalFilesHelper
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider
Expand Down Expand Up @@ -55,6 +56,8 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageLocationItem
import im.vector.app.features.home.room.detail.timeline.item.MessageLocationItem_
import im.vector.app.features.home.room.detail.timeline.item.MessageTextItem
import im.vector.app.features.home.room.detail.timeline.item.MessageTextItem_
import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastItem
import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastItem_
import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceItem
import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceItem_
import im.vector.app.features.home.room.detail.timeline.item.PollItem
Expand All @@ -77,6 +80,7 @@ import im.vector.app.features.media.ImageContentRenderer
import im.vector.app.features.media.VideoContentRenderer
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.voice.AudioWaveformView
import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import me.gujun.android.span.span
import org.matrix.android.sdk.api.MatrixUrls.isMxcUrl
Expand All @@ -102,7 +106,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageVerification
import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
import org.matrix.android.sdk.api.session.room.model.message.getThumbnailUrl
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
import org.matrix.android.sdk.api.settings.LightweightSettingsStorage
import org.matrix.android.sdk.api.util.MimeTypes
import javax.inject.Inject
Expand Down Expand Up @@ -163,7 +166,7 @@ class MessageItemFactory @Inject constructor(
return buildRedactedItem(attributes, highlight)
}

val messageContent = event.getLastMessageContent()
val messageContent = event.getVectorLastMessageContent()
if (messageContent == null) {
val malformedText = stringProvider.getString(R.string.malformed_message)
return defaultItemFactory.create(malformedText, informationData, highlight, callback)
Expand Down Expand Up @@ -197,6 +200,7 @@ class MessageItemFactory @Inject constructor(
is MessagePollContent -> buildPollItem(messageContent, informationData, highlight, callback, attributes)
is MessageLocationContent -> buildLocationItem(messageContent, informationData, highlight, attributes)
is MessageBeaconInfoContent -> liveLocationShareMessageItemFactory.create(params.event, highlight, attributes)
is MessageVoiceBroadcastInfoContent -> buildVoiceBroadcastItem(messageContent, highlight, callback, attributes)
else -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes)
}
return messageItem?.apply {
Expand Down Expand Up @@ -706,6 +710,20 @@ class MessageItemFactory @Inject constructor(
.highlighted(highlight)
}

private fun buildVoiceBroadcastItem(
messageContent: MessageVoiceBroadcastInfoContent,
highlight: Boolean,
callback: TimelineEventController.Callback?,
attributes: AbsMessageItem.Attributes,
): MessageVoiceBroadcastItem? {
return MessageVoiceBroadcastItem_()
.attributes(attributes)
.highlighted(highlight)
.voiceBroadcastState(messageContent.voiceBroadcastState)
.leftGuideline(avatarSizeProvider.leftGuideline)
.callback(callback)
}

private fun List<Int?>?.toFft(): List<Int>? {
return this
?.filterNotNull()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import im.vector.app.core.epoxy.TimelineEmptyItem_
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.features.analytics.DecryptionFailureTracker
import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventVisibilityHelper
import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import timber.log.Timber
Expand Down Expand Up @@ -88,6 +89,7 @@ class TimelineItemFactory @Inject constructor(
// State room create
EventType.STATE_ROOM_CREATE -> roomCreateItemFactory.create(params)
in EventType.STATE_ROOM_BEACON_INFO -> messageItemFactory.create(params)
STATE_ROOM_VOICE_BROADCAST_INFO -> messageItemFactory.create(params)
// Unhandled state event types
else -> {
// Should only happen when shouldShowHiddenEvents() settings is ON
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package im.vector.app.features.home.room.detail.timeline.format
import dagger.Lazy
import im.vector.app.EmojiSpanify
import im.vector.app.R
import im.vector.app.core.extensions.getVectorLastMessageContent
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.html.EventHtmlRenderer
Expand All @@ -34,7 +35,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
import org.matrix.android.sdk.api.session.room.model.message.MessageType
import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
import org.matrix.android.sdk.api.session.room.timeline.getTextDisplayableContent
import javax.inject.Inject

Expand All @@ -60,7 +60,7 @@ class DisplayableEventFormatter @Inject constructor(

return when (timelineEvent.root.getClearType()) {
EventType.MESSAGE -> {
timelineEvent.getLastMessageContent()?.let { messageContent ->
timelineEvent.getVectorLastMessageContent()?.let { messageContent ->
when (messageContent.msgType) {
MessageType.MSGTYPE_TEXT -> {
val body = messageContent.getTextDisplayableContent()
Expand Down
Loading