-
Notifications
You must be signed in to change notification settings - Fork 768
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Moving voice logic to the MessageComposer #4523
Changes from 5 commits
b505545
8837640
f140dbc
9ce228c
cca50ed
c0f8984
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -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 | ||
|
@@ -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 | ||
|
@@ -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 { | ||
|
@@ -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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this workaround is needed specifically because of the |
||
val fragment: RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment() | ||
return fragment.roomDetailViewModelFactory.create(state) | ||
} | ||
} | ||
|
||
init { | ||
|
@@ -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)) | ||
|
@@ -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 -> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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() | ||
|
@@ -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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not replaced by
MessageComposerViewModel
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unfortunately not :( when a view model is not in this module it's because it needs a workaround https://github.com/vector-im/element-android/blob/develop/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt#L161
Usually we can do...
which is where the
MavericksViewModelModule
comes in and injects for usbuttttt the voice helpers are marked as
ActivityScoped
which hilt doesn't let us inject into theViewModels
as they're some form of singleton scoped~, now that the voice helpers have moved to theMessageComposerViewModel
we can add theRoomDetailViewModel
to the normal way of providing theViewModels
The workaround involves injecting the assisted factory into the fragment and then using that field to provide the
ViewModel