moving voice recording logic to the TextComposerViewModel (name to be updated) from the RoomDetailViewModel
This commit is contained in:
parent
35f9bef94a
commit
b5055453d1
@ -41,7 +41,7 @@ import im.vector.app.features.home.PromoteRestrictedViewModel
|
|||||||
import im.vector.app.features.home.UnknownDeviceDetectorSharedViewModel
|
import im.vector.app.features.home.UnknownDeviceDetectorSharedViewModel
|
||||||
import im.vector.app.features.home.UnreadMessagesSharedViewModel
|
import im.vector.app.features.home.UnreadMessagesSharedViewModel
|
||||||
import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsViewModel
|
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.search.SearchViewModel
|
||||||
import im.vector.app.features.home.room.detail.timeline.action.MessageActionsViewModel
|
import im.vector.app.features.home.room.detail.timeline.action.MessageActionsViewModel
|
||||||
import im.vector.app.features.home.room.detail.timeline.edithistory.ViewEditHistoryViewModel
|
import im.vector.app.features.home.room.detail.timeline.edithistory.ViewEditHistoryViewModel
|
||||||
@ -505,8 +505,8 @@ interface MavericksViewModelModule {
|
|||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@MavericksViewModelKey(TextComposerViewModel::class)
|
@MavericksViewModelKey(RoomDetailViewModel::class)
|
||||||
fun textComposerViewModelFactory(factory: TextComposerViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
fun roomDetailViewModelFactory(factory: RoomDetailViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
|
@ -21,7 +21,6 @@ import android.view.View
|
|||||||
import im.vector.app.core.platform.VectorViewModelAction
|
import im.vector.app.core.platform.VectorViewModelAction
|
||||||
import im.vector.app.features.call.conference.ConferenceEvent
|
import im.vector.app.features.call.conference.ConferenceEvent
|
||||||
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
|
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.MessageStickerContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||||
@ -108,12 +107,4 @@ sealed class RoomDetailAction : VectorViewModelAction {
|
|||||||
object RemoveAllFailedMessages : RoomDetailAction()
|
object RemoveAllFailedMessages : RoomDetailAction()
|
||||||
|
|
||||||
data class RoomUpgradeSuccess(val replacementRoomId: String) : 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()
|
|
||||||
}
|
}
|
||||||
|
@ -241,7 +241,7 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
autoCompleterFactory: AutoCompleter.Factory,
|
autoCompleterFactory: AutoCompleter.Factory,
|
||||||
private val permalinkHandler: PermalinkHandler,
|
private val permalinkHandler: PermalinkHandler,
|
||||||
private val notificationDrawerManager: NotificationDrawerManager,
|
private val notificationDrawerManager: NotificationDrawerManager,
|
||||||
val roomDetailViewModelFactory: RoomDetailViewModel.Factory,
|
val textComposerViewModelFactory: TextComposerViewModel.Factory,
|
||||||
private val eventHtmlRenderer: EventHtmlRenderer,
|
private val eventHtmlRenderer: EventHtmlRenderer,
|
||||||
private val vectorPreferences: VectorPreferences,
|
private val vectorPreferences: VectorPreferences,
|
||||||
private val colorProvider: ColorProvider,
|
private val colorProvider: ColorProvider,
|
||||||
@ -414,23 +414,24 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
|
|
||||||
textComposerViewModel.observeViewEvents {
|
textComposerViewModel.observeViewEvents {
|
||||||
when (it) {
|
when (it) {
|
||||||
is TextComposerViewEvents.JoinRoomCommandSuccess -> handleJoinedToAnotherRoom(it)
|
is TextComposerViewEvents.JoinRoomCommandSuccess -> handleJoinedToAnotherRoom(it)
|
||||||
is TextComposerViewEvents.SendMessageResult -> renderSendMessageResult(it)
|
is TextComposerViewEvents.SendMessageResult -> renderSendMessageResult(it)
|
||||||
is TextComposerViewEvents.ShowMessage -> showSnackWithMessage(it.message)
|
is TextComposerViewEvents.ShowMessage -> showSnackWithMessage(it.message)
|
||||||
is TextComposerViewEvents.ShowRoomUpgradeDialog -> handleShowRoomUpgradeDialog(it)
|
is TextComposerViewEvents.ShowRoomUpgradeDialog -> handleShowRoomUpgradeDialog(it)
|
||||||
is TextComposerViewEvents.AnimateSendButtonVisibility -> handleSendButtonVisibilityChanged(it)
|
is TextComposerViewEvents.AnimateSendButtonVisibility -> handleSendButtonVisibilityChanged(it)
|
||||||
is TextComposerViewEvents.OpenRoomMemberProfile -> openRoomMemberProfile(it.userId)
|
is TextComposerViewEvents.OpenRoomMemberProfile -> openRoomMemberProfile(it.userId)
|
||||||
}.exhaustive
|
is TextComposerViewEvents.VoicePlaybackOrRecordingFailure -> {
|
||||||
}
|
|
||||||
|
|
||||||
roomDetailViewModel.observeViewEvents {
|
|
||||||
when (it) {
|
|
||||||
is RoomDetailViewEvents.Failure -> {
|
|
||||||
if (it.throwable is VoiceFailure.UnableToRecord) {
|
if (it.throwable is VoiceFailure.UnableToRecord) {
|
||||||
onCannotRecord()
|
onCannotRecord()
|
||||||
}
|
}
|
||||||
showErrorInSnackbar(it.throwable)
|
showErrorInSnackbar(it.throwable)
|
||||||
}
|
}
|
||||||
|
}.exhaustive
|
||||||
|
}
|
||||||
|
|
||||||
|
roomDetailViewModel.observeViewEvents {
|
||||||
|
when (it) {
|
||||||
|
is RoomDetailViewEvents.Failure -> showErrorInSnackbar(it.throwable)
|
||||||
is RoomDetailViewEvents.OnNewTimelineEvents -> scrollOnNewMessageCallback.addNewTimelineEventIds(it.eventIds)
|
is RoomDetailViewEvents.OnNewTimelineEvents -> scrollOnNewMessageCallback.addNewTimelineEventIds(it.eventIds)
|
||||||
is RoomDetailViewEvents.ActionSuccess -> displayRoomDetailActionSuccess(it)
|
is RoomDetailViewEvents.ActionSuccess -> displayRoomDetailActionSuccess(it)
|
||||||
is RoomDetailViewEvents.ActionFailure -> displayRoomDetailActionFailure(it)
|
is RoomDetailViewEvents.ActionFailure -> displayRoomDetailActionFailure(it)
|
||||||
@ -698,18 +699,18 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
|
|
||||||
override fun onVoiceRecordingStarted() {
|
override fun onVoiceRecordingStarted() {
|
||||||
if (checkPermissions(PERMISSIONS_FOR_VOICE_MESSAGE, requireActivity(), permissionVoiceMessageLauncher)) {
|
if (checkPermissions(PERMISSIONS_FOR_VOICE_MESSAGE, requireActivity(), permissionVoiceMessageLauncher)) {
|
||||||
roomDetailViewModel.handle(RoomDetailAction.StartRecordingVoiceMessage)
|
textComposerViewModel.handle(TextComposerAction.StartRecordingVoiceMessage)
|
||||||
vibrate(requireContext())
|
vibrate(requireContext())
|
||||||
updateRecordingUiState(RecordingUiState.Started)
|
updateRecordingUiState(RecordingUiState.Started)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onVoicePlaybackButtonClicked() {
|
override fun onVoicePlaybackButtonClicked() {
|
||||||
roomDetailViewModel.handle(RoomDetailAction.PlayOrPauseRecordingPlayback)
|
textComposerViewModel.handle(TextComposerAction.PlayOrPauseRecordingPlayback)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onVoiceRecordingCancelled() {
|
override fun onVoiceRecordingCancelled() {
|
||||||
roomDetailViewModel.handle(RoomDetailAction.EndRecordingVoiceMessage(isCancelled = true))
|
textComposerViewModel.handle(TextComposerAction.EndRecordingVoiceMessage(isCancelled = true))
|
||||||
updateRecordingUiState(RecordingUiState.Cancelled)
|
updateRecordingUiState(RecordingUiState.Cancelled)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -722,22 +723,22 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onSendVoiceMessage() {
|
override fun onSendVoiceMessage() {
|
||||||
roomDetailViewModel.handle(RoomDetailAction.EndRecordingVoiceMessage(isCancelled = false))
|
textComposerViewModel.handle(TextComposerAction.EndRecordingVoiceMessage(isCancelled = false))
|
||||||
updateRecordingUiState(RecordingUiState.None)
|
updateRecordingUiState(RecordingUiState.None)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDeleteVoiceMessage() {
|
override fun onDeleteVoiceMessage() {
|
||||||
roomDetailViewModel.handle(RoomDetailAction.EndRecordingVoiceMessage(isCancelled = true))
|
textComposerViewModel.handle(TextComposerAction.EndRecordingVoiceMessage(isCancelled = true))
|
||||||
updateRecordingUiState(RecordingUiState.None)
|
updateRecordingUiState(RecordingUiState.None)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRecordingLimitReached() {
|
override fun onRecordingLimitReached() {
|
||||||
roomDetailViewModel.handle(RoomDetailAction.PauseRecordingVoiceMessage)
|
textComposerViewModel.handle(TextComposerAction.PauseRecordingVoiceMessage)
|
||||||
updateRecordingUiState(RecordingUiState.Playback)
|
updateRecordingUiState(RecordingUiState.Playback)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRecordingWaveformClicked() {
|
override fun onRecordingWaveformClicked() {
|
||||||
roomDetailViewModel.handle(RoomDetailAction.PauseRecordingVoiceMessage)
|
textComposerViewModel.handle(TextComposerAction.PauseRecordingVoiceMessage)
|
||||||
updateRecordingUiState(RecordingUiState.Playback)
|
updateRecordingUiState(RecordingUiState.Playback)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1136,7 +1137,7 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
textComposerViewModel.handle(TextComposerAction.SaveDraft(views.composerLayout.text.toString()))
|
textComposerViewModel.handle(TextComposerAction.SaveDraft(views.composerLayout.text.toString()))
|
||||||
|
|
||||||
// We should improve the UX to support going into playback mode when paused and delete the media when the view is destroyed.
|
// We should improve the UX to support going into playback mode when paused and delete the media when the view is destroyed.
|
||||||
roomDetailViewModel.handle(RoomDetailAction.EndAllVoiceActions(deleteRecord = false))
|
textComposerViewModel.handle(TextComposerAction.EndAllVoiceActions(deleteRecord = false))
|
||||||
views.voiceMessageRecorderView.display(RecordingUiState.None)
|
views.voiceMessageRecorderView.display(RecordingUiState.None)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1883,7 +1884,7 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onVoiceControlButtonClicked(eventId: String, messageAudioContent: MessageAudioContent) {
|
override fun onVoiceControlButtonClicked(eventId: String, messageAudioContent: MessageAudioContent) {
|
||||||
roomDetailViewModel.handle(RoomDetailAction.PlayOrPauseVoicePlayback(eventId, messageAudioContent))
|
textComposerViewModel.handle(TextComposerAction.PlayOrPauseVoicePlayback(eventId, messageAudioContent))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onShareActionClicked(action: EventSharedAction.Share) {
|
private fun onShareActionClicked(action: EventSharedAction.Share) {
|
||||||
|
@ -21,24 +21,23 @@ import androidx.annotation.IdRes
|
|||||||
import androidx.lifecycle.asFlow
|
import androidx.lifecycle.asFlow
|
||||||
import com.airbnb.mvrx.Async
|
import com.airbnb.mvrx.Async
|
||||||
import com.airbnb.mvrx.Fail
|
import com.airbnb.mvrx.Fail
|
||||||
import com.airbnb.mvrx.FragmentViewModelContext
|
|
||||||
import com.airbnb.mvrx.Loading
|
import com.airbnb.mvrx.Loading
|
||||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.Uninitialized
|
import com.airbnb.mvrx.Uninitialized
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import im.vector.app.BuildConfig
|
import im.vector.app.BuildConfig
|
||||||
import im.vector.app.R
|
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.extensions.exhaustive
|
||||||
import im.vector.app.core.flow.chunk
|
import im.vector.app.core.flow.chunk
|
||||||
import im.vector.app.core.mvrx.runCatchingToAsync
|
import im.vector.app.core.mvrx.runCatchingToAsync
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.core.utils.BehaviorDataSource
|
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.ConferenceEvent
|
||||||
import im.vector.app.features.call.conference.JitsiActiveConferenceHolder
|
import im.vector.app.features.call.conference.JitsiActiveConferenceHolder
|
||||||
import im.vector.app.features.call.conference.JitsiService
|
import im.vector.app.features.call.conference.JitsiService
|
||||||
@ -47,7 +46,6 @@ import im.vector.app.features.call.webrtc.WebRtcCallManager
|
|||||||
import im.vector.app.features.createdirect.DirectRoomHelper
|
import im.vector.app.features.createdirect.DirectRoomHelper
|
||||||
import im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy
|
import im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy
|
||||||
import im.vector.app.features.crypto.verification.SupportedVerificationMethodsProvider
|
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.sticker.StickerPickerActionHandler
|
||||||
import im.vector.app.features.home.room.detail.timeline.factory.TimelineFactory
|
import im.vector.app.features.home.room.detail.timeline.factory.TimelineFactory
|
||||||
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
|
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
|
||||||
@ -56,7 +54,6 @@ import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
|
|||||||
import im.vector.app.features.session.coroutineScope
|
import im.vector.app.features.session.coroutineScope
|
||||||
import im.vector.app.features.settings.VectorDataStore
|
import im.vector.app.features.settings.VectorDataStore
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
import im.vector.app.features.voice.VoicePlayerHelper
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
@ -116,8 +113,6 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||||||
private val directRoomHelper: DirectRoomHelper,
|
private val directRoomHelper: DirectRoomHelper,
|
||||||
private val jitsiService: JitsiService,
|
private val jitsiService: JitsiService,
|
||||||
private val activeConferenceHolder: JitsiActiveConferenceHolder,
|
private val activeConferenceHolder: JitsiActiveConferenceHolder,
|
||||||
private val voiceMessageHelper: VoiceMessageHelper,
|
|
||||||
private val voicePlayerHelper: VoicePlayerHelper,
|
|
||||||
timelineFactory: TimelineFactory
|
timelineFactory: TimelineFactory
|
||||||
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
|
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState),
|
||||||
Timeline.Listener, ChatEffectManager.Delegate, CallProtocolsChecker.Listener {
|
Timeline.Listener, ChatEffectManager.Delegate, CallProtocolsChecker.Listener {
|
||||||
@ -144,22 +139,12 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||||||
private var prepareToEncrypt: Async<Unit> = Uninitialized
|
private var prepareToEncrypt: Async<Unit> = Uninitialized
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
interface Factory {
|
interface Factory : MavericksAssistedViewModelFactory<RoomDetailViewModel, RoomDetailViewState> {
|
||||||
fun create(initialState: RoomDetailViewState): RoomDetailViewModel
|
override fun create(initialState: RoomDetailViewState): RoomDetailViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
companion object : MavericksViewModelFactory<RoomDetailViewModel, RoomDetailViewState> by hiltMavericksViewModelFactory() {
|
||||||
* 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> {
|
|
||||||
|
|
||||||
const val PAGINATION_COUNT = 50
|
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 {
|
init {
|
||||||
@ -343,12 +328,6 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||||||
is RoomDetailAction.DoNotShowPreviewUrlFor -> handleDoNotShowPreviewUrlFor(action)
|
is RoomDetailAction.DoNotShowPreviewUrlFor -> handleDoNotShowPreviewUrlFor(action)
|
||||||
RoomDetailAction.RemoveAllFailedMessages -> handleRemoveAllFailedMessages()
|
RoomDetailAction.RemoveAllFailedMessages -> handleRemoveAllFailedMessages()
|
||||||
RoomDetailAction.ResendAll -> handleResendAll()
|
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 -> {
|
is RoomDetailAction.RoomUpgradeSuccess -> {
|
||||||
setState {
|
setState {
|
||||||
copy(joinUpgradedRoomAsync = Success(action.replacementRoomId))
|
copy(joinUpgradedRoomAsync = Success(action.replacementRoomId))
|
||||||
@ -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()
|
private fun isIntegrationEnabled() = session.integrationManagerService().isIntegrationEnabled()
|
||||||
|
|
||||||
fun isMenuItemVisible(@IdRes itemId: Int): Boolean = com.airbnb.mvrx.withState(this) { state ->
|
fun isMenuItemVisible(@IdRes itemId: Int): Boolean = com.airbnb.mvrx.withState(this) { state ->
|
||||||
|
@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail.composer
|
|||||||
|
|
||||||
import im.vector.app.core.platform.VectorViewModelAction
|
import im.vector.app.core.platform.VectorViewModelAction
|
||||||
import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView
|
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 {
|
sealed class TextComposerAction : VectorViewModelAction {
|
||||||
data class SaveDraft(val draft: String) : TextComposerAction()
|
data class SaveDraft(val draft: String) : TextComposerAction()
|
||||||
@ -28,5 +29,13 @@ sealed class TextComposerAction : VectorViewModelAction {
|
|||||||
data class EnterRegularMode(val text: String, val fromSharing: Boolean) : TextComposerAction()
|
data class EnterRegularMode(val text: String, val fromSharing: Boolean) : TextComposerAction()
|
||||||
data class UserIsTyping(val isTyping: Boolean) : TextComposerAction()
|
data class UserIsTyping(val isTyping: Boolean) : TextComposerAction()
|
||||||
data class OnTextChanged(val text: CharSequence) : TextComposerAction()
|
data class OnTextChanged(val text: CharSequence) : TextComposerAction()
|
||||||
|
|
||||||
|
// Voice Message
|
||||||
data class OnVoiceRecordingUiStateChanged(val uiState: VoiceMessageRecorderView.RecordingUiState) : TextComposerAction()
|
data class OnVoiceRecordingUiStateChanged(val uiState: VoiceMessageRecorderView.RecordingUiState) : TextComposerAction()
|
||||||
|
object StartRecordingVoiceMessage : TextComposerAction()
|
||||||
|
data class EndRecordingVoiceMessage(val isCancelled: Boolean) : TextComposerAction()
|
||||||
|
object PauseRecordingVoiceMessage : TextComposerAction()
|
||||||
|
data class PlayOrPauseVoicePlayback(val eventId: String, val messageAudioContent: MessageAudioContent) : TextComposerAction()
|
||||||
|
object PlayOrPauseRecordingPlayback : TextComposerAction()
|
||||||
|
data class EndAllVoiceActions(val deleteRecord: Boolean = true) : TextComposerAction()
|
||||||
}
|
}
|
||||||
|
@ -42,4 +42,6 @@ sealed class TextComposerViewEvents : VectorViewEvents {
|
|||||||
object SlashCommandNotImplemented : SendMessageResult()
|
object SlashCommandNotImplemented : SendMessageResult()
|
||||||
|
|
||||||
data class ShowRoomUpgradeDialog(val newVersion: String, val isPublic: Boolean) : TextComposerViewEvents()
|
data class ShowRoomUpgradeDialog(val newVersion: String, val isPublic: Boolean) : TextComposerViewEvents()
|
||||||
|
|
||||||
|
data class VoicePlaybackOrRecordingFailure(val throwable: Throwable) : TextComposerViewEvents()
|
||||||
}
|
}
|
||||||
|
@ -16,24 +16,27 @@
|
|||||||
|
|
||||||
package im.vector.app.features.home.room.detail.composer
|
package im.vector.app.features.home.room.detail.composer
|
||||||
|
|
||||||
|
import com.airbnb.mvrx.FragmentViewModelContext
|
||||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import im.vector.app.R
|
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.extensions.exhaustive
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
|
import im.vector.app.features.attachments.toContentAttachmentData
|
||||||
import im.vector.app.features.command.CommandParser
|
import im.vector.app.features.command.CommandParser
|
||||||
import im.vector.app.features.command.ParsedCommand
|
import im.vector.app.features.command.ParsedCommand
|
||||||
import im.vector.app.features.home.room.detail.ChatEffect
|
import im.vector.app.features.home.room.detail.ChatEffect
|
||||||
|
import im.vector.app.features.home.room.detail.RoomDetailFragment
|
||||||
import im.vector.app.features.home.room.detail.composer.rainbow.RainbowGenerator
|
import im.vector.app.features.home.room.detail.composer.rainbow.RainbowGenerator
|
||||||
import im.vector.app.features.home.room.detail.toMessageType
|
import im.vector.app.features.home.room.detail.toMessageType
|
||||||
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
|
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
|
||||||
import im.vector.app.features.session.coroutineScope
|
import im.vector.app.features.session.coroutineScope
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
|
import im.vector.app.features.voice.VoicePlayerHelper
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.commonmark.parser.Parser
|
import org.commonmark.parser.Parser
|
||||||
@ -60,7 +63,9 @@ class TextComposerViewModel @AssistedInject constructor(
|
|||||||
private val session: Session,
|
private val session: Session,
|
||||||
private val stringProvider: StringProvider,
|
private val stringProvider: StringProvider,
|
||||||
private val vectorPreferences: VectorPreferences,
|
private val vectorPreferences: VectorPreferences,
|
||||||
private val rainbowGenerator: RainbowGenerator
|
private val rainbowGenerator: RainbowGenerator,
|
||||||
|
private val voiceMessageHelper: VoiceMessageHelper,
|
||||||
|
private val voicePlayerHelper: VoicePlayerHelper
|
||||||
) : VectorViewModel<TextComposerViewState, TextComposerAction, TextComposerViewEvents>(initialState) {
|
) : VectorViewModel<TextComposerViewState, TextComposerAction, TextComposerViewEvents>(initialState) {
|
||||||
|
|
||||||
private val room = session.getRoom(initialState.roomId)!!
|
private val room = session.getRoom(initialState.roomId)!!
|
||||||
@ -86,6 +91,12 @@ class TextComposerViewModel @AssistedInject constructor(
|
|||||||
is TextComposerAction.UserIsTyping -> handleUserIsTyping(action)
|
is TextComposerAction.UserIsTyping -> handleUserIsTyping(action)
|
||||||
is TextComposerAction.OnTextChanged -> handleOnTextChanged(action)
|
is TextComposerAction.OnTextChanged -> handleOnTextChanged(action)
|
||||||
is TextComposerAction.OnVoiceRecordingUiStateChanged -> handleOnVoiceRecordingUiStateChanged(action)
|
is TextComposerAction.OnVoiceRecordingUiStateChanged -> handleOnVoiceRecordingUiStateChanged(action)
|
||||||
|
TextComposerAction.StartRecordingVoiceMessage -> handleStartRecordingVoiceMessage()
|
||||||
|
is TextComposerAction.EndRecordingVoiceMessage -> handleEndRecordingVoiceMessage(action.isCancelled)
|
||||||
|
is TextComposerAction.PlayOrPauseVoicePlayback -> handlePlayOrPauseVoicePlayback(action)
|
||||||
|
TextComposerAction.PauseRecordingVoiceMessage -> handlePauseRecordingVoiceMessage()
|
||||||
|
TextComposerAction.PlayOrPauseRecordingPlayback -> handlePlayOrPauseRecordingPlayback()
|
||||||
|
is TextComposerAction.EndAllVoiceActions -> handleEndAllVoiceActions(action.deleteRecord)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -688,6 +699,56 @@ class TextComposerViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleStartRecordingVoiceMessage() {
|
||||||
|
try {
|
||||||
|
voiceMessageHelper.startRecording()
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
_viewEvents.post(TextComposerViewEvents.VoicePlaybackOrRecordingFailure(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: TextComposerAction.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(TextComposerViewEvents.VoicePlaybackOrRecordingFailure(failure))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handlePlayOrPauseRecordingPlayback() {
|
||||||
|
voiceMessageHelper.startOrPauseRecordingPlayback()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleEndAllVoiceActions(deleteRecord: Boolean) {
|
||||||
|
voiceMessageHelper.stopAllVoiceActions(deleteRecord)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handlePauseRecordingVoiceMessage() {
|
||||||
|
voiceMessageHelper.pauseRecording()
|
||||||
|
}
|
||||||
|
|
||||||
private fun launchSlashCommandFlowSuspendable(block: suspend () -> Unit) {
|
private fun launchSlashCommandFlowSuspendable(block: suspend () -> Unit) {
|
||||||
_viewEvents.post(TextComposerViewEvents.SlashCommandLoading)
|
_viewEvents.post(TextComposerViewEvents.SlashCommandLoading)
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@ -703,9 +764,19 @@ class TextComposerViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
interface Factory : MavericksAssistedViewModelFactory<TextComposerViewModel, TextComposerViewState> {
|
interface Factory {
|
||||||
override fun create(initialState: TextComposerViewState): TextComposerViewModel
|
fun create(initialState: TextComposerViewState): TextComposerViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object : MavericksViewModelFactory<TextComposerViewModel, TextComposerViewState> by hiltMavericksViewModelFactory()
|
/**
|
||||||
|
* 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<TextComposerViewModel, TextComposerViewState> {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
override fun create(viewModelContext: ViewModelContext, state: TextComposerViewState): TextComposerViewModel {
|
||||||
|
val fragment: RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment()
|
||||||
|
return fragment.textComposerViewModelFactory.create(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user