diff --git a/legacy/core/src/main/java/com/fsck/k9/controller/MessagingController.java b/legacy/core/src/main/java/com/fsck/k9/controller/MessagingController.java index c2823d1e68c..9d5b1cebec4 100644 --- a/legacy/core/src/main/java/com/fsck/k9/controller/MessagingController.java +++ b/legacy/core/src/main/java/com/fsck/k9/controller/MessagingController.java @@ -45,6 +45,7 @@ import com.fsck.k9.controller.MessagingControllerCommands.PendingAppend; import com.fsck.k9.controller.MessagingControllerCommands.PendingCommand; import com.fsck.k9.controller.MessagingControllerCommands.PendingDelete; +import com.fsck.k9.controller.MessagingControllerCommands.PendingEmptySpam; import com.fsck.k9.controller.MessagingControllerCommands.PendingEmptyTrash; import com.fsck.k9.controller.MessagingControllerCommands.PendingExpunge; import com.fsck.k9.controller.MessagingControllerCommands.PendingMarkAllAsRead; @@ -2080,6 +2081,66 @@ private static List getUidsFromMessages(List messages) { return uids; } + void processPendingEmptySpam(Account account) throws MessagingException { + if (!account.hasSpamFolder()) { + return; + } + + long spamFolderId = account.getSpamFolderId(); + LocalStore localStore = localStoreProvider.getInstance(account); + LocalFolder folder = localStore.getFolder(spamFolderId); + folder.open(); + String spamFolderServerId = folder.getServerId(); + + Backend backend = getBackend(account); + backend.deleteAllMessages(spamFolderServerId); + + // Remove all messages marked as deleted + folder.destroyDeletedMessages(); + + compact(account); + } + + public void emptySpam(final Account account, MessagingListener listener) { + putBackground("emptySpam", listener, new Runnable() { + @Override + public void run() { + try { + Long spamFolderId = account.getSpamFolderId(); + if (spamFolderId == null) { + Timber.w("No Spam folder configured. Can't empty spam."); + return; + } + + LocalStore localStore = localStoreProvider.getInstance(account); + LocalFolder localFolder = localStore.getFolder(spamFolderId); + localFolder.open(); + + boolean isSpamLocalOnly = isSpamLocalOnly(account); + if (isSpamLocalOnly) { + localFolder.clearAllMessages(); + } else { + localFolder.destroyLocalOnlyMessages(); + localFolder.setFlags(Collections.singleton(Flag.DELETED), true); + } + + for (MessagingListener l : getListeners()) { + l.folderStatusChanged(account, spamFolderId); + } + + if (!isSpamLocalOnly) { + PendingCommand command = PendingEmptySpam.create(); + queuePendingCommand(account, command); + processPendingCommands(account); + } + } catch (Exception e) { + Timber.e(e, "emptySpam failed"); + } + } + }); + } + + void processPendingEmptyTrash(Account account) throws MessagingException { if (!account.hasTrashFolder()) { return; @@ -2157,6 +2218,22 @@ protected void clearFolderSynchronous(Account account, long folderId) { } + /** + * Find out whether the account type only supports a local Spam folder. + *

+ *

Note: Currently this is only the case for POP3 accounts.

+ * + * @param account + * The account to check. + * + * @return {@code true} if the account only has a local Spam folder that is not synchronized + * with a folder on the server. {@code false} otherwise. + */ + private boolean isSpamLocalOnly(Account account) { + Backend backend = getBackend(account); + return !backend.getSupportsSpamFolder(); + } + /** * Find out whether the account type only supports a local Trash folder. *

diff --git a/legacy/core/src/main/java/com/fsck/k9/controller/MessagingControllerCommands.java b/legacy/core/src/main/java/com/fsck/k9/controller/MessagingControllerCommands.java index aa4682b8856..2aab360ae05 100644 --- a/legacy/core/src/main/java/com/fsck/k9/controller/MessagingControllerCommands.java +++ b/legacy/core/src/main/java/com/fsck/k9/controller/MessagingControllerCommands.java @@ -21,6 +21,7 @@ public class MessagingControllerCommands { static final String COMMAND_EXPUNGE = "expunge"; static final String COMMAND_MOVE_OR_COPY = "move_or_copy"; static final String COMMAND_MOVE_AND_MARK_AS_READ = "move_and_mark_as_read"; + static final String COMMAND_EMPTY_SPAM = "empty_spam"; static final String COMMAND_EMPTY_TRASH = "empty_trash"; public abstract static class PendingCommand { @@ -95,6 +96,22 @@ public void execute(MessagingController controller, Account account) throws Mess } } + public static class PendingEmptySpam extends PendingCommand { + public static PendingEmptySpam create() { + return new PendingEmptySpam(); + } + + @Override + public String getCommandName() { + return COMMAND_EMPTY_SPAM; + } + + @Override + public void execute(MessagingController controller, Account account) throws MessagingException { + controller.processPendingEmptySpam(account); + } + } + public static class PendingEmptyTrash extends PendingCommand { public static PendingEmptyTrash create() { return new PendingEmptyTrash(); diff --git a/legacy/core/src/main/java/com/fsck/k9/controller/PendingCommandSerializer.java b/legacy/core/src/main/java/com/fsck/k9/controller/PendingCommandSerializer.java index ce109f67dfe..0a850762eed 100644 --- a/legacy/core/src/main/java/com/fsck/k9/controller/PendingCommandSerializer.java +++ b/legacy/core/src/main/java/com/fsck/k9/controller/PendingCommandSerializer.java @@ -10,6 +10,7 @@ import com.fsck.k9.controller.MessagingControllerCommands.PendingAppend; import com.fsck.k9.controller.MessagingControllerCommands.PendingCommand; import com.fsck.k9.controller.MessagingControllerCommands.PendingDelete; +import com.fsck.k9.controller.MessagingControllerCommands.PendingEmptySpam; import com.fsck.k9.controller.MessagingControllerCommands.PendingEmptyTrash; import com.fsck.k9.controller.MessagingControllerCommands.PendingExpunge; import com.fsck.k9.controller.MessagingControllerCommands.PendingMarkAllAsRead; @@ -37,6 +38,7 @@ private PendingCommandSerializer() { moshi.adapter(PendingMoveAndMarkAsRead.class)); adapters.put(MessagingControllerCommands.COMMAND_APPEND, moshi.adapter(PendingAppend.class)); adapters.put(MessagingControllerCommands.COMMAND_REPLACE, moshi.adapter(PendingReplace.class)); + adapters.put(MessagingControllerCommands.COMMAND_EMPTY_SPAM, moshi.adapter(PendingEmptySpam.class)); adapters.put(MessagingControllerCommands.COMMAND_EMPTY_TRASH, moshi.adapter(PendingEmptyTrash.class)); adapters.put(MessagingControllerCommands.COMMAND_EXPUNGE, moshi.adapter(PendingExpunge.class)); adapters.put(MessagingControllerCommands.COMMAND_MARK_ALL_AS_READ, moshi.adapter(PendingMarkAllAsRead.class)); diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListFragment.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListFragment.kt index daa47f82d25..54f032d8ab8 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListFragment.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListFragment.kt @@ -738,6 +738,12 @@ class MessageListFragment : } } + private fun onEmptySpam() { + if (isShowingSpamFolder) { + showDialog(R.id.dialog_confirm_empty_spam) + } + } + private fun onEmptyTrash() { if (isShowingTrashFolder) { showDialog(R.id.dialog_confirm_empty_trash) @@ -786,6 +792,14 @@ class MessageListFragment : ConfirmationDialogFragment.newInstance(dialogId, title, message, confirmText, cancelText) } + R.id.dialog_confirm_empty_spam -> { + val title = getString(R.string.dialog_confirm_empty_spam_title) + val message = getString(R.string.dialog_confirm_empty_spam_message) + val confirmText = getString(R.string.dialog_confirm_delete_confirm_button) + val cancelText = getString(R.string.dialog_confirm_delete_cancel_button) + ConfirmationDialogFragment.newInstance(dialogId, title, message, confirmText, cancelText) + } + R.id.dialog_confirm_empty_trash -> { val title = getString(R.string.dialog_confirm_empty_trash_title) val message = getString(R.string.dialog_confirm_empty_trash_message) @@ -820,6 +834,7 @@ class MessageListFragment : menu.findItem(R.id.set_sort).isVisible = true menu.findItem(R.id.select_all).isVisible = true menu.findItem(R.id.mark_all_as_read).isVisible = isMarkAllAsReadSupported + menu.findItem(R.id.empty_spam).isVisible = isShowingSpamFolder menu.findItem(R.id.empty_trash).isVisible = isShowingTrashFolder if (isSingleAccountMode) { @@ -843,6 +858,7 @@ class MessageListFragment : menu.findItem(R.id.select_all).isVisible = false menu.findItem(R.id.mark_all_as_read).isVisible = false menu.findItem(R.id.send_messages).isVisible = false + menu.findItem(R.id.empty_spam).isVisible = false menu.findItem(R.id.empty_trash).isVisible = false menu.findItem(R.id.expunge).isVisible = false menu.findItem(R.id.search_everywhere).isVisible = false @@ -862,6 +878,7 @@ class MessageListFragment : R.id.select_all -> selectAll() R.id.mark_all_as_read -> confirmMarkAllAsRead() R.id.send_messages -> onSendPendingMessages() + R.id.empty_spam -> onEmptySpam() R.id.empty_trash -> onEmptyTrash() R.id.expunge -> onExpunge() R.id.search_everywhere -> onSearchEverywhere() @@ -1225,6 +1242,10 @@ class MessageListFragment : markAllAsRead() } + R.id.dialog_confirm_empty_spam -> { + messagingController.emptySpam(account, null) + } + R.id.dialog_confirm_empty_trash -> { messagingController.emptyTrash(account, null) } diff --git a/legacy/ui/legacy/src/main/res/menu/message_list_option_menu.xml b/legacy/ui/legacy/src/main/res/menu/message_list_option_menu.xml index b1e543837e6..c797bef6920 100644 --- a/legacy/ui/legacy/src/main/res/menu/message_list_option_menu.xml +++ b/legacy/ui/legacy/src/main/res/menu/message_list_option_menu.xml @@ -206,6 +206,13 @@ app:showAsAction="never" /> + + + Will request read receipt Will not request read receipt Add attachment + Empty Spam Empty Trash Expunge About