Remove attachment table JSON join.

This commit is contained in:
Greyson Parrelli
2026-03-19 12:29:04 -04:00
committed by Cody Henthorne
parent 7253aaaa14
commit be4bf27ede
14 changed files with 106 additions and 185 deletions

View File

@@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.database.NoSuchMessageException
import org.thoughtcrime.securesms.database.SignalDatabase.Companion.messages
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.database.withAttachments
import org.thoughtcrime.securesms.util.hasAudio
import java.util.concurrent.Executor
import java.util.concurrent.Executors
@@ -258,7 +259,7 @@ class VoiceNotePlayerCallback(val context: Context, val player: VoiceNotePlayer)
private fun loadMediaItemsForSinglePlayback(messageId: Long): List<MediaItem> {
return try {
listOf(messages.getMessageRecord(messageId)).messageRecordsToVoiceNoteMediaItems()
listOf(messages.getMessageRecord(messageId)).withAttachments().messageRecordsToVoiceNoteMediaItems()
} catch (e: NoSuchMessageException) {
Log.w(TAG, "Could not find message.", e)
emptyList()
@@ -268,7 +269,7 @@ class VoiceNotePlayerCallback(val context: Context, val player: VoiceNotePlayer)
@WorkerThread
private fun loadMediaItemsForConsecutivePlayback(messageId: Long): List<MediaItem> {
return try {
messages.getMessagesAfterVoiceNoteInclusive(messageId, LIMIT).messageRecordsToVoiceNoteMediaItems()
messages.getMessagesAfterVoiceNoteInclusive(messageId, LIMIT).withAttachments().messageRecordsToVoiceNoteMediaItems()
} catch (e: NoSuchMessageException) {
Log.w(TAG, "Could not find message.", e)
emptyList()

View File

@@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.database.model.MessageId
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.thoughtcrime.securesms.database.withAttachments
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.keyboard.KeyboardUtil
import org.thoughtcrime.securesms.mms.GifSlide
@@ -219,7 +220,8 @@ class DraftRepository(
private fun loadDraftMessageEditInternal(serialized: String): ConversationMessage? {
val messageId = MessageId.deserialize(serialized)
val messageRecord: MessageRecord = SignalDatabase.messages.getMessageRecordOrNull(messageId.id) ?: return null
var messageRecord: MessageRecord = SignalDatabase.messages.getMessageRecordOrNull(messageId.id) ?: return null
messageRecord = messageRecord.withAttachments()
val threadRecipient: Recipient = requireNotNull(SignalDatabase.threads.getRecipientForThreadId(messageRecord.threadId))
if (messageRecord.hasTextSlide()) {
val textSlide = messageRecord.requireTextSlide()

View File

@@ -20,14 +20,11 @@ import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import android.media.MediaDataSource
import android.text.TextUtils
import androidx.annotation.VisibleForTesting
import androidx.annotation.WorkerThread
import androidx.core.content.contentValuesOf
import com.bumptech.glide.Glide
import okio.ByteString.Companion.toByteString
import org.json.JSONArray
import org.json.JSONException
import org.signal.blurhash.BlurHash
import org.signal.core.models.backup.MediaId
import org.signal.core.models.backup.MediaName
@@ -107,7 +104,6 @@ import org.thoughtcrime.securesms.stickers.StickerLocator
import org.thoughtcrime.securesms.util.BitmapDecodingException
import org.thoughtcrime.securesms.util.FileUtils
import org.thoughtcrime.securesms.util.ImageCompressionUtil
import org.thoughtcrime.securesms.util.JsonUtils.SaneJSONObject
import org.thoughtcrime.securesms.util.MediaUtil
import org.thoughtcrime.securesms.util.RemoteConfig
import org.thoughtcrime.securesms.video.EncryptedMediaDataSource
@@ -122,7 +118,6 @@ import java.io.InputStream
import java.security.DigestInputStream
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.LinkedList
import java.util.UUID
import kotlin.text.appendLine
import kotlin.time.Duration
@@ -183,8 +178,6 @@ class AttachmentTable(
const val QUOTE_TARGET_CONTENT_TYPE = "quote_target_content_type"
const val METADATA_ID = "metadata_id"
const val ATTACHMENT_JSON_ALIAS = "attachment_json"
private const val DIRECTORY = "parts"
const val TRANSFER_PROGRESS_DONE = 0
@@ -2327,80 +2320,6 @@ class AttachmentTable(
notifyConversationListeners(threadId)
}
fun getAttachments(cursor: Cursor): List<DatabaseAttachment> {
return try {
if (cursor.getColumnIndex(ATTACHMENT_JSON_ALIAS) != -1) {
if (cursor.isNull(ATTACHMENT_JSON_ALIAS)) {
return LinkedList()
}
val result: MutableList<DatabaseAttachment> = mutableListOf()
val array = JSONArray(cursor.requireString(ATTACHMENT_JSON_ALIAS))
for (i in 0 until array.length()) {
val jsonObject = SaneJSONObject(array.getJSONObject(i))
if (!jsonObject.isNull(ID)) {
val contentType = jsonObject.getString(CONTENT_TYPE)
result += DatabaseAttachment(
attachmentId = AttachmentId(jsonObject.getLong(ID)),
mmsId = jsonObject.getLong(MESSAGE_ID),
hasData = !TextUtils.isEmpty(jsonObject.getString(DATA_FILE)),
hasThumbnail = MediaUtil.isImageType(contentType) || MediaUtil.isVideoType(contentType),
contentType = contentType,
transferProgress = jsonObject.getInt(TRANSFER_STATE),
size = jsonObject.getLong(DATA_SIZE),
fileName = jsonObject.getString(FILE_NAME),
cdn = Cdn.deserialize(jsonObject.getInt(CDN_NUMBER)),
location = jsonObject.getString(REMOTE_LOCATION),
key = jsonObject.getString(REMOTE_KEY),
digest = null,
incrementalDigest = null,
incrementalMacChunkSize = 0,
fastPreflightId = jsonObject.getString(FAST_PREFLIGHT_ID),
voiceNote = jsonObject.getInt(VOICE_NOTE) != 0,
borderless = jsonObject.getInt(BORDERLESS) != 0,
videoGif = jsonObject.getInt(VIDEO_GIF) != 0,
width = jsonObject.getInt(WIDTH),
height = jsonObject.getInt(HEIGHT),
quote = jsonObject.getInt(QUOTE) != 0,
quoteTargetContentType = if (!jsonObject.isNull(QUOTE_TARGET_CONTENT_TYPE)) jsonObject.getString(QUOTE_TARGET_CONTENT_TYPE) else null,
caption = jsonObject.getString(CAPTION),
stickerLocator = if (jsonObject.getInt(STICKER_ID) >= 0) {
StickerLocator(
jsonObject.getString(STICKER_PACK_ID)!!,
jsonObject.getString(STICKER_PACK_KEY)!!,
jsonObject.getInt(STICKER_ID),
jsonObject.getString(STICKER_EMOJI)
)
} else {
null
},
blurHash = if (MediaUtil.isAudioType(contentType)) null else BlurHash.parseOrNull(jsonObject.getString(BLUR_HASH)),
audioHash = if (MediaUtil.isAudioType(contentType)) AudioHash.parseOrNull(jsonObject.getString(BLUR_HASH)) else null,
transformProperties = parseTransformProperties(jsonObject.getString(TRANSFORM_PROPERTIES)),
displayOrder = jsonObject.getInt(DISPLAY_ORDER),
uploadTimestamp = jsonObject.getLong(UPLOAD_TIMESTAMP),
dataHash = jsonObject.getString(DATA_HASH_END),
archiveCdn = if (jsonObject.isNull(ARCHIVE_CDN)) null else jsonObject.getInt(ARCHIVE_CDN),
thumbnailRestoreState = ThumbnailRestoreState.deserialize(jsonObject.getInt(THUMBNAIL_RESTORE_STATE)),
archiveTransferState = ArchiveTransferState.deserialize(jsonObject.getInt(ARCHIVE_TRANSFER_STATE)),
uuid = UuidUtil.parseOrNull(jsonObject.getString(ATTACHMENT_UUID)),
metadata = null
)
}
}
result
} else {
listOf(getAttachment(cursor))
}
} catch (e: JSONException) {
throw AssertionError(e)
}
}
fun hasStickerAttachments(): Boolean {
return readableDatabase
.exists(TABLE_NAME)
@@ -3395,7 +3314,7 @@ class AttachmentTable(
"""
}
private fun getAttachment(cursor: Cursor): DatabaseAttachment {
internal fun getAttachment(cursor: Cursor): DatabaseAttachment {
val contentType = cursor.requireString(CONTENT_TYPE)
return DatabaseAttachment(
@@ -3438,7 +3357,7 @@ class AttachmentTable(
}
private fun Cursor.readAttachments(): List<DatabaseAttachment> {
return getAttachments(this)
return listOf(getAttachment(this))
}
private fun Cursor.readAttachment(): DatabaseAttachment {

View File

@@ -245,10 +245,8 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD
companion object {
@JvmStatic
fun from(cursor: Cursor): MediaRecord {
val attachments = SignalDatabase.attachments.getAttachments(cursor)
return MediaRecord(
attachment = if (attachments.isNotEmpty()) attachments[0] else null,
attachment = SignalDatabase.attachments.getAttachment(cursor),
recipientId = RecipientId.from(cursor.requireLong(MessageTable.FROM_RECIPIENT_ID)),
threadId = cursor.requireLong(MessageTable.THREAD_ID),
threadRecipientId = RecipientId.from(cursor.requireLong(THREAD_RECIPIENT_ID)),

View File

@@ -393,48 +393,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
DELETED_BY
)
private val MMS_PROJECTION: Array<String> = MMS_PROJECTION_BASE + "NULL AS ${AttachmentTable.ATTACHMENT_JSON_ALIAS}"
private val MMS_PROJECTION_WITH_ATTACHMENTS: Array<String> = MMS_PROJECTION_BASE +
"""
json_group_array(
json_object(
'${AttachmentTable.ID}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ID},
'${AttachmentTable.MESSAGE_ID}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MESSAGE_ID},
'${AttachmentTable.DATA_SIZE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DATA_SIZE},
'${AttachmentTable.FILE_NAME}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.FILE_NAME},
'${AttachmentTable.DATA_FILE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DATA_FILE},
'${AttachmentTable.THUMBNAIL_FILE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.THUMBNAIL_FILE},
'${AttachmentTable.CONTENT_TYPE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.CONTENT_TYPE},
'${AttachmentTable.CDN_NUMBER}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.CDN_NUMBER},
'${AttachmentTable.REMOTE_LOCATION}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.REMOTE_LOCATION},
'${AttachmentTable.FAST_PREFLIGHT_ID}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.FAST_PREFLIGHT_ID},
'${AttachmentTable.VOICE_NOTE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.VOICE_NOTE},
'${AttachmentTable.BORDERLESS}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.BORDERLESS},
'${AttachmentTable.VIDEO_GIF}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.VIDEO_GIF},
'${AttachmentTable.WIDTH}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.WIDTH},
'${AttachmentTable.HEIGHT}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.HEIGHT},
'${AttachmentTable.QUOTE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.QUOTE},
'${AttachmentTable.REMOTE_KEY}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.REMOTE_KEY},
'${AttachmentTable.TRANSFER_STATE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.TRANSFER_STATE},
'${AttachmentTable.CAPTION}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.CAPTION},
'${AttachmentTable.STICKER_PACK_ID}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.STICKER_PACK_ID},
'${AttachmentTable.STICKER_PACK_KEY}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.STICKER_PACK_KEY},
'${AttachmentTable.STICKER_ID}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.STICKER_ID},
'${AttachmentTable.STICKER_EMOJI}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.STICKER_EMOJI},
'${AttachmentTable.BLUR_HASH}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.BLUR_HASH},
'${AttachmentTable.TRANSFORM_PROPERTIES}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.TRANSFORM_PROPERTIES},
'${AttachmentTable.DISPLAY_ORDER}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DISPLAY_ORDER},
'${AttachmentTable.UPLOAD_TIMESTAMP}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.UPLOAD_TIMESTAMP},
'${AttachmentTable.DATA_HASH_END}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DATA_HASH_END},
'${AttachmentTable.ARCHIVE_CDN}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ARCHIVE_CDN},
'${AttachmentTable.THUMBNAIL_RESTORE_STATE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.THUMBNAIL_RESTORE_STATE},
'${AttachmentTable.ARCHIVE_TRANSFER_STATE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ARCHIVE_TRANSFER_STATE},
'${AttachmentTable.ATTACHMENT_UUID}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ATTACHMENT_UUID},
'${AttachmentTable.QUOTE_TARGET_CONTENT_TYPE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.QUOTE_TARGET_CONTENT_TYPE}
)
) AS ${AttachmentTable.ATTACHMENT_JSON_ALIAS}
""".toSingleLine()
private val MMS_PROJECTION: Array<String> = MMS_PROJECTION_BASE
private const val IS_STORY_CLAUSE = "$STORY_TYPE > 0 AND $DELETED_BY IS NULL AND $STORY_ARCHIVED = 0"
private const val IS_ARCHIVED_STORY_CLAUSE = "$STORY_TYPE > 0 AND $DELETED_BY IS NULL AND $STORY_ARCHIVED > 0"
@@ -534,6 +493,11 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
return MmsReader(cursor)
}
@JvmStatic
fun withAttachmentData(record: MessageRecord): MessageRecord {
return record.withAttachments()
}
private fun getSharedContacts(cursor: Cursor, attachments: List<DatabaseAttachment>): List<Contact> {
val serializedContacts: String? = cursor.requireString(SHARED_CONTACTS)
@@ -666,7 +630,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
fun getExpirationStartedMessages(): Cursor {
val where = "$EXPIRE_STARTED > 0"
return rawQueryWithAttachments(where, null)
return queryMessages(where, null)
}
/**
@@ -1418,12 +1382,12 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
whereArgs = buildArgs(threadId)
}
return MmsReader(rawQueryWithAttachments(where, whereArgs))
return MmsReader(queryMessages(where, whereArgs))
}
fun getAllOutgoingStories(reverse: Boolean, limit: Int): Reader {
val where = "$IS_STORY_CLAUSE AND ($outgoingTypeClause)"
return MmsReader(rawQueryWithAttachments(where, null, reverse, limit.toLong()))
return MmsReader(queryMessages(where, null, reverse, limit.toLong()))
}
fun markAllIncomingStoriesRead(): List<MarkedMessageInfo> {
@@ -1465,7 +1429,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val threadId = threads.getThreadIdIfExistsFor(recipientId)
val where = "$IS_STORY_CLAUSE AND $THREAD_ID = ?"
val whereArgs = buildArgs(threadId)
val cursor = rawQueryWithAttachments(where, whereArgs, false, limit.toLong())
val cursor = queryMessages(where, whereArgs, false, limit.toLong())
return MmsReader(cursor)
}
@@ -1473,7 +1437,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val threadId = threads.getThreadIdIfExistsFor(recipientId)
val query = "$IS_STORY_CLAUSE AND NOT ($outgoingTypeClause) AND $THREAD_ID = ? AND $VIEWED_COLUMN = ?"
val args = buildArgs(threadId, 0)
return MmsReader(rawQueryWithAttachments(query, args, false, limit.toLong()))
return MmsReader(queryMessages(query, args, false, limit.toLong()))
}
fun getUnreadMissedCallCount(): Long {
@@ -1636,7 +1600,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
fun getStoryReplies(parentStoryId: Long): Cursor {
val where = "$PARENT_STORY_ID = ?"
val whereArgs = buildArgs(parentStoryId)
return rawQueryWithAttachments(where, whereArgs, false, 0)
return queryMessages(where, whereArgs, false, 0)
}
fun getNumberOfStoryReplies(parentStoryId: Long): Int {
@@ -1785,7 +1749,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val storyClause = if (includeActive) "$STORY_TYPE > 0 AND $DELETED_BY IS NULL" else IS_ARCHIVED_STORY_CLAUSE
val where = "$storyClause AND ($outgoingTypeClause)"
val order = if (sortNewest) "$TABLE_NAME.$DATE_SENT DESC" else "$TABLE_NAME.$DATE_SENT ASC"
return MmsReader(rawQueryWithAttachments(where, null, orderBy = order, limit = limit.toLong(), offset = offset.toLong()))
return MmsReader(queryMessages(where, null, orderBy = order, limit = limit.toLong(), offset = offset.toLong()))
}
fun getOldestArchivedStorySentTimestamp(): Long? {
@@ -2121,17 +2085,15 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
/**
* Note: [reverse] and [orderBy] are mutually exclusive. If you want the order to be reversed, explicitly use 'ASC' or 'DESC'
*/
private fun rawQueryWithAttachments(where: String, arguments: Array<String>?, reverse: Boolean = false, limit: Long = 0, offset: Long = 0, orderBy: String = ""): Cursor {
private fun queryMessages(where: String, arguments: Array<String>?, reverse: Boolean = false, limit: Long = 0, offset: Long = 0, orderBy: String = ""): Cursor {
val database = databaseHelper.signalReadableDatabase
var rawQueryString = """
SELECT
${Util.join(MMS_PROJECTION_WITH_ATTACHMENTS, ",")}
FROM
$TABLE_NAME LEFT OUTER JOIN ${AttachmentTable.TABLE_NAME} ON ($TABLE_NAME.$ID = ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MESSAGE_ID})
WHERE
$where
GROUP BY
$TABLE_NAME.$ID
SELECT
${Util.join(MMS_PROJECTION, ",")}
FROM
$TABLE_NAME
WHERE
$where
""".toSingleLine()
if (orderBy.isNotEmpty()) {
@@ -2151,24 +2113,24 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
private fun internalGetMessage(messageId: Long): Cursor {
return rawQueryWithAttachments(RAW_ID_WHERE, buildArgs(messageId))
return queryMessages(RAW_ID_WHERE, buildArgs(messageId))
}
@Throws(NoSuchMessageException::class)
fun getMessageRecord(messageId: Long): MessageRecord {
rawQueryWithAttachments(RAW_ID_WHERE, arrayOf(messageId.toString() + "")).use { cursor ->
queryMessages(RAW_ID_WHERE, arrayOf(messageId.toString() + "")).use { cursor ->
return MmsReader(cursor).getNext() ?: throw NoSuchMessageException("No message for ID: $messageId")
}
}
fun getMessageRecordOrNull(messageId: Long): MessageRecord? {
rawQueryWithAttachments(RAW_ID_WHERE, buildArgs(messageId)).use { cursor ->
queryMessages(RAW_ID_WHERE, buildArgs(messageId)).use { cursor ->
return MmsReader(cursor).firstOrNull()
}
}
fun getPinnedMessages(threadId: Long, orderByPinned: Boolean): List<MmsMessageRecord> {
val cursor = rawQueryWithAttachments(
val cursor = queryMessages(
where = "$THREAD_ID = ? AND $PINNED_UNTIL > 0",
arguments = buildArgs(threadId),
reverse = true,
@@ -2213,7 +2175,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
fun getMessages(messageIds: Collection<Long?>): MmsReader {
val ids = TextUtils.join(",", messageIds)
return mmsReaderFor(rawQueryWithAttachments("$TABLE_NAME.$ID IN ($ids)", null))
return mmsReaderFor(queryMessages("$TABLE_NAME.$ID IN ($ids)", null))
}
fun getMessageEditHistory(id: Long): MmsReader {
@@ -2672,7 +2634,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
@Throws(MmsException::class, NoSuchMessageException::class)
fun getOutgoingMessage(messageId: Long): OutgoingMessage {
return rawQueryWithAttachments(RAW_ID_WHERE, arrayOf(messageId.toString())).readToSingleObject { cursor ->
return queryMessages(RAW_ID_WHERE, arrayOf(messageId.toString())).readToSingleObject { cursor ->
val associatedAttachments = attachments.getAttachmentsForMessage(messageId)
val associatedPoll = polls.getPollForOutgoingMessage(messageId)
val mentions = mentions.getMentionsForMessage(messageId)
@@ -4236,7 +4198,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val where = "$TABLE_NAME.$THREAD_ID = ? AND $TABLE_NAME.$DATE_RECEIVED >= ? AND $TABLE_NAME.$SCHEDULED_DATE = -1 AND $TABLE_NAME.$LATEST_REVISION_ID IS NULL"
val args = buildArgs(threadId, timestamp)
return mmsReaderFor(rawQueryWithAttachments(where, args, false, limit)).use { reader ->
return mmsReaderFor(queryMessages(where, args, false, limit)).use { reader ->
reader.filterNotNull()
}
}
@@ -4896,7 +4858,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
return getConversationSnippetCursor(threadId)
.readToSingleObject { cursor ->
val id = cursor.requireLong(ID)
messages.getMessageRecord(id)
messages.getMessageRecord(id).withAttachments()
} ?: throw NoSuchMessageException("no message")
}
@@ -6455,7 +6417,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val mismatches = getMismatchedIdentities(mismatchDocument)
val networkFailures = getFailures(networkDocument)
val attachments = attachments.getAttachments(cursor)
val attachments: List<DatabaseAttachment> = emptyList()
val contacts = getSharedContacts(cursor, attachments)
val contactAttachments = contacts.mapNotNull { it.avatarAttachment }.toSet()
@@ -6465,7 +6427,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
val slideDeck = buildSlideDeck(attachments.filterNot { contactAttachments.contains(it) }.filterNot { previewAttachments.contains(it) })
val quote = getQuote(cursor)
val quote = getQuote(cursor, attachments)
val messageRanges: BodyRangeList? = if (messageRangesData != null) {
try {
@@ -6563,7 +6525,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
return emptySet()
}
private fun getQuote(cursor: Cursor): Quote? {
private fun getQuote(cursor: Cursor, attachments: List<DatabaseAttachment>): Quote? {
val quoteId = cursor.requireLong(QUOTE_ID)
val quoteAuthor = cursor.requireLong(QUOTE_AUTHOR)
var quoteText: CharSequence? = cursor.requireString(QUOTE_BODY)
@@ -6572,7 +6534,6 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
var quoteMentions = parseQuoteMentions(cursor)
val bodyRanges = parseQuoteBodyRanges(cursor)
val attachments = attachments.getAttachments(cursor)
val quoteAttachments: List<Attachment> = attachments.filter { it.quote }
val quoteDeck = SlideDeck(quoteAttachments)
@@ -6632,3 +6593,21 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
}
}
fun MessageRecord.withAttachments(): MessageRecord {
if (this !is MmsMessageRecord) return this
val fetchedAttachments = SignalDatabase.attachments.getAttachmentsForMessage(id)
return if (fetchedAttachments.isNotEmpty()) withAttachments(fetchedAttachments) else this
}
fun List<MessageRecord>.withAttachments(): List<MessageRecord> {
if (isEmpty()) return this
val allAttachments = SignalDatabase.attachments.getAttachmentsForMessages(map { it.id })
return map { record ->
if (record is MmsMessageRecord) {
allAttachments[record.id]?.let { record.withAttachments(it) } ?: record
} else {
record
}
}
}

View File

@@ -8,7 +8,6 @@ package org.thoughtcrime.securesms.jobs
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.MessageId
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.jobmanager.Job
import kotlin.time.Duration.Companion.days
@@ -52,7 +51,7 @@ class RetryPendingSendsJob private constructor(parameters: Parameters) : Job(par
reader.forEach { message ->
val threadRecipient = SignalDatabase.threads.getRecipientForThreadId(message.threadId)
if (threadRecipient != null) {
val hasMedia = (message as? MmsMessageRecord)?.slideDeck?.slides?.isNotEmpty() == true
val hasMedia = SignalDatabase.attachments.getAttachmentsForMessage(message.id).isNotEmpty()
Log.d(TAG, "[${message.dateSent}] Found pending message MessageId::${message.id}, enqueueing second check job")
AppDependencies.jobManager.add(RetryPendingSendSecondCheckJob(MessageId(message.id), threadRecipient, hasMedia))
}

View File

@@ -11,6 +11,7 @@ import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.conversation.ConversationMessage;
import org.thoughtcrime.securesms.database.MessageTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import java.util.Optional;
@@ -45,7 +46,11 @@ class LongMessageRepository {
@WorkerThread
private Optional<MmsMessageRecord> getMmsMessage(@NonNull MessageTable mmsDatabase, long messageId) {
try (Cursor cursor = mmsDatabase.getMessageCursor(messageId)) {
return Optional.ofNullable((MmsMessageRecord) MessageTable.mmsReaderFor(cursor).getNext());
MessageRecord record = MessageTable.mmsReaderFor(cursor).getNext();
if (record != null) {
record = MessageTable.withAttachmentData(record);
}
return Optional.ofNullable((MmsMessageRecord) record);
}
}

View File

@@ -21,6 +21,7 @@ import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.SignalDatabase.Companion.media
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.database.withAttachments
import org.thoughtcrime.securesms.jobs.MultiDeviceDeleteSyncJob
import org.thoughtcrime.securesms.longmessage.resolveBody
import org.thoughtcrime.securesms.recipients.RecipientId
@@ -85,7 +86,7 @@ class MediaPreviewRepository {
}
val messageIds = mediaRecords.mapNotNull { it.attachment?.mmsId }.toSet()
val messages: Map<Long, SpannableString> = SignalDatabase.messages.getMessages(messageIds)
val messages: Map<Long, SpannableString> = SignalDatabase.messages.getMessages(messageIds).toList().withAttachments()
.map { it as MmsMessageRecord }
.associate { it.id to it.resolveBody(context).getDisplayBody(context) }

View File

@@ -11,6 +11,7 @@ import org.thoughtcrime.securesms.database.model.MessageId
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.thoughtcrime.securesms.database.model.toBodyRangeList
import org.thoughtcrime.securesms.database.withAttachments
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob
@@ -69,7 +70,8 @@ object EditMessageProcessor {
val isMediaMessage = message.isMediaMessage
val groupId: GroupId.V2? = message.groupV2?.groupId
val originalMessage = targetMessage.originalMessageId?.let { SignalDatabase.messages.getMessageRecord(it.id) } ?: targetMessage
val originalMessageWithoutAttachments = targetMessage.originalMessageId?.let { SignalDatabase.messages.getMessageRecord(it.id) } ?: targetMessage
val originalMessage = originalMessageWithoutAttachments.withAttachments()
val validTiming = MessageConstraintsUtil.isValidEditMessageReceive(originalMessage, senderRecipient, envelope.serverTimestamp!!)
val validAuthor = senderRecipient.id == originalMessage.fromRecipient.id
val validGroup = groupId == targetThreadRecipient.groupId.orNull()

View File

@@ -4,6 +4,7 @@ import org.signal.paging.PagedDataSource
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.database.model.StoryType
import org.thoughtcrime.securesms.database.withAttachments
import org.thoughtcrime.securesms.keyvalue.SignalStore
class StoryArchivePagedDataSource(
@@ -17,25 +18,29 @@ class StoryArchivePagedDataSource(
}
override fun load(start: Int, length: Int, totalSize: Int, cancellationSignal: PagedDataSource.CancellationSignal): List<ArchivedStoryItem> {
return SignalDatabase.messages.getArchiveScreenStoriesPage(includeActive, sortNewest, start, length).use { reader ->
val rawRecords = SignalDatabase.messages.getArchiveScreenStoriesPage(includeActive, sortNewest, start, length).use { reader ->
reader.mapNotNull { record ->
if (cancellationSignal.isCanceled) return@use emptyList()
val mmsRecord = record as? MmsMessageRecord
ArchivedStoryItem(
messageId = record.id,
dateSent = record.dateSent,
thumbnailUri = mmsRecord?.slideDeck?.thumbnailSlide?.uri,
blurHash = mmsRecord?.slideDeck?.thumbnailSlide?.placeholderBlur,
storyType = mmsRecord?.storyType ?: StoryType.NONE,
body = record.body
)
record
}
}
return rawRecords.withAttachments().map { record ->
val mmsRecord = record as? MmsMessageRecord
ArchivedStoryItem(
messageId = record.id,
dateSent = record.dateSent,
thumbnailUri = mmsRecord?.slideDeck?.thumbnailSlide?.uri,
blurHash = mmsRecord?.slideDeck?.thumbnailSlide?.placeholderBlur,
storyType = mmsRecord?.storyType ?: StoryType.NONE,
body = record.body
)
}
}
override fun load(key: Long): ArchivedStoryItem? {
val record = SignalDatabase.messages.getMessageRecordOrNull(key) ?: return null
val mmsRecord = record as? MmsMessageRecord
val mmsRecord = record.withAttachments() as? MmsMessageRecord
return ArchivedStoryItem(
messageId = record.id,
dateSent = record.dateSent,

View File

@@ -9,6 +9,7 @@ import org.thoughtcrime.securesms.database.GroupReceiptTable
import org.thoughtcrime.securesms.database.RxDatabaseObserver
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.database.withAttachments
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.sms.MessageSender
@@ -27,14 +28,20 @@ class MyStoriesRepository(context: Context) {
.conversationList
.toObservable()
.map {
val storiesMap = mutableMapOf<Recipient, List<MessageRecord>>()
val allRecords = mutableListOf<MessageRecord>()
SignalDatabase.messages.getAllOutgoingStories(true, -1).use {
for (messageRecord in it) {
val currentList = storiesMap[messageRecord.toRecipient] ?: emptyList()
storiesMap[messageRecord.toRecipient] = (currentList + messageRecord)
allRecords.add(messageRecord)
}
}
val withAttachments = allRecords.withAttachments()
val storiesMap = mutableMapOf<Recipient, List<MessageRecord>>()
for (record in withAttachments) {
val currentList = storiesMap[record.toRecipient] ?: emptyList()
storiesMap[record.toRecipient] = (currentList + record)
}
storiesMap.toSortedMap(MyStoryBiasComparator()).map { (r, m) -> createDistributionSet(r, m) }
}
}

View File

@@ -6,6 +6,7 @@ import org.thoughtcrime.securesms.database.MessageTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.database.withAttachments
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
@@ -17,7 +18,7 @@ open class StoryViewerRepository {
fun getFirstStory(recipientId: RecipientId, storyId: Long): Single<MmsMessageRecord> {
return if (storyId > 0) {
Single.fromCallable {
SignalDatabase.messages.getMessageRecord(storyId) as MmsMessageRecord
SignalDatabase.messages.getMessageRecord(storyId).withAttachments() as MmsMessageRecord
}
} else {
Single.fromCallable {
@@ -32,7 +33,7 @@ open class StoryViewerRepository {
SignalDatabase.messages.getAllStoriesFor(recipientId, 1)
}
}
reader.use { it.iterator().next() } as MmsMessageRecord
reader.use { it.iterator().next() }.withAttachments() as MmsMessageRecord
}
}
}

View File

@@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.database.model.MessageId
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.database.model.databaseprotos.StoryTextPost
import org.thoughtcrime.securesms.database.withAttachments
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.jobs.MultiDeviceViewedUpdateJob
import org.thoughtcrime.securesms.jobs.SendViewedReceiptJob
@@ -58,7 +59,7 @@ open class StoryViewerPageRepository(context: Context, private val storyViewStat
recipient.isMyStory && it.toRecipient.isGroup
}
emitter.onNext(results)
emitter.onNext(results.withAttachments())
}
val storyObserver = DatabaseObserver.Observer {

View File

@@ -7,6 +7,7 @@ import org.thoughtcrime.securesms.database.MessageTypes
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.MessageId
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.database.withAttachments
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.recipients.Recipient
@@ -16,20 +17,20 @@ class StoryGroupReplyDataSource(private val parentStoryId: Long) : PagedDataSour
}
override fun load(start: Int, length: Int, totalSize: Int, cancellationSignal: PagedDataSource.CancellationSignal): MutableList<ReplyBody> {
val results: MutableList<ReplyBody> = ArrayList(length)
val rawRecords = mutableListOf<MmsMessageRecord>()
SignalDatabase.messages.getStoryReplies(parentStoryId).use { cursor ->
cursor.moveToPosition(start - 1)
val mmsReader = MessageTable.MmsReader(cursor)
while (cursor.moveToNext() && cursor.position < start + length) {
results.add(readRowFromRecord(mmsReader.getCurrent() as MmsMessageRecord))
rawRecords.add(mmsReader.getCurrent() as MmsMessageRecord)
}
}
return results
return rawRecords.withAttachments().map { readRowFromRecord(it as MmsMessageRecord) }.toMutableList()
}
override fun load(key: MessageId): ReplyBody {
return readRowFromRecord(SignalDatabase.messages.getMessageRecord(key.id) as MmsMessageRecord)
return readRowFromRecord(SignalDatabase.messages.getMessageRecord(key.id).withAttachments() as MmsMessageRecord)
}
override fun getKey(data: ReplyBody): MessageId {