Ensure images sent to stories respect media quality settings.

Stories should always use "Standard" quality, not L3 (high quality). This change ensures that we:

1. Always send stories at the appropriate quality
2. Do not corrupt or overwrite pre-existing image attachments
3. Close several streams when done (thanks StrictMode!)
This commit is contained in:
Alex Hart
2022-07-13 12:48:17 -03:00
committed by Cody Henthorne
parent c4bef8099f
commit b18542a839
20 changed files with 384 additions and 79 deletions

View File

@@ -790,8 +790,8 @@ public class AttachmentDatabase extends Database {
}
/**
* @param onlyModifyThisAttachment If false and more than one attachment shares this file, they will all be updated.
* If true, then guarantees not to affect other attachments.
* @param onlyModifyThisAttachment If false and more than one attachment shares this file and quality, they will all
* be updated. If true, then guarantees not to affect other attachments.
*/
public void updateAttachmentData(@NonNull DatabaseAttachment databaseAttachment,
@NonNull MediaStream mediaStream,
@@ -807,7 +807,8 @@ public class AttachmentDatabase extends Database {
File destination = oldDataInfo.file;
if (onlyModifyThisAttachment) {
boolean isSingleUseOfData = onlyModifyThisAttachment || oldDataInfo.hash == null;
if (isSingleUseOfData) {
if (fileReferencedByMoreThanOneAttachment(destination)) {
Log.i(TAG, "Creating a new file as this one is used by more than one attachment");
destination = newFile();
@@ -827,7 +828,10 @@ public class AttachmentDatabase extends Database {
contentValues.put(DATA_RANDOM, dataInfo.random);
contentValues.put(DATA_HASH, dataInfo.hash);
int updateCount = updateAttachmentAndMatchingHashes(database, databaseAttachment.getAttachmentId(), oldDataInfo.hash, contentValues);
int updateCount = updateAttachmentAndMatchingHashes(database,
databaseAttachment.getAttachmentId(),
isSingleUseOfData ? dataInfo.hash : oldDataInfo.hash,
contentValues);
Log.i(TAG, "[updateAttachmentData] Updated " + updateCount + " rows.");
}
@@ -846,10 +850,32 @@ public class AttachmentDatabase extends Database {
}
public void markAttachmentAsTransformed(@NonNull AttachmentId attachmentId) {
updateAttachmentTransformProperties(attachmentId, TransformProperties.forSkipTransform());
getWritableDatabase().beginTransaction();
try {
updateAttachmentTransformProperties(attachmentId, getTransformProperties(attachmentId).withSkipTransform());
getWritableDatabase().setTransactionSuccessful();
} catch (Exception e) {
Log.w(TAG, "Could not mark attachment as transformed.", e);
} finally {
getWritableDatabase().endTransaction();
}
}
public void updateAttachmentTransformProperties(@NonNull AttachmentId attachmentId, @NonNull TransformProperties transformProperties) {
public @NonNull TransformProperties getTransformProperties(@NonNull AttachmentId attachmentId) {
String[] projection = SqlUtil.buildArgs(TRANSFORM_PROPERTIES);
String[] args = attachmentId.toStrings();
try (Cursor cursor = getWritableDatabase().query(TABLE_NAME, projection, PART_ID_WHERE, args, null, null, null, null)) {
if (cursor.moveToFirst()) {
String serializedProperties = CursorUtil.requireString(cursor, TRANSFORM_PROPERTIES);
return TransformProperties.parse(serializedProperties);
} else {
throw new AssertionError("No such attachment.");
}
}
}
private void updateAttachmentTransformProperties(@NonNull AttachmentId attachmentId, @NonNull TransformProperties transformProperties) {
DataInfo dataInfo = getAttachmentDataFileInfo(attachmentId, DATA);
if (dataInfo == null) {
@@ -1017,15 +1043,12 @@ public class AttachmentDatabase extends Database {
}
}
private @Nullable DataInfo getAttachmentDataFileInfo(@NonNull AttachmentId attachmentId, @NonNull String dataType)
@VisibleForTesting
@Nullable DataInfo getAttachmentDataFileInfo(@NonNull AttachmentId attachmentId, @NonNull String dataType)
{
SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
Cursor cursor = null;
try {
cursor = database.query(TABLE_NAME, new String[]{dataType, SIZE, DATA_RANDOM, DATA_HASH}, PART_ID_WHERE, attachmentId.toStrings(),
null, null, null);
try (Cursor cursor = database.query(TABLE_NAME, new String[] { dataType, SIZE, DATA_RANDOM, DATA_HASH }, PART_ID_WHERE, attachmentId.toStrings(), null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
if (cursor.isNull(cursor.getColumnIndexOrThrow(dataType))) {
return null;
@@ -1038,9 +1061,6 @@ public class AttachmentDatabase extends Database {
} else {
return null;
}
} finally {
if (cursor != null)
cursor.close();
}
}
@@ -1127,7 +1147,7 @@ public class AttachmentDatabase extends Database {
Pair<String, String[]> selectorArgs = buildSharedFileSelectorArgs(hash, excludedAttachmentId);
try (Cursor cursor = database.query(TABLE_NAME,
new String[]{DATA, DATA_RANDOM, SIZE},
new String[]{DATA, DATA_RANDOM, SIZE, TRANSFORM_PROPERTIES},
selectorArgs.first,
selectorArgs.second,
null,
@@ -1298,7 +1318,8 @@ public class AttachmentDatabase extends Database {
template.getTransferState() == TRANSFER_PROGRESS_DONE &&
template.getTransformProperties().shouldSkipTransform() &&
template.getDigest() != null &&
!attachment.getTransformProperties().isVideoEdited();
!attachment.getTransformProperties().isVideoEdited() &&
template.getTransformProperties().sentMediaQuality == attachment.getTransformProperties().getSentMediaQuality();
ContentValues contentValues = new ContentValues();
contentValues.put(MMS_ID, mmsId);
@@ -1326,7 +1347,7 @@ public class AttachmentDatabase extends Database {
contentValues.put(TRANSFORM_PROPERTIES, attachment.getTransformProperties().serialize());
} else {
contentValues.put(VISUAL_HASH, getVisualHashStringOrNull(template));
contentValues.put(TRANSFORM_PROPERTIES, template.getTransformProperties().serialize());
contentValues.put(TRANSFORM_PROPERTIES, (useTemplateUpload ? template : attachment).getTransformProperties().serialize());
}
if (attachment.isSticker()) {
@@ -1340,7 +1361,7 @@ public class AttachmentDatabase extends Database {
contentValues.put(DATA, dataInfo.file.getAbsolutePath());
contentValues.put(SIZE, dataInfo.length);
contentValues.put(DATA_RANDOM, dataInfo.random);
if (attachment.getTransformProperties().isVideoEdited()) {
if (attachment.getTransformProperties().isVideoEdited() || attachment.getTransformProperties().sentMediaQuality != template.getTransformProperties().getSentMediaQuality()) {
contentValues.putNull(DATA_HASH);
} else {
contentValues.put(DATA_HASH, dataInfo.hash);
@@ -1408,7 +1429,8 @@ public class AttachmentDatabase extends Database {
return EncryptedMediaDataSource.createFor(attachmentSecret, dataInfo.file, dataInfo.random, dataInfo.length);
}
private static class DataInfo {
@VisibleForTesting
static class DataInfo {
private final File file;
private final long length;
private final byte[] random;
@@ -1420,6 +1442,22 @@ public class AttachmentDatabase extends Database {
this.random = random;
this.hash = hash;
}
@Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final DataInfo dataInfo = (DataInfo) o;
return length == dataInfo.length &&
Objects.equals(file, dataInfo.file) &&
Arrays.equals(random, dataInfo.random) &&
Objects.equals(hash, dataInfo.hash);
}
@Override public int hashCode() {
int result = Objects.hash(file, length, hash);
result = 31 * result + Arrays.hashCode(random);
return result;
}
}
private static final class DataUsageResult {
@@ -1520,6 +1558,10 @@ public class AttachmentDatabase extends Database {
return sentMediaQuality;
}
@NonNull TransformProperties withSkipTransform() {
return new TransformProperties(true, false, 0, 0, sentMediaQuality);
}
@NonNull String serialize() {
return JsonUtil.toJson(this);
}