Be more selective when removing notifications

- When opening a message for viewing, only the notification for that particular message is removed.
- When displaying the Unified Inbox, notifications for messages in the Unified Inbox are removed.
- When displaying the "new messages" list, notifications for all messages in that account are removed.
- When displaying the message list of a single folder, notifications for messages in that folder are removed.

Fixes #1664

Notifications removed

Notifications removed
### Expected behavior

If there are multiple notifications for mails, the user expands the multiline 'compressed' notification and clicks on one of the mails, this mail should be opened in K-9 and should be removed from the notifications. The other mails should stay in the notifications.
### Actual behavior

If I open one mail from the notification bar, K-9 opens this mail but all mails are removed from the notifications.
### Steps to reproduce
1. Get multiple new mails (=notifications)
2. Expand the summarized notification to get all mails
3. Klick on one mail
### Environment

K-9 Mail version: 5.111

Android version: 7.0 (Nexus 5X)

Account type (IMAP, POP3, WebDAV/Exchange): IMAP

Possibly this is an enhancement. There is a difference of opinion as to whether notifications should clear:
- On opening the app
- On viewing the unified inbox / inbox for that account
- On viewing the message itself

We may need a setting for this.

Yes, I am totally with you. I also think a setting would be very helpful.

./gradlew :app:core:testDebugUnitTest --tests "com.fsck.k9.notification.NotificationDataStoreTest" --tests "com.fsck.k9.notification.NewMailNotificationManagerTest"

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 ba4ca011e00..341a4bf038e 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
@@ -127,6 +127,7 @@ public class MessagingController {
     private final MemorizingMessagingListener memorizingMessagingListener = new MemorizingMessagingListener();
     private final MessageCountsProvider messageCountsProvider;
     private final DraftOperations draftOperations;
+    private final NotificationOperations notificationOperations;

     private MessagingListener checkMailListener = null;
@@ -166,6 +167,7 @@ public void run() {
         initializeControllerExtensions(controllerExtensions);

         draftOperations = new DraftOperations(this, messageStoreManager, saveMessageDataCreator);
+        notificationOperations = new NotificationOperations(notificationController, preferences, messageStoreManager);
     }

     private void initializeControllerExtensions(List<ControllerExtension> controllerExtensions) {
@@ -2553,9 +2555,9 @@ public void setCheckMailListener(MessagingListener checkMailListener) {
         }
     }

-    public void removeNotificationsForAccount(Account account) {
-        put("removeNotificationsForAccount", null, () -> {
-            notificationController.clearNewMailNotifications(account, false);
+    public void clearNotifications(LocalSearch search) {
+        put("clearNotifications", null, () -> {
+            notificationOperations.clearNotifications(search);
         });
     }

diff --git a/app/core/src/main/java/com/fsck/k9/controller/NotificationOperations.kt b/app/core/src/main/java/com/fsck/k9/controller/NotificationOperations.kt
new file mode 100644
index 00000000000..8ab0a7f4485
--- /dev/null
+++ b/app/core/src/main/java/com/fsck/k9/controller/NotificationOperations.kt
@@ -0,0 +1,63 @@
+package com.fsck.k9.controller
+
+import com.fsck.k9.Account
+import com.fsck.k9.Preferences
+import com.fsck.k9.mailstore.MessageStoreManager
+import com.fsck.k9.notification.NotificationController
+import com.fsck.k9.search.LocalSearch
+import com.fsck.k9.search.isNewMessages
+import com.fsck.k9.search.isSingleFolder
+import com.fsck.k9.search.isUnifiedInbox
+
+internal class NotificationOperations(
+    private val notificationController: NotificationController,
+    private val preferences: Preferences,
+    private val messageStoreManager: MessageStoreManager
+) {
+    fun clearNotifications(search: LocalSearch) {
+        if (search.isUnifiedInbox) {
+            clearUnifiedInboxNotifications()
+        } else if (search.isNewMessages) {
+            clearAllNotifications()
+        } else if (search.isSingleFolder) {
+            val account = search.firstAccount() ?: return
+            val folderId = search.folderIds.first()
+            clearNotifications(account, folderId)
+        } else {
+            // TODO: Remove notifications when updating the message list. That way we can easily remove only
+            //  notifications for messages that are currently displayed in the list.
+        }
+    }
+
+    private fun clearUnifiedInboxNotifications() {
+        for (account in preferences.accounts) {
+            val messageStore = messageStoreManager.getMessageStore(account)
+
+            val folderIds = messageStore.getFolders(excludeLocalOnly = true) { folderDetails ->
+                if (folderDetails.isIntegrate) folderDetails.id else null
+            }.filterNotNull().toSet()
+
+            if (folderIds.isNotEmpty()) {
+                notificationController.clearNewMailNotifications(account) { messageReferences ->
+                    messageReferences.filter { messageReference -> messageReference.folderId in folderIds }
+                }
+            }
+        }
+    }
+
+    private fun clearAllNotifications() {
+        for (account in preferences.accounts) {
+            notificationController.clearNewMailNotifications(account, clearNewMessageState = false)
+        }
+    }
+
+    private fun clearNotifications(account: Account, folderId: Long) {
+        notificationController.clearNewMailNotifications(account) { messageReferences ->
+            messageReferences.filter { messageReference -> messageReference.folderId == folderId }
+        }
+    }
+
+    private fun LocalSearch.firstAccount(): Account? {
+        return preferences.getAccount(accountUuids.first())
+    }
+}
diff --git a/app/core/src/main/java/com/fsck/k9/notification/NewMailNotificationController.kt b/app/core/src/main/java/com/fsck/k9/notification/NewMailNotificationController.kt
index 9040c686aaf..19014486211 100644
--- a/app/core/src/main/java/com/fsck/k9/notification/NewMailNotificationController.kt
+++ b/app/core/src/main/java/com/fsck/k9/notification/NewMailNotificationController.kt
@@ -30,8 +30,16 @@ internal class NewMailNotificationController(
         processNewMailNotificationData(notificationData)
     }

-    fun removeNewMailNotification(account: Account, messageReference: MessageReference) {
-        val notificationData = newMailNotificationManager.removeNewMailNotification(account, messageReference)
+    fun removeNewMailNotifications(
+        account: Account,
+        clearNewMessageState: Boolean,
+        selector: (List<MessageReference>) -> List<MessageReference>
+    ) {
+        val notificationData = newMailNotificationManager.removeNewMailNotifications(
+            account,
+            clearNewMessageState,
+            selector
+        )

         if (notificationData != null) {
             processNewMailNotificationData(notificationData)
diff --git a/app/core/src/main/java/com/fsck/k9/notification/NewMailNotificationManager.kt b/app/core/src/main/java/com/fsck/k9/notification/NewMailNotificationManager.kt
index fbdaedc295d..8e1a8c3fe26 100644
--- a/app/core/src/main/java/com/fsck/k9/notification/NewMailNotificationManager.kt
+++ b/app/core/src/main/java/com/fsck/k9/notification/NewMailNotificationManager.kt
@@ -63,39 +63,36 @@ internal class NewMailNotificationManager(
         )
     }

-    fun removeNewMailNotification(account: Account, messageReference: MessageReference): NewMailNotificationData? {
-        val result = notificationRepository.removeNotification(account, messageReference) ?: return null
+    fun removeNewMailNotifications(
+        account: Account,
+        clearNewMessageState: Boolean,
+        selector: (List<MessageReference>) -> List<MessageReference>
+    ): NewMailNotificationData? {
+        val result = notificationRepository.removeNotifications(account, clearNewMessageState, selector) ?: return null

         val cancelNotificationIds = when {
-            result.shouldCancelNotification && result.notificationData.isEmpty() -> {
-                listOf(NotificationIds.getNewMailSummaryNotificationId(account), result.cancelNotificationId)
-            }
-            result.shouldCancelNotification -> {
-                listOf(result.cancelNotificationId)
+            result.notificationData.isEmpty() -> {
+                result.cancelNotificationIds + NotificationIds.getNewMailSummaryNotificationId(account)
             }
             else -> {
-                emptyList()
+                result.cancelNotificationIds
             }
         }

-        val singleNotificationDataList = if (result.shouldCreateNotification) {
-            listOf(
-                createSingleNotificationData(
-                    account = account,
-                    notificationId = result.notificationHolder.notificationId,
-                    content = result.notificationHolder.content,
-                    timestamp = result.notificationHolder.timestamp,
-                    addLockScreenNotification = result.notificationData.isSingleMessageNotification
-                )
+        val singleNotificationData = result.notificationHolders.map { notificationHolder ->
+            createSingleNotificationData(
+                account = account,
+                notificationId = notificationHolder.notificationId,
+                content = notificationHolder.content,
+                timestamp = notificationHolder.timestamp,
+                addLockScreenNotification = result.notificationData.isSingleMessageNotification
             )
-        } else {
-            emptyList()
         }

         return NewMailNotificationData(
             cancelNotificationIds = cancelNotificationIds,
             baseNotificationData = createBaseNotificationData(result.notificationData),
-            singleNotificationData = singleNotificationDataList,
+            singleNotificationData = singleNotificationData,
             summaryNotificationData = createSummaryNotificationData(result.notificationData, silent = true)
         )
     }
diff --git a/app/core/src/main/java/com/fsck/k9/notification/NotificationController.kt b/app/core/src/main/java/com/fsck/k9/notification/NotificationController.kt
index ddf27daf696..fe0144108e8 100644
--- a/app/core/src/main/java/com/fsck/k9/notification/NotificationController.kt
+++ b/app/core/src/main/java/com/fsck/k9/notification/NotificationController.kt
@@ -65,7 +65,13 @@ class NotificationController internal constructor(
     }

     fun removeNewMailNotification(account: Account, messageReference: MessageReference) {
-        newMailNotificationController.removeNewMailNotification(account, messageReference)
+        newMailNotificationController.removeNewMailNotifications(account, clearNewMessageState = true) {
+            listOf(messageReference)
+        }
+    }
+
+    fun clearNewMailNotifications(account: Account, selector: (List<MessageReference>) -> List<MessageReference>) {
+        newMailNotificationController.removeNewMailNotifications(account, clearNewMessageState = false, selector)
     }

     fun clearNewMailNotifications(account: Account, clearNewMessageState: Boolean) {
diff --git a/app/core/src/main/java/com/fsck/k9/notification/NotificationData.kt b/app/core/src/main/java/com/fsck/k9/notification/NotificationData.kt
index 89b6f217f3b..c50985022b2 100644
--- a/app/core/src/main/java/com/fsck/k9/notification/NotificationData.kt
+++ b/app/core/src/main/java/com/fsck/k9/notification/NotificationData.kt
@@ -1,6 +1,7 @@
 package com.fsck.k9.notification

 import com.fsck.k9.Account
+import com.fsck.k9.controller.MessageReference

 /**
  * Holds information about active and inactive new message notifications of an account.
@@ -16,6 +17,19 @@ internal data class NotificationData(
     val isSingleMessageNotification: Boolean
         get() = activeNotifications.size == 1

+    @OptIn(ExperimentalStdlibApi::class)
+    val messageReferences: List<MessageReference>
+        get() {
+            return buildList(capacity = newMessagesCount) {
+                for (activeNotification in activeNotifications) {
+                    add(activeNotification.content.messageReference)
+                }
+                for (inactiveNotification in inactiveNotifications) {
+                    add(inactiveNotification.content.messageReference)
+                }
+            }
+        }
+
     fun isEmpty() = activeNotifications.isEmpty()

     companion object {
diff --git a/app/core/src/main/java/com/fsck/k9/notification/NotificationDataStore.kt b/app/core/src/main/java/com/fsck/k9/notification/NotificationDataStore.kt
index 41b31adfdb4..51372b0a939 100644
--- a/app/core/src/main/java/com/fsck/k9/notification/NotificationDataStore.kt
+++ b/app/core/src/main/java/com/fsck/k9/notification/NotificationDataStore.kt
@@ -69,62 +69,76 @@ internal class NotificationDataStore {
     }

     @Synchronized
-    fun removeNotification(account: Account, messageReference: MessageReference): RemoveNotificationResult? {
+    fun removeNotifications(
+        account: Account,
+        selector: (List<MessageReference>) -> List<MessageReference>
+    ): RemoveNotificationsResult? {
         val notificationData = getNotificationData(account)
         if (notificationData.isEmpty()) return null

-        val notificationHolder = notificationData.activeNotifications.firstOrNull {
-            it.content.messageReference == messageReference
-        }
+        val removeMessageReferences = selector.invoke(notificationData.messageReferences)

-        return if (notificationHolder == null) {
-            val inactiveNotificationHolder = notificationData.inactiveNotifications.firstOrNull {
-                it.content.messageReference == messageReference
-            } ?: return null
+        val operations = mutableListOf<NotificationStoreOperation>()
+        val newNotificationHolders = mutableListOf<NotificationHolder>()
+        val cancelNotificationIds = mutableListOf<Int>()

-            val operations = listOf(
-                NotificationStoreOperation.Remove(messageReference)
-            )
+        for (messageReference in removeMessageReferences) {
+            val notificationHolder = notificationData.activeNotifications.firstOrNull {
+                it.content.messageReference == messageReference
+            }

-            val newNotificationData = notificationData.copy(
-                inactiveNotifications = notificationData.inactiveNotifications - inactiveNotificationHolder
-            )
-            notificationDataMap[account.uuid] = newNotificationData
+            if (notificationHolder == null) {
+                val inactiveNotificationHolder = notificationData.inactiveNotifications.firstOrNull {
+                    it.content.messageReference == messageReference
+                } ?: continue

-            RemoveNotificationResult.recreateSummaryNotification(newNotificationData, operations)
-        } else if (notificationData.inactiveNotifications.isNotEmpty()) {
-            val newNotificationHolder = notificationData.inactiveNotifications.first()
-                .toNotificationHolder(notificationHolder.notificationId)
+                operations.add(NotificationStoreOperation.Remove(messageReference))

-            val operations = listOf(
-                NotificationStoreOperation.Remove(messageReference),
-                NotificationStoreOperation.ChangeToActive(
-                    newNotificationHolder.content.messageReference,
-                    newNotificationHolder.notificationId
+                val newNotificationData = notificationData.copy(
+                    inactiveNotifications = notificationData.inactiveNotifications - inactiveNotificationHolder
+                )
+                notificationDataMap[account.uuid] = newNotificationData
+            } else if (notificationData.inactiveNotifications.isNotEmpty()) {
+                val newNotificationHolder = notificationData.inactiveNotifications.first()
+                    .toNotificationHolder(notificationHolder.notificationId)
+
+                newNotificationHolders.add(newNotificationHolder)
+                cancelNotificationIds.add(notificationHolder.notificationId)
+
+                operations.add(NotificationStoreOperation.Remove(messageReference))
+                operations.add(
+                    NotificationStoreOperation.ChangeToActive(
+                        newNotificationHolder.content.messageReference,
+                        newNotificationHolder.notificationId
+                    )
                 )
-            )

-            val newNotificationData = notificationData.copy(
-                activeNotifications = notificationData.activeNotifications - notificationHolder + newNotificationHolder,
-                inactiveNotifications = notificationData.inactiveNotifications.drop(1)
-            )
-            notificationDataMap[account.uuid] = newNotificationData
+                val newNotificationData = notificationData.copy(
+                    activeNotifications = notificationData.activeNotifications - notificationHolder +
+                        newNotificationHolder,
+                    inactiveNotifications = notificationData.inactiveNotifications.drop(1)
+                )
+                notificationDataMap[account.uuid] = newNotificationData
+            } else {
+                cancelNotificationIds.add(notificationHolder.notificationId)

-            RemoveNotificationResult.replaceNotification(newNotificationData, operations, newNotificationHolder)
-        } else {
-            val operations = listOf(
-                NotificationStoreOperation.Remove(messageReference),
-            )
+                operations.add(NotificationStoreOperation.Remove(messageReference))

-            val newNotificationData = notificationData.copy(
-                activeNotifications = notificationData.activeNotifications - notificationHolder
-            )
-            notificationDataMap[account.uuid] = newNotificationData
+                val newNotificationData = notificationData.copy(
+                    activeNotifications = notificationData.activeNotifications - notificationHolder
+                )
+                notificationDataMap[account.uuid] = newNotificationData
+            }
+        }

-            RemoveNotificationResult.cancelNotification(
-                newNotificationData,
-                operations,
-                notificationHolder.notificationId
+        return if (operations.isEmpty()) {
+            null
+        } else {
+            RemoveNotificationsResult(
+                notificationData = getNotificationData(account),
+                notificationStoreOperations = operations,
+                notificationHolders = newNotificationHolders,
+                cancelNotificationIds = cancelNotificationIds
             )
         }
     }
diff --git a/app/core/src/main/java/com/fsck/k9/notification/NotificationRepository.kt b/app/core/src/main/java/com/fsck/k9/notification/NotificationRepository.kt
index 5ac77e94047..b18f3686aa7 100644
--- a/app/core/src/main/java/com/fsck/k9/notification/NotificationRepository.kt
+++ b/app/core/src/main/java/com/fsck/k9/notification/NotificationRepository.kt
@@ -44,9 +44,15 @@ internal class NotificationRepository(
     }

     @Synchronized
-    fun removeNotification(account: Account, messageReference: MessageReference): RemoveNotificationResult? {
-        return notificationDataStore.removeNotification(account, messageReference)?.also { result ->
-            persistNotificationDataStoreChanges(account, result.notificationStoreOperations)
+    fun removeNotifications(
+        account: Account,
+        clearNewMessageState: Boolean = true,
+        selector: (List<MessageReference>) -> List<MessageReference>
+    ): RemoveNotificationsResult? {
+        return notificationDataStore.removeNotifications(account, selector)?.also { result ->
+            if (clearNewMessageState) {
+                persistNotificationDataStoreChanges(account, result.notificationStoreOperations)
+            }
         }
     }

diff --git a/app/core/src/main/java/com/fsck/k9/notification/RemoveNotificationResult.kt b/app/core/src/main/java/com/fsck/k9/notification/RemoveNotificationResult.kt
deleted file mode 100644
index cfad5cfa1be..00000000000
--- a/app/core/src/main/java/com/fsck/k9/notification/RemoveNotificationResult.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.fsck.k9.notification
-
-internal class RemoveNotificationResult private constructor(
-    val notificationData: NotificationData,
-    val notificationStoreOperations: List<NotificationStoreOperation>,
-    private val holder: NotificationHolder?,
-    private val notificationId: Int?
-) {
-    val shouldCreateNotification: Boolean
-        get() = holder != null
-
-    val notificationHolder: NotificationHolder
-        get() = holder ?: error("shouldCreateNotification == false")
-
-    val shouldCancelNotification: Boolean
-        get() = notificationId != null
-
-    val cancelNotificationId: Int
-        get() = notificationId ?: error("shouldCancelNotification == false")
-
-    companion object {
-        fun cancelNotification(
-            notificationData: NotificationData,
-            notificationStoreOperations: List<NotificationStoreOperation>,
-            notificationId: Int
-        ): RemoveNotificationResult {
-            return RemoveNotificationResult(
-                notificationData = notificationData,
-                notificationStoreOperations = notificationStoreOperations,
-                holder = null,
-                notificationId = notificationId
-            )
-        }
-
-        fun replaceNotification(
-            notificationData: NotificationData,
-            notificationStoreOperations: List<NotificationStoreOperation>,
-            notificationHolder: NotificationHolder
-        ): RemoveNotificationResult {
-            return RemoveNotificationResult(
-                notificationData = notificationData,
-                notificationStoreOperations = notificationStoreOperations,
-                holder = notificationHolder,
-                notificationId = notificationHolder.notificationId
-            )
-        }
-
-        fun recreateSummaryNotification(
-            notificationData: NotificationData,
-            notificationStoreOperations: List<NotificationStoreOperation>
-        ): RemoveNotificationResult {
-            return RemoveNotificationResult(
-                notificationData = notificationData,
-                notificationStoreOperations = notificationStoreOperations,
-                holder = null,
-                notificationId = null
-            )
-        }
-    }
-}
diff --git a/app/core/src/main/java/com/fsck/k9/notification/RemoveNotificationsResult.kt b/app/core/src/main/java/com/fsck/k9/notification/RemoveNotificationsResult.kt
new file mode 100644
index 00000000000..f3c2d44bffa
--- /dev/null
+++ b/app/core/src/main/java/com/fsck/k9/notification/RemoveNotificationsResult.kt
@@ -0,0 +1,8 @@
+package com.fsck.k9.notification
+
+internal data class RemoveNotificationsResult(
+    val notificationData: NotificationData,
+    val notificationStoreOperations: List<NotificationStoreOperation>,
+    val notificationHolders: List<NotificationHolder>,
+    val cancelNotificationIds: List<Int>
+)
diff --git a/app/core/src/main/java/com/fsck/k9/notification/SummaryNotificationDataCreator.kt b/app/core/src/main/java/com/fsck/k9/notification/SummaryNotificationDataCreator.kt
index fefe5c7d151..77e12728265 100644
--- a/app/core/src/main/java/com/fsck/k9/notification/SummaryNotificationDataCreator.kt
+++ b/app/core/src/main/java/com/fsck/k9/notification/SummaryNotificationDataCreator.kt
@@ -2,7 +2,6 @@ package com.fsck.k9.notification

 import com.fsck.k9.Account
 import com.fsck.k9.K9
-import com.fsck.k9.controller.MessageReference

 private const val MAX_NUMBER_OF_MESSAGES_FOR_SUMMARY_NOTIFICATION = 5

@@ -92,17 +91,4 @@ internal class SummaryNotificationDataCreator(

     private val NotificationData.additionalMessagesCount: Int
         get() = (newMessagesCount - MAX_NUMBER_OF_MESSAGES_FOR_SUMMARY_NOTIFICATION).coerceAtLeast(0)
-
-    @OptIn(ExperimentalStdlibApi::class)
-    private val NotificationData.messageReferences: List<MessageReference>
-        get() {
-            return buildList(capacity = newMessagesCount) {
-                for (activeNotification in activeNotifications) {
-                    add(activeNotification.content.messageReference)
-                }
-                for (inactiveNotification in inactiveNotifications) {
-                    add(inactiveNotification.content.messageReference)
-                }
-            }
-        }
 }
diff --git a/app/core/src/main/java/com/fsck/k9/search/LocalSearchExtensions.kt b/app/core/src/main/java/com/fsck/k9/search/LocalSearchExtensions.kt
index 8e49538dac5..d528446987b 100644
--- a/app/core/src/main/java/com/fsck/k9/search/LocalSearchExtensions.kt
+++ b/app/core/src/main/java/com/fsck/k9/search/LocalSearchExtensions.kt
@@ -5,6 +5,18 @@ package com.fsck.k9.search
 import com.fsck.k9.Account
 import com.fsck.k9.Preferences

+val LocalSearch.isUnifiedInbox: Boolean
+    get() = id == SearchAccount.UNIFIED_INBOX
+
+val LocalSearch.isNewMessages: Boolean
+    get() = id == SearchAccount.NEW_MESSAGES
+
+val LocalSearch.isSingleAccount: Boolean
+    get() = accountUuids.size == 1
+
+val LocalSearch.isSingleFolder: Boolean
+    get() = isSingleAccount && folderIds.size == 1
+
 @JvmName("getAccountsFromLocalSearch")
 fun LocalSearch.getAccounts(preferences: Preferences): List<Account> {
     val accounts = preferences.accounts
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt b/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt
index a027d0482b9..510982e817e 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt
@@ -32,6 +32,7 @@ import com.fsck.k9.Preferences
 import com.fsck.k9.account.BackgroundAccountRemover
 import com.fsck.k9.activity.compose.MessageActions
 import com.fsck.k9.controller.MessageReference
+import com.fsck.k9.controller.MessagingController
 import com.fsck.k9.fragment.MessageListFragment
 import com.fsck.k9.fragment.MessageListFragment.MessageListFragmentListener
 import com.fsck.k9.helper.Contacts
@@ -44,6 +45,7 @@ import com.fsck.k9.search.SearchAccount
 import com.fsck.k9.search.SearchSpecification
 import com.fsck.k9.search.SearchSpecification.SearchCondition
 import com.fsck.k9.search.SearchSpecification.SearchField
+import com.fsck.k9.search.isUnifiedInbox
 import com.fsck.k9.ui.BuildConfig
 import com.fsck.k9.ui.K9Drawer
 import com.fsck.k9.ui.R
@@ -92,6 +94,7 @@ open class MessageList :
     private val defaultFolderProvider: DefaultFolderProvider by inject()
     private val accountRemover: BackgroundAccountRemover by inject()
     private val generalSettingsManager: GeneralSettingsManager by inject()
+    private val messagingController: MessagingController by inject()

     private val permissionUiHelper: PermissionUiHelper = K9PermissionUiHelper(this)

@@ -347,6 +350,7 @@ open class MessageList :
                     }
                 }
                 setDrawerLockState()
+                onMessageListDisplayed()
             }
         }
     }
@@ -541,6 +545,10 @@ open class MessageList :
             recreate()
         }

+        if (displayMode != DisplayMode.MESSAGE_VIEW) {
+            onMessageListDisplayed()
+        }
+
         if (this !is Search) {
             // necessary b/c no guarantee Search.onStop will be called before MessageList.onResume
             // when returning from search results
@@ -613,6 +621,8 @@ open class MessageList :
         search.addAllowedFolder(folderId)

         performSearch(search)
+
+        onMessageListDisplayed()
     }

     private fun openFolderImmediately(folderId: Long) {
@@ -1462,6 +1472,8 @@ open class MessageList :

         showDefaultTitleView()
         configureMenu(menu)
+
+        onMessageListDisplayed()
     }

     private fun setDrawerLockState() {
@@ -1519,6 +1531,14 @@ open class MessageList :
         }
     }

+    private fun onMessageListDisplayed() {
+        clearNotifications()
+    }
+
+    private fun clearNotifications() {
+        messagingController.clearNotifications(search)
+    }
+
     override fun startIntentSenderForResult(
         intent: IntentSender,
         requestCode: Int,
@@ -1591,9 +1611,6 @@ open class MessageList :
         }
     }

-    private val LocalSearch.isUnifiedInbox: Boolean
-        get() = id == SearchAccount.UNIFIED_INBOX
-
     private fun MessageReference.toLocalSearch(): LocalSearch {
         return LocalSearch().apply {
             addAccountUuid(accountUuid)
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/fragment/MessageListFragment.kt b/app/ui/legacy/src/main/java/com/fsck/k9/fragment/MessageListFragment.kt
index 9d6ead01099..ecc400c3e98 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/fragment/MessageListFragment.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/fragment/MessageListFragment.kt
@@ -475,11 +475,6 @@ class MessageListFragment :

         messagingController.addListener(activityListener)

-        for (account in localSearch.getAccounts(preferences)) {
-            // TODO: Only remove notifications for messages in the currently displayed message list
-            messagingController.removeNotificationsForAccount(account)
-        }
-
         updateTitle()
     }

diff --git a/app/core/src/test/java/com/fsck/k9/notification/NewMailNotificationManagerTest.kt b/app/core/src/test/java/com/fsck/k9/notification/NewMailNotificationManagerTest.kt
index 7393229c32f..873dd7cb5f7 100644
--- a/app/core/src/test/java/com/fsck/k9/notification/NewMailNotificationManagerTest.kt
+++ b/app/core/src/test/java/com/fsck/k9/notification/NewMailNotificationManagerTest.kt
@@ -128,7 +128,9 @@ class NewMailNotificationManagerTest {

     @Test
     fun `remove notification when none was added before should return null`() {
-        val result = manager.removeNewMailNotification(account, createMessageReference("any"))
+        val result = manager.removeNewMailNotifications(account, clearNewMessageState = true) {
+            listOf(createMessageReference("any"))
+        }

         assertThat(result).isNull()
     }
@@ -144,7 +146,9 @@ class NewMailNotificationManagerTest {
         )
         manager.addNewMailNotification(account, message, silent = false)

-        val result = manager.removeNewMailNotification(account, createMessageReference("untracked"))
+        val result = manager.removeNewMailNotifications(account, clearNewMessageState = true) {
+            listOf(createMessageReference("untracked"))
+        }

         assertThat(result).isNull()
     }
@@ -160,7 +164,9 @@ class NewMailNotificationManagerTest {
         )
         manager.addNewMailNotification(account, message, silent = false)

-        val result = manager.removeNewMailNotification(account, createMessageReference("msg-1"))
+        val result = manager.removeNewMailNotifications(account, clearNewMessageState = true) {
+            listOf(createMessageReference("msg-1"))
+        }

         assertNotNull(result) { data ->
             assertThat(data.cancelNotificationIds).containsExactly(
@@ -200,7 +206,9 @@ class NewMailNotificationManagerTest {
         )
         manager.addNewMailNotification(account, messageThree, silent = true)

-        val result = manager.removeNewMailNotification(account, createMessageReference("msg-2"))
+        val result = manager.removeNewMailNotifications(account, clearNewMessageState = true) {
+            listOf(createMessageReference("msg-2"))
+        }

         assertNotNull(result) { data ->
             assertThat(data.cancelNotificationIds).isEqualTo(listOf(notificationIdTwo))
@@ -230,7 +238,9 @@ class NewMailNotificationManagerTest {
         manager.addNewMailNotification(account, message, silent = false)
         addMaximumNumberOfNotifications()

-        val result = manager.removeNewMailNotification(account, createMessageReference("msg-1"))
+        val result = manager.removeNewMailNotifications(account, clearNewMessageState = true) {
+            listOf(createMessageReference("msg-1"))
+        }

         assertNotNull(result) { data ->
             assertThat(data.cancelNotificationIds).hasSize(1)
diff --git a/app/core/src/test/java/com/fsck/k9/notification/NotificationDataStoreTest.kt b/app/core/src/test/java/com/fsck/k9/notification/NotificationDataStoreTest.kt
index 082902a2486..9bb2c110bb2 100644
--- a/app/core/src/test/java/com/fsck/k9/notification/NotificationDataStoreTest.kt
+++ b/app/core/src/test/java/com/fsck/k9/notification/NotificationDataStoreTest.kt
@@ -53,12 +53,12 @@ class NotificationDataStoreTest : RobolectricTest() {
         val content = createNotificationContent("1")
         notificationDataStore.addNotification(account, content, TIMESTAMP)

-        val result = notificationDataStore.removeNotification(account, content.messageReference)
+        val result = notificationDataStore.removeNotifications(account) { listOf(content.messageReference) }

         assertNotNull(result) { removeResult ->
-            assertThat(removeResult.cancelNotificationId)
-                .isEqualTo(NotificationIds.getSingleMessageNotificationId(account, 0))
-            assertThat(removeResult.shouldCreateNotification).isFalse()
+            assertThat(removeResult.cancelNotificationIds)
+                .containsExactly(NotificationIds.getSingleMessageNotificationId(account, 0))
+            assertThat(removeResult.notificationHolders).isEmpty()
         }
     }

@@ -77,16 +77,16 @@ class NotificationDataStoreTest : RobolectricTest() {
         val latestContent = createNotificationContent("10")
         notificationDataStore.addNotification(account, latestContent, TIMESTAMP)

-        val result = notificationDataStore.removeNotification(account, latestContent.messageReference)
+        val result = notificationDataStore.removeNotifications(account) { listOf(latestContent.messageReference) }

         assertNotNull(result) { removeResult ->
-            assertThat(removeResult.cancelNotificationId)
-                .isEqualTo(NotificationIds.getSingleMessageNotificationId(account, 1))
-            assertThat(removeResult.shouldCreateNotification).isTrue()
-            assertNotNull(removeResult.notificationHolder) { holder ->
-                assertThat(holder.notificationId).isEqualTo(NotificationIds.getSingleMessageNotificationId(account, 1))
-                assertThat(holder.content).isEqualTo(content)
-            }
+            assertThat(removeResult.cancelNotificationIds)
+                .containsExactly(NotificationIds.getSingleMessageNotificationId(account, 1))
+            assertThat(removeResult.notificationHolders).hasSize(1)
+
+            val holder = removeResult.notificationHolders.first()
+            assertThat(holder.notificationId).isEqualTo(NotificationIds.getSingleMessageNotificationId(account, 1))
+            assertThat(holder.content).isEqualTo(content)
         }
     }

@@ -95,7 +95,7 @@ class NotificationDataStoreTest : RobolectricTest() {
         for (i in 1..MAX_NUMBER_OF_NEW_MESSAGE_NOTIFICATIONS + 1) {
             val content = createNotificationContent(i.toString())
             notificationDataStore.addNotification(account, content, TIMESTAMP)
-            notificationDataStore.removeNotification(account, content.messageReference)
+            notificationDataStore.removeNotifications(account) { listOf(content.messageReference) }
         }
     }


Fetch `List-Post` header when downloading partial message

`ReplyToParser` currently checks the `List-Post` header to figure out which email address to use as recipient when replying. With this change we make sure the `List-Post` header is always available, i.e. we explicitly fetch it when downloading a partial message.

Fixes #6414

Problematic behavior with mailing lists

Problematic behavior with mailing lists
### Checklist

- [X] I have used the search function to see if someone else has already submitted the same bug report.
- [X] I will describe the problem with as much detail as possible.

### App version

6.305

### Where did you get the app from?

Other

### Android version

Android 11 custom ROM

### Device model

Crosscall Core X4

### Steps to reproduce

Hello,

When I receive an email with a mailing list (known and not local) as recipient, I have the following behavior :
**Incomplete mail :**
_Answer :_ just mail author (label)(normal behavior)
_Answer all :_ mail author (label) + list (label)(normal behavior)
**Complete mail :**
_Answer :_ list (mail without label) (abnormal behavior)
_Answer all :_ mail author (label)+ list twice (one with label, one without) (abnormal behavior)

### Expected behavior

See steps to reproduce

### Actual behavior

See steps to reproduce

### Logs

_No response_

Hello @SvSky
I have been trying to reproduce the issue you described ((in Samsung A32, Android 13)), but I am having difficulty doing so. I was wondering if you could provide me with more information on the scenario you experienced in order to help us resolve the issue. you say the same word in expected behavior and actual behavior.
could you attach a picture or video for this scenario?

Your assistance in this matter would be greatly appreciated.
Hello,
I'm sorry, i tried to describe as well as possible but my english is far from good.
I temporarily solved by modifying the following code :

    if (replyToAddresses.length > 0) {
        candidateAddress = replyToAddresses;
    } else if (listPostAddresses.length > 0) {
        candidateAddress = listPostAddresses;
    } else {
        candidateAddress = fromAddresses;
    }

by :

//    if (replyToAddresses.length > 0) {
//        candidateAddress = replyToAddresses;
//    } else if (listPostAddresses.length > 0) {
//        candidateAddress = listPostAddresses;
//    } else {
        candidateAddress = fromAddresses;
//    }

in ReplyToParser.java

I didn't want to commit because it was a really specific problem and I didn't know the reasons for the order inside "if"

I can give you the example of my case to understand what was my problem.
My secretary sent an email (to a list with all the staff) asking for medical data.
When replying from k9, we fall into the case where "listPostAddresses" is not empty (which was the case), and that sent an email (from one of the people on the list) to the whole list, instead of sending it in reply to the secretary (person in "fromAddresses")

I hope I was able to shed some light on the problem I had

./gradlew :mail:protocols:imap:test --tests "com.fsck.k9.mail.store.imap.RealImapFolderTest"

diff --git a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolder.kt b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolder.kt
index 6746d890520..26ff4103d48 100644
--- a/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolder.kt
+++ b/mail/protocols/imap/src/main/java/com/fsck/k9/mail/store/imap/RealImapFolder.kt
@@ -543,7 +543,7 @@ internal class RealImapFolder(
             fetchFields.add("RFC822.SIZE")
             fetchFields.add(
                 "BODY.PEEK[HEADER.FIELDS (date subject from content-type to cc bcc " +
-                    "reply-to message-id references in-reply-to list-unsubscribe sender " +
+                    "reply-to message-id references in-reply-to list-post list-unsubscribe sender " +
                     K9MailLib.IDENTITY_HEADER + " " + K9MailLib.CHAT_HEADER + ")]",
             )
         }

diff --git a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapFolderTest.kt b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapFolderTest.kt
index 27225051ea0..c87615f2c8d 100644
--- a/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapFolderTest.kt
+++ b/mail/protocols/imap/src/test/java/com/fsck/k9/mail/store/imap/RealImapFolderTest.kt
@@ -673,7 +673,7 @@ class RealImapFolderTest {
         verify(imapConnection).sendCommand(
             "UID FETCH 1 (UID INTERNALDATE RFC822.SIZE BODY.PEEK[HEADER.FIELDS " +
                 "(date subject from content-type to cc bcc reply-to message-id references in-reply-to " +
-                "list-unsubscribe sender X-K9mail-Identity Chat-Version)])",
+                "list-post list-unsubscribe sender X-K9mail-Identity Chat-Version)])",
             false,
         )
     }


Add drawer account view

Add new drawer `AccountView`.

![AccountView](https://github.com/user-attachments/assets/a498675b-d92c-4871-a55c-f0c6b78208cb)

Resolves #8120

Add account top view

Add account top view
Implement the account top view highlighted in this design:

![Image](https://github.com/user-attachments/assets/2deb4b6e-37f4-4d29-8721-28847601ab37)

diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/SelectThemeColorScheme.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/SelectThemeColorScheme.kt
index ee9b3519cba..d1f44da6d8a 100644
--- a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/SelectThemeColorScheme.kt
+++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/SelectThemeColorScheme.kt
@@ -113,4 +113,4 @@ private fun ColorScheme.toDynamicThemeColorScheme(
     )
 }

-private fun Color.toHarmonizedColor(target: Color) = Color(MaterialColors.harmonize(toArgb(), target.toArgb()))
+fun Color.toHarmonizedColor(target: Color) = Color(MaterialColors.harmonize(toArgb(), target.toArgb()))
diff --git a/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContentPreview.kt b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContentPreview.kt
index b3c598e25d3..0d377461317 100644
--- a/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContentPreview.kt
+++ b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContentPreview.kt
@@ -3,11 +3,30 @@ package app.k9mail.feature.navigation.drawer.ui
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.tooling.preview.Preview
 import app.k9mail.core.ui.compose.designsystem.PreviewWithTheme
+import app.k9mail.feature.navigation.drawer.ui.account.FakeData.DISPLAY_ACCOUNT

 @Composable
 @Preview(showBackground = true)
 internal fun DrawerContentPreview() {
     PreviewWithTheme {
-        DrawerContent()
+        DrawerContent(
+            state = DrawerContract.State(
+                accounts = emptyList(),
+                currentAccount = null,
+            ),
+        )
+    }
+}
+
+@Composable
+@Preview(showBackground = true)
+fun DrawerContentWithAccountPreview() {
+    PreviewWithTheme {
+        DrawerContent(
+            state = DrawerContract.State(
+                accounts = listOf(DISPLAY_ACCOUNT),
+                currentAccount = DISPLAY_ACCOUNT,
+            ),
+        )
     }
 }
diff --git a/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountIndicatorPreview.kt b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountIndicatorPreview.kt
new file mode 100644
index 00000000000..dd9930c2123
--- /dev/null
+++ b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountIndicatorPreview.kt
@@ -0,0 +1,43 @@
+package app.k9mail.feature.navigation.drawer.ui.account
+
+import androidx.compose.foundation.layout.height
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.tooling.preview.Preview
+import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
+import app.k9mail.core.ui.compose.theme2.MainTheme
+
+@Composable
+@Preview(showBackground = true)
+internal fun AccountIndicatorPreview() {
+    PreviewWithThemes {
+        AccountIndicator(
+            accountColor = 0,
+            modifier = Modifier.height(MainTheme.spacings.double),
+        )
+    }
+}
+
+@Composable
+@Preview(showBackground = true)
+internal fun AccountIndicatorPreviewWithYellowAccountColor() {
+    PreviewWithThemes {
+        AccountIndicator(
+            accountColor = Color.Yellow.toArgb(),
+            modifier = Modifier.height(MainTheme.spacings.double),
+        )
+    }
+}
+
+@Composable
+@Preview(showBackground = true)
+internal fun AccountIndicatorPreviewWithGrayAccountColor() {
+    PreviewWithThemes {
+        AccountIndicator(
+            accountColor = Color.Gray.toArgb(),
+            modifier = Modifier.height(MainTheme.spacings.double),
+        )
+    }
+}
diff --git a/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountViewPreview.kt b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountViewPreview.kt
new file mode 100644
index 00000000000..b268f894894
--- /dev/null
+++ b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountViewPreview.kt
@@ -0,0 +1,56 @@
+package app.k9mail.feature.navigation.drawer.ui.account
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.tooling.preview.Preview
+import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
+import app.k9mail.feature.navigation.drawer.ui.account.FakeData.DISPLAY_NAME
+import app.k9mail.feature.navigation.drawer.ui.account.FakeData.EMAIL_ADDRESS
+import app.k9mail.feature.navigation.drawer.ui.account.FakeData.LONG_TEXT
+
+@Composable
+@Preview(showBackground = true)
+internal fun AccountViewPreview() {
+    PreviewWithThemes {
+        AccountView(
+            displayName = DISPLAY_NAME,
+            emailAddress = EMAIL_ADDRESS,
+            accountColor = 0,
+        )
+    }
+}
+
+@Composable
+@Preview(showBackground = true)
+internal fun AccountViewWithColorPreview() {
+    PreviewWithThemes {
+        AccountView(
+            displayName = DISPLAY_NAME,
+            emailAddress = EMAIL_ADDRESS,
+            accountColor = 0xFF0000,
+        )
+    }
+}
+
+@Composable
+@Preview(showBackground = true)
+internal fun AccountViewWithLongDisplayName() {
+    PreviewWithThemes {
+        AccountView(
+            displayName = "$LONG_TEXT $DISPLAY_NAME",
+            emailAddress = EMAIL_ADDRESS,
+            accountColor = 0,
+        )
+    }
+}
+
+@Composable
+@Preview(showBackground = true)
+internal fun AccountViewWithLongEmailPreview() {
+    PreviewWithThemes {
+        AccountView(
+            displayName = DISPLAY_NAME,
+            emailAddress = "$LONG_TEXT@example.com",
+            accountColor = 0,
+        )
+    }
+}
diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/NavigationDrawerModule.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/NavigationDrawerModule.kt
index 0c17eb6c327..edb1f961952 100644
--- a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/NavigationDrawerModule.kt
+++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/NavigationDrawerModule.kt
@@ -39,5 +39,9 @@ val navigationDrawerModule: Module = module {
         )
     }

-    viewModel { DrawerViewModel() }
+    viewModel {
+        DrawerViewModel(
+            getDisplayAccounts = get(),
+        )
+    }
 }
diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/DomainContract.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/DomainContract.kt
index 61d6fcc9bd1..dec8f13f825 100644
--- a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/DomainContract.kt
+++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/DomainContract.kt
@@ -7,7 +7,7 @@ interface DomainContract {

     interface UseCase {
         fun interface GetDisplayAccounts {
-            fun execute(): Flow<List<DisplayAccount>>
+            operator fun invoke(): Flow<List<DisplayAccount>>
         }
     }
 }
diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/usecase/GetDisplayAccounts.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/usecase/GetDisplayAccounts.kt
index 7327e90c478..17e1435a003 100644
--- a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/usecase/GetDisplayAccounts.kt
+++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/usecase/GetDisplayAccounts.kt
@@ -27,7 +27,7 @@ class GetDisplayAccounts(
 ) : UseCase.GetDisplayAccounts {

     @OptIn(ExperimentalCoroutinesApi::class)
-    override fun execute(): Flow<List<DisplayAccount>> {
+    override fun invoke(): Flow<List<DisplayAccount>> {
         return accountManager.getAccountsFlow()
             .flatMapLatest { accounts ->
                 val messageCountsFlows: List<Flow<MessageCounts>> = accounts.map { account ->
diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/legacy/AccountsViewModel.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/legacy/AccountsViewModel.kt
index ca630d6baba..351a9107d0c 100644
--- a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/legacy/AccountsViewModel.kt
+++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/legacy/AccountsViewModel.kt
@@ -9,5 +9,5 @@ import app.k9mail.feature.navigation.drawer.domain.entity.DisplayAccount
 class AccountsViewModel(
     getDisplayAccounts: UseCase.GetDisplayAccounts,
 ) : ViewModel() {
-    val displayAccountsLiveData: LiveData<List<DisplayAccount>> = getDisplayAccounts.execute().asLiveData()
+    val displayAccountsLiveData: LiveData<List<DisplayAccount>> = getDisplayAccounts().asLiveData()
 }
diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContent.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContent.kt
index b0319911310..fd765cacfee 100644
--- a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContent.kt
+++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContent.kt
@@ -1,17 +1,23 @@
 package app.k9mail.feature.navigation.drawer.ui

+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.testTag
+import app.k9mail.core.ui.compose.designsystem.atom.DividerHorizontal
 import app.k9mail.core.ui.compose.designsystem.atom.Surface
 import app.k9mail.core.ui.compose.designsystem.organism.drawer.NavigationDrawerItem
 import app.k9mail.core.ui.compose.theme2.MainTheme
+import app.k9mail.feature.navigation.drawer.ui.DrawerContract.State
+import app.k9mail.feature.navigation.drawer.ui.account.AccountView

 @Composable
 fun DrawerContent(
+    state: State,
     modifier: Modifier = Modifier,
 ) {
     Surface(
@@ -19,33 +25,48 @@ fun DrawerContent(
             .fillMaxSize()
             .testTag("DrawerContent"),
     ) {
-        LazyColumn(
+        Column(
             modifier = Modifier
                 .fillMaxSize()
                 .padding(
                     vertical = MainTheme.spacings.oneHalf,
                 ),
+            verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default),
         ) {
-            item {
-                NavigationDrawerItem(
-                    label = "Folder1",
-                    selected = true,
-                    onClick = {},
-                )
-            }
-            item {
-                NavigationDrawerItem(
-                    label = "Folder2",
-                    selected = false,
-                    onClick = {},
+            state.currentAccount?.let {
+                AccountView(
+                    displayName = it.account.displayName,
+                    emailAddress = it.account.email,
+                    accountColor = it.account.chipColor,
                 )
+
+                DividerHorizontal()
             }
-            item {
-                NavigationDrawerItem(
-                    label = "Folder3",
-                    selected = false,
-                    onClick = {},
-                )
+            LazyColumn(
+                modifier = Modifier
+                    .fillMaxSize(),
+            ) {
+                item {
+                    NavigationDrawerItem(
+                        label = "Folder1",
+                        selected = true,
+                        onClick = {},
+                    )
+                }
+                item {
+                    NavigationDrawerItem(
+                        label = "Folder2",
+                        selected = false,
+                        onClick = {},
+                    )
+                }
+                item {
+                    NavigationDrawerItem(
+                        label = "Folder3",
+                        selected = false,
+                        onClick = {},
+                    )
+                }
             }
         }
     }
diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContract.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContract.kt
index d06a981cd02..3f1cf11f9e8 100644
--- a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContract.kt
+++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContract.kt
@@ -1,12 +1,15 @@
 package app.k9mail.feature.navigation.drawer.ui

 import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
+import app.k9mail.feature.navigation.drawer.domain.entity.DisplayAccount

 interface DrawerContract {

     interface ViewModel : UnidirectionalViewModel<State, Event, Effect>

     data class State(
+        val currentAccount: DisplayAccount? = null,
+        val accounts: List<DisplayAccount> = emptyList(),
         val isLoading: Boolean = false,
     )

diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerView.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerView.kt
index 22411299d59..01a4b5b6ce0 100644
--- a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerView.kt
+++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerView.kt
@@ -15,6 +15,8 @@ fun DrawerView(
         isRefreshing = state.value.isLoading,
         onRefresh = { dispatch(DrawerContract.Event.OnRefresh) },
     ) {
-        DrawerContent()
+        DrawerContent(
+            state = state.value,
+        )
     }
 }
diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerViewModel.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerViewModel.kt
index 945e20eac8e..65b43bb4740 100644
--- a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerViewModel.kt
+++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerViewModel.kt
@@ -2,6 +2,8 @@ package app.k9mail.feature.navigation.drawer.ui

 import androidx.lifecycle.viewModelScope
 import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
+import app.k9mail.feature.navigation.drawer.domain.DomainContract.UseCase
+import app.k9mail.feature.navigation.drawer.domain.entity.DisplayAccount
 import app.k9mail.feature.navigation.drawer.ui.DrawerContract.Effect
 import app.k9mail.feature.navigation.drawer.ui.DrawerContract.Event
 import app.k9mail.feature.navigation.drawer.ui.DrawerContract.State
@@ -11,12 +13,35 @@ import kotlinx.coroutines.launch

 @Suppress("MagicNumber")
 class DrawerViewModel(
+    private val getDisplayAccounts: UseCase.GetDisplayAccounts,
     initialState: State = State(),
 ) : BaseViewModel<State, Event, Effect>(
     initialState = initialState,
 ),
     ViewModel {

+    init {
+        viewModelScope.launch {
+            getDisplayAccounts().collect { accounts -> updateAccounts(accounts) }
+        }
+    }
+
+    private fun updateAccounts(accounts: List<DisplayAccount>) {
+        val currentAccountUuid = state.value.currentAccount?.account?.uuid
+        val isCurrentAccountAvailable = accounts.any { currentAccountUuid == it.account.uuid }
+
+        updateState {
+            if (isCurrentAccountAvailable) {
+                it.copy(accounts = accounts)
+            } else {
+                it.copy(
+                    currentAccount = accounts.firstOrNull(),
+                    accounts = accounts,
+                )
+            }
+        }
+    }
+
     override fun event(event: Event) {
         when (event) {
             Event.OnRefresh -> refresh()
diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountIndicator.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountIndicator.kt
new file mode 100644
index 00000000000..fadd7734756
--- /dev/null
+++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountIndicator.kt
@@ -0,0 +1,32 @@
+package app.k9mail.feature.navigation.drawer.ui.account
+
+import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import app.k9mail.core.ui.compose.designsystem.atom.Surface
+import app.k9mail.core.ui.compose.theme2.MainTheme
+import app.k9mail.core.ui.compose.theme2.toHarmonizedColor
+
+@Composable
+fun AccountIndicator(
+    accountColor: Int,
+    modifier: Modifier = Modifier,
+) {
+    val color = if (accountColor == 0) {
+        MainTheme.colors.primary
+    } else {
+        Color(accountColor).toHarmonizedColor(MainTheme.colors.surface)
+    }
+
+    Surface(
+        modifier = modifier
+            .width(MainTheme.spacings.half)
+            .defaultMinSize(
+                minHeight = MainTheme.spacings.default,
+            ),
+        color = color,
+        shape = MainTheme.shapes.medium,
+    ) {}
+}
diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountView.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountView.kt
new file mode 100644
index 00000000000..d4d16d78894
--- /dev/null
+++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountView.kt
@@ -0,0 +1,58 @@
+package app.k9mail.feature.navigation.drawer.ui.account
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyLarge
+import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyMedium
+import app.k9mail.core.ui.compose.theme2.MainTheme
+
+@Composable
+fun AccountView(
+    displayName: String,
+    emailAddress: String,
+    accountColor: Int,
+    modifier: Modifier = Modifier,
+) {
+    Row(
+        modifier = modifier
+            .fillMaxWidth()
+            .height(intrinsicSize = IntrinsicSize.Max)
+            .padding(
+                top = MainTheme.spacings.default,
+                start = MainTheme.spacings.double,
+                end = MainTheme.spacings.triple,
+                bottom = MainTheme.spacings.oneHalf,
+            ),
+        verticalAlignment = Alignment.CenterVertically,
+    ) {
+        AccountIndicator(
+            accountColor = accountColor,
+            modifier = Modifier
+                .fillMaxHeight()
+                .padding(
+                    end = MainTheme.spacings.default,
+                ),
+        )
+        Column(
+            verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.half),
+        ) {
+            TextBodyLarge(
+                text = displayName,
+                color = MainTheme.colors.onSurface,
+            )
+            TextBodyMedium(
+                text = emailAddress,
+                color = MainTheme.colors.onSurfaceVariant,
+            )
+        }
+    }
+}

diff --git a/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/FakeData.kt b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/FakeData.kt
new file mode 100644
index 00000000000..3bcbd017480
--- /dev/null
+++ b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/FakeData.kt
@@ -0,0 +1,37 @@
+package app.k9mail.feature.navigation.drawer.ui.account
+
+import app.k9mail.feature.navigation.drawer.domain.entity.DisplayAccount
+import app.k9mail.legacy.account.Account
+import app.k9mail.legacy.account.Identity
+
+internal object FakeData {
+
+    const val ACCOUNT_UUID = "uuid"
+    const val DISPLAY_NAME = "Account Name"
+    const val EMAIL_ADDRESS = "test@example.com"
+
+    const val LONG_TEXT = "loremipsumdolorsitametconsetetursadipscingelitr" +
+        "seddiamnonumyeirmodtemporinviduntutlaboreetdoloremagnaaliquyameratseddiamvoluptua"
+
+    val ACCOUNT = Account(
+        uuid = ACCOUNT_UUID,
+    ).apply {
+        identities = ArrayList()
+
+        val identity = Identity(
+            signatureUse = false,
+            signature = "",
+            description = "",
+        )
+        identities.add(identity)
+
+        name = DISPLAY_NAME
+        email = EMAIL_ADDRESS
+    }
+
+    val DISPLAY_ACCOUNT = DisplayAccount(
+        account = ACCOUNT,
+        unreadMessageCount = 0,
+        starredMessageCount = 0,
+    )
+}
diff --git a/feature/navigation/drawer/src/test/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerViewModelTest.kt b/feature/navigation/drawer/src/test/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerViewModelTest.kt
index a0f39c6bfab..c1d0725bc4f 100644
--- a/feature/navigation/drawer/src/test/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerViewModelTest.kt
+++ b/feature/navigation/drawer/src/test/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerViewModelTest.kt
@@ -2,12 +2,18 @@ package app.k9mail.feature.navigation.drawer.ui

 import app.k9mail.core.ui.compose.testing.MainDispatcherRule
 import app.k9mail.core.ui.compose.testing.mvi.eventStateTest
+import app.k9mail.feature.navigation.drawer.domain.entity.DisplayAccount
 import app.k9mail.feature.navigation.drawer.ui.DrawerContract.Event
 import app.k9mail.feature.navigation.drawer.ui.DrawerContract.State
+import app.k9mail.legacy.account.Account
+import app.k9mail.legacy.account.Identity
 import assertk.assertThat
 import assertk.assertions.isEqualTo
 import kotlin.test.Test
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.test.advanceUntilIdle
 import kotlinx.coroutines.test.runTest
 import org.junit.Rule
@@ -18,10 +24,10 @@ class DrawerViewModelTest {
     @get:Rule
     val mainDispatcherRule = MainDispatcherRule()

-    private val testSubject = DrawerViewModel()
-
     @Test
     fun `should change loading state when OnRefresh event is received`() = runTest {
+        val testSubject = createTestSubject()
+
         eventStateTest(
             viewModel = testSubject,
             initialState = State(isLoading = false),
@@ -34,4 +40,98 @@ class DrawerViewModelTest {

         assertThat(testSubject.state.value.isLoading).isEqualTo(false)
     }
+
+    @Test
+    fun `should collect display accounts when created and select first as current`() = runTest {
+        val displayAccounts = createDisplayAccountList(3)
+        val getDisplayAccountsFlow = MutableStateFlow(displayAccounts)
+        val testSubject = createTestSubject(
+            getDisplayAccountsFlow = getDisplayAccountsFlow,
+        )
+
+        advanceUntilIdle()
+
+        assertThat(testSubject.state.value.accounts.size).isEqualTo(displayAccounts.size)
+        assertThat(testSubject.state.value.accounts).isEqualTo(displayAccounts)
+        assertThat(testSubject.state.value.currentAccount).isEqualTo(displayAccounts.first())
+    }
+
+    @Test
+    fun `should reselect current account when old not present anymore`() = runTest {
+        val displayAccounts = createDisplayAccountList(3)
+        val getDisplayAccountsFlow = MutableStateFlow(displayAccounts)
+        val testSubject = createTestSubject(
+            getDisplayAccountsFlow = getDisplayAccountsFlow,
+        )
+
+        advanceUntilIdle()
+
+        val newDisplayAccounts = displayAccounts.drop(1)
+        getDisplayAccountsFlow.emit(newDisplayAccounts)
+
+        advanceUntilIdle()
+
+        assertThat(testSubject.state.value.accounts.size).isEqualTo(newDisplayAccounts.size)
+        assertThat(testSubject.state.value.accounts).isEqualTo(newDisplayAccounts)
+        assertThat(testSubject.state.value.currentAccount).isEqualTo(newDisplayAccounts.first())
+    }
+
+    @Test
+    fun `should set current account to null when no accounts are present`() = runTest {
+        val getDisplayAccountsFlow = MutableStateFlow(emptyList<DisplayAccount>())
+        val testSubject = createTestSubject(
+            getDisplayAccountsFlow = getDisplayAccountsFlow,
+        )
+
+        advanceUntilIdle()
+
+        assertThat(testSubject.state.value.accounts.size).isEqualTo(0)
+        assertThat(testSubject.state.value.currentAccount).isEqualTo(null)
+    }
+
+    private fun createTestSubject(
+        getDisplayAccountsFlow: Flow<List<DisplayAccount>> = flow { emit(emptyList()) },
+    ): DrawerViewModel {
+        return DrawerViewModel(
+            getDisplayAccounts = { getDisplayAccountsFlow },
+        )
+    }
+
+    private fun createDisplayAccount(
+        uuid: String = "uuid",
+        name: String = "name",
+        email: String = "test@example.com",
+        unreadCount: Int = 0,
+        starredCount: Int = 0,
+    ): DisplayAccount {
+        val account = Account(
+            uuid = uuid,
+        ).also {
+            it.identities = ArrayList()
+
+            val identity = Identity(
+                signatureUse = false,
+                signature = "",
+                description = "",
+            )
+            it.identities.add(identity)
+
+            it.name = name
+            it.email = email
+        }
+
+        return DisplayAccount(
+            account = account,
+            unreadMessageCount = unreadCount,
+            starredMessageCount = starredCount,
+        )
+    }
+
+    private fun createDisplayAccountList(count: Int): List<DisplayAccount> {
+        return List(count) { index ->
+            createDisplayAccount(
+                uuid = "uuid-$index",
+            )
+        }
+    }
 }