-
Notifications
You must be signed in to change notification settings - Fork 857
Voice Draft support #4527
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
Voice Draft support #4527
Conversation
| private fun handleEntersBackground(composerText: String) { | ||
| withState { | ||
| if (it.isVoiceRecording) { | ||
| handleEndAllVoiceActions(deleteRecord = false)?.toContentAttachmentData()?.let { voiceDraft -> |
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.
in order to move all of the voice draft logic to the SDK, we'd also need to move the voice helpers, is that what you had in mind @bmarty ?
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.
Not really, I would like the SDK be able to manage the draft file storage itself, but not the logic. We can discuss this on Monday.
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.
I could imagine something like...
override fun initializeRecord(roomId: String) {
outputFile = draftService.readFile(key = roomId)
}
override fun startRecord(roomId: String) {
outputFile = draftService.createFile(key = roomId)
}|
This will fix @ouchadam ? #3833 in my case this could be reproduce in flight mode and more than one sendet Voice Messages after back online only one will be success send and the other show resend error state but couldn't be resend .... and have to dismiss and new recorded... Bug in voice-composer Commit: |
…d being able to edit messages
Not yet, but I have been able to reproduce the issue locally thanks to your steps, I'm looking into the cause 🤞
great catch! 5687c36
EDIT: turns out the offline messages fix was quite simple! 6e8c545
|
…starting a recording, which means any previous unsent recordings get deleted
| val content: String | ||
| val messageType: String | ||
|
|
||
| data class REGULAR(override val content: String, override val messageType: String) : UserDraft |
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.
I'm wondering if Voice should be a dedicated draft type rather than a messageType sub type 🤔
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.
I think the code would be clearer with a VOICE data class (they are allcaps...). But I do not know if we will end up with many other cases. @onurays WDYT?
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.
it would also mean we can avoid any database entity changes
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.
Good catch. That make sense and yes we are preserving the draftMode in DraftEntity. So no migration will be needed.
| override fun startRecord(roomId: String) { | ||
| init() | ||
| outputFile = File(outputDirectory, "Voice message.$filenameExt") | ||
| val fileName = "Voice message-${UUID.randomUUID()}.$filenameExt" |
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.
using unique files for the recording fixes #3833
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.
The filename can in some circumstance be visible to the user, is it possible to use a subfolder instead, and keep a human readable filename?
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.
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.
I'll have a look, out of interest do we need to display the real file name here, could the label be hardcoded?
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.
Yes, this is another way to fix the display issue. But also the file can be downloaded, etc. so a human readable filename should still be nice.
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.
bmarty
left a comment
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.
Nice work! Still some open question about who is responsible to save the draft voice file.
| when (mode) { | ||
| is SendMode.REGULAR -> renderRegularMode(mode.text) | ||
| is SendMode.REGULAR -> { | ||
| if (mode.messageType == MessageType.MSGTYPE_AUDIO) { |
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 where the code is not typed enough IMO, MSGTYPE_AUDIO is not necessarily a voice message. But for draft this is the only possible case I think...
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.
I think this is a sign to move away from the MSGTYPE and have a separate Voice send mode
| handleEndAllVoiceActions(deleteRecord = false)?.toContentAttachmentData()?.let { voiceDraft -> | ||
| handleSaveDraft(draft = voiceDraft.toJsonString(), messageType = MessageType.MSGTYPE_AUDIO) | ||
| } | ||
| setState { it.copy(voiceRecordingUiState = VoiceMessageRecorderView.RecordingUiState.None) } |
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.
Is it worth (re-)setting the state when entering Background? What about screen rotation?
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.
will check
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 state update isn't needed, will remove and for rotation the recording stops and the playback composer is displayed until sent or deleted
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.
Nice to have: the recording should not stop on screen rotation... (maybe handle that in another PR)
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.
Not stopping the recording will probably mean moving the recording/playback to a service (it would also enable recording/playing whilst the screen is off or in other screens), would need its own ticket/issue
The rotation is already a bit funny in develop~
| DEVELOP ROTATION | PR - RECORDING ROTATION | PR - DRAFT ROTATION |
|---|---|---|
![]() |
![]() |
![]() |
EDIT: we also lose some of the state during rotation because the VoiceMessagePlaybackTracker and the ViewModel creation isn't tied to the ViewModelFactory but instead the fragment/activity
| withState { | ||
| if (it.isVoiceRecording) { | ||
| handleEndAllVoiceActions(deleteRecord = false)?.toContentAttachmentData()?.let { voiceDraft -> | ||
| handleSaveDraft(draft = voiceDraft.toJsonString(), messageType = MessageType.MSGTYPE_AUDIO) |
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.
Here maybe give the voice file to the SDK (?)
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.
I'll give it a go 🤞
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.
if we pass the file it means the SDK will have parse the vector Uri https://github.com/vector-im/element-android/blob/develop/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt#L78 which includes interacting with the multipicker
the current recording and draft file are the same instance, the draft itself is the audio file metadata and the uri reference back to the file, other than creating the initial file and reopening I'm not sure we can move much else to the SDK (without moving more of the voice logic)
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.
OK
| val fileName = "Voice message-${UUID.randomUUID()}.$filenameExt" | ||
| val outputDirectoryForRoom = File(outputDirectory, roomId.md5()).apply { | ||
| if (!exists()) { | ||
| mkdirs() |
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.
mkdirs() do the existing check, do you remember :)
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.
|
closing whilst addressing comments |
…update the waveform once the view is ready
3c5832d to
bb2239c
Compare
…ent provider to provide a human readable display name
|
noticed some more oddities with rotation, closing again :( |
…rkaround to inject into the viewmodel, fixes multiple instances being created out of sync when rotating
…le exists at a previously created folder directory











Continuation of @onurays #4237 rebased on all of the refactors around the
VoiceMessageRecorderViewandMessageComposerViewModelrefactorsMessageComposerViewModel