|
|
|
@ -65,6 +65,7 @@ import org.thoughtcrime.securesms.database.model.Quote
|
|
|
|
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent.Companion.get
|
|
|
|
|
import org.thoughtcrime.securesms.mms.MmsException
|
|
|
|
|
import org.thoughtcrime.securesms.mms.SlideDeck
|
|
|
|
|
import org.thoughtcrime.securesms.util.asSequence
|
|
|
|
|
import java.io.Closeable
|
|
|
|
|
import java.io.IOException
|
|
|
|
|
import java.security.SecureRandom
|
|
|
|
@ -91,54 +92,22 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun addFailures(messageId: Long, failure: List<NetworkFailure>) {
|
|
|
|
|
try {
|
|
|
|
|
addToDocument(messageId, NETWORK_FAILURE, failure, NetworkFailureList::class.java)
|
|
|
|
|
} catch (e: IOException) {
|
|
|
|
|
Log.w(TAG, e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun removeFailure(messageId: Long, failure: NetworkFailure?) {
|
|
|
|
|
try {
|
|
|
|
|
removeFromDocument(messageId, NETWORK_FAILURE, failure, NetworkFailureList::class.java)
|
|
|
|
|
} catch (e: IOException) {
|
|
|
|
|
Log.w(TAG, e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun isOutgoingMessage(timestamp: Long): Boolean {
|
|
|
|
|
val database = databaseHelper.writableDatabase
|
|
|
|
|
var cursor: Cursor? = null
|
|
|
|
|
var isOutgoing = false
|
|
|
|
|
try {
|
|
|
|
|
cursor = database.query(
|
|
|
|
|
TABLE_NAME,
|
|
|
|
|
arrayOf<String>(ID, THREAD_ID, MESSAGE_BOX, ADDRESS),
|
|
|
|
|
DATE_SENT + " = ?",
|
|
|
|
|
arrayOf(timestamp.toString()),
|
|
|
|
|
null,
|
|
|
|
|
null,
|
|
|
|
|
null,
|
|
|
|
|
null
|
|
|
|
|
)
|
|
|
|
|
while (cursor.moveToNext()) {
|
|
|
|
|
if (MmsSmsColumns.Types.isOutgoingMessageType(
|
|
|
|
|
cursor.getLong(
|
|
|
|
|
cursor.getColumnIndexOrThrow(
|
|
|
|
|
MESSAGE_BOX
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
) {
|
|
|
|
|
isOutgoing = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
cursor?.close()
|
|
|
|
|
fun isOutgoingMessage(timestamp: Long): Boolean =
|
|
|
|
|
databaseHelper.writableDatabase.query(
|
|
|
|
|
TABLE_NAME,
|
|
|
|
|
arrayOf(ID, THREAD_ID, MESSAGE_BOX, ADDRESS),
|
|
|
|
|
DATE_SENT + " = ?",
|
|
|
|
|
arrayOf(timestamp.toString()),
|
|
|
|
|
null,
|
|
|
|
|
null,
|
|
|
|
|
null,
|
|
|
|
|
null
|
|
|
|
|
).use { cursor ->
|
|
|
|
|
cursor.asSequence()
|
|
|
|
|
.map { cursor.getColumnIndexOrThrow(MESSAGE_BOX) }
|
|
|
|
|
.map(cursor::getLong)
|
|
|
|
|
.any { MmsSmsColumns.Types.isOutgoingMessageType(it) }
|
|
|
|
|
}
|
|
|
|
|
return isOutgoing
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun incrementReceiptCount(
|
|
|
|
|
messageId: SyncMessageId,
|
|
|
|
@ -254,15 +223,6 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun getThreadIdFor(notification: NotificationInd): Long {
|
|
|
|
|
val fromString =
|
|
|
|
|
if (notification.from != null && notification.from.textString != null) toIsoString(
|
|
|
|
|
notification.from.textString
|
|
|
|
|
) else ""
|
|
|
|
|
val recipient = Recipient.from(context, fromExternal(context, fromString), false)
|
|
|
|
|
return get(context).threadDatabase().getOrCreateThreadIdFor(recipient)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun rawQuery(where: String, arguments: Array<String>?): Cursor {
|
|
|
|
|
val database = databaseHelper.readableDatabase
|
|
|
|
|
return database.rawQuery(
|
|
|
|
@ -273,10 +233,6 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun getMessages(idsAsString: String): Cursor {
|
|
|
|
|
return rawQuery(idsAsString, null)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun getMessage(messageId: Long): Cursor {
|
|
|
|
|
val cursor = rawQuery(RAW_ID_WHERE, arrayOf(messageId.toString()))
|
|
|
|
|
setNotifyConverationListeners(cursor, getThreadIdForMessage(messageId))
|
|
|
|
@ -306,48 +262,30 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun markAsPendingInsecureSmsFallback(messageId: Long) {
|
|
|
|
|
val threadId = getThreadIdForMessage(messageId)
|
|
|
|
|
private fun markAs(
|
|
|
|
|
messageId: Long,
|
|
|
|
|
baseType: Long,
|
|
|
|
|
threadId: Long = getThreadIdForMessage(messageId)
|
|
|
|
|
) {
|
|
|
|
|
updateMailboxBitmask(
|
|
|
|
|
messageId,
|
|
|
|
|
MmsSmsColumns.Types.BASE_TYPE_MASK,
|
|
|
|
|
MmsSmsColumns.Types.BASE_PENDING_INSECURE_SMS_FALLBACK,
|
|
|
|
|
baseType,
|
|
|
|
|
Optional.of(threadId)
|
|
|
|
|
)
|
|
|
|
|
notifyConversationListeners(threadId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun markAsSending(messageId: Long) {
|
|
|
|
|
val threadId = getThreadIdForMessage(messageId)
|
|
|
|
|
updateMailboxBitmask(
|
|
|
|
|
messageId,
|
|
|
|
|
MmsSmsColumns.Types.BASE_TYPE_MASK,
|
|
|
|
|
MmsSmsColumns.Types.BASE_SENDING_TYPE,
|
|
|
|
|
Optional.of(threadId)
|
|
|
|
|
)
|
|
|
|
|
notifyConversationListeners(threadId)
|
|
|
|
|
markAs(messageId, MmsSmsColumns.Types.BASE_SENDING_TYPE)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun markAsSentFailed(messageId: Long) {
|
|
|
|
|
val threadId = getThreadIdForMessage(messageId)
|
|
|
|
|
updateMailboxBitmask(
|
|
|
|
|
messageId,
|
|
|
|
|
MmsSmsColumns.Types.BASE_TYPE_MASK,
|
|
|
|
|
MmsSmsColumns.Types.BASE_SENT_FAILED_TYPE,
|
|
|
|
|
Optional.of(threadId)
|
|
|
|
|
)
|
|
|
|
|
notifyConversationListeners(threadId)
|
|
|
|
|
markAs(messageId, MmsSmsColumns.Types.BASE_SENT_FAILED_TYPE)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun markAsSent(messageId: Long, secure: Boolean) {
|
|
|
|
|
val threadId = getThreadIdForMessage(messageId)
|
|
|
|
|
updateMailboxBitmask(
|
|
|
|
|
messageId,
|
|
|
|
|
MmsSmsColumns.Types.BASE_TYPE_MASK,
|
|
|
|
|
MmsSmsColumns.Types.BASE_SENT_TYPE or if (secure) MmsSmsColumns.Types.PUSH_MESSAGE_BIT or MmsSmsColumns.Types.SECURE_MESSAGE_BIT else 0,
|
|
|
|
|
Optional.of(threadId)
|
|
|
|
|
)
|
|
|
|
|
notifyConversationListeners(threadId)
|
|
|
|
|
markAs(messageId, MmsSmsColumns.Types.BASE_SENT_TYPE or if (secure) MmsSmsColumns.Types.PUSH_MESSAGE_BIT or MmsSmsColumns.Types.SECURE_MESSAGE_BIT else 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun markUnidentified(messageId: Long, unidentified: Boolean) {
|
|
|
|
@ -371,13 +309,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
|
|
|
|
val mentionChange = if (hasMention) { 1 } else { 0 }
|
|
|
|
|
get(context).threadDatabase().decrementUnread(threadId, 1, mentionChange)
|
|
|
|
|
}
|
|
|
|
|
updateMailboxBitmask(
|
|
|
|
|
messageId,
|
|
|
|
|
MmsSmsColumns.Types.BASE_TYPE_MASK,
|
|
|
|
|
MmsSmsColumns.Types.BASE_DELETED_TYPE,
|
|
|
|
|
Optional.of(threadId)
|
|
|
|
|
)
|
|
|
|
|
notifyConversationListeners(threadId)
|
|
|
|
|
markAs(messageId, MmsSmsColumns.Types.BASE_DELETED_TYPE, threadId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun markExpireStarted(messageId: Long) {
|
|
|
|
@ -407,10 +339,6 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun setAllMessagesRead(): List<MarkedMessageInfo> {
|
|
|
|
|
return setMessagesRead(READ + " = 0", null)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun setMessagesRead(where: String, arguments: Array<String>?): List<MarkedMessageInfo> {
|
|
|
|
|
val database = databaseHelper.writableDatabase
|
|
|
|
|
val result: MutableList<MarkedMessageInfo> = LinkedList()
|
|
|
|
@ -419,7 +347,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
|
|
|
|
try {
|
|
|
|
|
cursor = database.query(
|
|
|
|
|
TABLE_NAME,
|
|
|
|
|
arrayOf<String>(ID, ADDRESS, DATE_SENT, MESSAGE_BOX, EXPIRES_IN, EXPIRE_STARTED),
|
|
|
|
|
arrayOf(ID, ADDRESS, DATE_SENT, MESSAGE_BOX, EXPIRES_IN, EXPIRE_STARTED),
|
|
|
|
|
where,
|
|
|
|
|
arguments,
|
|
|
|
|
null,
|
|
|
|
@ -1400,25 +1328,16 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
|
|
|
|
val attachments = get(context).attachmentDatabase().getAttachment(
|
|
|
|
|
cursor
|
|
|
|
|
)
|
|
|
|
|
val contacts: List<Contact?> = getSharedContacts(
|
|
|
|
|
cursor, attachments
|
|
|
|
|
)
|
|
|
|
|
val contactAttachments =
|
|
|
|
|
contacts.map { obj: Contact? -> obj!!.avatarAttachment }
|
|
|
|
|
.filter { a: Attachment? -> a != null }
|
|
|
|
|
.toSet()
|
|
|
|
|
val previews: List<LinkPreview?> = getLinkPreviews(
|
|
|
|
|
cursor, attachments
|
|
|
|
|
)
|
|
|
|
|
val previewAttachments =
|
|
|
|
|
previews.filter { lp: LinkPreview? -> lp!!.getThumbnail().isPresent }
|
|
|
|
|
.map { lp: LinkPreview? -> lp!!.getThumbnail().get() }
|
|
|
|
|
.toSet()
|
|
|
|
|
val contacts: List<Contact?> = getSharedContacts(cursor, attachments)
|
|
|
|
|
val contactAttachments: Set<Attachment?> =
|
|
|
|
|
contacts.mapNotNull { it?.avatarAttachment }.toSet()
|
|
|
|
|
val previews: List<LinkPreview?> = getLinkPreviews(cursor, attachments)
|
|
|
|
|
val previewAttachments: Set<Attachment?> =
|
|
|
|
|
previews.mapNotNull { it?.getThumbnail()?.orNull() }.toSet()
|
|
|
|
|
val slideDeck = getSlideDeck(
|
|
|
|
|
Stream.of(attachments)
|
|
|
|
|
.filterNot { o: DatabaseAttachment? -> contactAttachments.contains(o) }
|
|
|
|
|
.filterNot { o: DatabaseAttachment? -> previewAttachments.contains(o) }
|
|
|
|
|
.toList()
|
|
|
|
|
attachments
|
|
|
|
|
.filterNot { o: DatabaseAttachment? -> o in contactAttachments }
|
|
|
|
|
.filterNot { o: DatabaseAttachment? -> o in previewAttachments }
|
|
|
|
|
)
|
|
|
|
|
val quote = getQuote(cursor)
|
|
|
|
|
val reactions = get(context).reactionDatabase().getReactions(cursor)
|
|
|
|
@ -1623,4 +1542,4 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
|
|
|
|
const val CREATE_REACTIONS_LAST_SEEN_COMMAND = "ALTER TABLE $TABLE_NAME ADD COLUMN $REACTIONS_LAST_SEEN INTEGER DEFAULT 0;"
|
|
|
|
|
const val CREATE_HAS_MENTION_COMMAND = "ALTER TABLE $TABLE_NAME ADD COLUMN $HAS_MENTION INTEGER DEFAULT 0;"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|