From 8cc2f78da738cb44dc1f9962d4671330b02c7b61 Mon Sep 17 00:00:00 2001 From: jubb Date: Tue, 6 Jul 2021 16:53:44 +1000 Subject: [PATCH] fix: audio durations set accordingly for send and receive and doesn't break saving / uploading by exhausting the input stream --- .../conversation/v2/ConversationActivityV2.kt | 6 +-- .../v2/messages/VoiceMessageView.kt | 3 +- .../messaging/jobs/AttachmentDownloadJob.kt | 50 +++++++++++-------- .../messaging/jobs/AttachmentUploadJob.kt | 19 ++++++- 4 files changed, 53 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 96b3675e1e..075caf5f6e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -1032,10 +1032,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) val future = audioRecorder.stopRecording() stopAudioHandler.removeCallbacks(stopVoiceMessageRecordingTask) - future.addListener(object : ListenableFuture.Listener> { + future.addListener(object : ListenableFuture.Listener> { - override fun onSuccess(result: Pair) { - val audioSlide = AudioSlide(this@ConversationActivityV2, result.first, result.second!!, MediaTypes.AUDIO_AAC, true) + override fun onSuccess(result: Pair) { + val audioSlide = AudioSlide(this@ConversationActivityV2, result.first, result.second, MediaTypes.AUDIO_AAC, true) val slideDeck = SlideDeck() slideDeck.addSlide(audioSlide) sendAttachments(slideDeck.asAttachments(), null) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt index 22e6a33adc..894a078b30 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt @@ -59,8 +59,9 @@ class VoiceMessageView : LinearLayout, AudioSlidePlayer.Listener { (audio.asAttachment() as? DatabaseAttachment)?.let { attachment -> DatabaseFactory.getAttachmentDatabase(context).getAttachmentAudioExtras(attachment.attachmentId)?.let { audioExtras -> if (audioExtras.durationMs > 0) { + duration = audioExtras.durationMs voiceMessageViewDurationTextView.visibility = View.VISIBLE - voiceMessageViewDurationTextView.text = String.format("%02d:%02d", + voiceMessageViewDurationTextView.text = String.format("%01d:%02d", TimeUnit.MILLISECONDS.toMinutes(audioExtras.durationMs), TimeUnit.MILLISECONDS.toSeconds(audioExtras.durationMs)) } diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt index dfc27eb10f..605052f85c 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt @@ -7,17 +7,18 @@ import okhttp3.HttpUrl import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.open_groups.OpenGroupAPIV2 import org.session.libsession.messaging.sending_receiving.attachments.AttachmentState +import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment import org.session.libsession.messaging.utilities.Data +import org.session.libsession.snode.OnionRequestAPI import org.session.libsession.utilities.DecodedAudio import org.session.libsession.utilities.DownloadUtilities -import org.session.libsession.utilities.FileUtils import org.session.libsession.utilities.InputStreamMediaDataSource import org.session.libsignal.streams.AttachmentCipherInputStream import org.session.libsignal.utilities.Base64 import org.session.libsignal.utilities.Log import java.io.File -import java.io.FileDescriptor import java.io.FileInputStream +import java.io.InputStream class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long) : Job { override var delegate: JobDelegate? = null @@ -44,53 +45,62 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long) val storage = MessagingModuleConfiguration.shared.storage val messageDataProvider = MessagingModuleConfiguration.shared.messageDataProvider val handleFailure: (java.lang.Exception) -> Unit = { exception -> - if (exception == Error.NoAttachment) { + if (exception == Error.NoAttachment + || (exception is OnionRequestAPI.HTTPRequestFailedAtDestinationException && exception.statusCode == 400)) { messageDataProvider.setAttachmentState(AttachmentState.FAILED, attachmentID, databaseMessageID) this.handlePermanentFailure(exception) } else { this.handleFailure(exception) } } + var tempFile: File? = null try { val attachment = messageDataProvider.getDatabaseAttachment(attachmentID) ?: return handleFailure(Error.NoAttachment) messageDataProvider.setAttachmentState(AttachmentState.STARTED, attachmentID, this.databaseMessageID) - val tempFile = createTempFile() + tempFile = createTempFile() val threadID = storage.getThreadIdForMms(databaseMessageID) val openGroupV2 = storage.getV2OpenGroup(threadID) - val inputStream = if (openGroupV2 == null) { + if (openGroupV2 == null) { DownloadUtilities.downloadFile(tempFile, attachment.url) - // Assume we're retrieving an attachment for an open group server if the digest is not set - if (attachment.digest?.size ?: 0 == 0 || attachment.key.isNullOrEmpty()) { - FileInputStream(tempFile) - } else { - AttachmentCipherInputStream.createForAttachment(tempFile, attachment.size, Base64.decode(attachment.key), attachment.digest) - } } else { val url = HttpUrl.parse(attachment.url)!! val fileID = url.pathSegments().last() OpenGroupAPIV2.download(fileID.toLong(), openGroupV2.room, openGroupV2.server).get().let { tempFile.writeBytes(it) } - FileInputStream(tempFile) - } - - if (attachment.contentType.startsWith("audio/")) { - // process the duration - InputStreamMediaDataSource(inputStream).use { mediaDataSource -> - val durationMs = (DecodedAudio.create(mediaDataSource).totalDuration / 1000.0).toLong() - messageDataProvider.updateAudioAttachmentDuration(attachment.attachmentId, durationMs) - } } + val inputStream = getInputStream(tempFile, attachment) messageDataProvider.insertAttachment(databaseMessageID, attachment.attachmentId, inputStream) + if (attachment.contentType.startsWith("audio/")) { + // process the duration + try { + InputStreamMediaDataSource(getInputStream(tempFile, attachment)).use { mediaDataSource -> + val durationMs = (DecodedAudio.create(mediaDataSource).totalDuration / 1000.0).toLong() + messageDataProvider.updateAudioAttachmentDuration(attachment.attachmentId, durationMs) + } + } catch (e: Exception) { + Log.e("Loki", "Couldn't process audio attachment", e) + } + } tempFile.delete() handleSuccess() } catch (e: Exception) { + tempFile?.delete() return handleFailure(e) } } + private fun getInputStream(tempFile: File, attachment: DatabaseAttachment): InputStream { + // Assume we're retrieving an attachment for an open group server if the digest is not set + return if (attachment.digest?.size ?: 0 == 0 || attachment.key.isNullOrEmpty()) { + FileInputStream(tempFile) + } else { + AttachmentCipherInputStream.createForAttachment(tempFile, attachment.size, Base64.decode(attachment.key), attachment.digest) + } + } + private fun handleSuccess() { Log.w("AttachmentDownloadJob", "Attachment downloaded successfully.") delegate?.handleJobSucceeded(this) diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentUploadJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentUploadJob.kt index 9d50e96a98..332d674115 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentUploadJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentUploadJob.kt @@ -11,6 +11,8 @@ import org.session.libsession.messaging.messages.Message import org.session.libsession.messaging.open_groups.OpenGroupAPIV2 import org.session.libsession.messaging.sending_receiving.MessageSender import org.session.libsession.messaging.utilities.Data +import org.session.libsession.utilities.DecodedAudio +import org.session.libsession.utilities.InputStreamMediaDataSource import org.session.libsession.utilities.UploadResult import org.session.libsignal.streams.AttachmentCipherOutputStream import org.session.libsignal.messages.SignalServiceAttachmentStream @@ -108,7 +110,22 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess private fun handleSuccess(attachment: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: UploadResult) { Log.d(TAG, "Attachment uploaded successfully.") delegate?.handleJobSucceeded(this) - MessagingModuleConfiguration.shared.messageDataProvider.handleSuccessfulAttachmentUpload(attachmentID, attachment, attachmentKey, uploadResult) + val messageDataProvider = MessagingModuleConfiguration.shared.messageDataProvider + messageDataProvider.handleSuccessfulAttachmentUpload(attachmentID, attachment, attachmentKey, uploadResult) + if (attachment.contentType.startsWith("audio/")) { + // process the duration + try { + val inputStream = messageDataProvider.getAttachmentStream(attachmentID)!!.inputStream!! + InputStreamMediaDataSource(inputStream).use { mediaDataSource -> + val durationMs = (DecodedAudio.create(mediaDataSource).totalDuration / 1000.0).toLong() + messageDataProvider.getDatabaseAttachment(attachmentID)?.attachmentId?.let { attachmentId -> + messageDataProvider.updateAudioAttachmentDuration(attachmentId, durationMs) + } + } + } catch (e: Exception) { + Log.e("Loki", "Couldn't process audio attachment", e) + } + } MessagingModuleConfiguration.shared.storage.resumeMessageSendJobIfNeeded(messageSendJobID) }