Optimize uploads during media composition.

By uploading in advance (when on unmetered connections), media messages
can send almost instantly.
This commit is contained in:
Greyson Parrelli
2020-01-08 15:56:51 -05:00
parent 92e97e61c1
commit fadcc606f8
37 changed files with 1413 additions and 452 deletions

View File

@@ -76,6 +76,7 @@ import java.io.OutputStream;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
@@ -120,6 +121,7 @@ public class AttachmentDatabase extends Database {
private static final String DATA_HASH = "data_hash";
static final String BLUR_HASH = "blur_hash";
static final String TRANSFORM_PROPERTIES = "transform_properties";
static final String DISPLAY_ORDER = "display_order";
public static final String DIRECTORY = "parts";
@@ -128,6 +130,8 @@ public class AttachmentDatabase extends Database {
public static final int TRANSFER_PROGRESS_PENDING = 2;
public static final int TRANSFER_PROGRESS_FAILED = 3;
public static final long PREUPLOAD_MESSAGE_ID = -8675309;
private static final String PART_ID_WHERE = ROW_ID + " = ? AND " + UNIQUE_ID + " = ?";
private static final String PART_ID_WHERE_NOT = ROW_ID + " != ? AND " + UNIQUE_ID + " != ?";
@@ -138,7 +142,8 @@ public class AttachmentDatabase extends Database {
UNIQUE_ID, DIGEST, FAST_PREFLIGHT_ID, VOICE_NOTE,
QUOTE, DATA_RANDOM, THUMBNAIL_RANDOM, WIDTH, HEIGHT,
CAPTION, STICKER_PACK_ID, STICKER_PACK_KEY, STICKER_ID,
DATA_HASH, BLUR_HASH, TRANSFORM_PROPERTIES, TRANSFER_FILE };
DATA_HASH, BLUR_HASH, TRANSFORM_PROPERTIES, TRANSFER_FILE,
DISPLAY_ORDER };
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ROW_ID + " INTEGER PRIMARY KEY, " +
MMS_ID + " INTEGER, " + "seq" + " INTEGER DEFAULT 0, " +
@@ -154,7 +159,8 @@ public class AttachmentDatabase extends Database {
CAPTION + " TEXT DEFAULT NULL, " + STICKER_PACK_ID + " TEXT DEFAULT NULL, " +
STICKER_PACK_KEY + " DEFAULT NULL, " + STICKER_ID + " INTEGER DEFAULT -1, " +
DATA_HASH + " TEXT DEFAULT NULL, " + BLUR_HASH + " TEXT DEFAULT NULL, " +
TRANSFORM_PROPERTIES + " TEXT DEFAULT NULL, " + TRANSFER_FILE + " TEXT DEFAULT NULL);";
TRANSFORM_PROPERTIES + " TEXT DEFAULT NULL, " + TRANSFER_FILE + " TEXT DEFAULT NULL, " +
DISPLAY_ORDER + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS part_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");",
@@ -320,6 +326,33 @@ public class AttachmentDatabase extends Database {
notifyAttachmentListeners();
}
/**
* Deletes all attachments with an ID of {@link #PREUPLOAD_MESSAGE_ID}. These represent
* attachments that were pre-uploaded and haven't been assigned to a message. This should only be
* done when you *know* that all attachments *should* be assigned a real mmsId. For instance, when
* the app starts. Otherwise you could delete attachments that are legitimately being
* pre-uploaded.
*/
public int deleteAbandonedPreuploadedAttachments() {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
String query = MMS_ID + " = ?";
String[] args = new String[] { String.valueOf(PREUPLOAD_MESSAGE_ID) };
int count = 0;
try (Cursor cursor = db.query(TABLE_NAME, null, query, args, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(ROW_ID));
long uniqueId = cursor.getLong(cursor.getColumnIndexOrThrow(UNIQUE_ID));
AttachmentId id = new AttachmentId(rowId, uniqueId);
deleteAttachment(id);
count++;
}
}
return count;
}
public void deleteAttachmentFilesForViewOnceMessage(long mmsId) {
Log.d(TAG, "[deleteAttachmentFilesForViewOnceMessage] mmsId: " + mmsId);
@@ -538,6 +571,32 @@ public class AttachmentDatabase extends Database {
database.update(TABLE_NAME, contentValues, PART_ID_WHERE, destinationId.toStrings());
}
public void updateAttachmentCaption(@NonNull AttachmentId id, @Nullable String caption) {
ContentValues values = new ContentValues(1);
values.put(CAPTION, caption);
databaseHelper.getWritableDatabase().update(TABLE_NAME, values, PART_ID_WHERE, id.toStrings());
}
public void updateDisplayOrder(@NonNull Map<AttachmentId, Integer> orderMap) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.beginTransaction();
try {
for (Map.Entry<AttachmentId, Integer> entry : orderMap.entrySet()) {
ContentValues values = new ContentValues(1);
values.put(DISPLAY_ORDER, entry.getValue());
databaseHelper.getWritableDatabase().update(TABLE_NAME, values, PART_ID_WHERE, entry.getKey().toStrings());
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
public void updateAttachmentAfterUpload(@NonNull AttachmentId id, @NonNull Attachment attachment) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues values = new ContentValues();
@@ -554,6 +613,42 @@ public class AttachmentDatabase extends Database {
database.update(TABLE_NAME, values, PART_ID_WHERE, id.toStrings());
}
public @NonNull DatabaseAttachment insertAttachmentForPreUpload(@NonNull Attachment attachment) throws MmsException {
Map<Attachment, AttachmentId> result = insertAttachmentsForMessage(PREUPLOAD_MESSAGE_ID,
Collections.singletonList(attachment),
Collections.emptyList());
if (result.values().isEmpty()) {
throw new MmsException("Bad attachment result!");
}
DatabaseAttachment databaseAttachment = getAttachment(result.values().iterator().next());
if (databaseAttachment == null) {
throw new MmsException("Failed to retrieve attachment we just inserted!");
}
return databaseAttachment;
}
public void updateMessageId(@NonNull Collection<AttachmentId> attachmentIds, long mmsId) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.beginTransaction();
try {
ContentValues values = new ContentValues(1);
values.put(MMS_ID, mmsId);
for (AttachmentId attachmentId : attachmentIds) {
db.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings());
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
@NonNull Map<Attachment, AttachmentId> insertAttachmentsForMessage(long mmsId, @NonNull List<Attachment> attachments, @NonNull List<Attachment> quoteAttachment)
throws MmsException
{
@@ -957,7 +1052,8 @@ public class AttachmentDatabase extends Database {
object.getInt(STICKER_ID))
: null,
BlurHash.parseOrNull(object.getString(BLUR_HASH)),
TransformProperties.parse(object.getString(TRANSFORM_PROPERTIES))));
TransformProperties.parse(object.getString(TRANSFORM_PROPERTIES)),
object.getInt(DISPLAY_ORDER)));
}
}
@@ -988,7 +1084,8 @@ public class AttachmentDatabase extends Database {
cursor.getInt(cursor.getColumnIndexOrThrow(STICKER_ID)))
: null,
BlurHash.parseOrNull(cursor.getString(cursor.getColumnIndexOrThrow(BLUR_HASH))),
TransformProperties.parse(cursor.getString(cursor.getColumnIndexOrThrow(TRANSFORM_PROPERTIES)))));
TransformProperties.parse(cursor.getString(cursor.getColumnIndexOrThrow(TRANSFORM_PROPERTIES))),
cursor.getInt(cursor.getColumnIndexOrThrow(DISPLAY_ORDER))));
}
} catch (JSONException e) {
throw new AssertionError(e);

View File

@@ -44,6 +44,7 @@ public class MediaDatabase extends Database {
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.STICKER_ID + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.BLUR_HASH + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.TRANSFORM_PROPERTIES + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DISPLAY_ORDER + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CAPTION + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.NAME + ", "
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.MESSAGE_BOX + ", "
@@ -247,9 +248,9 @@ public class MediaDatabase extends Database {
}
public enum Sorting {
Newest (AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + " DESC"),
Oldest (AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + " ASC" ),
Largest(AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.SIZE + " DESC");
Newest (AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.MMS_ID + " DESC, " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DISPLAY_ORDER + " DESC"),
Oldest (AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.MMS_ID + " ASC, " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DISPLAY_ORDER + " DESC"),
Largest(AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.SIZE + " DESC, " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DISPLAY_ORDER + " DESC");
private final String postFix;

View File

@@ -212,7 +212,8 @@ public class MmsDatabase extends MessagingDatabase {
"'" + AttachmentDatabase.STICKER_PACK_KEY + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.STICKER_PACK_KEY + ", " +
"'" + AttachmentDatabase.STICKER_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.STICKER_ID + ", " +
"'" + AttachmentDatabase.BLUR_HASH + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.BLUR_HASH + ", " +
"'" + AttachmentDatabase.TRANSFORM_PROPERTIES + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.TRANSFORM_PROPERTIES +
"'" + AttachmentDatabase.TRANSFORM_PROPERTIES + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.TRANSFORM_PROPERTIES + ", " +
"'" + AttachmentDatabase.DISPLAY_ORDER + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DISPLAY_ORDER +
")) AS " + AttachmentDatabase.ATTACHMENT_JSON_ALIAS,
};
@@ -702,6 +703,7 @@ public class MmsDatabase extends MessagingDatabase {
List<Attachment> attachments = Stream.of(associatedAttachments).filterNot(Attachment::isQuote)
.filterNot(contactAttachments::contains)
.filterNot(previewAttachments::contains)
.sorted(new DatabaseAttachment.DisplayOrderComparator())
.map(a -> (Attachment)a).toList();
Recipient recipient = Recipient.resolved(RecipientId.from(recipientId));
@@ -865,7 +867,8 @@ public class MmsDatabase extends MessagingDatabase {
databaseAttachment.getCaption(),
databaseAttachment.getSticker(),
databaseAttachment.getBlurHash(),
databaseAttachment.getTransformProperties()));
databaseAttachment.getTransformProperties(),
databaseAttachment.getDisplayOrder()));
}
return insertMediaMessage(request.getBody(),
@@ -1563,7 +1566,7 @@ public class MmsDatabase extends MessagingDatabase {
List<NetworkFailure> networkFailures = getFailures(networkDocument);
List<DatabaseAttachment> attachments = DatabaseFactory.getAttachmentDatabase(context).getAttachment(cursor);
List<Contact> contacts = getSharedContacts(cursor, attachments);
Set<Attachment> contactAttachments = Stream.of(contacts).map(Contact::getAvatarAttachment).filter(a -> a != null).collect(Collectors.toSet());
Set<Attachment> contactAttachments = Stream.of(contacts).map(Contact::getAvatarAttachment).withoutNulls().collect(Collectors.toSet());
List<LinkPreview> previews = getLinkPreviews(cursor, attachments);
Set<Attachment> previewAttachments = Stream.of(previews).filter(lp -> lp.getThumbnail().isPresent()).map(lp -> lp.getThumbnail().get()).collect(Collectors.toSet());
SlideDeck slideDeck = getSlideDeck(Stream.of(attachments).filterNot(contactAttachments::contains).filterNot(previewAttachments::contains).toList());
@@ -1601,9 +1604,10 @@ public class MmsDatabase extends MessagingDatabase {
}
private SlideDeck getSlideDeck(@NonNull List<DatabaseAttachment> attachments) {
List<? extends Attachment> messageAttachments = Stream.of(attachments)
.filterNot(Attachment::isQuote)
.toList();
List<DatabaseAttachment> messageAttachments = Stream.of(attachments)
.filterNot(Attachment::isQuote)
.sorted(new DatabaseAttachment.DisplayOrderComparator())
.toList();
return new SlideDeck(context, messageAttachments);
}

View File

@@ -296,7 +296,8 @@ public class MmsSmsDatabase extends Database {
"'" + AttachmentDatabase.STICKER_PACK_KEY + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.STICKER_PACK_KEY + ", " +
"'" + AttachmentDatabase.STICKER_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.STICKER_ID + ", " +
"'" + AttachmentDatabase.BLUR_HASH + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.BLUR_HASH + ", " +
"'" + AttachmentDatabase.TRANSFORM_PROPERTIES + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.TRANSFORM_PROPERTIES +
"'" + AttachmentDatabase.TRANSFORM_PROPERTIES + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.TRANSFORM_PROPERTIES + ", " +
"'" + AttachmentDatabase.DISPLAY_ORDER + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DISPLAY_ORDER +
")) AS " + AttachmentDatabase.ATTACHMENT_JSON_ALIAS,
SmsDatabase.BODY, MmsSmsColumns.READ, MmsSmsColumns.THREAD_ID,
SmsDatabase.TYPE, SmsDatabase.RECIPIENT_ID, SmsDatabase.ADDRESS_DEVICE_ID, SmsDatabase.SUBJECT, MmsDatabase.MESSAGE_TYPE,

View File

@@ -101,8 +101,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int REACTIONS_UNREAD_INDEX = 39;
private static final int RESUMABLE_DOWNLOADS = 40;
private static final int KEY_VALUE_STORE = 41;
private static final int ATTACHMENT_DISPLAY_ORDER = 42;
private static final int DATABASE_VERSION = 41;
private static final int DATABASE_VERSION = 42;
private static final String DATABASE_NAME = "signal.db";
private final Context context;
@@ -694,6 +695,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
"type INTEGER)");
}
if (oldVersion < ATTACHMENT_DISPLAY_ORDER) {
db.execSQL("ALTER TABLE part ADD COLUMN display_order INTEGER DEFAULT 0");
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();