diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt index 10708d2290..3e828f62b7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt @@ -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() @@ -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() + } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt index 4bed477711..b251facb90 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt @@ -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 @@ -149,6 +150,7 @@ class TimelineViewModel @AssistedInject constructor( buildMeta: BuildMeta, timelineFactory: TimelineFactory, private val spaceStateHandler: SpaceStateHandler, + private val voiceBroadcastHelper: VoiceBroadcastHelper, ) : VectorViewModel(initialState), Timeline.Listener, ChatEffectManager.Delegate, CallProtocolsChecker.Listener, LocationSharingServiceConnection.Callback { @@ -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) @@ -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.Pause -> voiceBroadcastHelper.pauseVoiceBroadcast(room.roomId) + RoomDetailAction.VoiceBroadcastAction.Resume -> voiceBroadcastHelper.resumeVoiceBroadcast(room.roomId) + RoomDetailAction.VoiceBroadcastAction.Start -> voiceBroadcastHelper.startVoiceBroadcast(room.roomId) + RoomDetailAction.VoiceBroadcastAction.Stop -> voiceBroadcastHelper.stopVoiceBroadcast(room.roomId) + } + } } private fun handleOpenIntegrationManager() { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt index 21a87f092f..509e8cc4af 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt @@ -73,6 +73,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 @@ -653,7 +654,7 @@ class MessageComposerFragment : VectorBaseFragment(), A locationOwnerId = session.myUserId ) } - AttachmentTypeSelectorView.Type.VOICE_BROADCAST -> timelineViewModel.handle(RoomDetailAction.StartVoiceBroadcast) + AttachmentTypeSelectorView.Type.VOICE_BROADCAST -> timelineViewModel.handle(VoiceBroadcastAction.Start) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index eb7b0d4ed8..ecd34208f6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -77,6 +77,8 @@ 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.STATE_ROOM_VOICE_BROADCAST_INFO +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 @@ -102,6 +104,7 @@ 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.TimelineEvent 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 @@ -163,7 +166,7 @@ class MessageItemFactory @Inject constructor( return buildRedactedItem(attributes, highlight) } - val messageContent = event.getLastMessageContent() + val messageContent = getLastMessageContent(event) if (messageContent == null) { val malformedText = stringProvider.getString(R.string.malformed_message) return defaultItemFactory.create(malformedText, informationData, highlight, callback) @@ -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, informationData, highlight, callback, attributes) else -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes) } return messageItem?.apply { @@ -204,6 +208,17 @@ class MessageItemFactory @Inject constructor( } } + private fun getLastMessageContent(event: TimelineEvent): MessageContent? { + return with(event) { + // Iterate on event types which are not part of the matrix sdk, otherwise fallback to the sdk method + when (root.getClearType()) { + STATE_ROOM_VOICE_BROADCAST_INFO -> + (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel() + else -> event.getLastMessageContent() + } + } + } + private fun buildLocationItem( locationContent: MessageLocationContent, informationData: MessageInformationData, @@ -706,6 +721,36 @@ class MessageItemFactory @Inject constructor( .highlighted(highlight) } + private fun buildVoiceBroadcastItem( + messageContent: MessageVoiceBroadcastInfoContent, + @Suppress("UNUSED_PARAMETER") + informationData: MessageInformationData, + highlight: Boolean, + callback: TimelineEventController.Callback?, + attributes: AbsMessageItem.Attributes, + ): MessageTextItem? { + val htmlBody = "voice broadcast state: ${messageContent.voiceBroadcastState}" + val formattedBody = span { + text = htmlBody + textColor = colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary) + textStyle = "italic" + } + + val bindingOptions = spanUtils.getBindingOptions(htmlBody) + val message = formattedBody.linkify(callback) + + return MessageTextItem_() + .leftGuideline(avatarSizeProvider.leftGuideline) + .previewUrlRetriever(callback?.getPreviewUrlRetriever()) + .imageContentRenderer(imageContentRenderer) + .previewUrlCallback(callback) + .attributes(attributes) + .message(message.toEpoxyCharSequence()) + .bindingOptions(bindingOptions) + .highlighted(highlight) + .movementMethod(createLinkMovementMethod(callback)) + } + private fun List?.toFft(): List? { return this ?.filterNotNull() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt index 6c5a66d39d..0b8f95b4a1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt @@ -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 @@ -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