Skip to content

Commit

Permalink
Merge pull request #4523 from vector-im/feature/adm/voice-composer
Browse files Browse the repository at this point in the history
Moving voice logic to the MessageComposer
  • Loading branch information
bmarty authored Nov 22, 2021
2 parents 7236010 + c0f8984 commit 97a44a5
Show file tree
Hide file tree
Showing 11 changed files with 252 additions and 245 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import im.vector.app.features.home.PromoteRestrictedViewModel
import im.vector.app.features.home.UnknownDeviceDetectorSharedViewModel
import im.vector.app.features.home.UnreadMessagesSharedViewModel
import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsViewModel
import im.vector.app.features.home.room.detail.composer.TextComposerViewModel
import im.vector.app.features.home.room.detail.RoomDetailViewModel
import im.vector.app.features.home.room.detail.search.SearchViewModel
import im.vector.app.features.home.room.detail.timeline.action.MessageActionsViewModel
import im.vector.app.features.home.room.detail.timeline.edithistory.ViewEditHistoryViewModel
Expand Down Expand Up @@ -505,8 +505,8 @@ interface MavericksViewModelModule {

@Binds
@IntoMap
@MavericksViewModelKey(TextComposerViewModel::class)
fun textComposerViewModelFactory(factory: TextComposerViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@MavericksViewModelKey(RoomDetailViewModel::class)
fun roomDetailViewModelFactory(factory: RoomDetailViewModel.Factory): MavericksAssistedViewModelFactory<*, *>

@Binds
@IntoMap
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import android.view.View
import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.features.call.conference.ConferenceEvent
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
import org.matrix.android.sdk.api.session.room.timeline.Timeline
Expand Down Expand Up @@ -108,12 +107,4 @@ sealed class RoomDetailAction : VectorViewModelAction {
object RemoveAllFailedMessages : RoomDetailAction()

data class RoomUpgradeSuccess(val replacementRoomId: String) : RoomDetailAction()

// Voice Message
object StartRecordingVoiceMessage : RoomDetailAction()
data class EndRecordingVoiceMessage(val isCancelled: Boolean) : RoomDetailAction()
object PauseRecordingVoiceMessage : RoomDetailAction()
data class PlayOrPauseVoicePlayback(val eventId: String, val messageAudioContent: MessageAudioContent) : RoomDetailAction()
object PlayOrPauseRecordingPlayback : RoomDetailAction()
data class EndAllVoiceActions(val deleteRecord: Boolean = true) : RoomDetailAction()
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,23 @@ import androidx.annotation.IdRes
import androidx.lifecycle.asFlow
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MavericksViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.BuildConfig
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.exhaustive
import im.vector.app.core.flow.chunk
import im.vector.app.core.mvrx.runCatchingToAsync
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.BehaviorDataSource
import im.vector.app.features.attachments.toContentAttachmentData
import im.vector.app.features.call.conference.ConferenceEvent
import im.vector.app.features.call.conference.JitsiActiveConferenceHolder
import im.vector.app.features.call.conference.JitsiService
Expand All @@ -47,7 +46,6 @@ import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.createdirect.DirectRoomHelper
import im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy
import im.vector.app.features.crypto.verification.SupportedVerificationMethodsProvider
import im.vector.app.features.home.room.detail.composer.VoiceMessageHelper
import im.vector.app.features.home.room.detail.sticker.StickerPickerActionHandler
import im.vector.app.features.home.room.detail.timeline.factory.TimelineFactory
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
Expand All @@ -56,7 +54,6 @@ import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
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.voice.VoicePlayerHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collect
Expand Down Expand Up @@ -116,8 +113,6 @@ class RoomDetailViewModel @AssistedInject constructor(
private val directRoomHelper: DirectRoomHelper,
private val jitsiService: JitsiService,
private val activeConferenceHolder: JitsiActiveConferenceHolder,
private val voiceMessageHelper: VoiceMessageHelper,
private val voicePlayerHelper: VoicePlayerHelper,
timelineFactory: TimelineFactory
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
Timeline.Listener, ChatEffectManager.Delegate, CallProtocolsChecker.Listener {
Expand All @@ -144,22 +139,12 @@ class RoomDetailViewModel @AssistedInject constructor(
private var prepareToEncrypt: Async<Unit> = Uninitialized

@AssistedFactory
interface Factory {
fun create(initialState: RoomDetailViewState): RoomDetailViewModel
interface Factory : MavericksAssistedViewModelFactory<RoomDetailViewModel, RoomDetailViewState> {
override fun create(initialState: RoomDetailViewState): RoomDetailViewModel
}

/**
* Can't use the hiltMaverick here because some dependencies are injected here and in fragment but they don't share the graph.
*/
companion object : MavericksViewModelFactory<RoomDetailViewModel, RoomDetailViewState> {

companion object : MavericksViewModelFactory<RoomDetailViewModel, RoomDetailViewState> by hiltMavericksViewModelFactory() {
const val PAGINATION_COUNT = 50

@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: RoomDetailViewState): RoomDetailViewModel {
val fragment: RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment()
return fragment.roomDetailViewModelFactory.create(state)
}
}

init {
Expand Down Expand Up @@ -343,12 +328,6 @@ class RoomDetailViewModel @AssistedInject constructor(
is RoomDetailAction.DoNotShowPreviewUrlFor -> handleDoNotShowPreviewUrlFor(action)
RoomDetailAction.RemoveAllFailedMessages -> handleRemoveAllFailedMessages()
RoomDetailAction.ResendAll -> handleResendAll()
RoomDetailAction.StartRecordingVoiceMessage -> handleStartRecordingVoiceMessage()
is RoomDetailAction.EndRecordingVoiceMessage -> handleEndRecordingVoiceMessage(action.isCancelled)
is RoomDetailAction.PlayOrPauseVoicePlayback -> handlePlayOrPauseVoicePlayback(action)
RoomDetailAction.PauseRecordingVoiceMessage -> handlePauseRecordingVoiceMessage()
RoomDetailAction.PlayOrPauseRecordingPlayback -> handlePlayOrPauseRecordingPlayback()
is RoomDetailAction.EndAllVoiceActions -> handleEndAllVoiceActions(action.deleteRecord)
is RoomDetailAction.RoomUpgradeSuccess -> {
setState {
copy(joinUpgradedRoomAsync = Success(action.replacementRoomId))
Expand Down Expand Up @@ -612,56 +591,6 @@ class RoomDetailViewModel @AssistedInject constructor(
}
}

private fun handleStartRecordingVoiceMessage() {
try {
voiceMessageHelper.startRecording()
} catch (failure: Throwable) {
_viewEvents.post(RoomDetailViewEvents.Failure(failure))
}
}

private fun handleEndRecordingVoiceMessage(isCancelled: Boolean) {
voiceMessageHelper.stopPlayback()
if (isCancelled) {
voiceMessageHelper.deleteRecording()
} else {
voiceMessageHelper.stopRecording()?.let { audioType ->
if (audioType.duration > 1000) {
room.sendMedia(audioType.toContentAttachmentData(), false, emptySet())
} else {
voiceMessageHelper.deleteRecording()
}
}
}
}

private fun handlePlayOrPauseVoicePlayback(action: RoomDetailAction.PlayOrPauseVoicePlayback) {
viewModelScope.launch(Dispatchers.IO) {
try {
// Download can fail
val audioFile = session.fileService().downloadFile(action.messageAudioContent)
// Conversion can fail, fallback to the original file in this case and let the player fail for us
val convertedFile = voicePlayerHelper.convertFile(audioFile) ?: audioFile
// Play can fail
voiceMessageHelper.startOrPausePlayback(action.eventId, convertedFile)
} catch (failure: Throwable) {
_viewEvents.post(RoomDetailViewEvents.Failure(failure))
}
}
}

private fun handlePlayOrPauseRecordingPlayback() {
voiceMessageHelper.startOrPauseRecordingPlayback()
}

private fun handleEndAllVoiceActions(deleteRecord: Boolean) {
voiceMessageHelper.stopAllVoiceActions(deleteRecord)
}

private fun handlePauseRecordingVoiceMessage() {
voiceMessageHelper.pauseRecording()
}

private fun isIntegrationEnabled() = session.integrationManagerService().isIntegrationEnabled()

fun isMenuItemVisible(@IdRes itemId: Int): Boolean = com.airbnb.mvrx.withState(this) { state ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,24 @@ package im.vector.app.features.home.room.detail.composer

import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent

sealed class TextComposerAction : VectorViewModelAction {
data class SaveDraft(val draft: String) : TextComposerAction()
data class SendMessage(val text: CharSequence, val autoMarkdown: Boolean) : TextComposerAction()
data class EnterEditMode(val eventId: String, val text: String) : TextComposerAction()
data class EnterQuoteMode(val eventId: String, val text: String) : TextComposerAction()
data class EnterReplyMode(val eventId: String, val text: String) : TextComposerAction()
data class EnterRegularMode(val text: String, val fromSharing: Boolean) : TextComposerAction()
data class UserIsTyping(val isTyping: Boolean) : TextComposerAction()
data class OnTextChanged(val text: CharSequence) : TextComposerAction()
data class OnVoiceRecordingUiStateChanged(val uiState: VoiceMessageRecorderView.RecordingUiState) : TextComposerAction()
sealed class MessageComposerAction : VectorViewModelAction {
data class SaveDraft(val draft: String) : MessageComposerAction()
data class SendMessage(val text: CharSequence, val autoMarkdown: Boolean) : MessageComposerAction()
data class EnterEditMode(val eventId: String, val text: String) : MessageComposerAction()
data class EnterQuoteMode(val eventId: String, val text: String) : MessageComposerAction()
data class EnterReplyMode(val eventId: String, val text: String) : MessageComposerAction()
data class EnterRegularMode(val text: String, val fromSharing: Boolean) : MessageComposerAction()
data class UserIsTyping(val isTyping: Boolean) : MessageComposerAction()
data class OnTextChanged(val text: CharSequence) : MessageComposerAction()

// Voice Message
data class OnVoiceRecordingUiStateChanged(val uiState: VoiceMessageRecorderView.RecordingUiState) : MessageComposerAction()
object StartRecordingVoiceMessage : MessageComposerAction()
data class EndRecordingVoiceMessage(val isCancelled: Boolean) : MessageComposerAction()
object PauseRecordingVoiceMessage : MessageComposerAction()
data class PlayOrPauseVoicePlayback(val eventId: String, val messageAudioContent: MessageAudioContent) : MessageComposerAction()
object PlayOrPauseRecordingPlayback : MessageComposerAction()
data class EndAllVoiceActions(val deleteRecord: Boolean = true) : MessageComposerAction()
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import im.vector.app.databinding.ComposerLayoutBinding
/**
* Encapsulate the timeline composer UX.
*/
class TextComposerView @JvmOverloads constructor(
class MessageComposerView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0) : ConstraintLayout(context, attrs, defStyleAttr) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ import androidx.annotation.StringRes
import im.vector.app.core.platform.VectorViewEvents
import im.vector.app.features.command.Command

sealed class TextComposerViewEvents : VectorViewEvents {
sealed class MessageComposerViewEvents : VectorViewEvents {

data class AnimateSendButtonVisibility(val isVisible: Boolean) : TextComposerViewEvents()
data class AnimateSendButtonVisibility(val isVisible: Boolean) : MessageComposerViewEvents()

data class ShowMessage(val message: String) : TextComposerViewEvents()
data class ShowMessage(val message: String) : MessageComposerViewEvents()

abstract class SendMessageResult : TextComposerViewEvents()
abstract class SendMessageResult : MessageComposerViewEvents()

object MessageSent : SendMessageResult()
data class JoinRoomCommandSuccess(val roomId: String) : SendMessageResult()
Expand All @@ -36,10 +36,12 @@ sealed class TextComposerViewEvents : VectorViewEvents {
data class SlashCommandResultOk(@StringRes val messageRes: Int? = null) : SendMessageResult()
class SlashCommandResultError(val throwable: Throwable) : SendMessageResult()

data class OpenRoomMemberProfile(val userId: String) : TextComposerViewEvents()
data class OpenRoomMemberProfile(val userId: String) : MessageComposerViewEvents()

// TODO Remove
object SlashCommandNotImplemented : SendMessageResult()

data class ShowRoomUpgradeDialog(val newVersion: String, val isPublic: Boolean) : TextComposerViewEvents()
data class ShowRoomUpgradeDialog(val newVersion: String, val isPublic: Boolean) : MessageComposerViewEvents()

data class VoicePlaybackOrRecordingFailure(val throwable: Throwable) : MessageComposerViewEvents()
}
Loading

0 comments on commit 97a44a5

Please sign in to comment.