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

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

Merge pull request #4835 from k9mail/local_folder_server_id

Continue work on referring to folders only by their database ID
parents 3ffa488e 39db941d
Loading
Loading
Loading
Loading
+0 −5
Original line number Diff line number Diff line
@@ -27,11 +27,6 @@ import org.jetbrains.annotations.Nullable;
 * Account stores all of the settings for a single account defined by the user. Each account is defined by a UUID.
 */
public class Account implements BaseAccount {
    /**
     * This local folder is used to store messages to be sent.
     */
    public static final String OUTBOX = "K9MAIL_INTERNAL_OUTBOX";

    /**
     * Fixed name of outbox - not actually displayed.
     */
+43 −70
Original line number Diff line number Diff line
@@ -999,8 +999,9 @@ public class MessagingController {
    }

    void processPendingMarkAllAsRead(PendingMarkAllAsRead command, Account account) throws MessagingException {
        long folderId = command.folderId;
        LocalStore localStore = localStoreProvider.getInstance(account);
        LocalFolder localFolder = localStore.getFolder(command.folderId);
        LocalFolder localFolder = localStore.getFolder(folderId);

        localFolder.open();
        String folderServerId = localFolder.getServerId();
@@ -1016,7 +1017,7 @@ public class MessagingController {
        }

        for (MessagingListener l : getListeners()) {
            l.folderStatusChanged(account, folderServerId);
            l.folderStatusChanged(account, folderId);
        }

        Backend backend = getBackend(account);
@@ -1082,34 +1083,30 @@ public class MessagingController {
            Timber.e(e, "Couldn't set flags in local database");
        }

        // Read folder name and UID of messages from the database
        Map<String, List<String>> folderMap;
        // Read folder ID and UID of messages from the database
        Map<Long, List<String>> folderMap;
        try {
            folderMap = localStore.getFoldersAndUids(ids, threadedList);
            folderMap = localStore.getFolderIdsAndUids(ids, threadedList);
        } catch (MessagingException e) {
            Timber.e(e, "Couldn't get folder name and UID of messages");
            return;
        }

        // Loop over all folders
        for (Entry<String, List<String>> entry : folderMap.entrySet()) {
            String folderServerId = entry.getKey();
        for (Entry<Long, List<String>> entry : folderMap.entrySet()) {
            long folderId = entry.getKey();
            List<String> uids = entry.getValue();

            // Notify listeners of changed folder status
            for (MessagingListener l : getListeners()) {
                l.folderStatusChanged(account, folderServerId);
                l.folderStatusChanged(account, folderId);
            }

            // TODO: Skip the remote part for all local-only folders

            // Send flag change to server
            try {
                long folderId = getFolderId(account, folderServerId);
                queueSetFlag(account, folderId, newState, flag, entry.getValue());
            queueSetFlag(account, folderId, newState, flag, uids);
            processPendingCommands(account);
            } catch (MessagingException e) {
                Timber.e(e, "Error while trying to set flags");
            }
        }
    }

@@ -1130,9 +1127,8 @@ public class MessagingController {
            // Update the messages in the local store
            localFolder.setFlags(messages, Collections.singleton(flag), newState);

            String folderServerId = localFolder.getServerId();
            for (MessagingListener l : getListeners()) {
                l.folderStatusChanged(account, folderServerId);
                l.folderStatusChanged(account, folderId);
            }


@@ -1253,8 +1249,8 @@ public class MessagingController {

        LocalMessage message = localFolder.getMessage(uid);
        if (message == null || message.getDatabaseId() == 0) {
            String folderServerId = localFolder.getServerId();
            throw new IllegalArgumentException("Message not found: folder=" + folderServerId + ", uid=" + uid);
            String folderName = localFolder.getName();
            throw new IllegalArgumentException("Message not found: folder=" + folderName + ", uid=" + uid);
        }

        FetchProfile fp = new FetchProfile();
@@ -1274,8 +1270,8 @@ public class MessagingController {

        LocalMessage message = localFolder.getMessage(uid);
        if (message == null || message.getDatabaseId() == 0) {
            String folderServerId = localFolder.getServerId();
            throw new IllegalArgumentException("Message not found: folder=" + folderServerId + ", uid=" + uid);
            String folderName = localFolder.getName();
            throw new IllegalArgumentException("Message not found: folder=" + folderName + ", uid=" + uid);
        }

        FetchProfile fp = new FetchProfile();
@@ -1756,11 +1752,9 @@ public class MessagingController {

            LocalFolder localSrcFolder = localStore.getFolder(srcFolderId);
            localSrcFolder.open();
            String srcFolderServerId = localSrcFolder.getServerId();

            LocalFolder localDestFolder = localStore.getFolder(destFolderId);
            localDestFolder.open();
            String destFolderServerId = localDestFolder.getServerId();

            boolean unreadCountAffected = false;
            List<String> uids = new LinkedList<>();
@@ -1799,7 +1793,7 @@ public class MessagingController {
                        // If this copy operation changes the unread count in the destination
                        // folder, notify the listeners.
                        for (MessagingListener l : getListeners()) {
                            l.folderStatusChanged(account, destFolderServerId);
                            l.folderStatusChanged(account, destFolderId);
                        }
                    }
                } else {
@@ -1823,8 +1817,8 @@ public class MessagingController {
                        int unreadMessageCountSrc = localSrcFolder.getUnreadMessageCount();
                        int unreadMessageCountDest = localDestFolder.getUnreadMessageCount();
                        for (MessagingListener l : getListeners()) {
                            l.folderStatusChanged(account, srcFolderServerId);
                            l.folderStatusChanged(account, destFolderServerId);
                            l.folderStatusChanged(account, srcFolderId);
                            l.folderStatusChanged(account, destFolderId);
                        }
                    }
                }
@@ -1863,7 +1857,7 @@ public class MessagingController {
            String uid = localFolder.getMessageUidById(id);
            if (uid != null) {
                MessageReference messageReference = new MessageReference(account.getUuid(), folderId, uid, null);
                deleteMessage(messageReference, null);
                deleteMessage(messageReference);
            }
        } catch (MessagingException me) {
            Timber.e(me, "Error deleting draft");
@@ -1871,26 +1865,18 @@ public class MessagingController {
    }

    public void deleteThreads(final List<MessageReference> messages) {
        actOnMessagesGroupedByAccountAndFolder(messages, new MessageActor() {
            @Override
            public void act(final Account account, final LocalFolder messageFolder,
                    final List<LocalMessage> accountMessages) {
        actOnMessagesGroupedByAccountAndFolder(messages, (account, messageFolder, accountMessages) -> {
            suppressMessages(account, accountMessages);

                putBackground("deleteThreads", null, new Runnable() {
                    @Override
                    public void run() {
                        deleteThreadsSynchronous(account, messageFolder.getServerId(), accountMessages);
                    }
                });
            }
            putBackground("deleteThreads", null, () ->
                    deleteThreadsSynchronous(account, messageFolder.getDatabaseId(), accountMessages)
            );
        });
    }

    private void deleteThreadsSynchronous(Account account, String folderServerId, List<LocalMessage> messages) {
    private void deleteThreadsSynchronous(Account account, long folderId, List<LocalMessage> messages) {
        try {
            List<LocalMessage> messagesToDelete = collectMessagesInThreads(account, messages);
            deleteMessagesSynchronous(account, folderServerId, messagesToDelete, null);
            deleteMessagesSynchronous(account, folderId, messagesToDelete);
        } catch (MessagingException e) {
            Timber.e(e, "Something went wrong while deleting threads");
        }
@@ -1914,26 +1900,16 @@ public class MessagingController {
        return messagesInThreads;
    }

    public void deleteMessage(MessageReference message, final MessagingListener listener) {
        deleteMessages(Collections.singletonList(message), listener);
    public void deleteMessage(MessageReference message) {
        deleteMessages(Collections.singletonList(message));
    }

    public void deleteMessages(List<MessageReference> messages, final MessagingListener listener) {
        actOnMessagesGroupedByAccountAndFolder(messages, new MessageActor() {

            @Override
            public void act(final Account account, final LocalFolder messageFolder,
                    final List<LocalMessage> accountMessages) {
    public void deleteMessages(List<MessageReference> messages) {
        actOnMessagesGroupedByAccountAndFolder(messages, (account, messageFolder, accountMessages) -> {
            suppressMessages(account, accountMessages);

                putBackground("deleteMessages", null, new Runnable() {
                    @Override
                    public void run() {
                        deleteMessagesSynchronous(account, messageFolder.getServerId(), accountMessages, listener);
                    }
                });
            }

            putBackground("deleteMessages", null, () ->
                    deleteMessagesSynchronous(account, messageFolder.getDatabaseId(), accountMessages)
            );
        });
    }

@@ -1966,9 +1942,7 @@ public class MessagingController {

    }

    private void deleteMessagesSynchronous(final Account account, final String folder,
            final List<LocalMessage> messages,
            MessagingListener listener) {
    private void deleteMessagesSynchronous(Account account, long folderId, List<LocalMessage> messages) {
        try {
            List<LocalMessage> localOnlyMessages = new ArrayList<>();
            List<LocalMessage> syncedMessages = new ArrayList<>();
@@ -1986,9 +1960,8 @@ public class MessagingController {
            Backend backend = getBackend(account);

            LocalStore localStore = localStoreProvider.getInstance(account);
            LocalFolder localFolder = localStore.getFolder(folder);
            LocalFolder localFolder = localStore.getFolder(folderId);
            localFolder.open();
            long folderId = localFolder.getDatabaseId();

            Map<String, String> uidMap = null;
            Long trashFolderId = account.getTrashFolderId();
@@ -2014,9 +1987,9 @@ public class MessagingController {
            }

            for (MessagingListener l : getListeners()) {
                l.folderStatusChanged(account, folder);
                l.folderStatusChanged(account, folderId);
                if (localTrashFolder != null) {
                    l.folderStatusChanged(account, localTrashFolder.getServerId());
                    l.folderStatusChanged(account, trashFolderId);
                }
            }

@@ -2107,7 +2080,6 @@ public class MessagingController {
                    LocalStore localStore = localStoreProvider.getInstance(account);
                    LocalFolder localFolder = localStore.getFolder(trashFolderId);
                    localFolder.open();
                    String trashFolderServerId = localFolder.getServerId();

                    boolean isTrashLocalOnly = isTrashLocalOnly(account);
                    if (isTrashLocalOnly) {
@@ -2118,7 +2090,7 @@ public class MessagingController {
                    }

                    for (MessagingListener l : getListeners()) {
                        l.folderStatusChanged(account, trashFolderServerId);
                        l.folderStatusChanged(account, trashFolderId);
                    }

                    if (!isTrashLocalOnly) {
@@ -2832,8 +2804,9 @@ public class MessagingController {

        @Override
        public void folderStatusChanged(@NotNull String folderServerId) {
            long folderId = getFolderIdOrThrow(account, folderServerId);
            for (MessagingListener messagingListener : getListeners(listener)) {
                messagingListener.folderStatusChanged(account, folderServerId);
                messagingListener.folderStatusChanged(account, folderId);
            }
        }

+1 −1
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ public interface MessagingListener {
    void checkMailStarted(Context context, Account account);
    void checkMailFinished(Context context, Account account);

    void folderStatusChanged(Account account, String folderServerId);
    void folderStatusChanged(Account account, long folderId);

    void messageUidChanged(Account account, long folderId, String oldUid, String newUid);

+1 −1
Original line number Diff line number Diff line
@@ -79,7 +79,7 @@ public abstract class SimpleMessagingListener implements MessagingListener {
    }

    @Override
    public void folderStatusChanged(Account account, String folderServerId) {
    public void folderStatusChanged(Account account, long folderId) {
    }

    @Override
+133 −63
Original line number Diff line number Diff line
@@ -20,17 +20,11 @@ class FolderRepository(
            .thenByDescending { it.isInTopGroup }
            .thenBy(String.CASE_INSENSITIVE_ORDER) { it.folder.name }

    fun getFolders(): List<Folder> {
    fun getRemoteFolders(): List<RemoteFolder> {
        val folders = localStoreProvider.getInstance(account).getPersonalNamespaces(false)
        return folders.map { Folder(it.databaseId, it.serverId, it.name, it.type.toFolderType(), it.isLocalOnly) }
    }

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

        return folders
                .filterNot { it.isLocalOnly }
                .map { Folder(it.databaseId, it.serverId, it.name, it.type.toFolderType(), isLocalOnly = false) }
                .map { RemoteFolder(it.databaseId, it.serverId, it.name, it.type.toFolderType()) }
    }

    fun getDisplayFolders(displayMode: FolderMode?): List<DisplayFolder> {
@@ -43,66 +37,81 @@ class FolderRepository(
        return displayFolders.sortedWith(sortForDisplay)
    }

    fun getFolderDetails(folderId: Long): FolderDetails? {
        return getFolderDetails(selection = "id = ?", selectionArgs = arrayOf(folderId.toString())).firstOrNull()
    }

    fun getFolderDetails(): List<FolderDetails> {
        return getFolderDetails(selection = null, selectionArgs = null)
    }

    fun getFolderServerId(folderId: Long): String? {
    fun getFolder(folderId: Long): Folder? {
        val database = localStoreProvider.getInstance(account).database
        return database.execute(false) { db ->
            db.query(
                "folders",
                arrayOf("server_id"),
                arrayOf(
                    "id",
                    "name",
                    "local_only"
                ),
                "id = ?",
                arrayOf(folderId.toString()),
                null,
                null,
                null
            ).use { cursor ->
                if (cursor.moveToFirst()) cursor.getString(0) else null
            }
        }
    }

    fun getFolderId(folderServerId: String): Long? {
        val database = localStoreProvider.getInstance(account).database
        return database.execute(false) { db ->
            db.query(
                "folders",
                arrayOf("id"),
                "server_id = ?",
                arrayOf(folderServerId),
                null,
                null,
                if (cursor.moveToFirst()) {
                    val id = cursor.getLong(0)
                    Folder(
                        id = id,
                        name = cursor.getString(1),
                        type = folderTypeOf(id),
                        isLocalOnly = cursor.getInt(2) == 1
                    )
                } else {
                    null
            ).use { cursor ->
                if (cursor.moveToFirst()) cursor.getLong(0) else null
                }
            }
        }
    }

    fun isFolderPresent(folderId: Long): Boolean {
    fun getFolderDetails(folderId: Long): FolderDetails? {
        val database = localStoreProvider.getInstance(account).database
        return database.execute(false) { db ->
            db.query(
                "folders",
                arrayOf("id"),
                arrayOf(
                    "id",
                    "name",
                    "top_group",
                    "integrate",
                    "poll_class",
                    "display_class",
                    "notify_class",
                    "push_class",
                    "local_only"
                ),
                "id = ?",
                arrayOf(folderId.toString()),
                null,
                null,
                null
            ).use { cursor ->
                cursor.count != 0
                cursor.map {
                    val id = cursor.getLong(0)
                    FolderDetails(
                        folder = Folder(
                            id = id,
                            name = cursor.getString(1),
                            type = folderTypeOf(id),
                            isLocalOnly = cursor.getInt(8) == 1
                        ),
                        isInTopGroup = cursor.getInt(2) == 1,
                        isIntegrate = cursor.getInt(3) == 1,
                        syncClass = cursor.getStringOrNull(4).toFolderClass(),
                        displayClass = cursor.getStringOrNull(5).toFolderClass(),
                        notifyClass = cursor.getStringOrNull(6).toFolderClass(),
                        pushClass = cursor.getStringOrNull(7).toFolderClass()
                    )
                }
            }
        }.firstOrNull()
    }

    private fun getFolderDetails(selection: String?, selectionArgs: Array<String>?): List<FolderDetails> {
    fun getRemoteFolderDetails(): List<RemoteFolderDetails> {
        val database = localStoreProvider.getInstance(account).database
        return database.execute(false) { db ->
            db.query(
@@ -111,42 +120,92 @@ class FolderRepository(
                    "id",
                    "server_id",
                    "name",
                    "type",
                    "top_group",
                    "integrate",
                    "poll_class",
                    "display_class",
                    "notify_class",
                    "push_class",
                    "local_only"
                    "push_class"
                ),
                selection,
                selectionArgs,
                "local_only = 0",
                null,
                null,
                null,
                null
            ).use { cursor ->
                cursor.map {
                    val id = cursor.getLong(0)
                    FolderDetails(
                        folder = Folder(
                    RemoteFolderDetails(
                        folder = RemoteFolder(
                            id = id,
                            serverId = cursor.getString(1),
                            name = cursor.getString(2),
                            type = folderTypeOf(id),
                            isLocalOnly = cursor.getInt(9) == 1
                            type = cursor.getString(3).toFolderType().toFolderType()
                        ),
                        isInTopGroup = cursor.getInt(3) == 1,
                        isIntegrate = cursor.getInt(4) == 1,
                        syncClass = cursor.getStringOrNull(5).toFolderClass(),
                        displayClass = cursor.getStringOrNull(6).toFolderClass(),
                        notifyClass = cursor.getStringOrNull(7).toFolderClass(),
                        pushClass = cursor.getStringOrNull(8).toFolderClass()
                        isInTopGroup = cursor.getInt(4) == 1,
                        isIntegrate = cursor.getInt(5) == 1,
                        syncClass = cursor.getStringOrNull(6).toFolderClass(),
                        displayClass = cursor.getStringOrNull(7).toFolderClass(),
                        notifyClass = cursor.getStringOrNull(8).toFolderClass(),
                        pushClass = cursor.getStringOrNull(9).toFolderClass()
                    )
                }
            }
        }
    }

    fun getFolderServerId(folderId: Long): String? {
        val database = localStoreProvider.getInstance(account).database
        return database.execute(false) { db ->
            db.query(
                "folders",
                arrayOf("server_id"),
                "id = ?",
                arrayOf(folderId.toString()),
                null,
                null,
                null
            ).use { cursor ->
                if (cursor.moveToFirst()) cursor.getString(0) else null
            }
        }
    }

    fun getFolderId(folderServerId: String): Long? {
        val database = localStoreProvider.getInstance(account).database
        return database.execute(false) { db ->
            db.query(
                "folders",
                arrayOf("id"),
                "server_id = ?",
                arrayOf(folderServerId),
                null,
                null,
                null
            ).use { cursor ->
                if (cursor.moveToFirst()) cursor.getLong(0) else null
            }
        }
    }

    fun isFolderPresent(folderId: Long): Boolean {
        val database = localStoreProvider.getInstance(account).database
        return database.execute(false) { db ->
            db.query(
                "folders",
                arrayOf("id"),
                "id = ?",
                arrayOf(folderId.toString()),
                null,
                null,
                null
            ).use { cursor ->
                cursor.count != 0
            }
        }
    }

    fun updateFolderDetails(folderDetails: FolderDetails) {
        val database = localStoreProvider.getInstance(account).database
        database.execute(false) { db ->
@@ -164,7 +223,7 @@ class FolderRepository(

    private fun getDisplayFolders(db: SQLiteDatabase, displayMode: FolderMode): List<DisplayFolder> {
        val queryBuilder = StringBuilder("""
            SELECT f.id, f.server_id, f.name, f.top_group, f.local_only, (
            SELECT f.id, f.name, f.top_group, f.local_only, (
                SELECT COUNT(m.id) 
                FROM messages m 
                WHERE m.folder_id = f.id AND m.empty = 0 AND m.deleted = 0 AND m.read = 0
@@ -181,14 +240,13 @@ class FolderRepository(

            while (cursor.moveToNext()) {
                val id = cursor.getLong(0)
                val serverId = cursor.getString(1)
                val name = cursor.getString(2)
                val name = cursor.getString(1)
                val type = folderTypeOf(id)
                val isInTopGroup = cursor.getInt(3) == 1
                val isLocalOnly = cursor.getInt(4) == 1
                val unreadCount = cursor.getInt(5)
                val isInTopGroup = cursor.getInt(2) == 1
                val isLocalOnly = cursor.getInt(3) == 1
                val unreadCount = cursor.getInt(4)

                val folder = Folder(id, serverId, name, type, isLocalOnly)
                val folder = Folder(id, name, type, isLocalOnly)
                displayFolders.add(DisplayFolder(folder, isInTopGroup, unreadCount))
            }

@@ -271,7 +329,9 @@ class FolderRepository(
    }
}

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

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

data class FolderDetails(
    val folder: Folder,
@@ -283,6 +343,16 @@ data class FolderDetails(
    val pushClass: FolderClass
)

data class RemoteFolderDetails(
    val folder: RemoteFolder,
    val isInTopGroup: Boolean,
    val isIntegrate: Boolean,
    val syncClass: FolderClass,
    val displayClass: FolderClass,
    val notifyClass: FolderClass,
    val pushClass: FolderClass
)

data class DisplayFolder(
    val folder: Folder,
    val isInTopGroup: Boolean,
Loading