mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-17 19:58:57 +03:00
Merge pull request #7273 from vector-im/feature/fre/voice_broadcast_state_event
Voice Broadcast - Send state events
This commit is contained in:
commit
48d2cc4745
37 changed files with 1396 additions and 33 deletions
1
changelog.d/7273.wip
Normal file
1
changelog.d/7273.wip
Normal file
|
@ -0,0 +1 @@
|
|||
[Voice Broadcast] Add the "io.element.voice_broadcast_info" state event with a minimalist timeline widget
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -16,9 +16,14 @@
|
|||
|
||||
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
|
||||
|
@ -26,3 +31,15 @@ fun TimelineEvent.canReact(): Boolean {
|
|||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(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.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleOpenIntegrationManager() {
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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() }
|
||||
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail.timeline.helper
|
|||
|
||||
import im.vector.app.core.date.DateFormatKind
|
||||
import im.vector.app.core.date.VectorDateFormatter
|
||||
import im.vector.app.core.extensions.getVectorLastMessageContent
|
||||
import im.vector.app.core.extensions.localDateTime
|
||||
import im.vector.app.features.home.room.detail.timeline.factory.TimelineItemFactoryParams
|
||||
import im.vector.app.features.home.room.detail.timeline.item.E2EDecoration
|
||||
|
@ -41,7 +42,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
|||
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
|
||||
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 javax.inject.Inject
|
||||
|
||||
|
@ -123,7 +123,11 @@ class MessageInformationDataFactory @Inject constructor(
|
|||
isLastFromThisSender = isLastFromThisSender,
|
||||
e2eDecoration = e2eDecoration,
|
||||
sendStateDecoration = sendStateDecoration,
|
||||
messageType = if (event.root.isSticker()) { MessageType.MSGTYPE_STICKER_LOCAL } else { event.root.getMsgType() }
|
||||
messageType = if (event.root.isSticker()) {
|
||||
MessageType.MSGTYPE_STICKER_LOCAL
|
||||
} else {
|
||||
event.root.getMsgType()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -230,7 +234,7 @@ class MessageInformationDataFactory @Inject constructor(
|
|||
EventType.KEY_VERIFICATION_DONE,
|
||||
EventType.KEY_VERIFICATION_CANCEL -> true
|
||||
EventType.MESSAGE -> {
|
||||
event.getLastMessageContent() is MessageVerificationRequestContent
|
||||
event.getVectorLastMessageContent() is MessageVerificationRequestContent
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package im.vector.app.features.home.room.detail.timeline.helper
|
||||
|
||||
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
|
||||
|
||||
|
@ -24,7 +25,7 @@ object TimelineDisplayableEvents {
|
|||
/**
|
||||
* All types we have an item to build with. Every type not defined here will be shown as DefaultItem if forced to be shown, otherwise will be hidden.
|
||||
*/
|
||||
val DISPLAYABLE_TYPES = listOf(
|
||||
val DISPLAYABLE_TYPES: List<String> = listOf(
|
||||
EventType.MESSAGE,
|
||||
EventType.STATE_ROOM_WIDGET_LEGACY,
|
||||
EventType.STATE_ROOM_WIDGET,
|
||||
|
@ -51,7 +52,11 @@ object TimelineDisplayableEvents {
|
|||
EventType.STATE_ROOM_JOIN_RULES,
|
||||
EventType.KEY_VERIFICATION_DONE,
|
||||
EventType.KEY_VERIFICATION_CANCEL,
|
||||
) + EventType.POLL_START + EventType.STATE_ROOM_BEACON_INFO + EventType.BEACON_LOCATION_DATA
|
||||
STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
) +
|
||||
EventType.POLL_START +
|
||||
EventType.STATE_ROOM_BEACON_INFO +
|
||||
EventType.BEACON_LOCATION_DATA
|
||||
}
|
||||
|
||||
fun TimelineEvent.isRoomConfiguration(roomCreatorUserId: String?): Boolean {
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.home.room.detail.timeline.item
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
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.model.VoiceBroadcastState
|
||||
|
||||
@EpoxyModelClass
|
||||
abstract class MessageVoiceBroadcastItem : AbsMessageItem<MessageVoiceBroadcastItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
var callback: TimelineEventController.Callback? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var voiceBroadcastState: VoiceBroadcastState? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
bindVoiceBroadcastItem(holder)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n") // Temporary text
|
||||
private fun bindVoiceBroadcastItem(holder: Holder) {
|
||||
with(holder) {
|
||||
currentStateText.text = "Voice Broadcast state: ${voiceBroadcastState?.value ?: "None"}"
|
||||
playButton.isEnabled = voiceBroadcastState == VoiceBroadcastState.PAUSED
|
||||
pauseButton.isEnabled = voiceBroadcastState == VoiceBroadcastState.STARTED || voiceBroadcastState == VoiceBroadcastState.RESUMED
|
||||
stopButton.isEnabled = voiceBroadcastState == VoiceBroadcastState.STARTED ||
|
||||
voiceBroadcastState == VoiceBroadcastState.RESUMED ||
|
||||
voiceBroadcastState == VoiceBroadcastState.PAUSED
|
||||
playButton.setOnClickListener { attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Resume) }
|
||||
pauseButton.setOnClickListener { attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Pause) }
|
||||
stopButton.setOnClickListener { attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Stop) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getViewStubId() = STUB_ID
|
||||
|
||||
class Holder : AbsMessageLocationItem.Holder(STUB_ID) {
|
||||
val currentStateText by bind<TextView>(R.id.currentStateText)
|
||||
val playButton by bind<ImageButton>(R.id.playButton)
|
||||
val pauseButton by bind<ImageButton>(R.id.pauseButton)
|
||||
val stopButton by bind<ImageButton>(R.id.stopButton)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val STUB_ID = R.id.messageVoiceBroadcastStub
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail.timeline.style
|
|||
|
||||
import android.content.res.Resources
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.getVectorLastMessageContent
|
||||
import im.vector.app.core.extensions.localDateTime
|
||||
import im.vector.app.core.resources.LocaleProvider
|
||||
import im.vector.app.core.resources.isRTL
|
||||
|
@ -29,7 +30,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
|||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
|
||||
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.isEdition
|
||||
import org.matrix.android.sdk.api.session.room.timeline.isRootThread
|
||||
import javax.inject.Inject
|
||||
|
@ -126,7 +126,7 @@ class TimelineMessageLayoutFactory @Inject constructor(
|
|||
isLastFromThisSender = isLastFromThisSender
|
||||
)
|
||||
|
||||
val messageContent = event.getLastMessageContent()
|
||||
val messageContent = event.getVectorLastMessageContent()
|
||||
TimelineMessageLayout.Bubble(
|
||||
showAvatar = showInformation && !isSentByMe,
|
||||
showDisplayName = showInformation && !isSentByMe,
|
||||
|
@ -167,7 +167,7 @@ class TimelineMessageLayoutFactory @Inject constructor(
|
|||
private fun TimelineEvent.shouldBuildBubbleLayout(): Boolean {
|
||||
val type = root.getClearType()
|
||||
if (type in EVENT_TYPES_WITH_BUBBLE_LAYOUT) {
|
||||
val messageContent = getLastMessageContent()
|
||||
val messageContent = getVectorLastMessageContent()
|
||||
return messageContent?.msgType !in MSG_TYPES_WITHOUT_BUBBLE_LAYOUT
|
||||
}
|
||||
return false
|
||||
|
@ -212,7 +212,7 @@ class TimelineMessageLayoutFactory @Inject constructor(
|
|||
EventType.KEY_VERIFICATION_DONE,
|
||||
EventType.KEY_VERIFICATION_CANCEL -> true
|
||||
EventType.MESSAGE -> {
|
||||
event.getLastMessageContent() is MessageVerificationRequestContent
|
||||
event.getVectorLastMessageContent() is MessageVerificationRequestContent
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package im.vector.app.features.notifications
|
|||
|
||||
import android.net.Uri
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.getVectorLastMessageContent
|
||||
import im.vector.app.core.extensions.takeAs
|
||||
import im.vector.app.core.resources.BuildMeta
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
|
@ -45,7 +46,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachme
|
|||
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.timeline.getEditedEventId
|
||||
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import timber.log.Timber
|
||||
import java.util.UUID
|
||||
|
@ -231,7 +231,7 @@ class NotifiableEventResolver @Inject constructor(
|
|||
|
||||
private suspend fun TimelineEvent.downloadAndExportImage(session: Session): Uri? {
|
||||
return kotlin.runCatching {
|
||||
getLastMessageContent()?.takeAs<MessageWithAttachmentContent>()?.let { imageMessage ->
|
||||
getVectorLastMessageContent()?.takeAs<MessageWithAttachmentContent>()?.let { imageMessage ->
|
||||
val fileService = session.fileService()
|
||||
fileService.downloadFile(imageMessage)
|
||||
fileService.getTemporarySharableURI(imageMessage)
|
||||
|
|
|
@ -22,6 +22,7 @@ import dagger.assisted.AssistedFactory
|
|||
import dagger.assisted.AssistedInject
|
||||
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.features.poll.PollMode
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
|
@ -29,7 +30,6 @@ import org.matrix.android.sdk.api.session.getRoom
|
|||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.PollType
|
||||
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
|
||||
|
||||
class CreatePollViewModel @AssistedInject constructor(
|
||||
@Assisted private val initialState: CreatePollViewState,
|
||||
|
@ -72,7 +72,7 @@ class CreatePollViewModel @AssistedInject constructor(
|
|||
|
||||
private fun initializeEditedPoll(eventId: String) {
|
||||
val event = room.getTimelineEvent(eventId) ?: return
|
||||
val content = event.getLastMessageContent() as? MessagePollContent ?: return
|
||||
val content = event.getVectorLastMessageContent() as? MessagePollContent ?: return
|
||||
|
||||
val pollCreationInfo = content.getBestPollCreationInfo()
|
||||
val pollType = pollCreationInfo?.kind ?: PollType.DISCLOSED_UNSTABLE
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.voicebroadcast
|
||||
|
||||
/** Voice Broadcast State Event. */
|
||||
const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2021 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.voicebroadcast
|
||||
|
||||
import im.vector.app.features.voicebroadcast.usecase.PauseVoiceBroadcastUseCase
|
||||
import im.vector.app.features.voicebroadcast.usecase.ResumeVoiceBroadcastUseCase
|
||||
import im.vector.app.features.voicebroadcast.usecase.StartVoiceBroadcastUseCase
|
||||
import im.vector.app.features.voicebroadcast.usecase.StopVoiceBroadcastUseCase
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Helper class to record voice broadcast.
|
||||
*/
|
||||
class VoiceBroadcastHelper @Inject constructor(
|
||||
private val startVoiceBroadcastUseCase: StartVoiceBroadcastUseCase,
|
||||
private val pauseVoiceBroadcastUseCase: PauseVoiceBroadcastUseCase,
|
||||
private val resumeVoiceBroadcastUseCase: ResumeVoiceBroadcastUseCase,
|
||||
private val stopVoiceBroadcastUseCase: StopVoiceBroadcastUseCase,
|
||||
) {
|
||||
suspend fun startVoiceBroadcast(roomId: String) = startVoiceBroadcastUseCase.execute(roomId)
|
||||
|
||||
suspend fun pauseVoiceBroadcast(roomId: String) = pauseVoiceBroadcastUseCase.execute(roomId)
|
||||
|
||||
suspend fun resumeVoiceBroadcast(roomId: String) = resumeVoiceBroadcastUseCase.execute(roomId)
|
||||
|
||||
suspend fun stopVoiceBroadcast(roomId: String) = stopVoiceBroadcastUseCase.execute(roomId)
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.voicebroadcast.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
|
||||
import org.matrix.android.sdk.api.session.events.model.Content
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType.MSGTYPE_VOICE_BROADCAST_INFO
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Content of the state event of type [STATE_ROOM_VOICE_BROADCAST_INFO].
|
||||
*
|
||||
* It contains general info related to a voice broadcast.
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class MessageVoiceBroadcastInfoContent(
|
||||
/** Local message type, not from server. */
|
||||
@Transient override val msgType: String = MSGTYPE_VOICE_BROADCAST_INFO,
|
||||
@Json(name = "body") override val body: String = "",
|
||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||
@Json(name = "m.new_content") override val newContent: Content? = null,
|
||||
|
||||
/** The [VoiceBroadcastState] value. **/
|
||||
@Json(name = "state") val voiceBroadcastStateStr: String = "",
|
||||
/** The length of the voice chunks in seconds. **/
|
||||
@Json(name = "chunk_length") val chunkLength: Long? = null,
|
||||
) : MessageContent {
|
||||
|
||||
val voiceBroadcastState: VoiceBroadcastState? = VoiceBroadcastState.values()
|
||||
.find { it.value == voiceBroadcastStateStr }
|
||||
?: run {
|
||||
Timber.w("Invalid value for state: `$voiceBroadcastStateStr`")
|
||||
null
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.voicebroadcast.model
|
||||
|
||||
import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.RelationType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||
|
||||
/**
|
||||
* [Event] wrapper for [STATE_ROOM_VOICE_BROADCAST_INFO] event type.
|
||||
* Provides additional fields and functions related to voice broadcast.
|
||||
*/
|
||||
@JvmInline
|
||||
value class VoiceBroadcastEvent(val root: Event) {
|
||||
|
||||
/**
|
||||
* Reference on the initial voice broadcast state event (ie. with [MessageVoiceBroadcastInfoContent.voiceBroadcastState]=[VoiceBroadcastState.STARTED]).
|
||||
*/
|
||||
val reference: RelationDefaultContent?
|
||||
get() {
|
||||
val voiceBroadcastInfoContent = root.content.toModel<MessageVoiceBroadcastInfoContent>()
|
||||
return if (voiceBroadcastInfoContent?.voiceBroadcastState == VoiceBroadcastState.STARTED) {
|
||||
RelationDefaultContent(RelationType.REFERENCE, root.eventId)
|
||||
} else {
|
||||
voiceBroadcastInfoContent?.relatesTo
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The mapped [MessageVoiceBroadcastInfoContent] model of the event content.
|
||||
*/
|
||||
val content: MessageVoiceBroadcastInfoContent?
|
||||
get() = root.content.toModel()
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a [STATE_ROOM_VOICE_BROADCAST_INFO] state event to a [VoiceBroadcastEvent].
|
||||
*/
|
||||
fun Event.asVoiceBroadcastEvent() = if (type == STATE_ROOM_VOICE_BROADCAST_INFO) VoiceBroadcastEvent(this) else null
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.voicebroadcast.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
* Ref: https://github.com/vector-im/element-meta/discussions/632
|
||||
*/
|
||||
@JsonClass(generateAdapter = false)
|
||||
enum class VoiceBroadcastState(val value: String) {
|
||||
/**
|
||||
* The voice broadcast had been started and is currently being live.
|
||||
*/
|
||||
@Json(name = "started") STARTED("started"),
|
||||
|
||||
/**
|
||||
* The voice broadcast has been paused and may be resumed at any time by the recorder.
|
||||
*/
|
||||
@Json(name = "paused") PAUSED("paused"),
|
||||
|
||||
/**
|
||||
* The voice broadcast is currently being live again.
|
||||
*/
|
||||
@Json(name = "resumed") RESUMED("resumed"),
|
||||
|
||||
/**
|
||||
* The voice broadcast has ended.
|
||||
*/
|
||||
@Json(name = "stopped") STOPPED("stopped"),
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.voicebroadcast.usecase
|
||||
|
||||
import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
|
||||
import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
|
||||
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
|
||||
import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class PauseVoiceBroadcastUseCase @Inject constructor(
|
||||
private val session: Session,
|
||||
) {
|
||||
|
||||
suspend fun execute(roomId: String): Result<Unit> = runCatching {
|
||||
val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId")
|
||||
|
||||
Timber.d("## PauseVoiceBroadcastUseCase: Pause voice broadcast requested")
|
||||
|
||||
val lastVoiceBroadcastEvent = room.stateService().getStateEvent(
|
||||
STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
QueryStringValue.Equals(session.myUserId)
|
||||
)?.asVoiceBroadcastEvent()
|
||||
when (val voiceBroadcastState = lastVoiceBroadcastEvent?.content?.voiceBroadcastState) {
|
||||
VoiceBroadcastState.STARTED,
|
||||
VoiceBroadcastState.RESUMED -> pauseVoiceBroadcast(room, lastVoiceBroadcastEvent.reference)
|
||||
else -> Timber.d("## PauseVoiceBroadcastUseCase: Cannot pause voice broadcast: currentState=$voiceBroadcastState")
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun pauseVoiceBroadcast(room: Room, reference: RelationDefaultContent?) {
|
||||
Timber.d("## PauseVoiceBroadcastUseCase: Send new voice broadcast info state event")
|
||||
room.stateService().sendStateEvent(
|
||||
eventType = STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
stateKey = session.myUserId,
|
||||
body = MessageVoiceBroadcastInfoContent(
|
||||
relatesTo = reference,
|
||||
voiceBroadcastStateStr = VoiceBroadcastState.PAUSED.value,
|
||||
).toContent(),
|
||||
)
|
||||
|
||||
// TODO pause recording audio files
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.voicebroadcast.usecase
|
||||
|
||||
import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
|
||||
import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
|
||||
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
|
||||
import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class ResumeVoiceBroadcastUseCase @Inject constructor(
|
||||
private val session: Session,
|
||||
) {
|
||||
|
||||
suspend fun execute(roomId: String): Result<Unit> = runCatching {
|
||||
val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId")
|
||||
|
||||
Timber.d("## ResumeVoiceBroadcastUseCase: Resume voice broadcast requested")
|
||||
|
||||
val lastVoiceBroadcastEvent = room.stateService().getStateEvent(
|
||||
STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
QueryStringValue.Equals(session.myUserId)
|
||||
)?.asVoiceBroadcastEvent()
|
||||
when (val voiceBroadcastState = lastVoiceBroadcastEvent?.content?.voiceBroadcastState) {
|
||||
VoiceBroadcastState.PAUSED -> resumeVoiceBroadcast(room, lastVoiceBroadcastEvent.reference)
|
||||
else -> Timber.d("## ResumeVoiceBroadcastUseCase: Cannot resume voice broadcast: currentState=$voiceBroadcastState")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume a paused voice broadcast in the given room.
|
||||
*
|
||||
* @param room the room related to the voice broadcast
|
||||
* @param reference reference on the initial voice broadcast state event (ie. state=STARTED)
|
||||
*/
|
||||
private suspend fun resumeVoiceBroadcast(room: Room, reference: RelationDefaultContent?) {
|
||||
Timber.d("## ResumeVoiceBroadcastUseCase: Send new voice broadcast info state event")
|
||||
room.stateService().sendStateEvent(
|
||||
eventType = STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
stateKey = session.myUserId,
|
||||
body = MessageVoiceBroadcastInfoContent(
|
||||
relatesTo = reference,
|
||||
voiceBroadcastStateStr = VoiceBroadcastState.RESUMED.value,
|
||||
).toContent(),
|
||||
)
|
||||
|
||||
// TODO resume recording audio files
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.voicebroadcast.usecase
|
||||
|
||||
import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
|
||||
import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
|
||||
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
|
||||
import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class StartVoiceBroadcastUseCase @Inject constructor(
|
||||
private val session: Session,
|
||||
) {
|
||||
|
||||
suspend fun execute(roomId: String): Result<Unit> = runCatching {
|
||||
val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId")
|
||||
|
||||
Timber.d("## StartVoiceBroadcastUseCase: Start voice broadcast requested")
|
||||
|
||||
val onGoingVoiceBroadcastEvents = room.stateService().getStateEvents(
|
||||
setOf(STATE_ROOM_VOICE_BROADCAST_INFO),
|
||||
QueryStringValue.IsNotEmpty
|
||||
)
|
||||
.mapNotNull { it.asVoiceBroadcastEvent() }
|
||||
.filter { it.content?.voiceBroadcastState != VoiceBroadcastState.STOPPED }
|
||||
|
||||
if (onGoingVoiceBroadcastEvents.isEmpty()) {
|
||||
startVoiceBroadcast(room)
|
||||
} else {
|
||||
Timber.d("## StartVoiceBroadcastUseCase: Cannot start voice broadcast: currentVoiceBroadcastEvents=$onGoingVoiceBroadcastEvents")
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun startVoiceBroadcast(room: Room) {
|
||||
Timber.d("## StartVoiceBroadcastUseCase: Send new voice broadcast info state event")
|
||||
room.stateService().sendStateEvent(
|
||||
eventType = STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
stateKey = session.myUserId,
|
||||
body = MessageVoiceBroadcastInfoContent(
|
||||
voiceBroadcastStateStr = VoiceBroadcastState.STARTED.value,
|
||||
chunkLength = 5L, // TODO Get length from voice broadcast settings
|
||||
).toContent()
|
||||
)
|
||||
|
||||
// TODO start recording audio files
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.voicebroadcast.usecase
|
||||
|
||||
import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
|
||||
import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
|
||||
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
|
||||
import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.getRoom
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class StopVoiceBroadcastUseCase @Inject constructor(
|
||||
private val session: Session,
|
||||
) {
|
||||
|
||||
suspend fun execute(roomId: String): Result<Unit> = runCatching {
|
||||
val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId")
|
||||
|
||||
Timber.d("## StopVoiceBroadcastUseCase: Stop voice broadcast requested")
|
||||
|
||||
val lastVoiceBroadcastEvent = room.stateService().getStateEvent(
|
||||
STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
QueryStringValue.Equals(session.myUserId)
|
||||
)?.asVoiceBroadcastEvent()
|
||||
when (val voiceBroadcastState = lastVoiceBroadcastEvent?.content?.voiceBroadcastState) {
|
||||
VoiceBroadcastState.STARTED,
|
||||
VoiceBroadcastState.PAUSED,
|
||||
VoiceBroadcastState.RESUMED -> stopVoiceBroadcast(room, lastVoiceBroadcastEvent.reference)
|
||||
else -> Timber.d("## StopVoiceBroadcastUseCase: Cannot stop voice broadcast: currentState=$voiceBroadcastState")
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun stopVoiceBroadcast(room: Room, reference: RelationDefaultContent?) {
|
||||
Timber.d("## StopVoiceBroadcastUseCase: Send new voice broadcast info state event")
|
||||
room.stateService().sendStateEvent(
|
||||
eventType = STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
stateKey = session.myUserId,
|
||||
body = MessageVoiceBroadcastInfoContent(
|
||||
relatesTo = reference,
|
||||
voiceBroadcastStateStr = VoiceBroadcastState.STOPPED.value,
|
||||
).toContent(),
|
||||
)
|
||||
|
||||
// TODO stop recording audio files
|
||||
}
|
||||
}
|
|
@ -47,6 +47,13 @@
|
|||
android:layout="@layout/item_timeline_event_audio_stub"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/messageVoiceBroadcastStub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout="@layout/item_timeline_event_voice_broadcast_stub"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/messageContentPollStub"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/messageRootLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/layout_vertical_margin"
|
||||
tools:viewBindingIgnore="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/currentStateText"
|
||||
style="@style/Widget.Vector.TextView.HeadlineMedium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toTopOf="@id/playButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Voice Broadcast state: STARTED" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/playButton"
|
||||
android:layout_width="@dimen/item_event_message_media_button_size"
|
||||
android:layout_height="@dimen/item_event_message_media_button_size"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:contentDescription="@string/a11y_play_voice_message"
|
||||
android:src="@drawable/ic_play_pause_play"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/pauseButton"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/currentStateText"
|
||||
app:tint="@color/vector_content_primary_tint_selector" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/pauseButton"
|
||||
android:layout_width="@dimen/item_event_message_media_button_size"
|
||||
android:layout_height="@dimen/item_event_message_media_button_size"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:contentDescription="@string/a11y_pause_voice_message"
|
||||
android:src="@drawable/ic_play_pause_pause"
|
||||
app:layout_constraintBottom_toBottomOf="@id/playButton"
|
||||
app:layout_constraintEnd_toStartOf="@id/stopButton"
|
||||
app:layout_constraintStart_toEndOf="@id/playButton"
|
||||
app:layout_constraintTop_toTopOf="@id/playButton"
|
||||
app:tint="@color/vector_content_primary_tint_selector" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/stopButton"
|
||||
android:layout_width="@dimen/item_event_message_media_button_size"
|
||||
android:layout_height="@dimen/item_event_message_media_button_size"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:contentDescription="@string/a11y_stop_voice_message"
|
||||
android:src="@drawable/ic_close_24dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/pauseButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/pauseButton"
|
||||
app:layout_constraintTop_toTopOf="@id/playButton"
|
||||
app:tint="@color/vector_content_primary_tint_selector" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package im.vector.app.features.home.room.detail.timeline.action
|
||||
|
||||
import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
|
||||
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
||||
import io.mockk.mockk
|
||||
import org.amshove.kluent.shouldBe
|
||||
|
@ -34,7 +35,7 @@ class CheckIfCanRedactEventUseCaseTest {
|
|||
|
||||
@Test
|
||||
fun `given an event which can be redacted and owned by user when use case executes then the result is true`() {
|
||||
val canRedactEventTypes = listOf(EventType.MESSAGE, EventType.STICKER) +
|
||||
val canRedactEventTypes = listOf(EventType.MESSAGE, EventType.STICKER, STATE_ROOM_VOICE_BROADCAST_INFO) +
|
||||
EventType.POLL_START + EventType.STATE_ROOM_BEACON_INFO
|
||||
|
||||
canRedactEventTypes.forEach { eventType ->
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.voicebroadcast.model
|
||||
|
||||
import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
|
||||
import org.amshove.kluent.shouldBeEqualTo
|
||||
import org.amshove.kluent.shouldBeNull
|
||||
import org.amshove.kluent.shouldNotBeNull
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.RelationType
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.AudioInfo
|
||||
import org.matrix.android.sdk.api.session.room.model.message.AudioWaveformInfo
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.ReplyToContent
|
||||
|
||||
private const val AN_EVENT_ID = "event_id"
|
||||
private const val A_REFERENCED_EVENT_ID = "event_id_ref"
|
||||
private const val A_CHUNK_LENGTH = 3_600L
|
||||
|
||||
class VoiceBroadcastEventTest {
|
||||
|
||||
@Test
|
||||
fun `given a started Voice Broadcast Event, when mapping to VoiceBroadcastEvent, then return expected object`() {
|
||||
// Given
|
||||
val content = MessageVoiceBroadcastInfoContent(
|
||||
voiceBroadcastStateStr = VoiceBroadcastState.STARTED.value,
|
||||
chunkLength = A_CHUNK_LENGTH,
|
||||
relatesTo = RelationDefaultContent(RelationType.REFERENCE, A_REFERENCED_EVENT_ID),
|
||||
)
|
||||
val event = Event(
|
||||
eventId = AN_EVENT_ID,
|
||||
type = STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
content = content.toContent(),
|
||||
)
|
||||
val expectedReference = RelationDefaultContent(RelationType.REFERENCE, event.eventId)
|
||||
|
||||
// When
|
||||
val result = event.asVoiceBroadcastEvent()
|
||||
|
||||
// Then
|
||||
result.shouldNotBeNull()
|
||||
result.content shouldBeEqualTo content
|
||||
result.reference shouldBeEqualTo expectedReference
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a not started Voice Broadcast Event, when mapping to VoiceBroadcastEvent, then return expected object`() {
|
||||
// Given
|
||||
val content = MessageVoiceBroadcastInfoContent(
|
||||
voiceBroadcastStateStr = VoiceBroadcastState.PAUSED.value,
|
||||
chunkLength = A_CHUNK_LENGTH,
|
||||
relatesTo = RelationDefaultContent(RelationType.REFERENCE, A_REFERENCED_EVENT_ID),
|
||||
)
|
||||
val event = Event(
|
||||
type = STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
content = content.toContent(),
|
||||
)
|
||||
val expectedReference = content.relatesTo
|
||||
|
||||
// When
|
||||
val result = event.asVoiceBroadcastEvent()
|
||||
|
||||
// Then
|
||||
result.shouldNotBeNull()
|
||||
result.content shouldBeEqualTo content
|
||||
result.reference shouldBeEqualTo expectedReference
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a non Voice Broadcast Event, when mapping to VoiceBroadcastEvent, then return null`() {
|
||||
// Given
|
||||
val content = MessageAudioContent(
|
||||
msgType = MessageType.MSGTYPE_AUDIO,
|
||||
body = "audio",
|
||||
audioInfo = AudioInfo(
|
||||
duration = 300,
|
||||
mimeType = "",
|
||||
size = 500L
|
||||
),
|
||||
url = "a_url",
|
||||
audioWaveformInfo = AudioWaveformInfo(
|
||||
duration = 300,
|
||||
waveform = null
|
||||
),
|
||||
voiceMessageIndicator = emptyMap(),
|
||||
relatesTo = RelationDefaultContent(
|
||||
type = RelationType.THREAD,
|
||||
eventId = AN_EVENT_ID,
|
||||
isFallingBack = true,
|
||||
inReplyTo = ReplyToContent(eventId = A_REFERENCED_EVENT_ID)
|
||||
)
|
||||
)
|
||||
val event = Event(
|
||||
type = EventType.MESSAGE,
|
||||
content = content.toContent(),
|
||||
)
|
||||
|
||||
// When
|
||||
val result = event.asVoiceBroadcastEvent()
|
||||
|
||||
// Then
|
||||
result.shouldBeNull()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.voicebroadcast.usecase
|
||||
|
||||
import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
|
||||
import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
|
||||
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
|
||||
import im.vector.app.test.fakes.FakeRoom
|
||||
import im.vector.app.test.fakes.FakeRoomService
|
||||
import im.vector.app.test.fakes.FakeSession
|
||||
import io.mockk.clearAllMocks
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.slot
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.amshove.kluent.shouldBe
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.api.session.events.model.Content
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.RelationType
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||
|
||||
private const val A_ROOM_ID = "room_id"
|
||||
private const val AN_EVENT_ID = "event_id"
|
||||
private const val A_STARTED_VOICE_BROADCAST_EVENT_ID = "a_started_voice_broadcast_event_id"
|
||||
|
||||
class PauseVoiceBroadcastUseCaseTest {
|
||||
|
||||
private val fakeRoom = FakeRoom()
|
||||
private val fakeSession = FakeSession(fakeRoomService = FakeRoomService(fakeRoom))
|
||||
private val pauseVoiceBroadcastUseCase = PauseVoiceBroadcastUseCase(fakeSession)
|
||||
|
||||
@Test
|
||||
fun `given a room id with a potential existing voice broadcast state when calling execute then the voice broadcast is paused or not`() = runTest {
|
||||
val cases = listOf<VoiceBroadcastState?>(null).plus(VoiceBroadcastState.values()).map {
|
||||
when (it) {
|
||||
VoiceBroadcastState.STARTED,
|
||||
VoiceBroadcastState.RESUMED -> Case(it, true)
|
||||
VoiceBroadcastState.STOPPED,
|
||||
VoiceBroadcastState.PAUSED,
|
||||
null -> Case(it, false)
|
||||
}
|
||||
}
|
||||
|
||||
cases.forEach { case ->
|
||||
if (case.canPauseVoiceBroadcast) {
|
||||
testVoiceBroadcastPaused(case.previousState)
|
||||
} else {
|
||||
testVoiceBroadcastNotPaused(case.previousState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun testVoiceBroadcastPaused(previousState: VoiceBroadcastState?) {
|
||||
// Given
|
||||
clearAllMocks()
|
||||
givenAVoiceBroadcastState(previousState)
|
||||
val voiceBroadcastInfoContentInterceptor = slot<Content>()
|
||||
coEvery { fakeRoom.stateService().sendStateEvent(any(), any(), capture(voiceBroadcastInfoContentInterceptor)) } coAnswers { AN_EVENT_ID }
|
||||
|
||||
// When
|
||||
pauseVoiceBroadcastUseCase.execute(A_ROOM_ID)
|
||||
|
||||
// Then
|
||||
coVerify {
|
||||
fakeRoom.stateService().sendStateEvent(
|
||||
eventType = STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
stateKey = fakeSession.myUserId,
|
||||
body = any(),
|
||||
)
|
||||
}
|
||||
val voiceBroadcastInfoContent = voiceBroadcastInfoContentInterceptor.captured.toModel<MessageVoiceBroadcastInfoContent>()
|
||||
voiceBroadcastInfoContent?.voiceBroadcastState shouldBe VoiceBroadcastState.PAUSED
|
||||
voiceBroadcastInfoContent?.relatesTo?.type shouldBe RelationType.REFERENCE
|
||||
voiceBroadcastInfoContent?.relatesTo?.eventId shouldBe A_STARTED_VOICE_BROADCAST_EVENT_ID
|
||||
}
|
||||
|
||||
private suspend fun testVoiceBroadcastNotPaused(previousState: VoiceBroadcastState?) {
|
||||
// Given
|
||||
clearAllMocks()
|
||||
givenAVoiceBroadcastState(previousState)
|
||||
|
||||
// When
|
||||
pauseVoiceBroadcastUseCase.execute(A_ROOM_ID)
|
||||
|
||||
// Then
|
||||
coVerify(exactly = 0) { fakeRoom.stateService().sendStateEvent(any(), any(), any()) }
|
||||
}
|
||||
|
||||
private fun givenAVoiceBroadcastState(state: VoiceBroadcastState?) {
|
||||
val relatesTo = when (state) {
|
||||
VoiceBroadcastState.STARTED,
|
||||
null -> null
|
||||
VoiceBroadcastState.PAUSED,
|
||||
VoiceBroadcastState.RESUMED,
|
||||
VoiceBroadcastState.STOPPED -> RelationDefaultContent(RelationType.REFERENCE, A_STARTED_VOICE_BROADCAST_EVENT_ID)
|
||||
}
|
||||
val event = state?.let {
|
||||
Event(
|
||||
eventId = if (state == VoiceBroadcastState.STARTED) A_STARTED_VOICE_BROADCAST_EVENT_ID else AN_EVENT_ID,
|
||||
type = STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
stateKey = fakeSession.myUserId,
|
||||
content = MessageVoiceBroadcastInfoContent(
|
||||
voiceBroadcastStateStr = state.value,
|
||||
relatesTo = relatesTo
|
||||
).toContent()
|
||||
)
|
||||
}
|
||||
fakeRoom.stateService().givenGetStateEvent(event)
|
||||
}
|
||||
|
||||
private data class Case(val previousState: VoiceBroadcastState?, val canPauseVoiceBroadcast: Boolean)
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.voicebroadcast.usecase
|
||||
|
||||
import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
|
||||
import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
|
||||
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
|
||||
import im.vector.app.test.fakes.FakeRoom
|
||||
import im.vector.app.test.fakes.FakeRoomService
|
||||
import im.vector.app.test.fakes.FakeSession
|
||||
import io.mockk.clearAllMocks
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.slot
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.amshove.kluent.shouldBe
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.api.session.events.model.Content
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.RelationType
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||
|
||||
private const val A_ROOM_ID = "room_id"
|
||||
private const val AN_EVENT_ID = "event_id"
|
||||
private const val A_STARTED_VOICE_BROADCAST_EVENT_ID = "a_started_voice_broadcast_event_id"
|
||||
|
||||
class ResumeVoiceBroadcastUseCaseTest {
|
||||
|
||||
private val fakeRoom = FakeRoom()
|
||||
private val fakeSession = FakeSession(fakeRoomService = FakeRoomService(fakeRoom))
|
||||
private val resumeVoiceBroadcastUseCase = ResumeVoiceBroadcastUseCase(fakeSession)
|
||||
|
||||
@Test
|
||||
fun `given a room id with a potential existing voice broadcast state when calling execute then the voice broadcast is resumed or not`() = runTest {
|
||||
val cases = listOf<VoiceBroadcastState?>(null).plus(VoiceBroadcastState.values()).map {
|
||||
when (it) {
|
||||
VoiceBroadcastState.PAUSED -> Case(it, true)
|
||||
VoiceBroadcastState.STARTED,
|
||||
VoiceBroadcastState.RESUMED,
|
||||
VoiceBroadcastState.STOPPED,
|
||||
null -> Case(it, false)
|
||||
}
|
||||
}
|
||||
|
||||
cases.forEach { case ->
|
||||
if (case.canResumeVoiceBroadcast) {
|
||||
testVoiceBroadcastResumed(case.previousState)
|
||||
} else {
|
||||
testVoiceBroadcastNotResumed(case.previousState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun testVoiceBroadcastResumed(previousState: VoiceBroadcastState?) {
|
||||
// Given
|
||||
clearAllMocks()
|
||||
givenAVoiceBroadcastState(previousState)
|
||||
val voiceBroadcastInfoContentInterceptor = slot<Content>()
|
||||
coEvery { fakeRoom.stateService().sendStateEvent(any(), any(), capture(voiceBroadcastInfoContentInterceptor)) } coAnswers { AN_EVENT_ID }
|
||||
|
||||
// When
|
||||
resumeVoiceBroadcastUseCase.execute(A_ROOM_ID)
|
||||
|
||||
// Then
|
||||
coVerify {
|
||||
fakeRoom.stateService().sendStateEvent(
|
||||
eventType = STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
stateKey = fakeSession.myUserId,
|
||||
body = any(),
|
||||
)
|
||||
}
|
||||
val voiceBroadcastInfoContent = voiceBroadcastInfoContentInterceptor.captured.toModel<MessageVoiceBroadcastInfoContent>()
|
||||
voiceBroadcastInfoContent?.voiceBroadcastState shouldBe VoiceBroadcastState.RESUMED
|
||||
voiceBroadcastInfoContent?.relatesTo?.type shouldBe RelationType.REFERENCE
|
||||
voiceBroadcastInfoContent?.relatesTo?.eventId shouldBe A_STARTED_VOICE_BROADCAST_EVENT_ID
|
||||
}
|
||||
|
||||
private suspend fun testVoiceBroadcastNotResumed(previousState: VoiceBroadcastState?) {
|
||||
// Given
|
||||
clearAllMocks()
|
||||
givenAVoiceBroadcastState(previousState)
|
||||
|
||||
// When
|
||||
resumeVoiceBroadcastUseCase.execute(A_ROOM_ID)
|
||||
|
||||
// Then
|
||||
coVerify(exactly = 0) { fakeRoom.stateService().sendStateEvent(any(), any(), any()) }
|
||||
}
|
||||
|
||||
private fun givenAVoiceBroadcastState(state: VoiceBroadcastState?) {
|
||||
val relatesTo = when (state) {
|
||||
VoiceBroadcastState.STARTED,
|
||||
null -> null
|
||||
VoiceBroadcastState.PAUSED,
|
||||
VoiceBroadcastState.RESUMED,
|
||||
VoiceBroadcastState.STOPPED -> RelationDefaultContent(RelationType.REFERENCE, A_STARTED_VOICE_BROADCAST_EVENT_ID)
|
||||
}
|
||||
val event = state?.let {
|
||||
Event(
|
||||
eventId = if (state == VoiceBroadcastState.STARTED) A_STARTED_VOICE_BROADCAST_EVENT_ID else AN_EVENT_ID,
|
||||
type = STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
stateKey = fakeSession.myUserId,
|
||||
content = MessageVoiceBroadcastInfoContent(
|
||||
voiceBroadcastStateStr = state.value,
|
||||
relatesTo = relatesTo
|
||||
).toContent()
|
||||
)
|
||||
}
|
||||
fakeRoom.stateService().givenGetStateEvent(event)
|
||||
}
|
||||
|
||||
private data class Case(val previousState: VoiceBroadcastState?, val canResumeVoiceBroadcast: Boolean)
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.voicebroadcast.usecase
|
||||
|
||||
import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
|
||||
import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
|
||||
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
|
||||
import im.vector.app.test.fakes.FakeRoom
|
||||
import im.vector.app.test.fakes.FakeRoomService
|
||||
import im.vector.app.test.fakes.FakeSession
|
||||
import io.mockk.clearAllMocks
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.slot
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.amshove.kluent.shouldBe
|
||||
import org.amshove.kluent.shouldBeNull
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.events.model.Content
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
|
||||
private const val A_ROOM_ID = "room_id"
|
||||
private const val AN_EVENT_ID = "event_id"
|
||||
private const val A_USER_ID = "user_id"
|
||||
|
||||
class StartVoiceBroadcastUseCaseTest {
|
||||
|
||||
private val fakeRoom = FakeRoom()
|
||||
private val fakeSession = FakeSession(fakeRoomService = FakeRoomService(fakeRoom))
|
||||
private val startVoiceBroadcastUseCase = StartVoiceBroadcastUseCase(fakeSession)
|
||||
|
||||
@Test
|
||||
fun `given a room id with potential several existing voice broadcast states when calling execute then the voice broadcast is started or not`() = runTest {
|
||||
val cases = VoiceBroadcastState.values()
|
||||
.flatMap { first ->
|
||||
VoiceBroadcastState.values().map { second ->
|
||||
Case(
|
||||
voiceBroadcasts = listOf(VoiceBroadcast(fakeSession.myUserId, first), VoiceBroadcast(A_USER_ID, second)),
|
||||
canStartVoiceBroadcast = first == VoiceBroadcastState.STOPPED && second == VoiceBroadcastState.STOPPED
|
||||
)
|
||||
}
|
||||
}
|
||||
.plus(Case(emptyList(), true))
|
||||
|
||||
cases.forEach { case ->
|
||||
if (case.canStartVoiceBroadcast) {
|
||||
testVoiceBroadcastStarted(case.voiceBroadcasts)
|
||||
} else {
|
||||
testVoiceBroadcastNotStarted(case.voiceBroadcasts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun testVoiceBroadcastStarted(voiceBroadcasts: List<VoiceBroadcast>) {
|
||||
// Given
|
||||
clearAllMocks()
|
||||
givenAVoiceBroadcasts(voiceBroadcasts)
|
||||
val voiceBroadcastInfoContentInterceptor = slot<Content>()
|
||||
coEvery { fakeRoom.stateService().sendStateEvent(any(), any(), capture(voiceBroadcastInfoContentInterceptor)) } coAnswers { AN_EVENT_ID }
|
||||
|
||||
// When
|
||||
startVoiceBroadcastUseCase.execute(A_ROOM_ID)
|
||||
|
||||
// Then
|
||||
coVerify {
|
||||
fakeRoom.stateService().sendStateEvent(
|
||||
eventType = STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
stateKey = fakeSession.myUserId,
|
||||
body = any(),
|
||||
)
|
||||
}
|
||||
val voiceBroadcastInfoContent = voiceBroadcastInfoContentInterceptor.captured.toModel<MessageVoiceBroadcastInfoContent>()
|
||||
voiceBroadcastInfoContent?.voiceBroadcastState shouldBe VoiceBroadcastState.STARTED
|
||||
voiceBroadcastInfoContent?.relatesTo.shouldBeNull()
|
||||
}
|
||||
|
||||
private suspend fun testVoiceBroadcastNotStarted(voiceBroadcasts: List<VoiceBroadcast>) {
|
||||
// Given
|
||||
clearAllMocks()
|
||||
givenAVoiceBroadcasts(voiceBroadcasts)
|
||||
|
||||
// When
|
||||
startVoiceBroadcastUseCase.execute(A_ROOM_ID)
|
||||
|
||||
// Then
|
||||
coVerify(exactly = 0) { fakeRoom.stateService().sendStateEvent(any(), any(), any()) }
|
||||
}
|
||||
|
||||
private fun givenAVoiceBroadcasts(voiceBroadcasts: List<VoiceBroadcast>) {
|
||||
val events = voiceBroadcasts.map {
|
||||
Event(
|
||||
type = STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
stateKey = it.userId,
|
||||
content = MessageVoiceBroadcastInfoContent(
|
||||
voiceBroadcastStateStr = it.state.value
|
||||
).toContent()
|
||||
)
|
||||
}
|
||||
fakeRoom.stateService().givenGetStateEvents(QueryStringValue.IsNotEmpty, events)
|
||||
}
|
||||
|
||||
private data class VoiceBroadcast(val userId: String, val state: VoiceBroadcastState)
|
||||
private data class Case(val voiceBroadcasts: List<VoiceBroadcast>, val canStartVoiceBroadcast: Boolean)
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.voicebroadcast.usecase
|
||||
|
||||
import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
|
||||
import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
|
||||
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
|
||||
import im.vector.app.test.fakes.FakeRoom
|
||||
import im.vector.app.test.fakes.FakeRoomService
|
||||
import im.vector.app.test.fakes.FakeSession
|
||||
import io.mockk.clearAllMocks
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.slot
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.amshove.kluent.shouldBe
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.api.session.events.model.Content
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.RelationType
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||
|
||||
private const val A_ROOM_ID = "room_id"
|
||||
private const val AN_EVENT_ID = "event_id"
|
||||
private const val A_STARTED_VOICE_BROADCAST_EVENT_ID = "a_started_voice_broadcast_event_id"
|
||||
|
||||
class StopVoiceBroadcastUseCaseTest {
|
||||
|
||||
private val fakeRoom = FakeRoom()
|
||||
private val fakeSession = FakeSession(fakeRoomService = FakeRoomService(fakeRoom))
|
||||
private val stopVoiceBroadcastUseCase = StopVoiceBroadcastUseCase(fakeSession)
|
||||
|
||||
@Test
|
||||
fun `given a room id with a potential existing voice broadcast state when calling execute then the voice broadcast is stopped or not`() = runTest {
|
||||
val cases = listOf<VoiceBroadcastState?>(null).plus(VoiceBroadcastState.values()).map {
|
||||
when (it) {
|
||||
VoiceBroadcastState.STARTED,
|
||||
VoiceBroadcastState.RESUMED,
|
||||
VoiceBroadcastState.PAUSED -> Case(it, true)
|
||||
VoiceBroadcastState.STOPPED,
|
||||
null -> Case(it, false)
|
||||
}
|
||||
}
|
||||
|
||||
cases.forEach { case ->
|
||||
if (case.canStopVoiceBroadcast) {
|
||||
testVoiceBroadcastStopped(case.previousState)
|
||||
} else {
|
||||
testVoiceBroadcastNotStopped(case.previousState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun testVoiceBroadcastStopped(previousState: VoiceBroadcastState?) {
|
||||
// Given
|
||||
clearAllMocks()
|
||||
givenAVoiceBroadcastState(previousState)
|
||||
val voiceBroadcastInfoContentInterceptor = slot<Content>()
|
||||
coEvery { fakeRoom.stateService().sendStateEvent(any(), any(), capture(voiceBroadcastInfoContentInterceptor)) } coAnswers { AN_EVENT_ID }
|
||||
|
||||
// When
|
||||
stopVoiceBroadcastUseCase.execute(A_ROOM_ID)
|
||||
|
||||
// Then
|
||||
coVerify {
|
||||
fakeRoom.stateService().sendStateEvent(
|
||||
eventType = STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
stateKey = fakeSession.myUserId,
|
||||
body = any(),
|
||||
)
|
||||
}
|
||||
val voiceBroadcastInfoContent = voiceBroadcastInfoContentInterceptor.captured.toModel<MessageVoiceBroadcastInfoContent>()
|
||||
voiceBroadcastInfoContent?.voiceBroadcastState shouldBe VoiceBroadcastState.STOPPED
|
||||
voiceBroadcastInfoContent?.relatesTo?.type shouldBe RelationType.REFERENCE
|
||||
voiceBroadcastInfoContent?.relatesTo?.eventId shouldBe A_STARTED_VOICE_BROADCAST_EVENT_ID
|
||||
}
|
||||
|
||||
private suspend fun testVoiceBroadcastNotStopped(previousState: VoiceBroadcastState?) {
|
||||
// Given
|
||||
clearAllMocks()
|
||||
givenAVoiceBroadcastState(previousState)
|
||||
|
||||
// When
|
||||
stopVoiceBroadcastUseCase.execute(A_ROOM_ID)
|
||||
|
||||
// Then
|
||||
coVerify(exactly = 0) { fakeRoom.stateService().sendStateEvent(any(), any(), any()) }
|
||||
}
|
||||
|
||||
private fun givenAVoiceBroadcastState(state: VoiceBroadcastState?) {
|
||||
val relatesTo = when (state) {
|
||||
VoiceBroadcastState.STARTED,
|
||||
null -> null
|
||||
VoiceBroadcastState.PAUSED,
|
||||
VoiceBroadcastState.RESUMED,
|
||||
VoiceBroadcastState.STOPPED -> RelationDefaultContent(RelationType.REFERENCE, A_STARTED_VOICE_BROADCAST_EVENT_ID)
|
||||
}
|
||||
val event = state?.let {
|
||||
Event(
|
||||
eventId = if (state == VoiceBroadcastState.STARTED) A_STARTED_VOICE_BROADCAST_EVENT_ID else AN_EVENT_ID,
|
||||
type = STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
stateKey = fakeSession.myUserId,
|
||||
content = MessageVoiceBroadcastInfoContent(
|
||||
voiceBroadcastStateStr = state.value,
|
||||
relatesTo = relatesTo
|
||||
).toContent()
|
||||
)
|
||||
}
|
||||
fakeRoom.stateService().givenGetStateEvent(event)
|
||||
}
|
||||
|
||||
private data class Case(val previousState: VoiceBroadcastState?, val canStopVoiceBroadcast: Boolean)
|
||||
}
|
|
@ -24,6 +24,7 @@ class FakeRoom(
|
|||
private val fakeSendService: FakeSendService = FakeSendService(),
|
||||
private val fakeTimelineService: FakeTimelineService = FakeTimelineService(),
|
||||
private val fakeRelationService: FakeRelationService = FakeRelationService(),
|
||||
private val fakeStateService: FakeStateService = FakeStateService(),
|
||||
) : Room by mockk() {
|
||||
|
||||
override fun locationSharingService() = fakeLocationSharingService
|
||||
|
@ -33,4 +34,6 @@ class FakeRoom(
|
|||
override fun timelineService() = fakeTimelineService
|
||||
|
||||
override fun relationService() = fakeRelationService
|
||||
|
||||
override fun stateService() = fakeStateService
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2021 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.test.fakes
|
||||
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.matrix.android.sdk.api.query.QueryStateEventValue
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.room.state.StateService
|
||||
|
||||
class FakeStateService : StateService by mockk(relaxed = true) {
|
||||
|
||||
fun givenGetStateEvents(stateKey: QueryStateEventValue, result: List<Event>) {
|
||||
every { getStateEvents(any(), stateKey) } returns result
|
||||
}
|
||||
|
||||
fun givenGetStateEvent(event: Event?) {
|
||||
every { getStateEvent(any(), any()) } returns event
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue