mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 09:20:19 +01:00
Add quote reconstruction job for free-tier restores.
This commit is contained in:
@@ -307,8 +307,13 @@ class AttachmentTable(
|
||||
|
||||
private const val QUOTE_THUMBNAIL_DIMEN = 200
|
||||
private const val QUOTE_THUMBAIL_QUALITY = 50
|
||||
|
||||
/** Indicates a legacy quote is pending transcoding to a new quote thumbnail. */
|
||||
const val QUOTE_PENDING_TRANSCODE = 2
|
||||
|
||||
/** Indicates a quote from a free-tier backup restore is pending potential reconstruction from a parent attachment. */
|
||||
const val QUOTE_PENDING_RECONSTRUCTION = 3
|
||||
|
||||
@JvmStatic
|
||||
@Throws(IOException::class)
|
||||
fun newDataFile(context: Context): File {
|
||||
@@ -3163,6 +3168,96 @@ class AttachmentTable(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* After restoring from the free-tier, it's possible we'll be missing many of our quoted replies.
|
||||
* This marks quotes with a special flag to indicate that they'd be eligible for reconstruction.
|
||||
* See [QUOTE_PENDING_RECONSTRUCTION].
|
||||
*/
|
||||
fun markQuotesThatNeedReconstruction() {
|
||||
writableDatabase
|
||||
.update(TABLE_NAME)
|
||||
.values(QUOTE to QUOTE_PENDING_RECONSTRUCTION)
|
||||
.where("$QUOTE != 0 AND $DATA_FILE IS NULL AND $REMOTE_LOCATION IS NULL")
|
||||
.run()
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves data for the newest quote that is pending reconstruction (see [QUOTE_PENDING_RECONSTRUCTION]), if any.
|
||||
*/
|
||||
fun getNewestQuotePendingReconstruction(): DatabaseAttachment? {
|
||||
return readableDatabase
|
||||
.select(*PROJECTION)
|
||||
.from(TABLE_NAME)
|
||||
.where("$QUOTE = $QUOTE_PENDING_RECONSTRUCTION")
|
||||
.orderBy("$ID DESC")
|
||||
.limit(1)
|
||||
.run()
|
||||
.readToSingleObject { it.readAttachment() }
|
||||
}
|
||||
|
||||
/**
|
||||
* After reconstructing a thumbnail, this method can be used to write the data to the quote.
|
||||
* It'll handle duplicates as well as clearing the [QUOTE_PENDING_RECONSTRUCTION] flag.
|
||||
*/
|
||||
@Throws(MmsException::class)
|
||||
fun applyReconstructedQuoteData(attachmentId: AttachmentId, thumbnail: ImageCompressionUtil.Result) {
|
||||
val newDataFileInfo = writeToDataFile(newDataFile(context), thumbnail.data.inputStream(), TransformProperties.empty())
|
||||
|
||||
val foundDuplicate = writableDatabase.withinTransaction { db ->
|
||||
val existingMatch: DataFileInfo? = db
|
||||
.select(*DATA_FILE_INFO_PROJECTION)
|
||||
.from(TABLE_NAME)
|
||||
.where("$DATA_HASH_END = ?", newDataFileInfo.hash)
|
||||
.run()
|
||||
.readToSingleObject { it.readDataFileInfo() }
|
||||
|
||||
db.update(TABLE_NAME)
|
||||
.values(
|
||||
DATA_FILE to (existingMatch?.file?.absolutePath ?: newDataFileInfo.file.absolutePath),
|
||||
DATA_SIZE to (existingMatch?.length ?: newDataFileInfo.length),
|
||||
DATA_RANDOM to (existingMatch?.random ?: newDataFileInfo.random),
|
||||
DATA_HASH_START to (existingMatch?.hashStart ?: newDataFileInfo.hash),
|
||||
DATA_HASH_END to (existingMatch?.hashEnd ?: newDataFileInfo.hash),
|
||||
CONTENT_TYPE to thumbnail.mimeType,
|
||||
WIDTH to thumbnail.width,
|
||||
HEIGHT to thumbnail.height,
|
||||
QUOTE to 1
|
||||
)
|
||||
.where("$ID = ?", attachmentId)
|
||||
.run()
|
||||
|
||||
existingMatch != null
|
||||
}
|
||||
|
||||
if (foundDuplicate) {
|
||||
if (!newDataFileInfo.file.delete()) {
|
||||
Log.w(TAG, "[applyReconstructedQuoteData] Failed to delete a duplicated file!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the [QUOTE_PENDING_RECONSTRUCTION] status of an attachment. Used for when an error occurs and you can't call [applyReconstructedQuoteData].
|
||||
*/
|
||||
fun clearQuotePendingReconstruction(attachmentId: AttachmentId) {
|
||||
writableDatabase
|
||||
.update(TABLE_NAME)
|
||||
.values(QUOTE to 1)
|
||||
.where("$ID = ?", attachmentId)
|
||||
.run()
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all [QUOTE_PENDING_RECONSTRUCTION] flags on attachments.
|
||||
*/
|
||||
fun clearAllQuotesPendingReconstruction() {
|
||||
writableDatabase
|
||||
.update(TABLE_NAME)
|
||||
.values(QUOTE to 1)
|
||||
.where("$QUOTE = $QUOTE_PENDING_RECONSTRUCTION")
|
||||
.run()
|
||||
}
|
||||
|
||||
/**
|
||||
* Used in an app migration that creates quote thumbnails. Updates all quote attachments that share the same
|
||||
* [previousDataFile] to use the new thumbnail.
|
||||
|
||||
@@ -39,6 +39,7 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD
|
||||
${AttachmentTable.TABLE_NAME}.${AttachmentTable.WIDTH},
|
||||
${AttachmentTable.TABLE_NAME}.${AttachmentTable.HEIGHT},
|
||||
${AttachmentTable.TABLE_NAME}.${AttachmentTable.QUOTE},
|
||||
${AttachmentTable.TABLE_NAME}.${AttachmentTable.QUOTE_TARGET_CONTENT_TYPE},
|
||||
${AttachmentTable.TABLE_NAME}.${AttachmentTable.STICKER_PACK_ID},
|
||||
${AttachmentTable.TABLE_NAME}.${AttachmentTable.STICKER_PACK_KEY},
|
||||
${AttachmentTable.TABLE_NAME}.${AttachmentTable.STICKER_ID},
|
||||
|
||||
Reference in New Issue
Block a user