Skip to content

[SES-383] - Fix voice message duration issue #1200

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

Merged
merged 9 commits into from
May 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,6 @@ interface StorageProtocol {
fun getReceivedMessageTimestamps(): Set<Long>
fun addReceivedMessageTimestamp(timestamp: Long)
fun removeReceivedMessageTimestamps(timestamps: Set<Long>)
/**
* Returns the IDs of the saved attachments.
*/
fun persistAttachments(messageID: Long, attachments: List<Attachment>): List<Long>
fun getAttachmentsForMessage(mmsMessageId: Long): List<DatabaseAttachment>
fun getMessageBy(timestamp: Long, author: String): MessageRecord?
fun updateSentTimestamp(messageId: MessageId, openGroupSentTimestamp: Long, threadId: Long)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,13 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
val messageDataProvider = MessagingModuleConfiguration.shared.messageDataProvider
messageDataProvider.handleSuccessfulAttachmentUpload(attachmentID, attachment, attachmentKey, uploadResult)

// Outgoing voice messages do not have their final duration set because older Android versions (API 28 and below)
// can have bugs where the media duration is calculated incorrectly. In such cases we leave the correct "interim"
// voice message duration as the final duration as we know that it'll be correct..
// We don't need to calculate the duration for voice notes, as they will have it set already.
if (attachment.contentType.startsWith("audio/") && !attachment.voiceNote) {
// ..but for outgoing audio files we do process the duration to the best of our ability.
try {
val inputStream = messageDataProvider.getAttachmentStream(attachmentID)!!.inputStream!!
InputStreamMediaDataSource(inputStream).use { mediaDataSource ->
val durationMS = (DecodedAudio.create(mediaDataSource).totalDurationMicroseconds / 1000.0).toLong()
Log.d(TAG, "Audio attachment duration calculated as: $durationMS ms")
messageDataProvider.getDatabaseAttachment(attachmentID)?.attachmentId?.let { attachmentId ->
messageDataProvider.updateAudioAttachmentDuration(attachmentId, durationMS, threadID.toLong())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,53 @@

public abstract class Attachment {

@NonNull private final String contentType;
@NonNull
private final String contentType;
private final int transferState;
private final long size;
private final String filename;

@Nullable private final String location;
@Nullable private final String key;
@Nullable private final String relay;
@Nullable private final byte[] digest;
@Nullable private final String fastPreflightId;
@Nullable
private final String location;
@Nullable
private final String key;
@Nullable
private final String relay;
@Nullable
private final byte[] digest;
@Nullable
private final String fastPreflightId;
private final boolean voiceNote;
private final int width;
private final int height;
private final boolean quote;
@Nullable private final String caption;
@Nullable
private final String caption;
private final String url;

private final long audioDurationMs;

public Attachment(@NonNull String contentType, int transferState, long size, String filename,
@Nullable String location, @Nullable String key, @Nullable String relay,
@Nullable byte[] digest, @Nullable String fastPreflightId, boolean voiceNote,
int width, int height, boolean quote, @Nullable String caption, String url)
{
this.contentType = contentType;
this.transferState = transferState;
this.size = size;
this.filename = filename;
this.location = location;
this.key = key;
this.relay = relay;
this.digest = digest;
int width, int height, boolean quote, @Nullable String caption, String url,
long audioDurationMs) {
this.contentType = contentType;
this.transferState = transferState;
this.size = size;
this.filename = filename;
this.location = location;
this.key = key;
this.relay = relay;
this.digest = digest;
this.fastPreflightId = fastPreflightId;
this.voiceNote = voiceNote;
this.width = width;
this.height = height;
this.quote = quote;
this.caption = caption;
this.url = url;
this.voiceNote = voiceNote;
this.width = width;
this.height = height;
this.quote = quote;
this.caption = caption;
this.url = url;
this.audioDurationMs = audioDurationMs;
}

@Nullable
Expand All @@ -51,7 +61,9 @@ public Attachment(@NonNull String contentType, int transferState, long size, Str
@Nullable
public abstract Uri getThumbnailUri();

public int getTransferState() { return transferState; }
public int getTransferState() {
return transferState;
}

public boolean isInProgress() {
return transferState == AttachmentState.DOWNLOADING.getValue();
Expand All @@ -65,37 +77,75 @@ public boolean isFailed() {
return transferState == AttachmentState.FAILED.getValue();
}

public long getSize() { return size; }
public long getSize() {
return size;
}

public String getFilename() { return filename; }
public String getFilename() {
return filename;
}

@NonNull
public String getContentType() { return contentType; }
public String getContentType() {
return contentType;
}

@Nullable
public String getLocation() { return location; }
public String getLocation() {
return location;
}

@Nullable
public String getKey() { return key; }
public String getKey() {
return key;
}

@Nullable
public String getRelay() { return relay; }
public String getRelay() {
return relay;
}

@Nullable
public byte[] getDigest() { return digest; }
public byte[] getDigest() {
return digest;
}

@Nullable
public String getFastPreflightId() { return fastPreflightId; }
public String getFastPreflightId() {
return fastPreflightId;
}

public boolean isVoiceNote() {
return voiceNote;
}

public boolean isVoiceNote() { return voiceNote; }
public int getWidth() {
return width;
}

public int getWidth() { return width; }
public int getHeight() {
return height;
}

public int getHeight() { return height; }
public boolean isQuote() {
return quote;
}

public boolean isQuote() { return quote; }
public @Nullable String getCaption() {
return caption;
}

public @Nullable String getCaption() { return caption; }
public String getUrl() {
return url;
}

public String getUrl() { return url; }
/**
* Returns the duration of the audio in milliseconds.
* This is only relevant for audio attachments.
*
* Returns -1 if the information is not available.
*/
public long getAudioDurationMs() {
return audioDurationMs;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.session.libsession.messaging.sending_receiving.attachments;

import android.net.Uri;

import androidx.annotation.Nullable;

import org.session.libsession.messaging.MessagingModuleConfiguration;

public class DatabaseAttachment extends Attachment {
Expand All @@ -18,9 +20,9 @@ public DatabaseAttachment(AttachmentId attachmentId, long mmsId,
String filename, String location, String key, String relay,
byte[] digest, String fastPreflightId, boolean voiceNote,
int width, int height, boolean quote, @Nullable String caption,
String url
String url, long audioDurationMs
) {
super(contentType, transferProgress, size, filename, location, key, relay, digest, fastPreflightId, voiceNote, width, height, quote, caption, url);
super(contentType, transferProgress, size, filename, location, key, relay, digest, fastPreflightId, voiceNote, width, height, quote, caption, url, audioDurationMs);
this.attachmentId = attachmentId;
this.hasData = hasData;
this.hasThumbnail = hasThumbnail;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import org.session.libsignal.utilities.guava.Optional;
import org.session.libsignal.messages.SignalServiceAttachment;
import org.session.libsignal.messages.SignalServiceDataMessage;
import org.session.libsignal.utilities.Base64;
import org.session.libsignal.protos.SignalServiceProtos;

Expand All @@ -22,7 +21,7 @@ private PointerAttachment(@NonNull String contentType, int transferState, long s
@Nullable byte[] digest, @Nullable String fastPreflightId, boolean voiceNote,
int width, int height, @Nullable String caption, String url)
{
super(contentType, transferState, size, fileName, location, key, relay, digest, fastPreflightId, voiceNote, width, height, false, caption, url);
super(contentType, transferState, size, fileName, location, key, relay, digest, fastPreflightId, voiceNote, width, height, false, caption, url, -1L);
}

@Nullable
Expand Down Expand Up @@ -54,22 +53,6 @@ public static List<Attachment> forPointers(Optional<List<SignalServiceAttachment
return results;
}

public static List<Attachment> forPointersOfDataMessage(List<SignalServiceDataMessage.Quote.QuotedAttachment> pointers) {
List<Attachment> results = new LinkedList<>();

if (pointers != null) {
for (SignalServiceDataMessage.Quote.QuotedAttachment pointer : pointers) {
Optional<Attachment> result = forPointer(pointer);

if (result.isPresent()) {
results.add(result.get());
}
}
}

return results;
}

public static List<Attachment> forPointers(List<SignalServiceProtos.DataMessage.Quote.QuotedAttachment> pointers) {
List<Attachment> results = new LinkedList<>();

Expand Down Expand Up @@ -151,25 +134,6 @@ public static Optional<Attachment> forPointer(SignalServiceProtos.DataMessage.Qu
thumbnail != null ? thumbnail.getUrl() : ""));
}

public static Optional<Attachment> forPointer(SignalServiceDataMessage.Quote.QuotedAttachment pointer) {
SignalServiceAttachment thumbnail = pointer.getThumbnail();

return Optional.of(new PointerAttachment(pointer.getContentType(),
AttachmentState.PENDING.getValue(),
thumbnail != null ? thumbnail.asPointer().getSize().or(0) : 0,
pointer.getFileName(),
String.valueOf(thumbnail != null ? thumbnail.asPointer().getId() : 0),
thumbnail != null && thumbnail.asPointer().getKey() != null ? Base64.encodeBytes(thumbnail.asPointer().getKey()) : null,
null,
thumbnail != null ? thumbnail.asPointer().getDigest().orNull() : null,
null,
false,
thumbnail != null ? thumbnail.asPointer().getWidth() : 0,
thumbnail != null ? thumbnail.asPointer().getHeight() : 0,
thumbnail != null ? thumbnail.asPointer().getCaption().orNull() : null,
thumbnail != null ? thumbnail.asPointer().getUrl() : ""));
}

/**
* Converts a Session Attachment to a Signal Attachment
* @param attachment Session Attachment
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,23 @@ public class UriAttachment extends Attachment {
public UriAttachment(@NonNull Uri uri, @NonNull String contentType, int transferState, long size,
@Nullable String fileName, boolean voiceNote, boolean quote, @Nullable String caption)
{
this(uri, uri, contentType, transferState, size, 0, 0, fileName, null, voiceNote, quote, caption);
this(uri, uri, contentType, transferState, size, 0, 0, fileName, null, voiceNote, quote, caption, -1);
}

public UriAttachment(@NonNull Uri dataUri, @Nullable Uri thumbnailUri,
@NonNull String contentType, int transferState, long size, int width, int height,
@Nullable String fileName, @Nullable String fastPreflightId,
boolean voiceNote, boolean quote, @Nullable String caption)
boolean voiceNote, boolean quote, @Nullable String caption) {
this(dataUri, thumbnailUri, contentType, transferState, size, width, height, fileName, fastPreflightId,
voiceNote, quote, caption, -1);
}

public UriAttachment(@NonNull Uri dataUri, @Nullable Uri thumbnailUri,
@NonNull String contentType, int transferState, long size, int width, int height,
@Nullable String fileName, @Nullable String fastPreflightId,
boolean voiceNote, boolean quote, @Nullable String caption, long audioDurationMs)
{
super(contentType, transferState, size, fileName, null, null, null, null, fastPreflightId, voiceNote, width, height, quote, caption, "");
super(contentType, transferState, size, fileName, null, null, null, null, fastPreflightId, voiceNote, width, height, quote, caption, "", audioDurationMs);
this.dataUri = dataUri;
this.thumbnailUri = thumbnailUri;
}
Expand Down

This file was deleted.

Loading