Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit f168985b authored by cketti's avatar cketti Committed by GitHub
Browse files

Merge pull request #3709 from k9mail/automatic_special_folder_selection

Automatic special folder selection
parents b79d7ea6 eeb545dc
Loading
Loading
Loading
Loading
+72 −5
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import com.fsck.k9.search.LocalSearch;
import com.fsck.k9.search.SearchSpecification.Attribute;
import com.fsck.k9.search.SearchSpecification.SearchCondition;
import com.fsck.k9.search.SearchSpecification.SearchField;
import org.jetbrains.annotations.NotNull;
import timber.log.Timber;

import static com.fsck.k9.Preferences.getEnumStringPref;
@@ -172,6 +173,11 @@ public class Account implements BaseAccount, StoreConfig {
    private String trashFolder;
    private String archiveFolder;
    private String spamFolder;
    private SpecialFolderSelection draftsFolderSelection;
    private SpecialFolderSelection sentFolderSelection;
    private SpecialFolderSelection trashFolderSelection;
    private SpecialFolderSelection archiveFolderSelection;
    private SpecialFolderSelection spamFolderSelection;
    private String autoExpandFolder;
    private FolderMode folderDisplayMode;
    private FolderMode folderSyncMode;
@@ -244,6 +250,11 @@ public class Account implements BaseAccount, StoreConfig {
        NONE, ALL, FIRST_CLASS, FIRST_AND_SECOND_CLASS, NOT_SECOND_CLASS
    }

    public enum SpecialFolderSelection {
        AUTOMATIC,
        MANUAL
    }

    public enum ShowPictures {
        NEVER, ALWAYS, ONLY_FROM_CONTACTS
    }
@@ -311,6 +322,11 @@ public class Account implements BaseAccount, StoreConfig {
        sentFolder = null;
        spamFolder = null;
        trashFolder = null;
        archiveFolderSelection = SpecialFolderSelection.AUTOMATIC;
        draftsFolderSelection = SpecialFolderSelection.AUTOMATIC;
        sentFolderSelection = SpecialFolderSelection.AUTOMATIC;
        spamFolderSelection = SpecialFolderSelection.AUTOMATIC;
        trashFolderSelection = SpecialFolderSelection.AUTOMATIC;

        searchableFolders = Searchable.ALL;

@@ -371,6 +387,17 @@ public class Account implements BaseAccount, StoreConfig {
        trashFolder = storage.getString(accountUuid + ".trashFolderName", null);
        archiveFolder = storage.getString(accountUuid + ".archiveFolderName", null);
        spamFolder = storage.getString(accountUuid + ".spamFolderName", null);
        archiveFolderSelection = getEnumStringPref(storage, accountUuid + ".archiveFolderSelection",
                SpecialFolderSelection.AUTOMATIC);
        draftsFolderSelection = getEnumStringPref(storage, accountUuid + ".draftsFolderSelection",
                SpecialFolderSelection.AUTOMATIC);
        sentFolderSelection = getEnumStringPref(storage, accountUuid + ".sentFolderSelection",
                SpecialFolderSelection.AUTOMATIC);
        spamFolderSelection = getEnumStringPref(storage, accountUuid + ".spamFolderSelection",
                SpecialFolderSelection.AUTOMATIC);
        trashFolderSelection = getEnumStringPref(storage, accountUuid + ".trashFolderSelection",
                SpecialFolderSelection.AUTOMATIC);

        expungePolicy = getEnumStringPref(storage, accountUuid + ".expungePolicy", Expunge.EXPUNGE_IMMEDIATELY);
        syncRemoteDeletions = storage.getBoolean(accountUuid + ".syncRemoteDeletions", true);

@@ -491,6 +518,11 @@ public class Account implements BaseAccount, StoreConfig {
        editor.remove(accountUuid + ".trashFolderName");
        editor.remove(accountUuid + ".archiveFolderName");
        editor.remove(accountUuid + ".spamFolderName");
        editor.remove(accountUuid + ".archiveFolderSelection");
        editor.remove(accountUuid + ".draftsFolderSelection");
        editor.remove(accountUuid + ".sentFolderSelection");
        editor.remove(accountUuid + ".spamFolderSelection");
        editor.remove(accountUuid + ".trashFolderSelection");
        editor.remove(accountUuid + ".autoExpandFolderName");
        editor.remove(accountUuid + ".accountNumber");
        editor.remove(accountUuid + ".vibrate");
@@ -665,6 +697,11 @@ public class Account implements BaseAccount, StoreConfig {
        editor.putString(accountUuid + ".trashFolderName", trashFolder);
        editor.putString(accountUuid + ".archiveFolderName", archiveFolder);
        editor.putString(accountUuid + ".spamFolderName", spamFolder);
        editor.putString(accountUuid + ".archiveFolderSelection", archiveFolderSelection.name());
        editor.putString(accountUuid + ".draftsFolderSelection", draftsFolderSelection.name());
        editor.putString(accountUuid + ".sentFolderSelection", sentFolderSelection.name());
        editor.putString(accountUuid + ".spamFolderSelection", spamFolderSelection.name());
        editor.putString(accountUuid + ".trashFolderSelection", trashFolderSelection.name());
        editor.putString(accountUuid + ".autoExpandFolderName", autoExpandFolder);
        editor.putInt(accountUuid + ".accountNumber", accountNumber);
        editor.putString(accountUuid + ".sortTypeEnum", sortType.name());
@@ -937,8 +974,9 @@ public class Account implements BaseAccount, StoreConfig {
        return draftsFolder;
    }

    public synchronized void setDraftsFolder(String name) {
    public synchronized void setDraftsFolder(String name, SpecialFolderSelection selection) {
        draftsFolder = name;
        draftsFolderSelection = selection;
    }

    /**
@@ -953,8 +991,9 @@ public class Account implements BaseAccount, StoreConfig {
        return sentFolder;
    }

    public synchronized void setSentFolder(String name) {
    public synchronized void setSentFolder(String name, SpecialFolderSelection selection) {
        sentFolder = name;
        sentFolderSelection = selection;
    }

    /**
@@ -970,8 +1009,9 @@ public class Account implements BaseAccount, StoreConfig {
        return trashFolder;
    }

    public synchronized void setTrashFolder(String name) {
    public synchronized void setTrashFolder(String name, SpecialFolderSelection selection) {
        trashFolder = name;
        trashFolderSelection = selection;
    }

    /**
@@ -986,8 +1026,9 @@ public class Account implements BaseAccount, StoreConfig {
        return archiveFolder;
    }

    public synchronized void setArchiveFolder(String archiveFolder) {
    public synchronized void setArchiveFolder(String archiveFolder, SpecialFolderSelection selection) {
        this.archiveFolder = archiveFolder;
        archiveFolderSelection = selection;
    }

    /**
@@ -1002,8 +1043,9 @@ public class Account implements BaseAccount, StoreConfig {
        return spamFolder;
    }

    public synchronized void setSpamFolder(String name) {
    public synchronized void setSpamFolder(String name, SpecialFolderSelection selection) {
        spamFolder = name;
        spamFolderSelection = selection;
    }

    /**
@@ -1014,6 +1056,31 @@ public class Account implements BaseAccount, StoreConfig {
        return spamFolder != null;
    }

    @NotNull
    public SpecialFolderSelection getDraftsFolderSelection() {
        return draftsFolderSelection;
    }

    @NotNull
    public synchronized SpecialFolderSelection getSentFolderSelection() {
        return sentFolderSelection;
    }

    @NotNull
    public synchronized SpecialFolderSelection getTrashFolderSelection() {
        return trashFolderSelection;
    }

    @NotNull
    public synchronized SpecialFolderSelection getArchiveFolderSelection() {
        return archiveFolderSelection;
    }

    @NotNull
    public synchronized SpecialFolderSelection getSpamFolderSelection() {
        return spamFolderSelection;
    }

    public String getOutboxFolder() {
        return OUTBOX;
    }
+34 −3
Original line number Diff line number Diff line
@@ -3,20 +3,38 @@ package com.fsck.k9.mailstore
import com.fsck.k9.Account
import com.fsck.k9.Account.FolderMode
import com.fsck.k9.mail.Folder.FolderClass
import com.fsck.k9.mail.Folder.FolderType as RemoteFolderType

class FolderRepository(private val account: Account) {
class FolderRepository(
        private val specialFolderSelectionStrategy: SpecialFolderSelectionStrategy,
        private val account: Account
) {
    private val sortForDisplay = compareByDescending<LocalFolder> { it.serverId == account.inboxFolder }
            .thenByDescending { it.serverId == account.outboxFolder }
            .thenByDescending { account.isSpecialFolder(it.serverId) }
            .thenByDescending { it.isInTopGroup }
            .thenBy(String.CASE_INSENSITIVE_ORDER) { it.name }


    fun getRemoteFolders(): List<Folder> {
    fun getRemoteFolderInfo(): RemoteFolderInfo {
        val folders = getRemoteFolders()
        val automaticSpecialFolders = mapOf(
                FolderType.ARCHIVE to specialFolderSelectionStrategy.selectSpecialFolder(folders, FolderType.ARCHIVE),
                FolderType.DRAFTS to specialFolderSelectionStrategy.selectSpecialFolder(folders, FolderType.DRAFTS),
                FolderType.SENT to specialFolderSelectionStrategy.selectSpecialFolder(folders, FolderType.SENT),
                FolderType.SPAM to specialFolderSelectionStrategy.selectSpecialFolder(folders, FolderType.SPAM),
                FolderType.TRASH to specialFolderSelectionStrategy.selectSpecialFolder(folders, FolderType.TRASH)
        )

        return RemoteFolderInfo(folders, automaticSpecialFolders)
    }

    private fun getRemoteFolders(): List<Folder> {
        val folders = account.localStore.getPersonalNamespaces(false)

        return folders
                .filterNot { it.isLocalOnly }
                .map(::createFolderFromLocalFolder)
                .map { Folder(it.databaseId, it.serverId, it.name, it.type.toFolderType()) }
    }

    fun getDisplayFolders(): List<Folder> {
@@ -55,10 +73,23 @@ class FolderRepository(private val account: Account) {
        account.spamFolder -> FolderType.SPAM
        else -> FolderType.REGULAR
    }

    private fun RemoteFolderType.toFolderType(): FolderType = when (this) {
        RemoteFolderType.REGULAR -> FolderType.REGULAR
        RemoteFolderType.INBOX -> FolderType.INBOX
        RemoteFolderType.OUTBOX -> FolderType.REGULAR   // We currently don't support remote Outbox folders
        RemoteFolderType.DRAFTS -> FolderType.DRAFTS
        RemoteFolderType.SENT -> FolderType.SENT
        RemoteFolderType.TRASH -> FolderType.TRASH
        RemoteFolderType.SPAM -> FolderType.SPAM
        RemoteFolderType.ARCHIVE -> FolderType.ARCHIVE
    }
}

data class Folder(val id: Long, val serverId: String, val name: String, val type: FolderType)

data class RemoteFolderInfo(val folders: List<Folder>, val automaticSpecialFolders: Map<FolderType, Folder?>)

enum class FolderType {
    REGULAR,
    INBOX,
+2 −2
Original line number Diff line number Diff line
@@ -2,6 +2,6 @@ package com.fsck.k9.mailstore

import com.fsck.k9.Account

class FolderRepositoryManager {
    fun getFolderRepository(account: Account) = FolderRepository(account)
class FolderRepositoryManager(private val specialFolderSelectionStrategy: SpecialFolderSelectionStrategy) {
    fun getFolderRepository(account: Account) = FolderRepository(specialFolderSelectionStrategy, account)
}
+33 −0
Original line number Diff line number Diff line
@file:JvmName("FolderTypeConverter")
package com.fsck.k9.mailstore

import com.fsck.k9.mail.Folder.FolderType


@JvmName("fromDatabaseFolderType")
fun String.toFolderType(): FolderType {
    return when (this) {
        "regular" -> FolderType.REGULAR
        "inbox" -> FolderType.INBOX
        "outbox" -> FolderType.OUTBOX
        "drafts" -> FolderType.DRAFTS
        "sent" -> FolderType.SENT
        "trash" -> FolderType.TRASH
        "spam" -> FolderType.SPAM
        "archive" -> FolderType.ARCHIVE
        else -> throw AssertionError("Unknown folder type: $this")
    }
}

fun FolderType.toDatabaseFolderType(): String {
    return when (this) {
        FolderType.REGULAR -> "regular"
        FolderType.INBOX -> "inbox"
        FolderType.OUTBOX -> "outbox"
        FolderType.DRAFTS -> "drafts"
        FolderType.SENT -> "sent"
        FolderType.TRASH -> "trash"
        FolderType.SPAM -> "spam"
        FolderType.ARCHIVE -> "archive"
    }
}
+14 −3
Original line number Diff line number Diff line
@@ -8,11 +8,13 @@ import com.fsck.k9.Preferences
import com.fsck.k9.backend.api.BackendFolder
import com.fsck.k9.backend.api.BackendStorage
import com.fsck.k9.backend.api.FolderInfo
import com.fsck.k9.mail.Folder.FolderType as RemoteFolderType

class K9BackendStorage(
        private val preferences: Preferences,
        private val account: Account,
        private val localStore: LocalStore
        private val localStore: LocalStore,
        private val specialFolderUpdater: SpecialFolderUpdater
) : BackendStorage {
    private val database = localStore.database

@@ -35,8 +37,12 @@ class K9BackendStorage(
    override fun createFolders(folders: List<FolderInfo>) {
        if (folders.isEmpty()) return

        val localFolders = folders.map { localStore.getFolder(it.serverId, it.name) }
        val localFolders = folders.map { localStore.getFolder(it.serverId, it.name, it.type) }
        localStore.createFolders(localFolders, account.displayCount)

        if (folders.any { it.type != FolderType.REGULAR }) {
            specialFolderUpdater.updateSpecialFolders()
        }
    }

    override fun deleteFolders(folderServerIds: List<String>) {
@@ -44,16 +50,21 @@ class K9BackendStorage(
                .filterNot { account.isSpecialFolder(it) }
                .map { localStore.getFolder(it) }
                .forEach { it.delete() }

        specialFolderUpdater.updateSpecialFolders()
    }

    override fun changeFolder(folderServerId: String, name: String) {
    override fun changeFolder(folderServerId: String, name: String, type: RemoteFolderType) {
        database.execute(false) { db ->
            val values = ContentValues().apply {
                put("name", name)
                put("type", type.toDatabaseFolderType())
            }

            db.update("folders", values, "server_id = ?", arrayOf(folderServerId))
        }

        specialFolderUpdater.updateSpecialFolders()
    }

    override fun getExtraString(name: String): String? {
Loading