mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-24 18:45:19 +00:00
fix: audio waveforms decoded on the attachment download
This commit is contained in:
parent
9566120d66
commit
b329402faf
@ -194,8 +194,8 @@ android {
|
|||||||
versionCode canonicalVersionCode * postFixSize
|
versionCode canonicalVersionCode * postFixSize
|
||||||
versionName canonicalVersionName
|
versionName canonicalVersionName
|
||||||
|
|
||||||
minSdkVersion 23
|
minSdkVersion androidMinimumSdkVersion
|
||||||
targetSdkVersion 30
|
targetSdkVersion androidCompileSdkVersion
|
||||||
|
|
||||||
multiDexEnabled = true
|
multiDexEnabled = true
|
||||||
|
|
||||||
|
@ -97,6 +97,14 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper)
|
|||||||
attachmentDatabase.insertAttachmentsForPlaceholder(messageId, attachmentId, stream)
|
attachmentDatabase.insertAttachmentsForPlaceholder(messageId, attachmentId, stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun updateAudioAttachmentDuration(attachmentId: AttachmentId, durationMs: Long) {
|
||||||
|
DatabaseFactory.getAttachmentDatabase(context).setAttachmentAudioExtras(DatabaseAttachmentAudioExtras(
|
||||||
|
attachmentId = attachmentId,
|
||||||
|
visualSamples = byteArrayOf(),
|
||||||
|
durationMs = durationMs
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
override fun isOutgoingMessage(timestamp: Long): Boolean {
|
override fun isOutgoingMessage(timestamp: Long): Boolean {
|
||||||
val smsDatabase = DatabaseFactory.getSmsDatabase(context)
|
val smsDatabase = DatabaseFactory.getSmsDatabase(context)
|
||||||
val mmsDatabase = DatabaseFactory.getMmsDatabase(context)
|
val mmsDatabase = DatabaseFactory.getMmsDatabase(context)
|
||||||
|
@ -10,9 +10,11 @@ import android.widget.RelativeLayout
|
|||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import kotlinx.android.synthetic.main.view_voice_message.view.*
|
import kotlinx.android.synthetic.main.view_voice_message.view.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
|
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
||||||
import org.thoughtcrime.securesms.audio.AudioSlidePlayer
|
import org.thoughtcrime.securesms.audio.AudioSlidePlayer
|
||||||
import org.thoughtcrime.securesms.components.CornerMask
|
import org.thoughtcrime.securesms.components.CornerMask
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtilities
|
import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtilities
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
@ -44,27 +46,29 @@ class VoiceMessageView : LinearLayout, AudioSlidePlayer.Listener {
|
|||||||
val audio = message.slideDeck.audioSlide!!
|
val audio = message.slideDeck.audioSlide!!
|
||||||
val player = AudioSlidePlayer.createFor(context, audio, this)
|
val player = AudioSlidePlayer.createFor(context, audio, this)
|
||||||
this.player = player
|
this.player = player
|
||||||
isPreparing = true
|
|
||||||
if (!audio.isPendingDownload && !audio.isInProgress) {
|
|
||||||
player.play(0.0)
|
|
||||||
}
|
|
||||||
voiceMessageViewLoader.isVisible = audio.isPendingDownload
|
voiceMessageViewLoader.isVisible = audio.isPendingDownload
|
||||||
val cornerRadii = MessageBubbleUtilities.calculateRadii(context, isStartOfMessageCluster, isEndOfMessageCluster, message.isOutgoing)
|
val cornerRadii = MessageBubbleUtilities.calculateRadii(context, isStartOfMessageCluster, isEndOfMessageCluster, message.isOutgoing)
|
||||||
cornerMask.setTopLeftRadius(cornerRadii[0])
|
cornerMask.setTopLeftRadius(cornerRadii[0])
|
||||||
cornerMask.setTopRightRadius(cornerRadii[1])
|
cornerMask.setTopRightRadius(cornerRadii[1])
|
||||||
cornerMask.setBottomRightRadius(cornerRadii[2])
|
cornerMask.setBottomRightRadius(cornerRadii[2])
|
||||||
cornerMask.setBottomLeftRadius(cornerRadii[3])
|
cornerMask.setBottomLeftRadius(cornerRadii[3])
|
||||||
|
|
||||||
|
// only process audio if downloaded
|
||||||
|
if (audio.isPendingDownload || audio.isInProgress) return
|
||||||
|
|
||||||
|
(audio.asAttachment() as? DatabaseAttachment)?.let { attachment ->
|
||||||
|
DatabaseFactory.getAttachmentDatabase(context).getAttachmentAudioExtras(attachment.attachmentId)?.let { audioExtras ->
|
||||||
|
if (audioExtras.durationMs > 0) {
|
||||||
|
voiceMessageViewDurationTextView.visibility = View.VISIBLE
|
||||||
|
voiceMessageViewDurationTextView.text = String.format("%02d:%02d",
|
||||||
|
TimeUnit.MILLISECONDS.toMinutes(audioExtras.durationMs),
|
||||||
|
TimeUnit.MILLISECONDS.toSeconds(audioExtras.durationMs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPlayerStart(player: AudioSlidePlayer) {
|
override fun onPlayerStart(player: AudioSlidePlayer) {}
|
||||||
if (!isPreparing) { return }
|
|
||||||
isPreparing = false
|
|
||||||
duration = player.duration
|
|
||||||
voiceMessageViewDurationTextView.text = String.format("%01d:%02d",
|
|
||||||
TimeUnit.MILLISECONDS.toMinutes(duration),
|
|
||||||
TimeUnit.MILLISECONDS.toSeconds(duration))
|
|
||||||
player.stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPlayerProgress(player: AudioSlidePlayer, progress: Double, unused: Long) {
|
override fun onPlayerProgress(player: AudioSlidePlayer, progress: Double, unused: Long) {
|
||||||
if (progress == 1.0) {
|
if (progress == 1.0) {
|
||||||
|
@ -9,10 +9,11 @@ import org.session.libsession.messaging.utilities.Data
|
|||||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment
|
import org.session.libsession.messaging.sending_receiving.attachments.Attachment
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
|
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachmentAudioExtras
|
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachmentAudioExtras
|
||||||
|
import org.session.libsession.utilities.DecodedAudio
|
||||||
|
import org.session.libsession.utilities.InputStreamMediaDataSource
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job
|
import org.thoughtcrime.securesms.jobmanager.Job
|
||||||
import org.thoughtcrime.securesms.jobs.BaseJob
|
import org.thoughtcrime.securesms.jobs.BaseJob
|
||||||
import org.thoughtcrime.securesms.loki.utilities.DecodedAudio
|
|
||||||
import org.thoughtcrime.securesms.mms.PartAuthority
|
import org.thoughtcrime.securesms.mms.PartAuthority
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.lang.IllegalStateException
|
import java.lang.IllegalStateException
|
||||||
@ -133,35 +134,4 @@ class PrepareAttachmentAudioExtrasJob : BaseJob {
|
|||||||
|
|
||||||
/** Gets dispatched once the audio extras have been updated. */
|
/** Gets dispatched once the audio extras have been updated. */
|
||||||
data class AudioExtrasUpdatedEvent(val attachmentId: AttachmentId)
|
data class AudioExtrasUpdatedEvent(val attachmentId: AttachmentId)
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.M)
|
|
||||||
private class InputStreamMediaDataSource: MediaDataSource {
|
|
||||||
|
|
||||||
private val data: ByteArray
|
|
||||||
|
|
||||||
constructor(inputStream: InputStream): super() {
|
|
||||||
this.data = inputStream.readBytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int {
|
|
||||||
val length: Int = data.size
|
|
||||||
if (position >= length) {
|
|
||||||
return -1 // -1 indicates EOF
|
|
||||||
}
|
|
||||||
var actualSize = size
|
|
||||||
if (position + size > length) {
|
|
||||||
actualSize -= (position + size - length).toInt()
|
|
||||||
}
|
|
||||||
System.arraycopy(data, position.toInt(), buffer, offset, actualSize)
|
|
||||||
return actualSize
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getSize(): Long {
|
|
||||||
return data.size.toLong()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
// We don't need to close the wrapped stream.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -14,7 +14,7 @@ import android.view.ViewConfiguration
|
|||||||
import android.view.animation.DecelerateInterpolator
|
import android.view.animation.DecelerateInterpolator
|
||||||
import androidx.core.math.MathUtils
|
import androidx.core.math.MathUtils
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.loki.utilities.byteToNormalizedFloat
|
import org.session.libsession.utilities.byteToNormalizedFloat
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
@ -50,6 +50,7 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
project.ext {
|
project.ext {
|
||||||
|
androidMinimumSdkVersion = 23
|
||||||
androidCompileSdkVersion = 30
|
androidCompileSdkVersion = 30
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,6 +6,10 @@ plugins {
|
|||||||
android {
|
android {
|
||||||
compileSdkVersion androidCompileSdkVersion
|
compileSdkVersion androidCompileSdkVersion
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion androidMinimumSdkVersion
|
||||||
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
@ -20,6 +20,7 @@ interface MessageDataProvider {
|
|||||||
fun getSignalAttachmentPointer(attachmentId: Long): SignalServiceAttachmentPointer?
|
fun getSignalAttachmentPointer(attachmentId: Long): SignalServiceAttachmentPointer?
|
||||||
fun setAttachmentState(attachmentState: AttachmentState, attachmentId: Long, messageID: Long)
|
fun setAttachmentState(attachmentState: AttachmentState, attachmentId: Long, messageID: Long)
|
||||||
fun insertAttachment(messageId: Long, attachmentId: AttachmentId, stream : InputStream)
|
fun insertAttachment(messageId: Long, attachmentId: AttachmentId, stream : InputStream)
|
||||||
|
fun updateAudioAttachmentDuration(attachmentId: AttachmentId, durationMs: Long)
|
||||||
fun isOutgoingMessage(timestamp: Long): Boolean
|
fun isOutgoingMessage(timestamp: Long): Boolean
|
||||||
fun handleSuccessfulAttachmentUpload(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: UploadResult)
|
fun handleSuccessfulAttachmentUpload(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: UploadResult)
|
||||||
fun handleFailedAttachmentUpload(attachmentId: Long)
|
fun handleFailedAttachmentUpload(attachmentId: Long)
|
||||||
|
@ -1,15 +1,22 @@
|
|||||||
package org.session.libsession.messaging.jobs
|
package org.session.libsession.messaging.jobs
|
||||||
|
|
||||||
|
import android.content.ContentResolver
|
||||||
|
import android.media.MediaDataSource
|
||||||
|
import android.media.MediaExtractor
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
|
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.AttachmentState
|
||||||
import org.session.libsession.messaging.utilities.Data
|
import org.session.libsession.messaging.utilities.Data
|
||||||
|
import org.session.libsession.utilities.DecodedAudio
|
||||||
import org.session.libsession.utilities.DownloadUtilities
|
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.streams.AttachmentCipherInputStream
|
||||||
import org.session.libsignal.utilities.Base64
|
import org.session.libsignal.utilities.Base64
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.FileDescriptor
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
|
||||||
class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long) : Job {
|
class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long) : Job {
|
||||||
@ -67,6 +74,15 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
|
|||||||
}
|
}
|
||||||
FileInputStream(tempFile)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
messageDataProvider.insertAttachment(databaseMessageID, attachment.attachmentId, inputStream)
|
messageDataProvider.insertAttachment(databaseMessageID, attachment.attachmentId, inputStream)
|
||||||
tempFile.delete()
|
tempFile.delete()
|
||||||
handleSuccess()
|
handleSuccess()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.utilities
|
package org.session.libsession.utilities
|
||||||
|
|
||||||
import android.media.AudioFormat
|
import android.media.AudioFormat
|
||||||
import android.media.MediaCodec
|
import android.media.MediaCodec
|
||||||
@ -11,6 +11,7 @@ import androidx.annotation.RequiresApi
|
|||||||
|
|
||||||
import java.io.FileDescriptor
|
import java.io.FileDescriptor
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
import java.nio.ShortBuffer
|
import java.nio.ShortBuffer
|
||||||
@ -365,4 +366,34 @@ inline fun byteToNormalizedFloat(value: Byte): Float {
|
|||||||
/** Turns a [0..1] float into a signed byte. */
|
/** Turns a [0..1] float into a signed byte. */
|
||||||
inline fun normalizedFloatToByte(value: Float): Byte {
|
inline fun normalizedFloatToByte(value: Float): Byte {
|
||||||
return (255f * value - 128f).roundToInt().toByte()
|
return (255f * value - 128f).roundToInt().toByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
class InputStreamMediaDataSource: MediaDataSource {
|
||||||
|
|
||||||
|
private val data: ByteArray
|
||||||
|
|
||||||
|
constructor(inputStream: InputStream): super() {
|
||||||
|
this.data = inputStream.readBytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int {
|
||||||
|
val length: Int = data.size
|
||||||
|
if (position >= length) {
|
||||||
|
return -1 // -1 indicates EOF
|
||||||
|
}
|
||||||
|
var actualSize = size
|
||||||
|
if (position + size > length) {
|
||||||
|
actualSize -= (position + size - length).toInt()
|
||||||
|
}
|
||||||
|
System.arraycopy(data, position.toInt(), buffer, offset, actualSize)
|
||||||
|
return actualSize
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSize(): Long {
|
||||||
|
return data.size.toLong()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
// We don't need to close the wrapped stream.
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user