From 903babe917e57b58c55c8e5753f4a89c4239c44d Mon Sep 17 00:00:00 2001 From: cketti Date: Sat, 17 Apr 2021 22:54:49 +0200 Subject: [PATCH 1/2] Save drafts using MessageStore --- .../com/fsck/k9/controller/DraftOperations.kt | 81 +++++----- .../java/com/fsck/k9/controller/KoinModule.kt | 2 + .../k9/controller/MessagingController.java | 7 +- .../com/fsck/k9/mailstore/MessageStore.kt | 8 + .../k9/mailstore/SaveMessageDataCreator.kt | 7 +- .../controller/MessagingControllerTest.java | 5 +- .../k9/storage/messages/K9MessageStore.kt | 4 + .../storage/messages/SaveMessageOperations.kt | 111 +++++++++++--- .../messages/SaveMessageOperationsTest.kt | 138 ++++++++++++++++++ 9 files changed, 302 insertions(+), 61 deletions(-) diff --git a/app/core/src/main/java/com/fsck/k9/controller/DraftOperations.kt b/app/core/src/main/java/com/fsck/k9/controller/DraftOperations.kt index dd93183bf..8bd9c4b67 100644 --- a/app/core/src/main/java/com/fsck/k9/controller/DraftOperations.kt +++ b/app/core/src/main/java/com/fsck/k9/controller/DraftOperations.kt @@ -6,14 +6,21 @@ import com.fsck.k9.backend.api.Backend import com.fsck.k9.controller.MessagingControllerCommands.PendingAppend import com.fsck.k9.controller.MessagingControllerCommands.PendingReplace import com.fsck.k9.mail.FetchProfile -import com.fsck.k9.mail.Flag import com.fsck.k9.mail.Message import com.fsck.k9.mail.MessagingException import com.fsck.k9.mailstore.LocalFolder import com.fsck.k9.mailstore.LocalMessage +import com.fsck.k9.mailstore.MessageStoreManager +import com.fsck.k9.mailstore.SaveMessageData +import com.fsck.k9.mailstore.SaveMessageDataCreator +import org.jetbrains.annotations.NotNull import timber.log.Timber -internal class DraftOperations(private val messagingController: MessagingController) { +internal class DraftOperations( + private val messagingController: @NotNull MessagingController, + private val messageStoreManager: @NotNull MessageStoreManager, + private val saveMessageDataCreator: SaveMessageDataCreator +) { fun saveDraft( account: Account, @@ -24,22 +31,13 @@ internal class DraftOperations(private val messagingController: MessagingControl return try { val draftsFolderId = account.draftsFolderId ?: error("No Drafts folder configured") - val localStore = messagingController.getLocalStoreOrThrow(account) - val localFolder = localStore.getFolder(draftsFolderId) - localFolder.open() - - val localMessage = if (messagingController.supportsUpload(account)) { - saveAndUploadDraft(account, message, localFolder, existingDraftId) + val messageId = if (messagingController.supportsUpload(account)) { + saveAndUploadDraft(account, message, draftsFolderId, existingDraftId, plaintextSubject) } else { - saveDraftLocally(message, localFolder, existingDraftId) + saveDraftLocally(account, message, draftsFolderId, existingDraftId, plaintextSubject) } - localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true) - if (plaintextSubject != null) { - localMessage.setCachedDecryptedSubject(plaintextSubject) - } - - localMessage.databaseId + messageId } catch (e: MessagingException) { Timber.e(e, "Unable to save message as draft.") null @@ -49,41 +47,52 @@ internal class DraftOperations(private val messagingController: MessagingControl private fun saveAndUploadDraft( account: Account, message: Message, - localFolder: LocalFolder, - existingDraftId: Long? - ): LocalMessage { - localFolder.appendMessages(listOf(message)) + folderId: Long, + existingDraftId: Long?, + subject: String? + ): Long { + val messageStore = messageStoreManager.getMessageStore(account) - val localMessage = localFolder.getMessage(message.uid) - val previousDraftMessage = if (existingDraftId != null) localFolder.getMessage(existingDraftId) else null + val messageId = messageStore.saveLocalMessage(folderId, message.toSaveMessageData(subject)) + + val previousDraftMessage = if (existingDraftId != null) { + val localStore = messagingController.getLocalStoreOrThrow(account) + val localFolder = localStore.getFolder(folderId) + localFolder.open() + + localFolder.getMessage(existingDraftId) + } else { + null + } - val folderId = localFolder.databaseId if (previousDraftMessage != null) { previousDraftMessage.delete() - val uploadMessageId = localMessage.databaseId val deleteMessageId = previousDraftMessage.databaseId - val command = PendingReplace.create(folderId, uploadMessageId, deleteMessageId) + val command = PendingReplace.create(folderId, messageId, deleteMessageId) messagingController.queuePendingCommand(account, command) } else { - val command = PendingAppend.create(folderId, localMessage.uid) + val fakeMessageServerId = messageStore.getMessageServerId(messageId) + val command = PendingAppend.create(folderId, fakeMessageServerId) messagingController.queuePendingCommand(account, command) } messagingController.processPendingCommands(account) - return localMessage + return messageId } - private fun saveDraftLocally(message: Message, localFolder: LocalFolder, existingDraftId: Long?): LocalMessage { - if (existingDraftId != null) { - // Setting the UID will cause LocalFolder.appendMessages() to replace the existing draft. - message.uid = localFolder.getMessageUidById(existingDraftId) - } + private fun saveDraftLocally( + account: Account, + message: Message, + folderId: Long, + existingDraftId: Long?, + plaintextSubject: String? + ): Long { + val messageStore = messageStoreManager.getMessageStore(account) + val messageData = message.toSaveMessageData(plaintextSubject) - localFolder.appendMessages(listOf(message)) - - return localFolder.getMessage(message.uid) + return messageStore.saveLocalMessage(folderId, messageData, existingDraftId) } fun processPendingReplace(command: PendingReplace, account: Account) { @@ -153,4 +162,8 @@ internal class DraftOperations(private val messagingController: MessagingControl messagingController.destroyPlaceholderMessages(localFolder, messageServerIds) } + + private fun Message.toSaveMessageData(subject: String?): SaveMessageData { + return saveMessageDataCreator.createSaveMessageData(this, partialMessage = false, subject) + } } diff --git a/app/core/src/main/java/com/fsck/k9/controller/KoinModule.kt b/app/core/src/main/java/com/fsck/k9/controller/KoinModule.kt index ea0abf4c1..eedc4b217 100644 --- a/app/core/src/main/java/com/fsck/k9/controller/KoinModule.kt +++ b/app/core/src/main/java/com/fsck/k9/controller/KoinModule.kt @@ -5,6 +5,7 @@ import com.fsck.k9.Preferences import com.fsck.k9.backend.BackendManager import com.fsck.k9.mailstore.LocalStoreProvider import com.fsck.k9.mailstore.MessageStoreManager +import com.fsck.k9.mailstore.SaveMessageDataCreator import com.fsck.k9.notification.NotificationController import com.fsck.k9.notification.NotificationStrategy import org.koin.core.qualifier.named @@ -21,6 +22,7 @@ val controllerModule = module { get(), get(), get(), + get(), get(named("controllerExtensions")) ) } diff --git a/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java b/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java index cc154e052..59bced8bf 100644 --- a/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java +++ b/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java @@ -72,6 +72,7 @@ import com.fsck.k9.mailstore.MessageStore; import com.fsck.k9.mailstore.MessageStoreManager; import com.fsck.k9.mailstore.OutboxState; import com.fsck.k9.mailstore.OutboxStateRepository; +import com.fsck.k9.mailstore.SaveMessageDataCreator; import com.fsck.k9.mailstore.SendState; import com.fsck.k9.mailstore.UnavailableStorageException; import com.fsck.k9.notification.NotificationController; @@ -116,6 +117,7 @@ public class MessagingController { private final BackendManager backendManager; private final Preferences preferences; private final MessageStoreManager messageStoreManager; + private final SaveMessageDataCreator saveMessageDataCreator; private final Thread controllerThread; @@ -140,7 +142,7 @@ public class MessagingController { NotificationStrategy notificationStrategy, LocalStoreProvider localStoreProvider, UnreadMessageCountProvider unreadMessageCountProvider, BackendManager backendManager, Preferences preferences, MessageStoreManager messageStoreManager, - List controllerExtensions) { + SaveMessageDataCreator saveMessageDataCreator, List controllerExtensions) { this.context = context; this.notificationController = notificationController; this.notificationStrategy = notificationStrategy; @@ -149,6 +151,7 @@ public class MessagingController { this.backendManager = backendManager; this.preferences = preferences; this.messageStoreManager = messageStoreManager; + this.saveMessageDataCreator = saveMessageDataCreator; controllerThread = new Thread(new Runnable() { @Override @@ -162,7 +165,7 @@ public class MessagingController { initializeControllerExtensions(controllerExtensions); - draftOperations = new DraftOperations(this); + draftOperations = new DraftOperations(this, messageStoreManager, saveMessageDataCreator); } private void initializeControllerExtensions(List controllerExtensions) { diff --git a/app/core/src/main/java/com/fsck/k9/mailstore/MessageStore.kt b/app/core/src/main/java/com/fsck/k9/mailstore/MessageStore.kt index b0b0a219b..f41b897c9 100644 --- a/app/core/src/main/java/com/fsck/k9/mailstore/MessageStore.kt +++ b/app/core/src/main/java/com/fsck/k9/mailstore/MessageStore.kt @@ -19,6 +19,14 @@ interface MessageStore { */ fun saveRemoteMessage(folderId: Long, messageServerId: String, messageData: SaveMessageData) + /** + * Save a local message in this store. + * + * @param existingMessageId The message with this ID is replaced if not `null`. + * @return The message ID of the saved message. + */ + fun saveLocalMessage(folderId: Long, messageData: SaveMessageData, existingMessageId: Long? = null): Long + /** * Move a message to another folder. * diff --git a/app/core/src/main/java/com/fsck/k9/mailstore/SaveMessageDataCreator.kt b/app/core/src/main/java/com/fsck/k9/mailstore/SaveMessageDataCreator.kt index 7735807f2..36f160c73 100644 --- a/app/core/src/main/java/com/fsck/k9/mailstore/SaveMessageDataCreator.kt +++ b/app/core/src/main/java/com/fsck/k9/mailstore/SaveMessageDataCreator.kt @@ -12,16 +12,17 @@ class SaveMessageDataCreator( private val messageFulltextCreator: MessageFulltextCreator, private val attachmentCounter: AttachmentCounter ) { - fun createSaveMessageData(message: Message, partialMessage: Boolean): SaveMessageData { + fun createSaveMessageData(message: Message, partialMessage: Boolean, subject: String? = null): SaveMessageData { val now = System.currentTimeMillis() val date = message.sentDate?.time ?: now val internalDate = message.internalDate?.time ?: now + val displaySubject = subject ?: message.subject val encryptionResult = encryptionExtractor.extractEncryption(message) return if (encryptionResult != null) { SaveMessageData( message = message, - subject = message.subject, + subject = displaySubject, date = date, internalDate = internalDate, partialMessage = partialMessage, @@ -33,7 +34,7 @@ class SaveMessageDataCreator( } else { SaveMessageData( message = message, - subject = message.subject, + subject = displaySubject, date = date, internalDate = internalDate, partialMessage = partialMessage, diff --git a/app/core/src/test/java/com/fsck/k9/controller/MessagingControllerTest.java b/app/core/src/test/java/com/fsck/k9/controller/MessagingControllerTest.java index 3e38b2b39..c7e6429b5 100644 --- a/app/core/src/test/java/com/fsck/k9/controller/MessagingControllerTest.java +++ b/app/core/src/test/java/com/fsck/k9/controller/MessagingControllerTest.java @@ -26,6 +26,7 @@ import com.fsck.k9.mailstore.LocalStoreProvider; import com.fsck.k9.mailstore.MessageStoreManager; import com.fsck.k9.mailstore.OutboxState; import com.fsck.k9.mailstore.OutboxStateRepository; +import com.fsck.k9.mailstore.SaveMessageDataCreator; import com.fsck.k9.mailstore.SendState; import com.fsck.k9.mailstore.UnavailableStorageException; import com.fsck.k9.notification.NotificationController; @@ -78,6 +79,8 @@ public class MessagingControllerTest extends K9RobolectricTest { @Mock private MessageStoreManager messageStoreManager; @Mock + private SaveMessageDataCreator saveMessageDataCreator; + @Mock private SimpleMessagingListener listener; @Mock private LocalSearch search; @@ -133,7 +136,7 @@ public class MessagingControllerTest extends K9RobolectricTest { controller = new MessagingController(appContext, notificationController, notificationStrategy, localStoreProvider, unreadMessageCountProvider, backendManager, preferences, messageStoreManager, - Collections.emptyList()); + saveMessageDataCreator, Collections.emptyList()); configureAccount(); configureBackendManager(); diff --git a/app/storage/src/main/java/com/fsck/k9/storage/messages/K9MessageStore.kt b/app/storage/src/main/java/com/fsck/k9/storage/messages/K9MessageStore.kt index 25b9b7c4f..839c77cf4 100644 --- a/app/storage/src/main/java/com/fsck/k9/storage/messages/K9MessageStore.kt +++ b/app/storage/src/main/java/com/fsck/k9/storage/messages/K9MessageStore.kt @@ -48,6 +48,10 @@ class K9MessageStore( localStore.notifyChange() } + override fun saveLocalMessage(folderId: Long, messageData: SaveMessageData, existingMessageId: Long?): Long { + return saveMessageOperations.saveLocalMessage(folderId, messageData, existingMessageId) + } + override fun moveMessage(messageId: Long, destinationFolderId: Long): Long { return moveMessageOperations.moveMessage(messageId, destinationFolderId).also { localStore.notifyChange() diff --git a/app/storage/src/main/java/com/fsck/k9/storage/messages/SaveMessageOperations.kt b/app/storage/src/main/java/com/fsck/k9/storage/messages/SaveMessageOperations.kt index cedde0608..205b915dd 100644 --- a/app/storage/src/main/java/com/fsck/k9/storage/messages/SaveMessageOperations.kt +++ b/app/storage/src/main/java/com/fsck/k9/storage/messages/SaveMessageOperations.kt @@ -2,6 +2,7 @@ package com.fsck.k9.storage.messages import android.content.ContentValues import android.database.sqlite.SQLiteDatabase +import com.fsck.k9.K9 import com.fsck.k9.mail.Address import com.fsck.k9.mail.Body import com.fsck.k9.mail.BoundaryGenerator @@ -28,6 +29,7 @@ import java.io.IOException import java.io.InputStream import java.util.Locale import java.util.Stack +import java.util.UUID import org.apache.commons.io.IOUtils import org.apache.james.mime4j.codec.Base64InputStream import org.apache.james.mime4j.codec.QuotedPrintableInputStream @@ -42,11 +44,45 @@ internal class SaveMessageOperations( private val threadMessageOperations: ThreadMessageOperations ) { fun saveRemoteMessage(folderId: Long, messageServerId: String, messageData: SaveMessageData) { - lockableDatabase.execute(true) { database -> + saveMessage(folderId, messageServerId, messageData) + } + + fun saveLocalMessage(folderId: Long, messageData: SaveMessageData, existingMessageId: Long?): Long { + return if (existingMessageId == null) { + saveLocalMessage(folderId, messageData) + } else { + replaceLocalMessage(folderId, existingMessageId, messageData) + } + } + + private fun saveLocalMessage(folderId: Long, messageData: SaveMessageData): Long { + val fakeServerId = K9.LOCAL_UID_PREFIX + UUID.randomUUID().toString() + return saveMessage(folderId, fakeServerId, messageData) + } + + private fun replaceLocalMessage(folderId: Long, messageId: Long, messageData: SaveMessageData): Long { + return lockableDatabase.execute(true) { database -> + val (messageServerId, rootMessagePartId) = getLocalMessageInfo(folderId, messageId) + + replaceMessage( + database, + folderId, + messageServerId, + existingMessageId = messageId, + existingRootMessagePartId = rootMessagePartId, + messageData + ) + + messageId + } + } + + private fun saveMessage(folderId: Long, messageServerId: String, messageData: SaveMessageData): Long { + return lockableDatabase.execute(true) { database -> val message = messageData.message val existingMessageInfo = getMessage(folderId, messageServerId) - if (existingMessageInfo != null) { + return@execute if (existingMessageInfo != null) { val (existingMessageId, existingRootMessagePartId) = existingMessageInfo replaceMessage( database, @@ -56,29 +92,42 @@ internal class SaveMessageOperations( existingRootMessagePartId, messageData ) - return@execute + + existingMessageId + } else { + insertMessage(database, folderId, messageServerId, message, messageData) } - - val threadInfo = threadMessageOperations.doMessageThreading(database, folderId, message.toThreadHeaders()) - - val rootMessagePartId = saveMessageParts(database, message) - val messageId = saveMessage( - database, - folderId, - messageServerId, - rootMessagePartId, - messageData, - replaceMessageId = threadInfo.messageId - ) - - if (threadInfo.threadId == null) { - threadMessageOperations.createThreadEntry(database, messageId, threadInfo.rootId, threadInfo.parentId) - } - - createOrReplaceFulltextEntry(database, messageId, messageData) } } + private fun insertMessage( + database: SQLiteDatabase, + folderId: Long, + messageServerId: String, + message: Message, + messageData: SaveMessageData + ): Long { + val threadInfo = threadMessageOperations.doMessageThreading(database, folderId, message.toThreadHeaders()) + + val rootMessagePartId = saveMessageParts(database, message) + val messageId = saveMessage( + database, + folderId, + messageServerId, + rootMessagePartId, + messageData, + replaceMessageId = threadInfo.messageId + ) + + if (threadInfo.threadId == null) { + threadMessageOperations.createThreadEntry(database, messageId, threadInfo.rootId, threadInfo.parentId) + } + + createOrReplaceFulltextEntry(database, messageId, messageData) + + return messageId + } + private fun replaceMessage( database: SQLiteDatabase, folderId: Long, @@ -413,6 +462,26 @@ internal class SaveMessageOperations( } } + private fun getLocalMessageInfo(folderId: Long, messageId: Long): Pair { + return lockableDatabase.execute(false) { db -> + db.query( + "messages", + arrayOf("uid", "message_part_id"), + "folder_id = ? AND id = ?", + arrayOf(folderId.toString(), messageId.toString()), + null, + null, + null + ).use { cursor -> + if (!cursor.moveToFirst()) error("Local message not found $folderId:$messageId") + + val messageServerId = cursor.getString(0)!! + val messagePartId = cursor.getLong(1) + messageServerId to messagePartId + } + } + } + private fun deleteMessagePartsAndDataFromDisk(database: SQLiteDatabase, rootMessagePartId: Long) { deleteMessageDataFromDisk(database, rootMessagePartId) deleteMessageParts(database, rootMessagePartId) diff --git a/app/storage/src/test/java/com/fsck/k9/storage/messages/SaveMessageOperationsTest.kt b/app/storage/src/test/java/com/fsck/k9/storage/messages/SaveMessageOperationsTest.kt index c455f0180..4c82fa98e 100644 --- a/app/storage/src/test/java/com/fsck/k9/storage/messages/SaveMessageOperationsTest.kt +++ b/app/storage/src/test/java/com/fsck/k9/storage/messages/SaveMessageOperationsTest.kt @@ -1,5 +1,6 @@ package com.fsck.k9.storage.messages +import com.fsck.k9.K9 import com.fsck.k9.mail.Address import com.fsck.k9.mail.Flag import com.fsck.k9.mail.Message @@ -358,6 +359,143 @@ class SaveMessageOperationsTest : RobolectricTest() { assertThat(thread.messageId).isEqualTo(message.id) } + @Test + fun `save local message`() { + val messageData = buildMessage { + textBody("local") + }.toSaveMessageData( + subject = "Provided subject", + date = 1618191720000L, + internalDate = 1618191720000L, + previewResult = PreviewResult.text("Preview") + ) + + val newMessageId = saveMessageOperations.saveLocalMessage(folderId = 1, messageData, existingMessageId = null) + + val messages = sqliteDatabase.readMessages() + assertThat(messages).hasSize(1) + + val message = messages.first() + with(message) { + assertThat(id).isEqualTo(newMessageId) + assertThat(deleted).isEqualTo(0) + assertThat(folderId).isEqualTo(1) + assertThat(uid).startsWith(K9.LOCAL_UID_PREFIX) + assertThat(subject).isEqualTo("Provided subject") + assertThat(date).isEqualTo(1618191720000L) + assertThat(internalDate).isEqualTo(1618191720000L) + assertThat(flags).isEqualTo("X_DOWNLOADED_FULL") + assertThat(senderList).isEqualTo("") + assertThat(toList).isEqualTo("") + assertThat(ccList).isEqualTo("") + assertThat(bccList).isEqualTo("") + assertThat(replyToList).isEqualTo("") + assertThat(attachmentCount).isEqualTo(0) + assertThat(messageId).isNull() + assertThat(previewType).isEqualTo("text") + assertThat(preview).isEqualTo("Preview") + assertThat(mimeType).isEqualTo("text/plain") + assertThat(empty).isEqualTo(0) + assertThat(read).isEqualTo(0) + assertThat(flagged).isEqualTo(0) + assertThat(answered).isEqualTo(0) + assertThat(forwarded).isEqualTo(0) + assertThat(encryptionType).isNull() + } + + val messageParts = sqliteDatabase.readMessageParts() + assertThat(messageParts).hasSize(1) + + val messagePart = messageParts.first() + with(messagePart) { + assertThat(type).isEqualTo(MessagePartType.UNKNOWN) + assertThat(root).isEqualTo(messagePart.id) + assertThat(parent).isEqualTo(-1) + assertThat(mimeType).isEqualTo("text/plain") + assertThat(displayName).isEqualTo("noname.txt") + assertThat(header).isNotNull() + assertThat(encoding).isEqualTo("quoted-printable") + assertThat(charset).isNull() + assertThat(dataLocation).isEqualTo(DataLocation.IN_DATABASE) + assertThat(decodedBodySize).isEqualTo(5) + assertThat(data?.toString(Charsets.UTF_8)).isEqualTo("local") + assertThat(preamble).isNull() + assertThat(epilogue).isNull() + assertThat(boundary).isNull() + assertThat(contentId).isNull() + assertThat(serverExtra).isNull() + } + + val threads = sqliteDatabase.readThreads() + assertThat(threads).hasSize(1) + + val thread = threads.first() + assertThat(thread.root).isEqualTo(thread.id) + assertThat(thread.parent).isNull() + assertThat(thread.messageId).isEqualTo(message.id) + } + + @Test + fun `replace local message`() { + val existingMessageData = buildMessage { + multipart("alternative") { + bodyPart("text/plain") { + textBody("plain") + } + bodyPart("text/html") { + textBody("html") + } + } + }.toSaveMessageData() + val existingMessageId = saveMessageOperations.saveLocalMessage( + folderId = 1, + existingMessageData, + existingMessageId = null + ) + val messageData = buildMessage { + textBody("new") + }.toSaveMessageData() + + val newMessageId = saveMessageOperations.saveLocalMessage(folderId = 1, messageData, existingMessageId) + + val messages = sqliteDatabase.readMessages() + assertThat(messages).hasSize(1) + + assertThat(messages.first().id).isEqualTo(newMessageId) + + val messageParts = sqliteDatabase.readMessageParts() + assertThat(messageParts).hasSize(1) + + val messagePart = messageParts.first() + with(messagePart) { + assertThat(type).isEqualTo(MessagePartType.UNKNOWN) + assertThat(root).isEqualTo(messagePart.id) + assertThat(parent).isEqualTo(-1) + assertThat(mimeType).isEqualTo("text/plain") + assertThat(displayName).isEqualTo("noname.txt") + assertThat(header).isNotNull() + assertThat(encoding).isEqualTo("quoted-printable") + assertThat(charset).isNull() + assertThat(dataLocation).isEqualTo(DataLocation.IN_DATABASE) + assertThat(decodedBodySize).isEqualTo(3) + assertThat(data?.toString(Charsets.UTF_8)).isEqualTo("new") + assertThat(preamble).isNull() + assertThat(epilogue).isNull() + assertThat(boundary).isNull() + assertThat(contentId).isNull() + assertThat(serverExtra).isNull() + } + + val threads = sqliteDatabase.readThreads() + assertThat(threads).hasSize(1) + + val thread = threads.first() + val message = messages.first() + assertThat(thread.root).isEqualTo(thread.id) + assertThat(thread.parent).isNull() + assertThat(thread.messageId).isEqualTo(message.id) + } + private fun Message.toSaveMessageData( subject: String? = getSubject(), date: Long = sentDate?.time ?: System.currentTimeMillis(), From abd336addb68d9a20b5b3ec6c166a0f6a46e0022 Mon Sep 17 00:00:00 2001 From: cketti Date: Sun, 18 Apr 2021 02:27:20 +0200 Subject: [PATCH 2/2] Save message in Outbox folder using MessageStore --- .../k9/controller/MessagingController.java | 37 +++++++------------ .../com/fsck/k9/mailstore/LocalFolder.java | 15 -------- 2 files changed, 14 insertions(+), 38 deletions(-) diff --git a/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java b/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java index 59bced8bf..734bba538 100644 --- a/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java +++ b/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java @@ -72,6 +72,7 @@ import com.fsck.k9.mailstore.MessageStore; import com.fsck.k9.mailstore.MessageStoreManager; import com.fsck.k9.mailstore.OutboxState; import com.fsck.k9.mailstore.OutboxStateRepository; +import com.fsck.k9.mailstore.SaveMessageData; import com.fsck.k9.mailstore.SaveMessageDataCreator; import com.fsck.k9.mailstore.SendState; import com.fsck.k9.mailstore.UnavailableStorageException; @@ -1363,39 +1364,29 @@ public class MessagingController { } /** - * Stores the given message in the Outbox and starts a sendPendingMessages command to - * attempt to send the message. + * Stores the given message in the Outbox and starts a sendPendingMessages command to attempt to send the message. */ - public void sendMessage(final Account account, - final Message message, - String plaintextSubject, - MessagingListener listener) { + public void sendMessage(Account account, Message message, String plaintextSubject, MessagingListener listener) { try { - LocalStore localStore = localStoreProvider.getInstance(account); - LocalFolder localFolder = localStore.getFolder(account.getOutboxFolderId()); - localFolder.open(); - message.setFlag(Flag.SEEN, true); - localFolder.appendMessages(Collections.singletonList(message)); - LocalMessage localMessage = localFolder.getMessage(message.getUid()); - long messageId = localMessage.getDatabaseId(); - localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true); - if (plaintextSubject != null) { - localMessage.setCachedDecryptedSubject(plaintextSubject); + Long outboxFolderId = account.getOutboxFolderId(); + if (outboxFolderId == null) { + Timber.e("Error sending message. No Outbox folder configured."); + return; } + message.setFlag(Flag.SEEN, true); + + MessageStore messageStore = messageStoreManager.getMessageStore(account); + SaveMessageData messageData = saveMessageDataCreator.createSaveMessageData(message, false, plaintextSubject); + long messageId = messageStore.saveLocalMessage(outboxFolderId, messageData, null); + + LocalStore localStore = localStoreProvider.getInstance(account); OutboxStateRepository outboxStateRepository = localStore.getOutboxStateRepository(); outboxStateRepository.initializeOutboxState(messageId); sendPendingMessages(account, listener); } catch (Exception e) { - /* - for (MessagingListener l : getListeners()) - { - // TODO general failed - } - */ Timber.e(e, "Error sending message"); - } } diff --git a/app/core/src/main/java/com/fsck/k9/mailstore/LocalFolder.java b/app/core/src/main/java/com/fsck/k9/mailstore/LocalFolder.java index 2668e9d60..9c1f80c3f 100644 --- a/app/core/src/main/java/com/fsck/k9/mailstore/LocalFolder.java +++ b/app/core/src/main/java/com/fsck/k9/mailstore/LocalFolder.java @@ -795,21 +795,6 @@ public class LocalFolder { return folder.appendMessages(msgs, true); } - /** - * The method differs slightly from the contract; If an incoming message already has a uid - * assigned and it matches the uid of an existing message then this message will replace the - * old message. It is implemented as a delete/insert. This functionality is used in saving - * of drafts and re-synchronization of updated server messages. - * - * NOTE that although this method is located in the LocalStore class, it is not guaranteed - * that the messages supplied as parameters are actually {@link LocalMessage} instances (in - * fact, in most cases, they are not). Therefore, if you want to make local changes only to a - * message, retrieve the appropriate local message instance first (if it already exists). - */ - public Map appendMessages(List messages) throws MessagingException { - return appendMessages(messages, false); - } - public void destroyMessages(final List messages) { try { this.localStore.getDatabase().execute(true, new DbCallback() {