Repository: thunderbird/thunderbird-android (language: kotlin)

Issue:
Message view redesign
We're currently working on a new design for the message view. A (non-final) preview can be seen below.

### Current state

<img src="https://user-images.githubusercontent.com/218061/190913295-709ad7d1-5f93-407a-a4b3-1fc2ae91f53c.png" alt="image" width=300>

### Preview

![Message header - HiFi - no download](https://user-images.githubusercontent.com/218061/190913577-e27dd2f1-6b93-4a7b-a332-3cb3ada070f6.png)


Notes on the image:
- The chip containing "Thunderbird Ryan" is the name of the account the message belongs to. The chip is colored using the account color. It will only be displayed if there's more than one account.
- The text below the chip is the subject of the message.
- The circle-shaped image is the contact picture of the sender of the message.
- The text at the top of the gray area, to the right of the contact picture is the name of the sender.
- To the right we have a reply action and an overflow menu containing more actions.
- Below the sender name there's information about the recipients. If the account only has one identity and the message was addressed to it, "to me" will be displayed. If the account has multiple identities the text that is shown will be "to &lt;name of identity>". If the message was addressed to someone else their display name will be shows. For now at most two recipient names will be displayed. If there's more recipients "+x" will be shown.
- If a crypto provider is enabled for the account, an encryption indicator is displayed in front of the recipient text.
- The "Important, Todo, Work" text is labels added to the message. This won't be relevant until #769 is implemented.
- The "show remote images" button is not final and might move and/or change appearance.

Tapping any part of the gray area other than the action buttons on the right (reply, overflow menu), will open a bottom sheet containing details about the message.

![Message header - Bottom Drawer HiFi](https://user-images.githubusercontent.com/218061/190913663-8bb66aa2-3bbc-4b31-8479-21d770af4d73.png)

If there are a lot of recipients the bottom sheet can be dragged up to make it cover the whole screen. In that case we intend to provide a way to search/filter the recipient list.


### Feedback

We're still working on the design. But if you're missing a feature or have other feedback, please let us know in a comment.

---
### Initial design considerations

A message view redesign should include the following features:
- #6180

And it should keep in mind these features that we want to add further down the line:
- #6181 
   Email addresses need to be far enough apart so tapping them reliably is not a problem.
- #1119 
   We probably want to display the email address below the name and then hide the recipient addresses.
- #769
   Figure out where to display them.
- #762 
  Figure out where to display the folder name(s) (JMAP supports messages living in multiple folders).
- #6197 
  All of the per-message information/actions should live in the message header area, because at some point we'll want to display more than one message in this view. We need some touch area that collapses/expands the whole message.
- #1397 
  We need an overflow menu or some other place to put (less important) per-message actions like this.

This is what the message view looks like in other apps…

Gmail:
<img src="https://user-images.githubusercontent.com/218061/181325163-382d986e-24b6-450e-a48b-8802d9cc1c8b.png" alt="image" width=300> <img src="https://user-images.githubusercontent.com/218061/181325707-71db978e-5d33-4bd2-9deb-1b52509f2a03.png" alt="image" width=300>

Outlook:
<img src="https://user-images.githubusercontent.com/218061/181329504-6e21e461-87b6-4db2-a4dc-cdbde0c7d7a8.png" alt="image" width=300> <img src="https://user-images.githubusercontent.com/218061/181331199-cdc8b989-e39f-4a9b-af5f-9176f8bf0d3f.png" alt="image" width=300>

FairEmail:
<img src="https://user-images.githubusercontent.com/218061/181333692-a178adc5-f01c-480d-ba79-13e34e2bba21.png" alt="image" width=300> <img src="https://user-images.githubusercontent.com/218061/181334269-1cce6bc4-9079-46fe-b2d5-c210b9ea52f7.png" alt="image" width=300>

EDIT 2022-09-18: Updated with latest design.


Discussion hints:
What do you think about moving the "Load Images" button above the Line of Death? Granted, showing images is already loading offsite resources, _but_ it's not potentially taking you in your browser to some arbitrary link.

I imagine someone could CSS up a link that looks pretty close (although not exactly) like your button at the top of an email's body without using any images. (Maybe with Unicode characters?)

Pull request: Add the redesigned message view screen
This merges the `message-view-redesign` branch into `main`. The current state should be stable enough to be included in a beta build.

Closes #6198

Test command: ./gradlew :app:ui:legacy:testDebugUnitTest --tests "com.fsck.k9.ui.messageview.DisplayRecipientsExtractorTest" --tests "com.fsck.k9.ui.messageview.RecipientLayoutCreatorTest" --tests "com.fsck.k9.view.MessageHeaderTest" :app:core:testDebugUnitTest --tests "com.fsck.k9.message.ReplyActionStrategyTest" --tests "com.fsck.k9.helper.RealAddressFormatterTest" :app:storage:testDebugUnitTest --tests "com.fsck.k9.storage.messages.RetrieveMessageOperationsTest" :mail:protocols:imap:test --tests "com.fsck.k9.mail.store.imap.RealImapFolderTest"

Fix patch:
diff --git a/app/core/build.gradle b/app/core/build.gradle
index 9c8ca307969..46bba048477 100644
--- a/app/core/build.gradle
+++ b/app/core/build.gradle
@@ -25,6 +25,7 @@ dependencies {
     implementation libs.moshi
     implementation libs.timber
     implementation libs.mime4j.core
+    implementation libs.mime4j.dom
 
     testImplementation project(':mail:testing')
     testImplementation project(":backend:imap")
diff --git a/app/core/src/main/java/com/fsck/k9/FontSizes.java b/app/core/src/main/java/com/fsck/k9/FontSizes.java
index 94e050f7071..f6c7ce7689c 100644
--- a/app/core/src/main/java/com/fsck/k9/FontSizes.java
+++ b/app/core/src/main/java/com/fsck/k9/FontSizes.java
@@ -16,10 +16,7 @@ public class FontSizes {
     private static final String MESSAGE_LIST_DATE = "fontSizeMessageListDate";
     private static final String MESSAGE_LIST_PREVIEW = "fontSizeMessageListPreview";
     private static final String MESSAGE_VIEW_SENDER = "fontSizeMessageViewSender";
-    private static final String MESSAGE_VIEW_TO = "fontSizeMessageViewTo";
-    private static final String MESSAGE_VIEW_CC = "fontSizeMessageViewCC";
-    private static final String MESSAGE_VIEW_BCC = "fontSizeMessageViewBCC";
-    private static final String MESSAGE_VIEW_ADDITIONAL_HEADERS = "fontSizeMessageViewAdditionalHeaders";
+    private static final String MESSAGE_VIEW_RECIPIENTS = "fontSizeMessageViewTo";
     private static final String MESSAGE_VIEW_SUBJECT = "fontSizeMessageViewSubject";
     private static final String MESSAGE_VIEW_DATE = "fontSizeMessageViewDate";
     private static final String MESSAGE_VIEW_CONTENT_PERCENT = "fontSizeMessageViewContentPercent";
@@ -40,10 +37,7 @@ public class FontSizes {
     private int messageListDate;
     private int messageListPreview;
     private int messageViewSender;
-    private int messageViewTo;
-    private int messageViewCC;
-    private int messageViewBCC;
-    private int messageViewAdditionalHeaders;
+    private int messageViewRecipients;
     private int messageViewSubject;
     private int messageViewDate;
     private int messageViewContentPercent;
@@ -57,10 +51,7 @@ public FontSizes() {
         messageListPreview = FONT_DEFAULT;
 
         messageViewSender = FONT_DEFAULT;
-        messageViewTo = FONT_DEFAULT;
-        messageViewCC = FONT_DEFAULT;
-        messageViewBCC = FONT_DEFAULT;
-        messageViewAdditionalHeaders = FONT_DEFAULT;
+        messageViewRecipients = FONT_DEFAULT;
         messageViewSubject = FONT_DEFAULT;
         messageViewDate = FONT_DEFAULT;
         messageViewContentPercent = 100;
@@ -75,10 +66,7 @@ public void save(StorageEditor editor) {
         editor.putInt(MESSAGE_LIST_PREVIEW, messageListPreview);
 
         editor.putInt(MESSAGE_VIEW_SENDER, messageViewSender);
-        editor.putInt(MESSAGE_VIEW_TO, messageViewTo);
-        editor.putInt(MESSAGE_VIEW_CC, messageViewCC);
-        editor.putInt(MESSAGE_VIEW_BCC, messageViewBCC);
-        editor.putInt(MESSAGE_VIEW_ADDITIONAL_HEADERS, messageViewAdditionalHeaders);
+        editor.putInt(MESSAGE_VIEW_RECIPIENTS, messageViewRecipients);
         editor.putInt(MESSAGE_VIEW_SUBJECT, messageViewSubject);
         editor.putInt(MESSAGE_VIEW_DATE, messageViewDate);
         editor.putInt(MESSAGE_VIEW_CONTENT_PERCENT, getMessageViewContentAsPercent());
@@ -93,10 +81,7 @@ public void load(Storage storage) {
         messageListPreview = storage.getInt(MESSAGE_LIST_PREVIEW, messageListPreview);
 
         messageViewSender = storage.getInt(MESSAGE_VIEW_SENDER, messageViewSender);
-        messageViewTo = storage.getInt(MESSAGE_VIEW_TO, messageViewTo);
-        messageViewCC = storage.getInt(MESSAGE_VIEW_CC, messageViewCC);
-        messageViewBCC = storage.getInt(MESSAGE_VIEW_BCC, messageViewBCC);
-        messageViewAdditionalHeaders = storage.getInt(MESSAGE_VIEW_ADDITIONAL_HEADERS, messageViewAdditionalHeaders);
+        messageViewRecipients = storage.getInt(MESSAGE_VIEW_RECIPIENTS, messageViewRecipients);
         messageViewSubject = storage.getInt(MESSAGE_VIEW_SUBJECT, messageViewSubject);
         messageViewDate = storage.getInt(MESSAGE_VIEW_DATE, messageViewDate);
 
@@ -149,36 +134,12 @@ public void setMessageViewSender(int messageViewSender) {
         this.messageViewSender = messageViewSender;
     }
 
-    public int getMessageViewTo() {
-        return messageViewTo;
+    public int getMessageViewRecipients() {
+        return messageViewRecipients;
     }
 
-    public void setMessageViewTo(int messageViewTo) {
-        this.messageViewTo = messageViewTo;
-    }
-
-    public int getMessageViewCC() {
-        return messageViewCC;
-    }
-
-    public void setMessageViewCC(int messageViewCC) {
-        this.messageViewCC = messageViewCC;
-    }
-
-    public int getMessageViewBCC() {
-        return messageViewBCC;
-    }
-
-    public void setMessageViewBCC(int messageViewBCC) {
-        this.messageViewBCC = messageViewBCC;
-    }
-
-    public int getMessageViewAdditionalHeaders() {
-        return messageViewAdditionalHeaders;
-    }
-
-    public void setMessageViewAdditionalHeaders(int messageViewAdditionalHeaders) {
-        this.messageViewAdditionalHeaders = messageViewAdditionalHeaders;
+    public void setMessageViewRecipients(int messageViewRecipients) {
+        this.messageViewRecipients = messageViewRecipients;
     }
 
     public int getMessageViewSubject() {
diff --git a/app/core/src/main/java/com/fsck/k9/helper/AddressFormatter.kt b/app/core/src/main/java/com/fsck/k9/helper/AddressFormatter.kt
new file mode 100644
index 00000000000..1e004aff101
--- /dev/null
+++ b/app/core/src/main/java/com/fsck/k9/helper/AddressFormatter.kt
@@ -0,0 +1,74 @@
+package com.fsck.k9.helper
+
+import android.text.Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
+import android.text.SpannableString
+import android.text.style.ForegroundColorSpan
+import com.fsck.k9.Account
+import com.fsck.k9.Identity
+import com.fsck.k9.mail.Address
+
+/**
+ * Get the display name for an email address.
+ */
+interface AddressFormatter {
+    fun getDisplayName(address: Address): CharSequence
+}
+
+class RealAddressFormatter(
+    private val contactNameProvider: ContactNameProvider,
+    private val account: Account,
+    private val showCorrespondentNames: Boolean,
+    private val showContactNames: Boolean,
+    private val contactNameColor: Int?,
+    private val meText: String
+) : AddressFormatter {
+    override fun getDisplayName(address: Address): CharSequence {
+        val identity = account.findIdentity(address)
+        if (identity != null) {
+            return getIdentityName(identity)
+        }
+
+        return if (!showCorrespondentNames) {
+            address.address
+        } else if (showContactNames) {
+            getContactName(address)
+        } else {
+            buildDisplayName(address)
+        }
+    }
+
+    private fun getIdentityName(identity: Identity): String {
+        return if (account.identities.size == 1) {
+            meText
+        } else {
+            identity.description ?: identity.name ?: identity.email ?: meText
+        }
+    }
+
+    private fun getContactName(address: Address): CharSequence {
+        val contactName = contactNameProvider.getNameForAddress(address.address) ?: return buildDisplayName(address)
+
+        return if (contactNameColor != null) {
+            SpannableString(contactName).apply {
+                setSpan(ForegroundColorSpan(contactNameColor), 0, contactName.length, SPAN_EXCLUSIVE_EXCLUSIVE)
+            }
+        } else {
+            contactName
+        }
+    }
+
+    private fun buildDisplayName(address: Address): CharSequence {
+        return address.personal?.takeIf {
+            it.isNotBlank() && !it.equals(meText, ignoreCase = true) && !isSpoofAddress(it)
+        } ?: address.address
+    }
+
+    private fun isSpoofAddress(displayName: String): Boolean {
+        val atIndex = displayName.indexOf('@')
+        return if (atIndex > 0) {
+            displayName[atIndex - 1] != '('
+        } else {
+            false
+        }
+    }
+}
diff --git a/app/core/src/main/java/com/fsck/k9/helper/ContactNameProvider.kt b/app/core/src/main/java/com/fsck/k9/helper/ContactNameProvider.kt
new file mode 100644
index 00000000000..4abbb732d8f
--- /dev/null
+++ b/app/core/src/main/java/com/fsck/k9/helper/ContactNameProvider.kt
@@ -0,0 +1,11 @@
+package com.fsck.k9.helper
+
+interface ContactNameProvider {
+    fun getNameForAddress(address: String): String?
+}
+
+class RealContactNameProvider(private val contacts: Contacts) : ContactNameProvider {
+    override fun getNameForAddress(address: String): String? {
+        return contacts.getNameForAddress(address)
+    }
+}
diff --git a/app/core/src/main/java/com/fsck/k9/helper/Contacts.java b/app/core/src/main/java/com/fsck/k9/helper/Contacts.java
index 2043e6bd5d8..d2642f7d044 100644
--- a/app/core/src/main/java/com/fsck/k9/helper/Contacts.java
+++ b/app/core/src/main/java/com/fsck/k9/helper/Contacts.java
@@ -1,6 +1,8 @@
 package com.fsck.k9.helper;
 
 
+import java.util.HashMap;
+
 import android.Manifest;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -9,13 +11,12 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.provider.ContactsContract;
-import timber.log.Timber;
 import android.provider.ContactsContract.CommonDataKinds.Photo;
-import androidx.core.content.ContextCompat;
 
+import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
 import com.fsck.k9.mail.Address;
-
-import java.util.HashMap;
+import timber.log.Timber;
 
 /**
  * Helper class to access the contacts stored on the device.
@@ -37,7 +38,8 @@ public class Contacts {
             ContactsContract.CommonDataKinds.Email._ID,
             ContactsContract.Contacts.DISPLAY_NAME,
             ContactsContract.CommonDataKinds.Email.CONTACT_ID,
-            Photo.PHOTO_URI
+            Photo.PHOTO_URI,
+            ContactsContract.Contacts.LOOKUP_KEY
     };
 
     /**
@@ -52,6 +54,8 @@ public class Contacts {
      */
     protected static final int CONTACT_ID_INDEX = 2;
 
+    protected static final int LOOKUP_KEY_INDEX = 4;
+
 
     /**
      * Get instance of the Contacts class.
@@ -169,6 +173,24 @@ public boolean isAnyInContacts(final Address[] addresses) {
         return false;
     }
 
+    @Nullable
+    public Uri getContactUri(String emailAddress) {
+        Cursor cursor = getContactByAddress(emailAddress);
+        if (cursor == null) {
+            return null;
+        }
+
+        try (cursor) {
+            if (!cursor.moveToFirst()) {
+                return null;
+            }
+
+            long contactId = cursor.getLong(CONTACT_ID_INDEX);
+            String lookupKey = cursor.getString(LOOKUP_KEY_INDEX);
+            return ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
+        }
+    }
+
     /**
      * Get the name of the contact an email address belongs to.
      *
diff --git a/app/core/src/main/java/com/fsck/k9/mailstore/MessageDetails.kt b/app/core/src/main/java/com/fsck/k9/mailstore/MessageDetails.kt
new file mode 100644
index 00000000000..19c14855f3e
--- /dev/null
+++ b/app/core/src/main/java/com/fsck/k9/mailstore/MessageDetails.kt
@@ -0,0 +1,22 @@
+package com.fsck.k9.mailstore
+
+import com.fsck.k9.mail.Address
+import java.util.Date
+
+data class MessageDetails(
+    val date: MessageDate,
+    val from: List<Address>,
+    val sender: Address?,
+    val replyTo: List<Address>,
+    val to: List<Address>,
+    val cc: List<Address>,
+    val bcc: List<Address>
+)
+
+sealed interface MessageDate {
+    data class ValidDate(val date: Date) : MessageDate
+
+    data class InvalidDate(val dateHeader: String) : MessageDate
+
+    object MissingDate : MessageDate
+}
diff --git a/app/core/src/main/java/com/fsck/k9/mailstore/MessageRepository.kt b/app/core/src/main/java/com/fsck/k9/mailstore/MessageRepository.kt
index aa88bc1e15d..e2fc950b16e 100644
--- a/app/core/src/main/java/com/fsck/k9/mailstore/MessageRepository.kt
+++ b/app/core/src/main/java/com/fsck/k9/mailstore/MessageRepository.kt
@@ -1,11 +1,69 @@
 package com.fsck.k9.mailstore
 
 import com.fsck.k9.controller.MessageReference
+import com.fsck.k9.mail.Address
 import com.fsck.k9.mail.Header
+import com.fsck.k9.mail.internet.MimeUtility
+import org.apache.james.mime4j.dom.field.DateTimeField
+import org.apache.james.mime4j.field.DefaultFieldParser
 
 class MessageRepository(private val messageStoreManager: MessageStoreManager) {
     fun getHeaders(messageReference: MessageReference): List<Header> {
         val messageStore = messageStoreManager.getMessageStore(messageReference.accountUuid)
         return messageStore.getHeaders(messageReference.folderId, messageReference.uid)
     }
+
+    fun getMessageDetails(messageReference: MessageReference): MessageDetails {
+        val messageStore = messageStoreManager.getMessageStore(messageReference.accountUuid)
+        val headers = messageStore.getHeaders(messageReference.folderId, messageReference.uid, MESSAGE_DETAILS_HEADERS)
+
+        val messageDate = headers.parseDate("date")
+        val fromAddresses = headers.parseAddresses("from")
+        val senderAddresses = headers.parseAddresses("sender")
+        val replyToAddresses = headers.parseAddresses("reply-to")
+        val toAddresses = headers.parseAddresses("to")
+        val ccAddresses = headers.parseAddresses("cc")
+        val bccAddresses = headers.parseAddresses("bcc")
+
+        return MessageDetails(
+            date = messageDate,
+            from = fromAddresses,
+            sender = senderAddresses.firstOrNull(),
+            replyTo = replyToAddresses,
+            to = toAddresses,
+            cc = ccAddresses,
+            bcc = bccAddresses
+        )
+    }
+
+    private fun List<Header>.firstHeaderOrNull(name: String): String? {
+        return firstOrNull { it.name.equals(name, ignoreCase = true) }?.value
+    }
+
+    private fun List<Header>.parseAddresses(headerName: String): List<Address> {
+        return Address.parse(MimeUtility.unfold(firstHeaderOrNull(headerName))).toList()
+    }
+
+    private fun List<Header>.parseDate(headerName: String): MessageDate {
+        val dateHeader = firstHeaderOrNull(headerName) ?: return MessageDate.MissingDate
+
+        return try {
+            val dateTimeField = DefaultFieldParser.parse("Date: $dateHeader") as DateTimeField
+            return MessageDate.ValidDate(date = dateTimeField.date)
+        } catch (e: Exception) {
+            MessageDate.InvalidDate(dateHeader)
+        }
+    }
+
+    companion object {
+        private val MESSAGE_DETAILS_HEADERS = setOf(
+            "date",
+            "from",
+            "sender",
+            "reply-to",
+            "to",
+            "cc",
+            "bcc",
+        )
+    }
 }
diff --git a/app/core/src/main/java/com/fsck/k9/mailstore/MessageStore.kt b/app/core/src/main/java/com/fsck/k9/mailstore/MessageStore.kt
index 75c292e237e..b7b5e88d612 100644
--- a/app/core/src/main/java/com/fsck/k9/mailstore/MessageStore.kt
+++ b/app/core/src/main/java/com/fsck/k9/mailstore/MessageStore.kt
@@ -155,6 +155,11 @@ interface MessageStore {
      */
     fun getHeaders(folderId: Long, messageServerId: String): List<Header>
 
+    /**
+     * Retrieve selected header fields of a message.
+     */
+    fun getHeaders(folderId: Long, messageServerId: String, headerNames: Set<String>): List<Header>
+
     /**
      * Return the size of this message store in bytes.
      */
diff --git a/app/core/src/main/java/com/fsck/k9/message/ReplyActionStrategy.kt b/app/core/src/main/java/com/fsck/k9/message/ReplyActionStrategy.kt
new file mode 100644
index 00000000000..a208b982093
--- /dev/null
+++ b/app/core/src/main/java/com/fsck/k9/message/ReplyActionStrategy.kt
@@ -0,0 +1,36 @@
+package com.fsck.k9.message
+
+import com.fsck.k9.Account
+import com.fsck.k9.helper.ReplyToParser
+import com.fsck.k9.mail.Message
+
+/**
+ * Figures out which reply actions are available to the user.
+ */
+class ReplyActionStrategy(private val replyRoParser: ReplyToParser) {
+    fun getReplyActions(account: Account, message: Message): ReplyActions {
+        val recipientsToReplyTo = replyRoParser.getRecipientsToReplyTo(message, account)
+        val recipientsToReplyAllTo = replyRoParser.getRecipientsToReplyAllTo(message, account)
+
+        val replyToAllCount = recipientsToReplyAllTo.to.size + recipientsToReplyAllTo.cc.size
+        return if (replyToAllCount <= 1) {
+            if (recipientsToReplyTo.to.isEmpty()) {
+                ReplyActions(defaultAction = null)
+            } else {
+                ReplyActions(defaultAction = ReplyAction.REPLY)
+            }
+        } else {
+            ReplyActions(defaultAction = ReplyAction.REPLY_ALL, additionalActions = listOf(ReplyAction.REPLY))
+        }
+    }
+}
+
+data class ReplyActions(
+    val defaultAction: ReplyAction?,
+    val additionalActions: List<ReplyAction> = emptyList()
+)
+
+enum class ReplyAction {
+    REPLY,
+    REPLY_ALL
+}
diff --git a/app/k9mail/proguard-rules.pro b/app/k9mail/proguard-rules.pro
index bbb161dcc7e..d18ccdefcec 100644
--- a/app/k9mail/proguard-rules.pro
+++ b/app/k9mail/proguard-rules.pro
@@ -29,6 +29,10 @@
 -dontnote com.fsck.k9.ui.messageview.**
 -dontnote com.fsck.k9.view.**
 
+-assumevalues class * extends android.view.View {
+    boolean isInEditMode() return false;
+}
+
 -keep public class org.openintents.openpgp.**
 
 -keepclassmembers class * extends androidx.appcompat.widget.SearchView {
diff --git a/app/storage/src/main/java/com/fsck/k9/storage/messages/K9MessageStore.kt b/app/storage/src/main/java/com/fsck/k9/storage/messages/K9MessageStore.kt
index ca1e7dec6ab..aeecdc0352b 100644
--- a/app/storage/src/main/java/com/fsck/k9/storage/messages/K9MessageStore.kt
+++ b/app/storage/src/main/java/com/fsck/k9/storage/messages/K9MessageStore.kt
@@ -132,6 +132,10 @@ class K9MessageStore(
         return retrieveMessageOperations.getHeaders(folderId, messageServerId)
     }
 
+    override fun getHeaders(folderId: Long, messageServerId: String, headerNames: Set<String>): List<Header> {
+        return retrieveMessageOperations.getHeaders(folderId, messageServerId, headerNames)
+    }
+
     override fun destroyMessages(folderId: Long, messageServerIds: Collection<String>) {
         deleteMessageOperations.destroyMessages(folderId, messageServerIds)
     }
diff --git a/app/storage/src/main/java/com/fsck/k9/storage/messages/RetrieveMessageOperations.kt b/app/storage/src/main/java/com/fsck/k9/storage/messages/RetrieveMessageOperations.kt
index c094ae3b3c2..041dda74745 100644
--- a/app/storage/src/main/java/com/fsck/k9/storage/messages/RetrieveMessageOperations.kt
+++ b/app/storage/src/main/java/com/fsck/k9/storage/messages/RetrieveMessageOperations.kt
@@ -2,6 +2,7 @@ package com.fsck.k9.storage.messages
 
 import androidx.core.database.getLongOrNull
 import com.fsck.k9.K9
+import com.fsck.k9.helper.mapToSet
 import com.fsck.k9.mail.Flag
 import com.fsck.k9.mail.Header
 import com.fsck.k9.mail.internet.MimeHeader
@@ -167,7 +168,7 @@ internal class RetrieveMessageOperations(private val lockableDatabase: LockableD
         }
     }
 
-    fun getHeaders(folderId: Long, messageServerId: String): List<Header> {
+    fun getHeaders(folderId: Long, messageServerId: String, headerNames: Set<String>? = null): List<Header> {
         return lockableDatabase.execute(false) { database ->
             database.rawQuery(
                 "SELECT message_parts.header FROM messages" +
@@ -178,10 +179,13 @@ internal class RetrieveMessageOperations(private val lockableDatabase: LockableD
                 if (!cursor.moveToFirst()) throw MessageNotFoundException(folderId, messageServerId)
 
                 val headerBytes = cursor.getBlob(0)
+                val lowercaseHeaderNames = headerNames?.mapToSet(headerNames.size) { it.lowercase() }
 
                 val header = MimeHeader()
                 MessageHeaderParser.parse(headerBytes.inputStream()) { name, value ->
-                    header.addRawHeader(name, value)
+                    if (lowercaseHeaderNames == null || name.lowercase() in lowercaseHeaderNames) {
+                        header.addRawHeader(name, value)
+                    }
                 }
 
                 header.headers
diff --git a/app/ui/legacy/build.gradle b/app/ui/legacy/build.gradle
index 19dfe3538b7..8acc84bc9e8 100644
--- a/app/ui/legacy/build.gradle
+++ b/app/ui/legacy/build.gradle
@@ -11,6 +11,7 @@ dependencies {
     implementation project(":app:autodiscovery:api")
     implementation project(":app:autodiscovery:providersxml")
     implementation project(":mail:common")
+    implementation project(":ui-utils:ToolbarBottomSheet")
 
     //TODO: Remove AccountSetupIncoming's dependency on these
     compileOnly project(":mail:protocols:imap")
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/UiKoinModules.kt b/app/ui/legacy/src/main/java/com/fsck/k9/UiKoinModules.kt
index 79f928ec363..9ae3e125bc6 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/UiKoinModules.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/UiKoinModules.kt
@@ -11,6 +11,7 @@ import com.fsck.k9.ui.choosefolder.chooseFolderUiModule
 import com.fsck.k9.ui.endtoend.endToEndUiModule
 import com.fsck.k9.ui.folders.foldersUiModule
 import com.fsck.k9.ui.managefolders.manageFoldersUiModule
+import com.fsck.k9.ui.messagedetails.messageDetailsUiModule
 import com.fsck.k9.ui.messagelist.messageListUiModule
 import com.fsck.k9.ui.messagesource.messageSourceModule
 import com.fsck.k9.ui.settings.settingsUiModule
@@ -33,5 +34,6 @@ val uiModules = listOf(
     viewModule,
     changelogUiModule,
     messageSourceModule,
-    accountUiModule
+    accountUiModule,
+    messageDetailsUiModule
 )
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 cdf2aaa93de..a810a67b0f7 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
@@ -129,6 +129,9 @@ open class MessageList :
     private var messageListWasDisplayed = false
     private var viewSwitcher: ViewSwitcher? = null
 
+    private val isShowAccountChip: Boolean
+        get() = messageListFragment?.isShowAccountChip ?: true
+
     public override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
@@ -955,7 +958,7 @@ open class MessageList :
         if (draftsFolderId != null && folderId == draftsFolderId) {
             MessageActions.actionEditDraft(this, messageReference)
         } else {
-            val fragment = MessageViewContainerFragment.newInstance(messageReference)
+            val fragment = MessageViewContainerFragment.newInstance(messageReference, isShowAccountChip)
             supportFragmentManager.commitNow {
                 replace(R.id.message_view_container, fragment, FRAGMENT_TAG_MESSAGE_VIEW_CONTAINER)
             }
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageListActivityConfig.kt b/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageListActivityConfig.kt
index bbfc0ff5f32..ba099bca565 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageListActivityConfig.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageListActivityConfig.kt
@@ -27,10 +27,7 @@ data class MessageListActivityConfig(
     val fontSizeMessageListDate: Int,
     val fontSizeMessageListPreview: Int,
     val fontSizeMessageViewSender: Int,
-    val fontSizeMessageViewTo: Int,
-    val fontSizeMessageViewCC: Int,
-    val fontSizeMessageViewBCC: Int,
-    val fontSizeMessageViewAdditionalHeaders: Int,
+    val fontSizeMessageViewRecipients: Int,
     val fontSizeMessageViewSubject: Int,
     val fontSizeMessageViewDate: Int,
     val fontSizeMessageViewContentAsPercent: Int,
@@ -62,10 +59,7 @@ data class MessageListActivityConfig(
                 fontSizeMessageListDate = K9.fontSizes.messageListDate,
                 fontSizeMessageListPreview = K9.fontSizes.messageListPreview,
                 fontSizeMessageViewSender = K9.fontSizes.messageViewSender,
-                fontSizeMessageViewTo = K9.fontSizes.messageViewTo,
-                fontSizeMessageViewCC = K9.fontSizes.messageViewCC,
-                fontSizeMessageViewBCC = K9.fontSizes.messageViewBCC,
-                fontSizeMessageViewAdditionalHeaders = K9.fontSizes.messageViewAdditionalHeaders,
+                fontSizeMessageViewRecipients = K9.fontSizes.messageViewRecipients,
                 fontSizeMessageViewSubject = K9.fontSizes.messageViewSubject,
                 fontSizeMessageViewDate = K9.fontSizes.messageViewDate,
                 fontSizeMessageViewContentAsPercent = K9.fontSizes.messageViewContentAsPercent,
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/helper/BottomBaselineTextView.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/helper/BottomBaselineTextView.kt
new file mode 100644
index 00000000000..8769cb805a1
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/helper/BottomBaselineTextView.kt
@@ -0,0 +1,21 @@
+package com.fsck.k9.ui.helper
+
+import android.content.Context
+import android.util.AttributeSet
+import androidx.appcompat.widget.AppCompatTextView
+
+/**
+ * Return the baseline of the last line of text, instead of TextView's default of the first line.
+ */
+// Source: https://stackoverflow.com/a/62419876
+class BottomBaselineTextView @JvmOverloads constructor(
+    context: Context,
+    attrs: AttributeSet? = null
+) : AppCompatTextView(context, attrs) {
+
+    override fun getBaseline(): Int {
+        val layout = layout ?: return super.getBaseline()
+        val baselineOffset = super.getBaseline() - layout.getLineBaseline(0)
+        return baselineOffset + layout.getLineBaseline(layout.lineCount - 1)
+    }
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/AddToContactsLauncher.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/AddToContactsLauncher.kt
new file mode 100644
index 00000000000..399883c40c5
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/AddToContactsLauncher.kt
@@ -0,0 +1,23 @@
+package com.fsck.k9.ui.messagedetails
+
+import android.content.Context
+import android.content.Intent
+import android.provider.ContactsContract
+
+internal class AddToContactsLauncher {
+    fun launch(context: Context, name: String?, email: String) {
+        val intent = Intent(Intent.ACTION_INSERT).apply {
+            type = ContactsContract.Contacts.CONTENT_TYPE
+
+            putExtra(ContactsContract.Intents.Insert.EMAIL, email)
+
+            if (name != null) {
+                putExtra(ContactsContract.Intents.Insert.NAME, name)
+            }
+
+            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+        }
+
+        context.startActivity(intent)
+    }
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/ContactSettingsProvider.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/ContactSettingsProvider.kt
new file mode 100644
index 00000000000..eefb56e8e00
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/ContactSettingsProvider.kt
@@ -0,0 +1,8 @@
+package com.fsck.k9.ui.messagedetails
+
+import com.fsck.k9.K9
+
+class ContactSettingsProvider {
+    val isShowContactPicture: Boolean
+        get() = K9.isShowContactPicture
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/CryptoStatusItem.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/CryptoStatusItem.kt
new file mode 100644
index 00000000000..5f4116e06d1
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/CryptoStatusItem.kt
@@ -0,0 +1,52 @@
+package com.fsck.k9.ui.messagedetails
+
+import android.content.res.ColorStateList
+import android.view.View
+import android.widget.ImageView
+import android.widget.TextView
+import com.fsck.k9.ui.R
+import com.fsck.k9.ui.resolveColorAttribute
+import com.mikepenz.fastadapter.FastAdapter
+import com.mikepenz.fastadapter.items.AbstractItem
+
+internal class CryptoStatusItem(val cryptoDetails: CryptoDetails) : AbstractItem<CryptoStatusItem.ViewHolder>() {
+    override val type = R.id.message_details_crypto_status
+    override val layoutRes = R.layout.message_details_crypto_status_item
+
+    override fun getViewHolder(v: View) = ViewHolder(v)
+
+    class ViewHolder(view: View) : FastAdapter.ViewHolder<CryptoStatusItem>(view) {
+        private val titleTextView = view.findViewById<TextView>(R.id.crypto_status_title)
+        private val descriptionTextView = view.findViewById<TextView>(R.id.crypto_status_description)
+        private val imageView = view.findViewById<ImageView>(R.id.crypto_status_icon)
+        private val originalBackground = view.background
+
+        override fun bindView(item: CryptoStatusItem, payloads: List<Any>) {
+            val context = itemView.context
+            val cryptoDetails = item.cryptoDetails
+            val cryptoStatus = cryptoDetails.cryptoStatus
+
+            imageView.setImageResource(cryptoStatus.statusIconRes)
+            val tintColor = context.theme.resolveColorAttribute(cryptoStatus.colorAttr)
+            imageView.imageTintList = ColorStateList.valueOf(tintColor)
+
+            cryptoStatus.titleTextRes?.let { stringResId ->
+                titleTextView.text = context.getString(stringResId)
+            }
+            cryptoStatus.descriptionTextRes?.let { stringResId ->
+                descriptionTextView.text = context.getString(stringResId)
+            }
+
+            if (!cryptoDetails.isClickable) {
+                itemView.background = null
+            }
+        }
+
+        override fun unbindView(item: CryptoStatusItem) {
+            imageView.setImageDrawable(null)
+            titleTextView.text = null
+            descriptionTextView.text = null
+            itemView.background = originalBackground
+        }
+    }
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/KoinModule.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/KoinModule.kt
new file mode 100644
index 00000000000..75f6cd59572
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/KoinModule.kt
@@ -0,0 +1,19 @@
+package com.fsck.k9.ui.messagedetails
+
+import org.koin.androidx.viewmodel.dsl.viewModel
+import org.koin.dsl.module
+
+val messageDetailsUiModule = module {
+    viewModel {
+        MessageDetailsViewModel(
+            resources = get(),
+            messageRepository = get(),
+            contactSettingsProvider = get(),
+            contacts = get(),
+            clipboardManager = get()
+        )
+    }
+    factory { ContactSettingsProvider() }
+    factory { AddToContactsLauncher() }
+    factory { ShowContactLauncher() }
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDateItem.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDateItem.kt
new file mode 100644
index 00000000000..0c5a52d7f35
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDateItem.kt
@@ -0,0 +1,26 @@
+package com.fsck.k9.ui.messagedetails
+
+import android.view.View
+import android.widget.TextView
+import com.fsck.k9.ui.R
+import com.mikepenz.fastadapter.FastAdapter
+import com.mikepenz.fastadapter.items.AbstractItem
+
+internal class MessageDateItem(private val date: String) : AbstractItem<MessageDateItem.ViewHolder>() {
+    override val type: Int = R.id.message_details_date
+    override val layoutRes = R.layout.message_details_date_item
+
+    override fun getViewHolder(v: View) = ViewHolder(v)
+
+    class ViewHolder(view: View) : FastAdapter.ViewHolder<MessageDateItem>(view) {
+        private val textView = view.findViewById<TextView>(R.id.date)
+
+        override fun bindView(item: MessageDateItem, payloads: List<Any>) {
+            textView.text = item.date
+        }
+
+        override fun unbindView(item: MessageDateItem) {
+            textView.text = null
+        }
+    }
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDetailsDividerItem.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDetailsDividerItem.kt
new file mode 100644
index 00000000000..9706b2d4b29
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDetailsDividerItem.kt
@@ -0,0 +1,19 @@
+package com.fsck.k9.ui.messagedetails
+
+import android.view.View
+import com.fsck.k9.ui.R
+import com.mikepenz.fastadapter.FastAdapter
+import com.mikepenz.fastadapter.items.AbstractItem
+
+internal class MessageDetailsDividerItem : AbstractItem<MessageDetailsDividerItem.ViewHolder>() {
+    override val type: Int = R.id.message_details_divider
+    override val layoutRes = R.layout.message_details_divider_item
+
+    override fun getViewHolder(v: View) = ViewHolder(v)
+
+    class ViewHolder(view: View) : FastAdapter.ViewHolder<MessageDetailsDividerItem>(view) {
+        override fun bindView(item: MessageDetailsDividerItem, payloads: List<Any>) = Unit
+
+        override fun unbindView(item: MessageDetailsDividerItem) = Unit
+    }
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDetailsFragment.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDetailsFragment.kt
new file mode 100644
index 00000000000..fcf95ef6a24
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDetailsFragment.kt
@@ -0,0 +1,313 @@
+package com.fsck.k9.ui.messagedetails
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ProgressBar
+import androidx.annotation.StringRes
+import androidx.appcompat.widget.PopupMenu
+import androidx.core.content.ContextCompat
+import androidx.core.os.bundleOf
+import androidx.core.view.isVisible
+import androidx.fragment.app.setFragmentResult
+import androidx.recyclerview.widget.RecyclerView
+import app.k9mail.ui.utils.bottomsheet.ToolbarBottomSheetDialogFragment
+import com.fsck.k9.activity.MessageCompose
+import com.fsck.k9.contacts.ContactPictureLoader
+import com.fsck.k9.controller.MessageReference
+import com.fsck.k9.mail.Address
+import com.fsck.k9.mailstore.CryptoResultAnnotation
+import com.fsck.k9.ui.R
+import com.fsck.k9.ui.observe
+import com.fsck.k9.ui.withArguments
+import com.mikepenz.fastadapter.FastAdapter
+import com.mikepenz.fastadapter.GenericItem
+import com.mikepenz.fastadapter.adapters.ItemAdapter
+import com.mikepenz.fastadapter.listeners.ClickEventHook
+import org.koin.android.ext.android.inject
+import org.koin.androidx.viewmodel.ext.android.viewModel
+
+class MessageDetailsFragment : ToolbarBottomSheetDialogFragment() {
+    private val viewModel: MessageDetailsViewModel by viewModel()
+    private val addToContactsLauncher: AddToContactsLauncher by inject()
+    private val showContactLauncher: ShowContactLauncher by inject()
+    private val contactPictureLoader: ContactPictureLoader by inject()
+
+    private lateinit var messageReference: MessageReference
+
+    // FIXME: Replace this with a mechanism that survives process death
+    var cryptoResult: CryptoResultAnnotation? = null
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        messageReference = MessageReference.parse(arguments?.getString(ARG_REFERENCE))
+            ?: error("Missing argument $ARG_REFERENCE")
+    }
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        return inflater.inflate(R.layout.message_bottom_sheet, container, false)
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        cryptoResult?.let {
+            viewModel.cryptoResult = it
+        }
+
+        val dialog = checkNotNull(dialog)
+        dialog.isDismissWithAnimation = true
+
+        val toolbar = checkNotNull(toolbar)
+        toolbar.apply {
+            title = getString(R.string.message_details_toolbar_title)
+            navigationIcon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_close)
+
+            setNavigationOnClickListener {
+                dismiss()
+            }
+        }
+
+        val progressBar = view.findViewById<ProgressBar>(R.id.message_details_progress)
+        val errorView = view.findViewById<View>(R.id.message_details_error)
+        val recyclerView = view.findViewById<RecyclerView>(R.id.message_details_list)
+
+        viewModel.uiEvents.observe(this) { event ->
+            when (event) {
+                is MessageDetailEvent.ShowCryptoKeys -> showCryptoKeys(event.pendingIntent)
+                MessageDetailEvent.SearchCryptoKeys -> searchCryptoKeys()
+                MessageDetailEvent.ShowCryptoWarning -> showCryptoWarning()
+            }
+        }
+
+        viewModel.loadData(messageReference).observe(this) { state ->
+            when (state) {
+                MessageDetailsState.Loading -> {
+                    progressBar.isVisible = true
+                    errorView.isVisible = false
+                    recyclerView.isVisible = false
+                }
+                MessageDetailsState.Error -> {
+                    progressBar.isVisible = false
+                    errorView.isVisible = true
+                    recyclerView.isVisible = false
+                }
+                is MessageDetailsState.DataLoaded -> {
+                    progressBar.isVisible = false
+                    errorView.isVisible = false
+                    recyclerView.isVisible = true
+                    setMessageDetails(recyclerView, state.details, state.showContactPicture)
+                }
+            }
+        }
+    }
+
+    private fun setMessageDetails(recyclerView: RecyclerView, details: MessageDetailsUi, showContactPicture: Boolean) {
+        val itemAdapter = ItemAdapter<GenericItem>().apply {
+            add(MessageDateItem(details.date ?: getString(R.string.message_details_missing_date)))
+
+            if (details.cryptoDetails != null) {
+                add(CryptoStatusItem(details.cryptoDetails))
+            }
+
+            addParticipants(details.from, R.string.message_details_from_section_title, showContactPicture)
+            addParticipants(details.sender, R.string.message_details_sender_section_title, showContactPicture)
+            addParticipants(details.replyTo, R.string.message_details_replyto_section_title, showContactPicture)
+
+            add(MessageDetailsDividerItem())
+
+            addParticipants(details.to, R.string.message_details_to_section_title, showContactPicture)
+            addParticipants(details.cc, R.string.message_details_cc_section_title, showContactPicture)
+            addParticipants(details.bcc, R.string.message_details_bcc_section_title, showContactPicture)
+        }
+
+        val adapter = FastAdapter.with(itemAdapter).apply {
+            addEventHook(cryptoStatusClickEventHook)
+            addEventHook(participantClickEventHook)
+            addEventHook(addToContactsClickEventHook)
+            addEventHook(composeClickEventHook)
+            addEventHook(overflowClickEventHook)
+        }
+
+        recyclerView.adapter = adapter
+    }
+
+    private fun ItemAdapter<GenericItem>.addParticipants(
+        participants: List<Participant>,
+        @StringRes title: Int,
+        showContactPicture: Boolean
+    ) {
+        if (participants.isNotEmpty()) {
+            val extraText = if (participants.size > 1) participants.size.toString() else null
+            add(SectionHeaderItem(title = getString(title), extra = extraText))
+
+            for (participant in participants) {
+                add(ParticipantItem(contactPictureLoader, showContactPicture, participant))
+            }
+        }
+    }
+
+    private val cryptoStatusClickEventHook = object : ClickEventHook<CryptoStatusItem>() {
+        override fun onBind(viewHolder: RecyclerView.ViewHolder): View? {
+            return if (viewHolder is CryptoStatusItem.ViewHolder) {
+                viewHolder.itemView
+            } else {
+                null
+            }
+        }
+
+        override fun onClick(
+            v: View,
+            position: Int,
+            fastAdapter: FastAdapter<CryptoStatusItem>,
+            item: CryptoStatusItem
+        ) {
+            if (item.cryptoDetails.isClickable) {
+                viewModel.onCryptoStatusClicked()
+            }
+        }
+    }
+
+    private val participantClickEventHook = object : ClickEventHook<ParticipantItem>() {
+        override fun onBind(viewHolder: RecyclerView.ViewHolder): View? {
+            return if (viewHolder is ParticipantItem.ViewHolder) {
+                viewHolder.itemView
+            } else {
+                null
+            }
+        }
+
+        override fun onClick(v: View, position: Int, fastAdapter: FastAdapter<ParticipantItem>, item: ParticipantItem) {
+            val contactLookupUri = item.participant.contactLookupUri ?: return
+            showContact(contactLookupUri)
+        }
+    }
+
+    private fun showContact(contactLookupUri: Uri) {
+        showContactLauncher.launch(requireContext(), contactLookupUri)
+    }
+
+    private val addToContactsClickEventHook = object : ClickEventHook<ParticipantItem>() {
+        override fun onBind(viewHolder: RecyclerView.ViewHolder): View? {
+            return if (viewHolder is ParticipantItem.ViewHolder) {
+                viewHolder.menuAddContact
+            } else {
+                null
+            }
+        }
+
+        override fun onClick(v: View, position: Int, fastAdapter: FastAdapter<ParticipantItem>, item: ParticipantItem) {
+            val address = item.participant.address
+            addToContacts(address)
+        }
+    }
+
+    private fun addToContacts(address: Address) {
+        addToContactsLauncher.launch(context = requireContext(), name = address.personal, email = address.address)
+    }
+
+    private val composeClickEventHook = object : ClickEventHook<ParticipantItem>() {
+        override fun onBind(viewHolder: RecyclerView.ViewHolder): View? {
+            return if (viewHolder is ParticipantItem.ViewHolder) {
+                viewHolder.menuCompose
+            } else {
+                null
+            }
+        }
+
+        override fun onClick(v: View, position: Int, fastAdapter: FastAdapter<ParticipantItem>, item: ParticipantItem) {
+            val address = item.participant.address
+            composeMessageToAddress(address)
+        }
+    }
+
+    private fun composeMessageToAddress(address: Address) {
+        // TODO: Use the identity this message was sent to as sender identity
+
+        val intent = Intent(context, MessageCompose::class.java).apply {
+            action = Intent.ACTION_SEND
+            putExtra(Intent.EXTRA_EMAIL, arrayOf(address.toString()))
+            putExtra(MessageCompose.EXTRA_ACCOUNT, messageReference.accountUuid)
+        }
+
+        dismiss()
+        requireContext().startActivity(intent)
+    }
+
+    private val overflowClickEventHook = object : ClickEventHook<ParticipantItem>() {
+        override fun onBind(viewHolder: RecyclerView.ViewHolder): View? {
+            return if (viewHolder is ParticipantItem.ViewHolder) {
+                viewHolder.menuOverflow
+            } else {
+                null
+            }
+        }
+
+        override fun onClick(v: View, position: Int, fastAdapter: FastAdapter<ParticipantItem>, item: ParticipantItem) {
+            showOverflowMenu(v, item.participant)
+        }
+    }
+
+    private fun showOverflowMenu(view: View, participant: Participant) {
+        val popupMenu = PopupMenu(requireContext(), view).apply {
+            inflate(R.menu.participant_overflow_menu)
+        }
+
+        if (participant.address.personal == null) {
+            popupMenu.menu.findItem(R.id.copy_name_and_email_address).isVisible = false
+        }
+
+        popupMenu.setOnMenuItemClickListener { item: MenuItem ->
+            onOverflowMenuItemClick(item.itemId, participant)
+            true
+        }
+
+        popupMenu.show()
+    }
+
+    private fun onOverflowMenuItemClick(itemId: Int, participant: Participant) {
+        when (itemId) {
+            R.id.copy_email_address -> viewModel.onCopyEmailAddressToClipboard(participant)
+            R.id.copy_name_and_email_address -> viewModel.onCopyNameAndEmailAddressToClipboard(participant)
+        }
+    }
+
+    private fun showCryptoKeys(pendingIntent: PendingIntent) {
+        requireActivity().startIntentSender(pendingIntent.intentSender, null, 0, 0, 0)
+    }
+
+    private fun searchCryptoKeys() {
+        setFragmentResult(FRAGMENT_RESULT_KEY, bundleOf(RESULT_ACTION to ACTION_SEARCH_KEYS))
+        dismiss()
+    }
+
+    private fun showCryptoWarning() {
+        setFragmentResult(FRAGMENT_RESULT_KEY, bundleOf(RESULT_ACTION to ACTION_SHOW_WARNING))
+        dismiss()
+    }
+
+    companion object {
+        private const val ARG_REFERENCE = "reference"
+
+        const val FRAGMENT_RESULT_KEY = "messageDetailsResult"
+        const val RESULT_ACTION = "action"
+        const val ACTION_SEARCH_KEYS = "search_keys"
+        const val ACTION_SHOW_WARNING = "show_warning"
+
+        fun create(messageReference: MessageReference): MessageDetailsFragment {
+            return MessageDetailsFragment().withArguments(
+                ARG_REFERENCE to messageReference.toIdentityString()
+            )
+        }
+    }
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDetailsUi.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDetailsUi.kt
new file mode 100644
index 00000000000..2d333ed2193
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDetailsUi.kt
@@ -0,0 +1,29 @@
+package com.fsck.k9.ui.messagedetails
+
+import android.net.Uri
+import com.fsck.k9.mail.Address
+import com.fsck.k9.view.MessageCryptoDisplayStatus
+
+data class MessageDetailsUi(
+    val date: String?,
+    val cryptoDetails: CryptoDetails?,
+    val from: List<Participant>,
+    val sender: List<Participant>,
+    val replyTo: List<Participant>,
+    val to: List<Participant>,
+    val cc: List<Participant>,
+    val bcc: List<Participant>
+)
+
+data class CryptoDetails(
+    val cryptoStatus: MessageCryptoDisplayStatus,
+    val isClickable: Boolean
+)
+
+data class Participant(
+    val address: Address,
+    val contactLookupUri: Uri?
+) {
+    val isInContacts: Boolean
+        get() = contactLookupUri != null
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDetailsViewModel.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDetailsViewModel.kt
new file mode 100644
index 00000000000..8232f1f3a8c
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/MessageDetailsViewModel.kt
@@ -0,0 +1,142 @@
+package com.fsck.k9.ui.messagedetails
+
+import android.app.PendingIntent
+import android.content.res.Resources
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.fsck.k9.controller.MessageReference
+import com.fsck.k9.helper.ClipboardManager
+import com.fsck.k9.helper.Contacts
+import com.fsck.k9.mail.Address
+import com.fsck.k9.mailstore.CryptoResultAnnotation
+import com.fsck.k9.mailstore.MessageDate
+import com.fsck.k9.mailstore.MessageRepository
+import com.fsck.k9.ui.R
+import com.fsck.k9.view.MessageCryptoDisplayStatus
+import java.text.DateFormat
+import java.util.Locale
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.launch
+
+internal class MessageDetailsViewModel(
+    private val resources: Resources,
+    private val messageRepository: MessageRepository,
+    private val contactSettingsProvider: ContactSettingsProvider,
+    private val contacts: Contacts,
+    private val clipboardManager: ClipboardManager
+) : ViewModel() {
+    private val dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM, Locale.getDefault())
+    private val uiState = MutableStateFlow<MessageDetailsState>(MessageDetailsState.Loading)
+    private val eventChannel = Channel<MessageDetailEvent>()
+
+    val uiEvents = eventChannel.receiveAsFlow()
+    var cryptoResult: CryptoResultAnnotation? = null
+
+    fun loadData(messageReference: MessageReference): StateFlow<MessageDetailsState> {
+        viewModelScope.launch(Dispatchers.IO) {
+            uiState.value = try {
+                val messageDetails = messageRepository.getMessageDetails(messageReference)
+
+                val senderList = messageDetails.sender?.let { listOf(it) } ?: emptyList()
+                val messageDetailsUi = MessageDetailsUi(
+                    date = buildDisplayDate(messageDetails.date),
+                    cryptoDetails = cryptoResult?.toCryptoDetails(),
+                    from = messageDetails.from.toParticipants(),
+                    sender = senderList.toParticipants(),
+                    replyTo = messageDetails.replyTo.toParticipants(),
+                    to = messageDetails.to.toParticipants(),
+                    cc = messageDetails.cc.toParticipants(),
+                    bcc = messageDetails.bcc.toParticipants()
+                )
+
+                MessageDetailsState.DataLoaded(
+                    showContactPicture = contactSettingsProvider.isShowContactPicture,
+                    details = messageDetailsUi
+                )
+            } catch (e: Exception) {
+                MessageDetailsState.Error
+            }
+        }
+
+        return uiState
+    }
+
+    private fun buildDisplayDate(messageDate: MessageDate): String? {
+        return when (messageDate) {
+            is MessageDate.InvalidDate -> messageDate.dateHeader
+            MessageDate.MissingDate -> null
+            is MessageDate.ValidDate -> dateFormat.format(messageDate.date)
+        }
+    }
+
+    private fun CryptoResultAnnotation.toCryptoDetails(): CryptoDetails {
+        val messageCryptoDisplayStatus = MessageCryptoDisplayStatus.fromResultAnnotation(this)
+        return CryptoDetails(
+            cryptoStatus = messageCryptoDisplayStatus,
+            isClickable = messageCryptoDisplayStatus.hasAssociatedKey() || messageCryptoDisplayStatus.isUnknownKey ||
+                hasOpenPgpInsecureWarningPendingIntent()
+        )
+    }
+
+    private fun List<Address>.toParticipants(): List<Participant> {
+        return this.map { address ->
+            Participant(
+                address = address,
+                contactLookupUri = contacts.getContactUri(address.address)
+            )
+        }
+    }
+
+    fun onCryptoStatusClicked() {
+        val cryptoResult = cryptoResult ?: return
+        val cryptoStatus = MessageCryptoDisplayStatus.fromResultAnnotation(cryptoResult)
+
+        if (cryptoStatus.hasAssociatedKey()) {
+            val pendingIntent = cryptoResult.openPgpSigningKeyIntentIfAny
+            if (pendingIntent != null) {
+                viewModelScope.launch {
+                    eventChannel.send(MessageDetailEvent.ShowCryptoKeys(pendingIntent))
+                }
+            }
+        } else if (cryptoStatus.isUnknownKey) {
+            viewModelScope.launch {
+                eventChannel.send(MessageDetailEvent.SearchCryptoKeys)
+            }
+        } else if (cryptoResult.hasOpenPgpInsecureWarningPendingIntent()) {
+            viewModelScope.launch {
+                eventChannel.send(MessageDetailEvent.ShowCryptoWarning)
+            }
+        }
+    }
+
+    fun onCopyEmailAddressToClipboard(participant: Participant) {
+        val label = resources.getString(R.string.clipboard_label_email_address)
+        val emailAddress = participant.address.address
+        clipboardManager.setText(label, emailAddress)
+    }
+
+    fun onCopyNameAndEmailAddressToClipboard(participant: Participant) {
+        val label = resources.getString(R.string.clipboard_label_name_and_email_address)
+        val nameAndEmailAddress = participant.address.toString()
+        clipboardManager.setText(label, nameAndEmailAddress)
+    }
+}
+
+sealed interface MessageDetailsState {
+    object Loading : MessageDetailsState
+    object Error : MessageDetailsState
+    data class DataLoaded(
+        val showContactPicture: Boolean,
+        val details: MessageDetailsUi
+    ) : MessageDetailsState
+}
+
+sealed interface MessageDetailEvent {
+    data class ShowCryptoKeys(val pendingIntent: PendingIntent) : MessageDetailEvent
+    object SearchCryptoKeys : MessageDetailEvent
+    object ShowCryptoWarning : MessageDetailEvent
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/ParticipantItem.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/ParticipantItem.kt
new file mode 100644
index 00000000000..51800bd8c0d
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/ParticipantItem.kt
@@ -0,0 +1,74 @@
+package com.fsck.k9.ui.messagedetails
+
+import android.view.View
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.appcompat.widget.TooltipCompat
+import androidx.core.view.isVisible
+import com.fsck.k9.contacts.ContactPictureLoader
+import com.fsck.k9.ui.R
+import com.mikepenz.fastadapter.FastAdapter
+import com.mikepenz.fastadapter.items.AbstractItem
+
+internal class ParticipantItem(
+    private val contactPictureLoader: ContactPictureLoader,
+    private val showContactsPicture: Boolean,
+    val participant: Participant
+) : AbstractItem<ParticipantItem.ViewHolder>() {
+    override val type: Int = R.id.message_details_participant
+    override val layoutRes = R.layout.message_details_participant_item
+
+    override fun getViewHolder(v: View) = ViewHolder(v)
+
+    class ViewHolder(view: View) : FastAdapter.ViewHolder<ParticipantItem>(view) {
+        val menuAddContact: View = view.findViewById(R.id.menu_add_contact)
+        val menuCompose: View = view.findViewById(R.id.menu_compose)
+        val menuOverflow: View = view.findViewById(R.id.menu_overflow)
+
+        private val contactPicture: ImageView = view.findViewById(R.id.contact_picture)
+        private val name = view.findViewById<TextView>(R.id.name)
+        private val email = view.findViewById<TextView>(R.id.email)
+        private val originalBackground = view.background
+
+        init {
+            TooltipCompat.setTooltipText(menuAddContact, menuAddContact.contentDescription)
+            TooltipCompat.setTooltipText(menuCompose, menuCompose.contentDescription)
+            TooltipCompat.setTooltipText(menuOverflow, menuOverflow.contentDescription)
+        }
+
+        override fun bindView(item: ParticipantItem, payloads: List<Any>) {
+            val participant = item.participant
+            val address = participant.address
+            val participantName = address.personal
+
+            if (participantName != null) {
+                name.text = participantName
+                email.text = address.address
+            } else {
+                name.text = address.address
+                email.isVisible = false
+            }
+            menuAddContact.isVisible = !participant.isInContacts
+
+            if (item.showContactsPicture) {
+                item.contactPictureLoader.setContactPicture(contactPicture, address)
+            } else {
+                contactPicture.isVisible = false
+            }
+
+            if (!item.participant.isInContacts) {
+                itemView.isClickable = false
+                itemView.background = null
+            }
+        }
+
+        override fun unbindView(item: ParticipantItem) {
+            name.text = null
+            email.text = null
+            email.isVisible = true
+            contactPicture.isVisible = true
+            itemView.background = originalBackground
+            itemView.isClickable = true
+        }
+    }
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/SectionHeaderItem.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/SectionHeaderItem.kt
new file mode 100644
index 00000000000..487aba72907
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/SectionHeaderItem.kt
@@ -0,0 +1,32 @@
+package com.fsck.k9.ui.messagedetails
+
+import android.view.View
+import android.widget.TextView
+import com.fsck.k9.ui.R
+import com.mikepenz.fastadapter.FastAdapter
+import com.mikepenz.fastadapter.items.AbstractItem
+
+internal class SectionHeaderItem(
+    private val title: String,
+    private val extra: String?
+) : AbstractItem<SectionHeaderItem.ViewHolder>() {
+    override val type: Int = R.id.message_details_section_header
+    override val layoutRes = R.layout.message_details_section_header_item
+
+    override fun getViewHolder(v: View) = ViewHolder(v)
+
+    class ViewHolder(view: View) : FastAdapter.ViewHolder<SectionHeaderItem>(view) {
+        private val textView = view.findViewById<TextView>(R.id.title)
+        private val extraTextView = view.findViewById<TextView>(R.id.extra)
+
+        override fun bindView(item: SectionHeaderItem, payloads: List<Any>) {
+            textView.text = item.title
+            extraTextView.text = item.extra
+        }
+
+        override fun unbindView(item: SectionHeaderItem) {
+            textView.text = null
+            extraTextView.text = null
+        }
+    }
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/ShowContactLauncher.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/ShowContactLauncher.kt
new file mode 100644
index 00000000000..b6ef5d799ab
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagedetails/ShowContactLauncher.kt
@@ -0,0 +1,16 @@
+package com.fsck.k9.ui.messagedetails
+
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+
+internal class ShowContactLauncher {
+    fun launch(context: Context, contactLookupUri: Uri) {
+        val intent = Intent(Intent.ACTION_VIEW).apply {
+            data = contactLookupUri
+            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+        }
+
+        context.startActivity(intent)
+    }
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListFragment.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListFragment.kt
index a099cefdf36..51390db733a 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListFragment.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListFragment.kt
@@ -157,6 +157,9 @@ class MessageListFragment :
             maybeHideFloatingActionButton()
         }
 
+    val isShowAccountChip: Boolean
+        get() = !isSingleAccountMode
+
     override fun onAttach(context: Context) {
         super.onAttach(context)
 
@@ -571,7 +574,7 @@ class MessageListFragment :
             showContactPicture = K9.isShowContactPicture,
             showingThreadedList = showingThreadedList,
             backGroundAsReadIndicator = K9.isUseBackgroundAsUnreadIndicator,
-            showAccountChip = !isSingleAccountMode
+            showAccountChip = isShowAccountChip
         )
 
     private fun getFolderInfoHolder(folderId: Long, account: Account): FolderInfoHolder {
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/CryptoInfoDialog.java b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/CryptoInfoDialog.java
deleted file mode 100644
index cdbc6d3b984..00000000000
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/CryptoInfoDialog.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package com.fsck.k9.ui.messageview;
-
-
-import android.annotation.SuppressLint;
-import android.app.AlertDialog;
-import android.app.AlertDialog.Builder;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.os.Bundle;
-import androidx.annotation.AttrRes;
-import androidx.annotation.ColorInt;
-import androidx.annotation.DrawableRes;
-import androidx.annotation.StringRes;
-import androidx.fragment.app.DialogFragment;
-import androidx.fragment.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.fsck.k9.ui.R;
-import com.fsck.k9.view.MessageCryptoDisplayStatus;
-import com.fsck.k9.view.ThemeUtils;
-
-
-public class CryptoInfoDialog extends DialogFragment {
-    public static final String ARG_DISPLAY_STATUS = "display_status";
-    public static final String ARG_HAS_SECURITY_WARNING = "has_security_warning";
-
-
-    private ImageView statusIcon;
-    private TextView titleText;
-    private TextView descriptionText;
-
-
-    public static CryptoInfoDialog newInstance(MessageCryptoDisplayStatus displayStatus, boolean hasSecurityWarning) {
-        CryptoInfoDialog frag = new CryptoInfoDialog();
-
-        Bundle args = new Bundle();
-        args.putString(ARG_DISPLAY_STATUS, displayStatus.toString());
-        args.putBoolean(ARG_HAS_SECURITY_WARNING, hasSecurityWarning);
-        frag.setArguments(args);
-
-        return frag;
-    }
-
-    @SuppressLint("InflateParams") // inflating without root element is fine for creating a dialog
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        Builder b = new AlertDialog.Builder(getActivity());
-
-        View dialogView = LayoutInflater.from(getActivity()).inflate(R.layout.message_crypto_info_dialog, null);
-
-        statusIcon = dialogView.findViewById(R.id.crypto_info_top_icon_1);
-        titleText = dialogView.findViewById(R.id.crypto_info_title);
-        descriptionText = dialogView.findViewById(R.id.crypto_info_text);
-
-        MessageCryptoDisplayStatus displayStatus =
-                MessageCryptoDisplayStatus.valueOf(getArguments().getString(ARG_DISPLAY_STATUS));
-        setMessageForDisplayStatus(displayStatus);
-
-        b.setView(dialogView);
-        b.setPositiveButton(R.string.crypto_info_ok, new OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialogInterface, int i) {
-                dismiss();
-            }
-        });
-        boolean hasSecurityWarning = getArguments().getBoolean(ARG_HAS_SECURITY_WARNING);
-        if (hasSecurityWarning) {
-            b.setNeutralButton(R.string.crypto_info_view_security_warning, new OnClickListener() {
-                @Override
-                public void onClick(DialogInterface dialogInterface, int i) {
-                    Fragment frag = getTargetFragment();
-                    if (!(frag instanceof OnClickShowCryptoKeyListener)) {
-                        throw new AssertionError("Displaying activity must implement OnClickShowCryptoKeyListener!");
-                    }
-                    ((OnClickShowCryptoKeyListener) frag).onClickShowSecurityWarning();
-                }
-            });
-        } else if (displayStatus.isUnknownKey()) {
-            b.setNeutralButton(R.string.crypto_info_search_key, new OnClickListener() {
-                @Override
-                public void onClick(DialogInterface dialogInterface, int i) {
-                    Fragment frag = getTargetFragment();
-                    if (! (frag instanceof OnClickShowCryptoKeyListener)) {
-                        throw new AssertionError("Displaying activity must implement OnClickShowCryptoKeyListener!");
-                    }
-                    ((OnClickShowCryptoKeyListener) frag).onClickSearchKey();
-                }
-            });
-        } else if (displayStatus.hasAssociatedKey()) {
-            int buttonLabel = displayStatus.isUnencryptedSigned() ?
-                    R.string.crypto_info_view_signer : R.string.crypto_info_view_sender;
-            b.setNeutralButton(buttonLabel, new OnClickListener() {
-                @Override
-                public void onClick(DialogInterface dialogInterface, int i) {
-                    Fragment frag = getTargetFragment();
-                    if (! (frag instanceof OnClickShowCryptoKeyListener)) {
-                        throw new AssertionError("Displaying activity must implement OnClickShowCryptoKeyListener!");
-                    }
-                    ((OnClickShowCryptoKeyListener) frag).onClickShowCryptoKey();
-                }
-            });
-        }
-
-        return b.create();
-    }
-
-    private void setMessageForDisplayStatus(MessageCryptoDisplayStatus displayStatus) {
-        if (displayStatus.getTitleTextRes() == null) {
-            throw new AssertionError("Crypto info dialog can only be displayed for items with text!");
-        }
-
-        setMessageSingleLine(displayStatus.getColorAttr(), displayStatus.getTitleTextRes(),
-                displayStatus.getDescriptionTextRes(), displayStatus.getStatusIconRes());
-    }
-
-    private void setMessageSingleLine(@AttrRes int colorAttr, @StringRes int titleTextRes,
-            @StringRes Integer descTextRes, @DrawableRes int statusIconRes) {
-        @ColorInt int color = ThemeUtils.getStyledColor(getActivity(), colorAttr);
-
-        statusIcon.setImageResource(statusIconRes);
-        statusIcon.setColorFilter(color);
-        titleText.setText(titleTextRes);
-        if (descTextRes != null) {
-            descriptionText.setText(descTextRes);
-            descriptionText.setVisibility(View.VISIBLE);
-        } else {
-            descriptionText.setVisibility(View.GONE);
-        }
-    }
-
-    public interface OnClickShowCryptoKeyListener {
-        void onClickShowCryptoKey();
-        void onClickShowSecurityWarning();
-        void onClickSearchKey();
-    }
-}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/DisplayRecipientsExtractor.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/DisplayRecipientsExtractor.kt
new file mode 100644
index 00000000000..269c5cb317f
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/DisplayRecipientsExtractor.kt
@@ -0,0 +1,59 @@
+package com.fsck.k9.ui.messageview
+
+import com.fsck.k9.Account
+import com.fsck.k9.helper.AddressFormatter
+import com.fsck.k9.mail.Address
+import com.fsck.k9.mail.Message
+
+/**
+ * Extract recipient names from a message to display them in the message view.
+ *
+ * This class extracts up to [maxNumberOfDisplayRecipients] recipients from the message and converts them to their
+ * display name using an [AddressFormatter].
+ */
+internal class DisplayRecipientsExtractor(
+    private val addressFormatter: AddressFormatter,
+    private val maxNumberOfDisplayRecipients: Int
+) {
+    fun extractDisplayRecipients(message: Message, account: Account): DisplayRecipients {
+        val toRecipients = message.getRecipients(Message.RecipientType.TO)
+        val ccRecipients = message.getRecipients(Message.RecipientType.CC)
+        val bccRecipients = message.getRecipients(Message.RecipientType.BCC)
+
+        val numberOfRecipients = toRecipients.size + ccRecipients.size + bccRecipients.size
+
+        val identity = sequenceOf(toRecipients, ccRecipients, bccRecipients)
+            .flatMap { addressArray -> addressArray.asSequence() }
+            .mapNotNull { address -> account.findIdentity(address) }
+            .firstOrNull()
+
+        val identityEmail = identity?.email
+        val maxAdditionalRecipients = if (identity != null) {
+            maxNumberOfDisplayRecipients - 1
+        } else {
+            maxNumberOfDisplayRecipients
+        }
+
+        val recipientNames = sequenceOf(toRecipients, ccRecipients, bccRecipients)
+            .flatMap { addressArray -> addressArray.asSequence() }
+            .filter { address -> address.address != identityEmail }
+            .map { address -> addressFormatter.getDisplayName(address) }
+            .take(maxAdditionalRecipients)
+            .toList()
+
+        return if (identity != null) {
+            val identityAddress = Address(identity.email)
+            val meName = addressFormatter.getDisplayName(identityAddress)
+            val recipients = listOf(meName) + recipientNames
+
+            DisplayRecipients(recipients, numberOfRecipients)
+        } else {
+            DisplayRecipients(recipientNames, numberOfRecipients)
+        }
+    }
+}
+
+internal data class DisplayRecipients(
+    val recipientNames: List<CharSequence>,
+    val numberOfRecipients: Int
+)
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageCryptoPresenter.java b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageCryptoPresenter.java
index 6a192e5ea9d..260ff8b81aa 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageCryptoPresenter.java
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageCryptoPresenter.java
@@ -20,7 +20,7 @@
 
 
 @SuppressWarnings("WeakerAccess")
-public class MessageCryptoPresenter implements OnCryptoClickListener {
+public class MessageCryptoPresenter {
     public static final int REQUEST_CODE_UNKNOWN_KEY = 123;
     public static final int REQUEST_CODE_SECURITY_WARNING = 124;
 
@@ -38,6 +38,10 @@ public MessageCryptoPresenter(MessageCryptoMvpView messageCryptoMvpView) {
         this.messageCryptoMvpView = messageCryptoMvpView;
     }
 
+    public CryptoResultAnnotation getCryptoResultAnnotation() {
+        return cryptoResultAnnotation;
+    }
+
     public void onResume() {
         if (reloadOnResumeWithoutRecreateFlag) {
             reloadOnResumeWithoutRecreateFlag = false;
@@ -96,23 +100,6 @@ public boolean maybeHandleShowMessage(MessageTopView messageView, Account accoun
         return true;
     }
 
-    @Override
-    public void onCryptoClick() {
-        if (cryptoResultAnnotation == null) {
-            return;
-        }
-        MessageCryptoDisplayStatus displayStatus =
-                MessageCryptoDisplayStatus.fromResultAnnotation(cryptoResultAnnotation);
-        switch (displayStatus) {
-            case LOADING:
-                // no need to do anything, there is a progress bar...
-                break;
-            default:
-                displayCryptoInfoDialog(displayStatus);
-                break;
-        }
-    }
-
     @SuppressWarnings("UnusedParameters") // for consistency with Activity.onActivityResult
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (requestCode == REQUEST_CODE_UNKNOWN_KEY) {
@@ -128,11 +115,6 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
         }
     }
 
-    private void displayCryptoInfoDialog(MessageCryptoDisplayStatus displayStatus) {
-        messageCryptoMvpView.showCryptoInfoDialog(
-                displayStatus, cryptoResultAnnotation.hasOpenPgpInsecureWarningPendingIntent());
-    }
-
     void onClickSearchKey() {
         try {
             PendingIntent pendingIntent = cryptoResultAnnotation.getOpenPgpSigningKeyIntentIfAny();
@@ -145,18 +127,6 @@ void onClickSearchKey() {
         }
     }
 
-    public void onClickShowCryptoKey() {
-        try {
-            PendingIntent pendingIntent = cryptoResultAnnotation.getOpenPgpSigningKeyIntentIfAny();
-            if (pendingIntent != null) {
-                messageCryptoMvpView.startPendingIntentForCryptoPresenter(
-                        pendingIntent.getIntentSender(), null, null, 0, 0, 0);
-            }
-        } catch (IntentSender.SendIntentException e) {
-            Timber.e(e, "SendIntentException");
-        }
-    }
-
     public void onClickRetryCryptoOperation() {
         messageCryptoMvpView.restartMessageCryptoProcessing();
     }
@@ -204,7 +174,6 @@ public interface MessageCryptoMvpView {
         void startPendingIntentForCryptoPresenter(IntentSender si, Integer requestCode, Intent fillIntent,
                 int flagsMask, int flagValues, int extraFlags) throws IntentSender.SendIntentException;
 
-        void showCryptoInfoDialog(MessageCryptoDisplayStatus displayStatus, boolean hasSecurityWarning);
         void showCryptoConfigDialog();
     }
 }
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageHeaderClickListener.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageHeaderClickListener.kt
new file mode 100644
index 00000000000..601d25be373
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageHeaderClickListener.kt
@@ -0,0 +1,6 @@
+package com.fsck.k9.ui.messageview
+
+interface MessageHeaderClickListener {
+    fun onParticipantsContainerClick()
+    fun onMenuItemClick(itemId: Int)
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageTopView.java b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageTopView.java
index 0e5e910c32b..d1a6c9fe831 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageTopView.java
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageTopView.java
@@ -9,7 +9,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import androidx.annotation.NonNull;
-import androidx.appcompat.widget.PopupMenu.OnMenuItemClickListener;
+
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -51,10 +51,13 @@ public class MessageTopView extends LinearLayout {
     private ViewGroup containerView;
     private Button mDownloadRemainder;
     private AttachmentViewCallback attachmentCallback;
+    private View extraHeaderContainer;
     private Button showPicturesButton;
     private boolean isShowingProgress;
     private boolean showPicturesButtonClicked;
 
+    private boolean showAccountChip;
+
     private MessageCryptoPresenter messageCryptoPresenter;
 
 
@@ -76,6 +79,7 @@ public void onFinishInflate() {
         mDownloadRemainder = findViewById(R.id.download_remainder);
         mDownloadRemainder.setVisibility(View.GONE);
 
+        extraHeaderContainer = findViewById(R.id.extra_header_container);
         showPicturesButton = findViewById(R.id.show_pictures);
         setShowPicturesButtonListener();
 
@@ -84,6 +88,10 @@ public void onFinishInflate() {
         hideHeaderView();
     }
 
+    public void setShowAccountChip(boolean showAccountChip) {
+        this.showAccountChip = showAccountChip;
+    }
+
     private void setShowPicturesButtonListener() {
         showPicturesButton.setOnClickListener(new OnClickListener() {
             @Override
@@ -208,7 +216,7 @@ public MessageHeader getMessageHeaderView() {
     }
 
     public void setHeaders(Message message, Account account, boolean showStar) {
-        mHeaderContainer.populate(message, account, showStar);
+        mHeaderContainer.populate(message, account, showStar, showAccountChip);
         mHeaderContainer.setVisibility(View.VISIBLE);
     }
 
@@ -220,8 +228,8 @@ public void setOnToggleFlagClickListener(OnClickListener listener) {
         mHeaderContainer.setOnFlagListener(listener);
     }
 
-    public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
-        mHeaderContainer.setOnMenuItemClickListener(listener);
+    public void setMessageHeaderClickListener(MessageHeaderClickListener listener) {
+        mHeaderContainer.setMessageHeaderClickListener(listener);
     }
 
     private void hideHeaderView() {
@@ -238,7 +246,6 @@ public void setAttachmentCallback(AttachmentViewCallback callback) {
 
     public void setMessageCryptoPresenter(MessageCryptoPresenter messageCryptoPresenter) {
         this.messageCryptoPresenter = messageCryptoPresenter;
-        mHeaderContainer.setOnCryptoClickListener(messageCryptoPresenter);
     }
 
     public void enableDownloadButton() {
@@ -259,11 +266,11 @@ private void setShowDownloadButton(MessageViewInfo messageViewInfo) {
     }
 
     private void showShowPicturesButton() {
-        showPicturesButton.setVisibility(View.VISIBLE);
+        extraHeaderContainer.setVisibility(View.VISIBLE);
     }
 
     private void hideShowPicturesButton() {
-        showPicturesButton.setVisibility(View.GONE);
+        extraHeaderContainer.setVisibility(View.GONE);
     }
 
     private boolean shouldAutomaticallyLoadPictures(ShowPictures showPicturesSetting, Message message) {
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewContainerFragment.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewContainerFragment.kt
index d5dd3509f80..f2caf1b71b8 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewContainerFragment.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewContainerFragment.kt
@@ -29,6 +29,8 @@ class MessageViewContainerFragment : Fragment() {
             setMenuVisibility(value)
         }
 
+    private var showAccountChip: Boolean = true
+
     lateinit var messageReference: MessageReference
         private set
 
@@ -72,7 +74,9 @@ class MessageViewContainerFragment : Fragment() {
             lastDirection = savedInstanceState.getSerializable(STATE_LAST_DIRECTION) as Direction?
         }
 
-        adapter = MessageViewContainerAdapter(this)
+        showAccountChip = arguments?.getBoolean(ARG_SHOW_ACCOUNT_CHIP) ?: showAccountChip
+
+        adapter = MessageViewContainerAdapter(this, showAccountChip)
     }
 
     override fun onAttach(context: Context) {
@@ -229,7 +233,11 @@ class MessageViewContainerFragment : Fragment() {
         findMessageViewFragment().onPendingIntentResult(requestCode, resultCode, data)
     }
 
-    private class MessageViewContainerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
+    private class MessageViewContainerAdapter(
+        fragment: Fragment,
+        private val showAccountChip: Boolean
+    ) : FragmentStateAdapter(fragment) {
+
         var messageList: List<MessageListItem> = emptyList()
             set(value) {
                 val diffResult = DiffUtil.calculateDiff(
@@ -257,7 +265,7 @@ class MessageViewContainerFragment : Fragment() {
             check(position in messageList.indices)
 
             val messageReference = messageList[position].messageReference
-            return MessageViewFragment.newInstance(messageReference)
+            return MessageViewFragment.newInstance(messageReference, showAccountChip)
         }
 
         fun getMessageReference(position: Int): MessageReference? {
@@ -298,13 +306,15 @@ class MessageViewContainerFragment : Fragment() {
 
     companion object {
         private const val ARG_REFERENCE = "reference"
+        private const val ARG_SHOW_ACCOUNT_CHIP = "showAccountChip"
 
         private const val STATE_MESSAGE_REFERENCE = "messageReference"
         private const val STATE_LAST_DIRECTION = "lastDirection"
 
-        fun newInstance(reference: MessageReference): MessageViewContainerFragment {
+        fun newInstance(reference: MessageReference, showAccountChip: Boolean): MessageViewContainerFragment {
             return MessageViewContainerFragment().withArguments(
-                ARG_REFERENCE to reference.toIdentityString()
+                ARG_REFERENCE to reference.toIdentityString(),
+                ARG_SHOW_ACCOUNT_CHIP to showAccountChip
             )
         }
     }
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.kt
index 54fc7634789..b1d70c222c3 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewFragment.kt
@@ -21,6 +21,7 @@ import androidx.core.app.ActivityCompat
 import androidx.core.content.withStyledAttributes
 import androidx.fragment.app.DialogFragment
 import androidx.fragment.app.Fragment
+import androidx.fragment.app.setFragmentResultListener
 import com.fsck.k9.Account
 import com.fsck.k9.K9
 import com.fsck.k9.activity.MessageCompose
@@ -45,13 +46,12 @@ import com.fsck.k9.ui.R
 import com.fsck.k9.ui.base.Theme
 import com.fsck.k9.ui.base.ThemeManager
 import com.fsck.k9.ui.choosefolder.ChooseFolderActivity
+import com.fsck.k9.ui.messagedetails.MessageDetailsFragment
 import com.fsck.k9.ui.messagesource.MessageSourceActivity
-import com.fsck.k9.ui.messageview.CryptoInfoDialog.OnClickShowCryptoKeyListener
 import com.fsck.k9.ui.messageview.MessageCryptoPresenter.MessageCryptoMvpView
 import com.fsck.k9.ui.settings.account.AccountSettingsActivity
 import com.fsck.k9.ui.share.ShareIntentBuilder
 import com.fsck.k9.ui.withArguments
-import com.fsck.k9.view.MessageCryptoDisplayStatus
 import java.util.Locale
 import org.koin.android.ext.android.inject
 import timber.log.Timber
@@ -59,8 +59,7 @@ import timber.log.Timber
 class MessageViewFragment :
     Fragment(),
     ConfirmationDialogFragmentListener,
-    AttachmentViewCallback,
-    OnClickShowCryptoKeyListener {
+    AttachmentViewCallback {
 
     private val themeManager: ThemeManager by inject()
     private val messageLoaderHelperFactory: MessageLoaderHelperFactory by inject()
@@ -86,6 +85,7 @@ class MessageViewFragment :
 
     private lateinit var account: Account
     lateinit var messageReference: MessageReference
+    private var showAccountChip: Boolean = true
 
     private var currentAttachmentViewInfo: AttachmentViewInfo? = null
     private var isDeleteMenuItemDisabled: Boolean = false
@@ -118,6 +118,9 @@ class MessageViewFragment :
         messageReference = MessageReference.parse(arguments?.getString(ARG_REFERENCE))
             ?: error("Invalid argument '$ARG_REFERENCE'")
 
+        showAccountChip = arguments?.getBoolean(ARG_SHOW_ACCOUNT_CHIP)
+            ?: error("Missing argument: '$ARG_SHOW_ACCOUNT_CHIP'")
+
         if (savedInstanceState != null) {
             wasMessageMarkedAsOpened = savedInstanceState.getBoolean(STATE_WAS_MESSAGE_MARKED_AS_OPENED)
         }
@@ -129,6 +132,8 @@ class MessageViewFragment :
             fragmentManager = parentFragmentManager,
             callback = messageLoaderCallbacks
         )
+
+        setFragmentResultListener(MessageDetailsFragment.FRAGMENT_RESULT_KEY, ::onMessageDetailsResult)
     }
 
     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
@@ -145,6 +150,8 @@ class MessageViewFragment :
     }
 
     private fun initializeMessageTopView(messageTopView: MessageTopView) {
+        messageTopView.setShowAccountChip(showAccountChip)
+
         messageTopView.setAttachmentCallback(this)
         messageTopView.setMessageCryptoPresenter(messageCryptoPresenter)
 
@@ -152,9 +159,7 @@ class MessageViewFragment :
             onToggleFlagged()
         }
 
-        messageTopView.setOnMenuItemClickListener { item ->
-            onReplyMenuItemClicked(item.itemId)
-        }
+        messageTopView.setMessageHeaderClickListener(messageHeaderClickListener)
 
         messageTopView.setOnDownloadButtonClickListener {
             onDownloadButtonClicked()
@@ -376,17 +381,23 @@ class MessageViewFragment :
         messageTopView.setSubject(displaySubject)
     }
 
-    private fun onReplyMenuItemClicked(itemId: Int): Boolean {
-        when (itemId) {
-            R.id.reply -> onReply()
-            R.id.reply_all -> onReplyAll()
-            R.id.forward -> onForward()
-            R.id.forward_as_attachment -> onForwardAsAttachment()
-            R.id.share -> onSendAlternate()
-            else -> error("Missing handler for reply menu item $itemId")
+    private val messageHeaderClickListener = object : MessageHeaderClickListener {
+        override fun onParticipantsContainerClick() {
+            val messageDetailsFragment = MessageDetailsFragment.create(messageReference)
+            messageDetailsFragment.cryptoResult = messageCryptoPresenter.cryptoResultAnnotation
+            messageDetailsFragment.show(parentFragmentManager, "message_details")
         }
 
-        return true
+        override fun onMenuItemClick(itemId: Int) {
+            when (itemId) {
+                R.id.reply -> onReply()
+                R.id.reply_all -> onReplyAll()
+                R.id.forward -> onForward()
+                R.id.forward_as_attachment -> onForwardAsAttachment()
+                R.id.share -> onSendAlternate()
+                else -> error("Missing handler for reply menu item $itemId")
+            }
+        }
     }
 
     private fun onDownloadButtonClicked() {
@@ -568,6 +579,20 @@ class MessageViewFragment :
         }
     }
 
+    private fun onMessageDetailsResult(requestKey: String, result: Bundle) {
+        when (val action = result.getString(MessageDetailsFragment.RESULT_ACTION)) {
+            MessageDetailsFragment.ACTION_SEARCH_KEYS -> {
+                messageCryptoPresenter.onClickSearchKey()
+            }
+            MessageDetailsFragment.ACTION_SHOW_WARNING -> {
+                messageCryptoPresenter.onClickShowCryptoWarningDetails()
+            }
+            else -> {
+                error("Unsupported action: $action")
+            }
+        }
+    }
+
     private fun onCreateDocumentResult(data: Intent?) {
         if (data != null && data.data != null) {
             createAttachmentController(currentAttachmentViewInfo).saveAttachmentTo(data.data)
@@ -806,12 +831,6 @@ class MessageViewFragment :
             )
         }
 
-        override fun showCryptoInfoDialog(displayStatus: MessageCryptoDisplayStatus, hasSecurityWarning: Boolean) {
-            val dialog = CryptoInfoDialog.newInstance(displayStatus, hasSecurityWarning)
-            dialog.setTargetFragment(this@MessageViewFragment, 0)
-            dialog.show(parentFragmentManager, "crypto_info_dialog")
-        }
-
         override fun restartMessageCryptoProcessing() {
             messageTopView.setToLoadingState()
             messageLoaderHelper.asyncRestartMessageCryptoProcessing()
@@ -822,18 +841,6 @@ class MessageViewFragment :
         }
     }
 
-    override fun onClickShowSecurityWarning() {
-        messageCryptoPresenter.onClickShowCryptoWarningDetails()
-    }
-
-    override fun onClickSearchKey() {
-        messageCryptoPresenter.onClickSearchKey()
-    }
-
-    override fun onClickShowCryptoKey() {
-        messageCryptoPresenter.onClickShowCryptoKey()
-    }
-
     interface MessageViewFragmentListener {
         fun onForward(messageReference: MessageReference, decryptionResultForReply: Parcelable?)
         fun onForwardAsAttachment(messageReference: MessageReference, decryptionResultForReply: Parcelable?)
@@ -960,6 +967,7 @@ class MessageViewFragment :
         const val PROGRESS_THRESHOLD_MILLIS = 500 * 1000
 
         private const val ARG_REFERENCE = "reference"
+        private const val ARG_SHOW_ACCOUNT_CHIP = "showAccountChip"
 
         private const val STATE_WAS_MESSAGE_MARKED_AS_OPENED = "wasMessageMarkedAsOpened"
 
@@ -967,9 +975,10 @@ class MessageViewFragment :
         private const val ACTIVITY_CHOOSE_FOLDER_COPY = 2
         private const val REQUEST_CODE_CREATE_DOCUMENT = 3
 
-        fun newInstance(reference: MessageReference): MessageViewFragment {
+        fun newInstance(reference: MessageReference, showAccountChip: Boolean): MessageViewFragment {
             return MessageViewFragment().withArguments(
-                ARG_REFERENCE to reference.toIdentityString()
+                ARG_REFERENCE to reference.toIdentityString(),
+                ARG_SHOW_ACCOUNT_CHIP to showAccountChip
             )
         }
     }
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/OnCryptoClickListener.java b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/OnCryptoClickListener.java
deleted file mode 100644
index 7df9d45e787..00000000000
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/OnCryptoClickListener.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.fsck.k9.ui.messageview;
-
-
-public interface OnCryptoClickListener {
-    void onCryptoClick();
-}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientLayoutCreator.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientLayoutCreator.kt
new file mode 100644
index 00000000000..179caaae88e
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientLayoutCreator.kt
@@ -0,0 +1,119 @@
+package com.fsck.k9.ui.messageview
+
+import android.text.SpannableStringBuilder
+
+private const val LIST_SEPARATOR = ", "
+
+/**
+ * Calculates how many recipient names can be displayed given the available width.
+ *
+ * We display up to [maxNumberOfRecipientNames] recipient names, then the number of additional recipients.
+ *
+ * Example:
+ *   to me, Alice, Bob, Charly, Dora +11
+ *
+ * If there's not enough room to display the first recipient name, we return it anyway and expect the component that is
+ * actually rendering the text to ellipsize [RecipientLayoutData.recipientNames], but not
+ * [RecipientLayoutData.additionalRecipients].
+ */
+internal class RecipientLayoutCreator(
+    private val textMeasure: TextMeasure,
+    private val maxNumberOfRecipientNames: Int,
+    private val recipientsPrefix: String,
+    private val additionalRecipientSpacing: Int,
+    private val additionalRecipientsPrefix: String
+) {
+    fun createRecipientLayout(
+        recipientNames: List<CharSequence>,
+        totalNumberOfRecipients: Int,
+        availableWidth: Int
+    ): RecipientLayoutData {
+        require(recipientNames.isNotEmpty())
+
+        val displayRecipientsBuilder = SpannableStringBuilder()
+
+        if (recipientNames.size == 1) {
+            displayRecipientsBuilder.append(recipientsPrefix)
+            displayRecipientsBuilder.append(recipientNames.first())
+
+            return RecipientLayoutData(
+                recipientNames = displayRecipientsBuilder,
+                additionalRecipients = null
+            )
+        }
+
+        val additionalRecipientsBuilder = StringBuilder(additionalRecipientsPrefix + 10)
+
+        val maxRecipientNames = recipientNames.size.coerceAtMost(maxNumberOfRecipientNames)
+        for (numberOfDisplayRecipients in maxRecipientNames downTo 2) {
+            displayRecipientsBuilder.clear()
+            displayRecipientsBuilder.append(recipientsPrefix)
+
+            recipientNames.asSequence()
+                .take(numberOfDisplayRecipients)
+                .joinTo(displayRecipientsBuilder, separator = LIST_SEPARATOR)
+
+            additionalRecipientsBuilder.setLength(0)
+            val numberOfAdditionalRecipients = totalNumberOfRecipients - numberOfDisplayRecipients
+            if (numberOfAdditionalRecipients > 0) {
+                additionalRecipientsBuilder.append(additionalRecipientsPrefix)
+                additionalRecipientsBuilder.append(numberOfAdditionalRecipients)
+            }
+
+            if (doesTextFitAvailableWidth(displayRecipientsBuilder, additionalRecipientsBuilder, availableWidth)) {
+                return RecipientLayoutData(
+                    recipientNames = displayRecipientsBuilder,
+                    additionalRecipients = additionalRecipientsBuilder.toStringOrNull()
+                )
+            }
+        }
+
+        displayRecipientsBuilder.clear()
+        displayRecipientsBuilder.append(recipientsPrefix)
+        displayRecipientsBuilder.append(recipientNames.first())
+
+        return RecipientLayoutData(
+            recipientNames = displayRecipientsBuilder,
+            additionalRecipients = "$additionalRecipientsPrefix${totalNumberOfRecipients - 1}"
+        )
+    }
+
+    private fun doesTextFitAvailableWidth(
+        displayRecipients: CharSequence,
+        additionalRecipients: CharSequence,
+        availableWidth: Int
+    ): Boolean {
+        val recipientNamesWidth = textMeasure.measureRecipientNames(displayRecipients)
+        if (recipientNamesWidth > availableWidth) {
+            return false
+        } else if (additionalRecipients.isEmpty()) {
+            return true
+        }
+
+        val totalWidth = recipientNamesWidth + additionalRecipientSpacing +
+            textMeasure.measureRecipientCount(additionalRecipients)
+
+        return totalWidth <= availableWidth
+    }
+}
+
+private fun StringBuilder.toStringOrNull(): String? {
+    return if (isEmpty()) null else toString()
+}
+
+internal data class RecipientLayoutData(
+    val recipientNames: CharSequence,
+    val additionalRecipients: String?
+)
+
+internal interface TextMeasure {
+    /**
+     * Measure the width of the supplied recipient names when rendered.
+     */
+    fun measureRecipientNames(text: CharSequence): Int
+
+    /**
+     * Measure the width of the supplied recipient count when rendered.
+     */
+    fun measureRecipientCount(text: CharSequence): Int
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientNamesView.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientNamesView.kt
new file mode 100644
index 00000000000..b4bc9f0432c
--- /dev/null
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/RecipientNamesView.kt
@@ -0,0 +1,184 @@
+package com.fsck.k9.ui.messageview
+
+import android.content.Context
+import android.util.AttributeSet
+import android.util.TypedValue
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.core.view.isGone
+import com.fsck.k9.ui.R
+
+private const val MAX_NUMBER_OF_RECIPIENT_NAMES = 5
+
+/**
+ * View that displays the names of recipients of a message.
+ *
+ * Up to [MAX_NUMBER_OF_RECIPIENT_NAMES] names of recipients are displayed, followed by the number of recipients that
+ * weren't displayed.
+ *
+ * Examples:
+ * - to me, Alice, Bob, Charly +3
+ * - to Camila Hyphenated-Nam… +5
+ *
+ * This custom layout uses [RecipientLayoutCreator] to figure out how many recipient names can be displayed without
+ * being truncated. If not even one recipient name can be displayed without being truncated, we first measure the space
+ * needed for number of additional recipients, then use the rest to display the first recipient and ellipsize the end.
+ */
+class RecipientNamesView(context: Context, attrs: AttributeSet?) : ViewGroup(context, attrs) {
+    val maxNumberOfRecipientNames: Int = MAX_NUMBER_OF_RECIPIENT_NAMES
+
+    private val recipientLayoutCreator: RecipientLayoutCreator
+
+    private val recipientNameTextView: TextView
+    private val recipientCountTextView: TextView
+    private val additionRecipientSpacing: Int
+
+    init {
+        LayoutInflater.from(context).inflate(R.layout.recipient_names, this, true)
+        recipientNameTextView = findViewById(R.id.recipient_names)
+        recipientCountTextView = findViewById(R.id.recipient_count)
+        additionRecipientSpacing = (recipientCountTextView.layoutParams as MarginLayoutParams).marginStart
+    }
+
+    private var recipientNames: List<CharSequence> = emptyList()
+    private var numberOfRecipients: Int = 0
+
+    private val textMeasure = object : TextMeasure {
+        override fun measureRecipientNames(text: CharSequence): Int {
+            return measureWidth(recipientNameTextView, text)
+        }
+
+        override fun measureRecipientCount(text: CharSequence): Int {
+            return measureWidth(recipientCountTextView, text)
+        }
+
+        private fun measureWidth(textView: TextView, text: CharSequence): Int {
+            textView.text = text
+
+            val widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
+            val heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)
+            textView.measure(widthMeasureSpec, heightMeasureSpec)
+
+            return textView.measuredWidth
+        }
+    }
+
+    init {
+        recipientLayoutCreator = RecipientLayoutCreator(
+            textMeasure = textMeasure,
+            maxNumberOfRecipientNames = MAX_NUMBER_OF_RECIPIENT_NAMES,
+            recipientsPrefix = context.getString(R.string.message_view_recipient_prefix),
+            additionalRecipientSpacing = additionRecipientSpacing,
+            additionalRecipientsPrefix = context.getString(R.string.message_view_additional_recipient_prefix)
+        )
+
+        if (isInEditMode) {
+            recipientNames = listOf(
+                "Grace Hopper", "Katherine Johnson", "Margaret Hamilton", "Adele Goldberg", "Steve Shirley"
+            )
+            numberOfRecipients = 8
+        }
+    }
+
+    fun setTextSize(textSize: Int) {
+        recipientNameTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize.toFloat())
+        recipientCountTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize.toFloat())
+    }
+
+    fun setRecipients(recipientNames: List<CharSequence>, numberOfRecipients: Int) {
+        if (recipientNames != this.recipientNames && numberOfRecipients != this.numberOfRecipients) {
+            this.recipientNames = recipientNames
+            this.numberOfRecipients = numberOfRecipients
+            requestLayout()
+        }
+    }
+
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        require(MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
+            "Width of RecipientNamesView needs to be constrained"
+        }
+
+        recipientNameTextView.measure(widthMeasureSpec, heightMeasureSpec)
+        recipientCountTextView.measure(widthMeasureSpec, heightMeasureSpec)
+
+        val width = MeasureSpec.getSize(widthMeasureSpec)
+        val height = maxOf(recipientNameTextView.measuredHeight, recipientCountTextView.measuredHeight)
+        setMeasuredDimension(width, height)
+    }
+
+    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
+        val availableWidth = width
+
+        val recipientLayoutData = recipientLayoutCreator.createRecipientLayout(
+            recipientNames, numberOfRecipients, availableWidth
+        )
+
+        recipientNameTextView.text = recipientLayoutData.recipientNames
+        val additionalRecipientsVisible = recipientLayoutData.additionalRecipients != null
+        val remainingWidth: Int
+        if (additionalRecipientsVisible) {
+            recipientCountTextView.isGone = false
+            recipientCountTextView.text = recipientLayoutData.additionalRecipients
+
+            recipientCountTextView.measure(
+                MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
+                MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.AT_MOST)
+            )
+
+            remainingWidth = availableWidth - additionRecipientSpacing - recipientCountTextView.measuredWidth
+        } else {
+            recipientCountTextView.isGone = true
+            remainingWidth = availableWidth
+        }
+
+        recipientNameTextView.measure(
+            MeasureSpec.makeMeasureSpec(remainingWidth, MeasureSpec.AT_MOST),
+            MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.AT_MOST)
+        )
+
+        if (layoutDirection == LAYOUT_DIRECTION_LTR) {
+            val recipientNameRight = recipientNameTextView.measuredWidth
+            recipientNameTextView.layout(
+                0,
+                0,
+                recipientNameRight,
+                recipientNameTextView.measuredHeight
+            )
+            val recipientCountLeft = recipientNameRight + additionRecipientSpacing
+            recipientCountTextView.layout(
+                recipientCountLeft,
+                0,
+                recipientCountLeft + recipientCountTextView.measuredWidth,
+                recipientCountTextView.measuredHeight
+            )
+        } else {
+            val recipientNameLeft = width - recipientNameTextView.measuredWidth
+            recipientNameTextView.layout(
+                recipientNameLeft,
+                0,
+                right,
+                recipientNameTextView.measuredHeight
+            )
+            val recipientCountRight = recipientNameLeft - additionRecipientSpacing
+            recipientCountTextView.layout(
+                recipientCountRight - recipientCountTextView.measuredWidth,
+                0,
+                recipientCountRight,
+                0 + recipientCountTextView.measuredHeight
+            )
+        }
+    }
+
+    override fun checkLayoutParams(p: LayoutParams?): Boolean {
+        return p is MarginLayoutParams
+    }
+
+    override fun generateDefaultLayoutParams(): LayoutParams {
+        return MarginLayoutParams(0, 0)
+    }
+
+    override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {
+        return MarginLayoutParams(context, attrs)
+    }
+}
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/general/GeneralSettingsDataStore.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/general/GeneralSettingsDataStore.kt
index c89fd6c3900..8308befea57 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/general/GeneralSettingsDataStore.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/general/GeneralSettingsDataStore.kt
@@ -117,12 +117,9 @@ class GeneralSettingsDataStore(
             "message_list_date_font" -> K9.fontSizes.messageListDate.toString()
             "message_list_preview_font" -> K9.fontSizes.messageListPreview.toString()
             "message_view_sender_font" -> K9.fontSizes.messageViewSender.toString()
-            "message_view_to_font" -> K9.fontSizes.messageViewTo.toString()
-            "message_view_cc_font" -> K9.fontSizes.messageViewCC.toString()
-            "message_view_bcc_font" -> K9.fontSizes.messageViewBCC.toString()
+            "message_view_recipients_font" -> K9.fontSizes.messageViewRecipients.toString()
             "message_view_subject_font" -> K9.fontSizes.messageViewSubject.toString()
             "message_view_date_font" -> K9.fontSizes.messageViewDate.toString()
-            "message_view_additional_headers_font" -> K9.fontSizes.messageViewAdditionalHeaders.toString()
             "message_compose_input_font" -> K9.fontSizes.messageComposeInput.toString()
             "swipe_action_right" -> swipeActionToString(K9.swipeRightAction)
             "swipe_action_left" -> swipeActionToString(K9.swipeLeftAction)
@@ -154,12 +151,9 @@ class GeneralSettingsDataStore(
             "message_list_date_font" -> K9.fontSizes.messageListDate = value.toInt()
             "message_list_preview_font" -> K9.fontSizes.messageListPreview = value.toInt()
             "message_view_sender_font" -> K9.fontSizes.messageViewSender = value.toInt()
-            "message_view_to_font" -> K9.fontSizes.messageViewTo = value.toInt()
-            "message_view_cc_font" -> K9.fontSizes.messageViewCC = value.toInt()
-            "message_view_bcc_font" -> K9.fontSizes.messageViewBCC = value.toInt()
+            "message_view_recipients_font" -> K9.fontSizes.messageViewRecipients = value.toInt()
             "message_view_subject_font" -> K9.fontSizes.messageViewSubject = value.toInt()
             "message_view_date_font" -> K9.fontSizes.messageViewDate = value.toInt()
-            "message_view_additional_headers_font" -> K9.fontSizes.messageViewAdditionalHeaders = value.toInt()
             "message_compose_input_font" -> K9.fontSizes.messageComposeInput = value.toInt()
             "swipe_action_right" -> K9.swipeRightAction = stringToSwipeAction(value)
             "swipe_action_left" -> K9.swipeLeftAction = stringToSwipeAction(value)
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/view/KoinModule.kt b/app/ui/legacy/src/main/java/com/fsck/k9/view/KoinModule.kt
index 076cf0c583b..269d220fd9c 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/view/KoinModule.kt
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/view/KoinModule.kt
@@ -1,7 +1,13 @@
 package com.fsck.k9.view
 
+import com.fsck.k9.helper.ReplyToParser
+import com.fsck.k9.message.ReplyActionStrategy
+import com.fsck.k9.ui.helper.RelativeDateTimeFormatter
 import org.koin.dsl.module
 
 val viewModule = module {
-    single { WebViewConfigProvider(get()) }
+    single { WebViewConfigProvider(themeManager = get()) }
+    factory { RelativeDateTimeFormatter(context = get(), clock = get()) }
+    factory { ReplyToParser() }
+    factory { ReplyActionStrategy(replyRoParser = get()) }
 }
diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/view/MessageHeader.java b/app/ui/legacy/src/main/java/com/fsck/k9/view/MessageHeader.java
index 47c384edfa0..c340d746e93 100644
--- a/app/ui/legacy/src/main/java/com/fsck/k9/view/MessageHeader.java
+++ b/app/ui/legacy/src/main/java/com/fsck/k9/view/MessageHeader.java
@@ -1,24 +1,23 @@
 package com.fsck.k9.view;
 
 
-import java.util.Arrays;
+import java.util.List;
 
 import android.content.Context;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
+import android.content.res.ColorStateList;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
-import android.widget.CheckBox;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 import android.widget.Toast;
 
+import androidx.annotation.DrawableRes;
 import androidx.annotation.NonNull;
 import androidx.appcompat.widget.PopupMenu;
-import androidx.appcompat.widget.PopupMenu.OnMenuItemClickListener;
+import androidx.appcompat.widget.TooltipCompat;
 import com.fsck.k9.Account;
 import com.fsck.k9.DI;
 import com.fsck.k9.FontSizes;
@@ -28,112 +27,90 @@
 import com.fsck.k9.helper.ClipboardManager;
 import com.fsck.k9.helper.Contacts;
 import com.fsck.k9.helper.MessageHelper;
+import com.fsck.k9.helper.RealAddressFormatter;
+import com.fsck.k9.helper.RealContactNameProvider;
 import com.fsck.k9.mail.Address;
 import com.fsck.k9.mail.Flag;
 import com.fsck.k9.mail.Message;
-import com.fsck.k9.ui.ContactBadge;
+import com.fsck.k9.message.ReplyAction;
+import com.fsck.k9.message.ReplyActionStrategy;
+import com.fsck.k9.message.ReplyActions;
 import com.fsck.k9.ui.R;
-import com.fsck.k9.ui.messageview.OnCryptoClickListener;
-import timber.log.Timber;
+import com.fsck.k9.ui.helper.RelativeDateTimeFormatter;
+import com.fsck.k9.ui.messageview.DisplayRecipients;
+import com.fsck.k9.ui.messageview.DisplayRecipientsExtractor;
+import com.fsck.k9.ui.messageview.MessageHeaderClickListener;
+import com.fsck.k9.ui.messageview.RecipientNamesView;
+import com.google.android.material.chip.Chip;
 
 
 public class MessageHeader extends LinearLayout implements OnClickListener, OnLongClickListener {
     private static final int DEFAULT_SUBJECT_LINES = 3;
 
-    private final ClipboardManager clipboardManager = DI.get(ClipboardManager.class);
-
-    private Context mContext;
-    private TextView mFromView;
-    private TextView mSenderView;
-    private TextView mDateView;
-    private TextView mToView;
-    private TextView mToLabel;
-    private TextView mCcView;
-    private TextView mCcLabel;
-    private TextView mBccView;
-    private TextView mBccLabel;
-    private TextView mSubjectView;
-    private ImageView mCryptoStatusIcon;
-
-    private View mChip;
-    private CheckBox mFlagged;
-    private int defaultSubjectColor;
-    private View singleMessageOptionIcon;
-    private View mAnsweredIcon;
-    private View mForwardedIcon;
-    private Message mMessage;
-    private Account mAccount;
-    private FontSizes mFontSizes = K9.getFontSizes();
-    private Contacts mContacts;
-
-    private MessageHelper mMessageHelper;
-    private ContactPictureLoader mContactsPictureLoader;
-    private ContactBadge mContactBadge;
-
-    private OnCryptoClickListener onCryptoClickListener;
-    private OnMenuItemClickListener onMenuItemClickListener;
+    private final ReplyActionStrategy replyActionStrategy = DI.get(ReplyActionStrategy.class);
+    private final FontSizes fontSizes = K9.getFontSizes();
+
+    private Chip accountChip;
+    private TextView subjectView;
+    private ImageView starView;
+    private ImageView contactPictureView;
+    private TextView fromView;
+    private ImageView cryptoStatusIcon;
+    private RecipientNamesView recipientNamesView;
+    private TextView dateView;
+    private ImageView menuPrimaryActionView;
+
+    private MessageHelper messageHelper;
+    private RelativeDateTimeFormatter relativeDateTimeFormatter;
+
+    private MessageHeaderClickListener messageHeaderClickListener;
+    private ReplyActions replyActions;
 
 
     public MessageHeader(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mContext = context;
-        mContacts = Contacts.getInstance(mContext);
+
+        if (!isInEditMode()) {
+            messageHelper = MessageHelper.getInstance(getContext());
+            relativeDateTimeFormatter = DI.get(RelativeDateTimeFormatter.class);
+        }
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        mAnsweredIcon = findViewById(R.id.answered);
-        mForwardedIcon = findViewById(R.id.forwarded);
-        mFromView = findViewById(R.id.from);
-        mSenderView = findViewById(R.id.sender);
-        mToView = findViewById(R.id.to);
-        mToLabel = findViewById(R.id.to_label);
-        mCcView = findViewById(R.id.cc);
-        mCcLabel = findViewById(R.id.cc_label);
-        mBccView = findViewById(R.id.bcc);
-        mBccLabel = findViewById(R.id.bcc_label);
-
-        mContactBadge = findViewById(R.id.contact_badge);
-
-        singleMessageOptionIcon = findViewById(R.id.icon_single_message_options);
-
-        mSubjectView = findViewById(R.id.subject);
-        mChip = findViewById(R.id.chip);
-        mDateView = findViewById(R.id.date);
-        mFlagged = findViewById(R.id.flagged);
-
-        defaultSubjectColor = mSubjectView.getCurrentTextColor();
-        mFontSizes.setViewTextSize(mSubjectView, mFontSizes.getMessageViewSubject());
-        mFontSizes.setViewTextSize(mDateView, mFontSizes.getMessageViewDate());
-
-        mFontSizes.setViewTextSize(mFromView, mFontSizes.getMessageViewSender());
-        mFontSizes.setViewTextSize(mToView, mFontSizes.getMessageViewTo());
-        mFontSizes.setViewTextSize(mToLabel, mFontSizes.getMessageViewTo());
-        mFontSizes.setViewTextSize(mCcView, mFontSizes.getMessageViewCC());
-        mFontSizes.setViewTextSize(mCcLabel, mFontSizes.getMessageViewCC());
-        mFontSizes.setViewTextSize(mBccView, mFontSizes.getMessageViewBCC());
-        mFontSizes.setViewTextSize(mBccLabel, mFontSizes.getMessageViewBCC());
-
-        singleMessageOptionIcon.setOnClickListener(this);
-
-        mSubjectView.setOnClickListener(this);
-        mFromView.setOnClickListener(this);
-        mToView.setOnClickListener(this);
-        mCcView.setOnClickListener(this);
-        mBccView.setOnClickListener(this);
-
-        mSubjectView.setOnLongClickListener(this);
-        mFromView.setOnLongClickListener(this);
-        mToView.setOnLongClickListener(this);
-        mCcView.setOnLongClickListener(this);
-        mBccView.setOnLongClickListener(this);
-
-        mCryptoStatusIcon = findViewById(R.id.crypto_status_icon);
-        mCryptoStatusIcon.setOnClickListener(this);
-
-        mMessageHelper = MessageHelper.getInstance(mContext);
+        accountChip = findViewById(R.id.chip);
+        subjectView = findViewById(R.id.subject);
+        starView = findViewById(R.id.flagged);
+        contactPictureView = findViewById(R.id.contact_picture);
+        fromView = findViewById(R.id.from);
+        cryptoStatusIcon = findViewById(R.id.crypto_status_icon);
+        recipientNamesView = findViewById(R.id.recipients);
+        dateView = findViewById(R.id.date);
+
+        fontSizes.setViewTextSize(subjectView, fontSizes.getMessageViewSubject());
+        fontSizes.setViewTextSize(dateView, fontSizes.getMessageViewDate());
+        fontSizes.setViewTextSize(fromView, fontSizes.getMessageViewSender());
+
+        int recipientTextSize = fontSizes.getMessageViewRecipients();
+        if (recipientTextSize != FontSizes.FONT_DEFAULT) {
+            recipientNamesView.setTextSize(recipientTextSize);
+        }
+
+        subjectView.setOnClickListener(this);
+        subjectView.setOnLongClickListener(this);
+
+        menuPrimaryActionView = findViewById(R.id.menu_primary_action);
+        menuPrimaryActionView.setOnClickListener(this);
+
+        View menuOverflowView = findViewById(R.id.menu_overflow);
+        menuOverflowView.setOnClickListener(this);
+        String menuOverflowDescription =
+                getContext().getString(androidx.appcompat.R.string.abc_action_menu_overflow_description);
+        TooltipCompat.setTooltipText(menuOverflowView, menuOverflowDescription);
+
+        findViewById(R.id.participants_container).setOnClickListener(this);
     }
 
     @Override
@@ -141,18 +118,45 @@ public void onClick(View view) {
         int id = view.getId();
         if (id == R.id.subject) {
             toggleSubjectViewMaxLines();
-        } else if (id == R.id.from) {
-            onAddSenderToContacts();
-        } else if (id == R.id.to || id == R.id.cc || id == R.id.bcc) {
-            expand((TextView)view, ((TextView)view).getEllipsize() != null);
-        } else if (id == R.id.crypto_status_icon) {
-            onCryptoClickListener.onCryptoClick();
-        } else if (id == R.id.icon_single_message_options) {
-            PopupMenu popupMenu = new PopupMenu(getContext(), view);
-            popupMenu.setOnMenuItemClickListener(onMenuItemClickListener);
-            popupMenu.inflate(R.menu.single_message_options);
-            popupMenu.show();
+        } else if (id == R.id.menu_primary_action) {
+            performPrimaryReplyAction();
+        } else if (id == R.id.menu_overflow) {
+            showOverflowMenu(view);
+        } else if (id == R.id.participants_container) {
+            messageHeaderClickListener.onParticipantsContainerClick();
+        }
+    }
+
+    private void performPrimaryReplyAction() {
+        ReplyAction defaultAction = replyActions.getDefaultAction();
+        if (defaultAction == null) {
+            return;
         }
+
+        switch (defaultAction) {
+            case REPLY: {
+                messageHeaderClickListener.onMenuItemClick(R.id.reply);
+                break;
+            }
+            case REPLY_ALL: {
+                messageHeaderClickListener.onMenuItemClick(R.id.reply_all);
+                break;
+            }
+            default: {
+                throw new IllegalStateException("Unknown reply action: " + defaultAction);
+            }
+        }
+    }
+
+    private void showOverflowMenu(View view) {
+        PopupMenu popupMenu = new PopupMenu(getContext(), view);
+        popupMenu.setOnMenuItemClickListener(item -> {
+            messageHeaderClickListener.onMenuItemClick(item.getItemId());
+            return true;
+        });
+        popupMenu.inflate(R.menu.single_message_options);
+        setAdditionalReplyActions(popupMenu);
+        popupMenu.show();
     }
 
     @Override
@@ -160,149 +164,171 @@ public boolean onLongClick(View view) {
         int id = view.getId();
 
         if (id == R.id.subject) {
-            onAddSubjectToClipboard(mSubjectView.getText().toString());
-        } else if (id == R.id.from) {
-            onAddAddressesToClipboard(mMessage.getFrom());
-        } else if (id == R.id.to) {
-            onAddRecipientsToClipboard(Message.RecipientType.TO);
-        } else if (id == R.id.cc) {
-            onAddRecipientsToClipboard(Message.RecipientType.CC);
+            onAddSubjectToClipboard(subjectView.getText().toString());
         }
 
         return true;
     }
 
     private void toggleSubjectViewMaxLines() {
-        if (mSubjectView.getMaxLines() == DEFAULT_SUBJECT_LINES) {
-            mSubjectView.setMaxLines(Integer.MAX_VALUE);
+        if (subjectView.getMaxLines() == DEFAULT_SUBJECT_LINES) {
+            subjectView.setMaxLines(Integer.MAX_VALUE);
         } else {
-            mSubjectView.setMaxLines(DEFAULT_SUBJECT_LINES);
+            subjectView.setMaxLines(DEFAULT_SUBJECT_LINES);
         }
     }
 
     private void onAddSubjectToClipboard(String subject) {
+        ClipboardManager clipboardManager = DI.get(ClipboardManager.class);
         clipboardManager.setText("subject", subject);
 
-        Toast.makeText(mContext, createMessageForSubject(), Toast.LENGTH_LONG).show();
-    }
-
-    private void onAddSenderToContacts() {
-        if (mMessage != null) {
-            try {
-                final Address senderEmail = mMessage.getFrom()[0];
-                mContacts.createContact(senderEmail);
-            } catch (Exception e) {
-                Timber.e(e, "Couldn't create contact");
-            }
-        }
+        Toast.makeText(getContext(), createMessageForSubject(), Toast.LENGTH_LONG).show();
     }
 
     public String createMessageForSubject() {
-        return  mContext.getResources().getString(R.string.copy_subject_to_clipboard);
-    }
-
-    public String createMessage(int addressesCount) {
-        return mContext.getResources().getQuantityString(R.plurals.copy_address_to_clipboard, addressesCount);
-    }
-
-    private void onAddAddressesToClipboard(Address[] addresses) {
-        String addressList = Address.toString(addresses);
-        clipboardManager.setText("addresses", addressList);
-
-        Toast.makeText(mContext, createMessage(addresses.length), Toast.LENGTH_LONG).show();
-    }
-
-    private void onAddRecipientsToClipboard(Message.RecipientType recipientType) {
-        onAddAddressesToClipboard(mMessage.getRecipients(recipientType));
+        return getResources().getString(R.string.copy_subject_to_clipboard);
     }
 
     public void setOnFlagListener(OnClickListener listener) {
-        mFlagged.setOnClickListener(listener);
+        starView.setOnClickListener(listener);
     }
 
-    public void populate(final Message message, final Account account, boolean showStar) {
+    public void populate(final Message message, final Account account, boolean showStar, boolean showAccountChip) {
+        if (showAccountChip) {
+            accountChip.setVisibility(View.VISIBLE);
+            accountChip.setText(account.getDisplayName());
+            accountChip.setChipBackgroundColor(ColorStateList.valueOf(account.getChipColor()));
+        } else {
+            accountChip.setVisibility(View.GONE);
+        }
+
         Address fromAddress = null;
         Address[] fromAddresses = message.getFrom();
         if (fromAddresses.length > 0) {
             fromAddress = fromAddresses[0];
         }
 
-        final Contacts contacts = K9.isShowContactName() ? mContacts : null;
-        final CharSequence from = mMessageHelper.getSenderDisplayName(fromAddress);
-        final CharSequence to = MessageHelper.toFriendly(message.getRecipients(Message.RecipientType.TO), contacts);
-        final CharSequence cc = MessageHelper.toFriendly(message.getRecipients(Message.RecipientType.CC), contacts);
-        final CharSequence bcc = MessageHelper.toFriendly(message.getRecipients(Message.RecipientType.BCC), contacts);
+        if (K9.isShowContactPicture()) {
+            contactPictureView.setVisibility(View.VISIBLE);
+            if (fromAddress != null) {
+                ContactPictureLoader contactsPictureLoader = ContactPicture.getContactPictureLoader();
+                contactsPictureLoader.setContactPicture(contactPictureView, fromAddress);
+            } else {
+                contactPictureView.setImageResource(R.drawable.ic_contact_picture);
+            }
+        } else {
+            contactPictureView.setVisibility(View.GONE);
+        }
 
-        mMessage = message;
-        mAccount = account;
+        CharSequence from = messageHelper.getSenderDisplayName(fromAddress);
+        fromView.setText(from);
 
-        if (K9.isShowContactPicture()) {
-            mContactBadge.setVisibility(View.VISIBLE);
-            mContactsPictureLoader = ContactPicture.getContactPictureLoader();
-        }  else {
-            mContactBadge.setVisibility(View.GONE);
+        if (showStar) {
+            starView.setVisibility(View.VISIBLE);
+            starView.setSelected(message.isSet(Flag.FLAGGED));
+        } else {
+            starView.setVisibility(View.GONE);
         }
 
-        if (shouldShowSender(message)) {
-            mSenderView.setVisibility(VISIBLE);
-            String sender = getResources().getString(R.string.message_view_sender_label,
-                    MessageHelper.toFriendly(message.getSender(), contacts));
-            mSenderView.setText(sender);
+        if (message.getSentDate() != null) {
+            dateView.setText(relativeDateTimeFormatter.formatDate(message.getSentDate().getTime()));
         } else {
-            mSenderView.setVisibility(View.GONE);
+            dateView.setText("");
         }
 
-        String dateTime = DateUtils.formatDateTime(mContext,
-                message.getSentDate().getTime(),
-                DateUtils.FORMAT_SHOW_DATE
-                | DateUtils.FORMAT_ABBREV_ALL
-                | DateUtils.FORMAT_SHOW_TIME
-                | DateUtils.FORMAT_SHOW_YEAR);
-        mDateView.setText(dateTime);
+        setRecipientNames(message, account);
 
-        if (K9.isShowContactPicture()) {
-            if (fromAddress != null) {
-                mContactBadge.setContact(fromAddress);
-                mContactsPictureLoader.setContactPicture(mContactBadge, fromAddress);
-            } else {
-                mContactBadge.setImageResource(R.drawable.ic_contact_picture);
-            }
-        }
+        setReplyActions(message, account);
 
-        mFromView.setText(from);
+        setVisibility(View.VISIBLE);
+    }
 
-        updateAddressField(mToView, to, mToLabel);
-        updateAddressField(mCcView, cc, mCcLabel);
-        updateAddressField(mBccView, bcc, mBccLabel);
-        mAnsweredIcon.setVisibility(message.isSet(Flag.ANSWERED) ? View.VISIBLE : View.GONE);
-        mForwardedIcon.setVisibility(message.isSet(Flag.FORWARDED) ? View.VISIBLE : View.GONE);
+    private void setRecipientNames(Message message, Account account) {
+        Integer contactNameColor = K9.isChangeContactNameColor() ? K9.getContactNameColor() : null;
 
-        if (showStar) {
-            mFlagged.setVisibility(View.VISIBLE);
-            mFlagged.setChecked(message.isSet(Flag.FLAGGED));
+        RealContactNameProvider contactNameProvider = new RealContactNameProvider(Contacts.getInstance(getContext()));
+
+        RealAddressFormatter addressFormatter = new RealAddressFormatter(contactNameProvider, account,
+                K9.isShowCorrespondentNames(), K9.isShowContactName(), contactNameColor,
+                getContext().getString(R.string.message_view_me_text));
+
+        DisplayRecipientsExtractor displayRecipientsExtractor = new DisplayRecipientsExtractor(addressFormatter,
+                recipientNamesView.getMaxNumberOfRecipientNames());
+
+        DisplayRecipients displayRecipients = displayRecipientsExtractor.extractDisplayRecipients(message, account);
+
+        recipientNamesView.setRecipients(displayRecipients.getRecipientNames(),
+                displayRecipients.getNumberOfRecipients());
+    }
+
+    private void setReplyActions(Message message, Account account) {
+        ReplyActions replyActions = replyActionStrategy.getReplyActions(account, message);
+        this.replyActions = replyActions;
+
+        setDefaultReplyAction(replyActions.getDefaultAction());
+    }
+
+    private void setDefaultReplyAction(ReplyAction defaultAction) {
+        if (defaultAction == null) {
+            menuPrimaryActionView.setVisibility(View.GONE);
         } else {
-            mFlagged.setVisibility(View.GONE);
-        }
+            int replyIconResource = getReplyImageResource(defaultAction);
+            menuPrimaryActionView.setImageResource(replyIconResource);
 
-        mChip.setBackgroundColor(mAccount.getChipColor());
+            String replyActionName = getReplyActionName(defaultAction);
+            TooltipCompat.setTooltipText(menuPrimaryActionView, replyActionName);
 
-        setVisibility(View.VISIBLE);
+            menuPrimaryActionView.setVisibility(View.VISIBLE);
+        }
     }
 
-    public void setSubject(@NonNull String subject) {
-        mSubjectView.setText(subject);
-        mSubjectView.setTextColor(0xff000000 | defaultSubjectColor);
+    @DrawableRes
+    private int getReplyImageResource(@NonNull ReplyAction replyAction) {
+        switch (replyAction) {
+            case REPLY: {
+                return R.drawable.ic_reply;
+            }
+            case REPLY_ALL: {
+                return R.drawable.ic_reply_all;
+            }
+            default: {
+                throw new IllegalStateException("Unknown reply action: " + replyAction);
+            }
+        }
     }
 
-    public static boolean shouldShowSender(Message message) {
-        Address[] from = message.getFrom();
-        Address[] sender = message.getSender();
+    @NonNull
+    private String getReplyActionName(@NonNull ReplyAction replyAction) {
+        Context context = getContext();
+        switch (replyAction) {
+            case REPLY: {
+                return context.getString(R.string.reply_action);
+            }
+            case REPLY_ALL: {
+                return context.getString(R.string.reply_all_action);
+            }
+            default: {
+                throw new IllegalStateException("Unknown reply action: " + replyAction);
+            }
+        }
+    }
 
-        return sender != null && sender.length != 0 && !Arrays.equals(from, sender);
+    private void setAdditionalReplyActions(PopupMenu popupMenu) {
+        List<ReplyAction> additionalActions = replyActions.getAdditionalActions();
+        if (!additionalActions.contains(ReplyAction.REPLY)) {
+            popupMenu.getMenu().removeItem(R.id.reply);
+        }
+        if (!additionalActions.contains(ReplyAction.REPLY_ALL)) {
+            popupMenu.getMenu().removeItem(R.id.reply_all);
+        }
+    }
+
+    public void setSubject(@NonNull String subject) {
+        subjectView.setText(subject);
     }
 
     public void hideCryptoStatus() {
-        mCryptoStatusIcon.setVisibility(View.GONE);
+        cryptoStatusIcon.setVisibility(View.GONE);
     }
 
     public void setCryptoStatusLoading() {
@@ -319,38 +345,13 @@ public void setCryptoStatus(MessageCryptoDisplayStatus displayStatus) {
 
     private void setCryptoDisplayStatus(MessageCryptoDisplayStatus displayStatus) {
         int color = ThemeUtils.getStyledColor(getContext(), displayStatus.getColorAttr());
-        mCryptoStatusIcon.setEnabled(displayStatus.isEnabled());
-        mCryptoStatusIcon.setVisibility(View.VISIBLE);
-        mCryptoStatusIcon.setImageResource(displayStatus.getStatusIconRes());
-        mCryptoStatusIcon.setColorFilter(color);
-    }
-
-    private void updateAddressField(TextView v, CharSequence text, View label) {
-        boolean hasText = !TextUtils.isEmpty(text);
-
-        v.setText(text);
-        v.setVisibility(hasText ? View.VISIBLE : View.GONE);
-        label.setVisibility(hasText ? View.VISIBLE : View.GONE);
-    }
-
-    /**
-     * Expand or collapse a TextView by removing or adding the 2 lines limitation
-     */
-    private void expand(TextView v, boolean expand) {
-       if (expand) {
-           v.setMaxLines(Integer.MAX_VALUE);
-           v.setEllipsize(null);
-       } else {
-           v.setMaxLines(2);
-           v.setEllipsize(android.text.TextUtils.TruncateAt.END);
-       }
-    }
-
-    public void setOnCryptoClickListener(OnCryptoClickListener onCryptoClickListener) {
-        this.onCryptoClickListener = onCryptoClickListener;
+        cryptoStatusIcon.setEnabled(displayStatus.isEnabled());
+        cryptoStatusIcon.setVisibility(View.VISIBLE);
+        cryptoStatusIcon.setImageResource(displayStatus.getStatusIconRes());
+        cryptoStatusIcon.setColorFilter(color);
     }
 
-    public void setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) {
-        this.onMenuItemClickListener = onMenuItemClickListener;
+    public void setMessageHeaderClickListener(MessageHeaderClickListener messageHeaderClickListener) {
+        this.messageHeaderClickListener = messageHeaderClickListener;
     }
 }
diff --git a/app/ui/legacy/src/main/res/drawable/btn_select_star.xml b/app/ui/legacy/src/main/res/drawable/btn_select_star.xml
new file mode 100644
index 00000000000..dbab9d79d28
--- /dev/null
+++ b/app/ui/legacy/src/main/res/drawable/btn_select_star.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:drawable="@drawable/ic_star" />
+    <item android:state_selected="false" android:drawable="@drawable/ic_star_border" />
+</selector>
diff --git a/app/ui/legacy/src/main/res/drawable/dots_vertical.xml b/app/ui/legacy/src/main/res/drawable/dots_vertical.xml
new file mode 100644
index 00000000000..14ff51ef8b5
--- /dev/null
+++ b/app/ui/legacy/src/main/res/drawable/dots_vertical.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?attr/colorControlNormal"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />
+</vector>
diff --git a/app/ui/legacy/src/main/res/drawable/ic_person_plus.xml b/app/ui/legacy/src/main/res/drawable/ic_person_add.xml
similarity index 59%
rename from app/ui/legacy/src/main/res/drawable/ic_person_plus.xml
rename to app/ui/legacy/src/main/res/drawable/ic_person_add.xml
index 842c6df3988..111621988cb 100644
--- a/app/ui/legacy/src/main/res/drawable/ic_person_plus.xml
+++ b/app/ui/legacy/src/main/res/drawable/ic_person_add.xml
@@ -6,5 +6,5 @@
     android:viewportHeight="24">
     <path
         android:fillColor="@android:color/white"
-        android:pathData="M15,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM6,10L6,7L4,7v3L1,10v2h3v3h2v-3h3v-2L6,10zM15,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z" />
+        android:pathData="M13,8c0,-2.21 -1.79,-4 -4,-4S5,5.79 5,8s1.79,4 4,4S13,10.21 13,8zM15,10v2h3v3h2v-3h3v-2h-3V7h-2v3H15zM1,18v2h16v-2c0,-2.66 -5.33,-4 -8,-4S1,15.34 1,18z" />
 </vector>
diff --git a/app/ui/legacy/src/main/res/drawable/ic_reply.xml b/app/ui/legacy/src/main/res/drawable/ic_reply.xml
new file mode 100644
index 00000000000..bd35ee9e7e1
--- /dev/null
+++ b/app/ui/legacy/src/main/res/drawable/ic_reply.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?attr/colorControlNormal"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M10,9V5l-7,7 7,7v-4.1c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z" />
+</vector>
diff --git a/app/ui/legacy/src/main/res/layout/message.xml b/app/ui/legacy/src/main/res/layout/message.xml
index 4d8f1f43d9c..1ac80286db7 100644
--- a/app/ui/legacy/src/main/res/layout/message.xml
+++ b/app/ui/legacy/src/main/res/layout/message.xml
@@ -2,7 +2,7 @@
 <com.fsck.k9.ui.messageview.MessageTopView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    xmlns:custom="http://schemas.android.com/apk/res-auto"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/message_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
@@ -25,19 +25,30 @@
             <!-- Header area -->
             <include layout="@layout/message_view_header"/>
 
-            <Button
-                android:id="@+id/show_pictures"
-                style="?android:attr/buttonStyleSmall"
-                android:layout_width="wrap_content"
+            <FrameLayout
+                android:id="@+id/extra_header_container"
+                android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_gravity="end"
-                android:layout_marginBottom="4dip"
-                android:layout_marginLeft="6dip"
-                android:layout_marginRight="6dip"
-                android:layout_marginTop="4dip"
-                android:text="@string/message_view_show_pictures_action"
+                android:background="?attr/extraMessageHeaderBackground"
+                android:paddingVertical="4dp"
                 android:visibility="gone"
-                tools:visibility="visible"/>
+                tools:visibility="visible">
+
+                <Button
+                    android:id="@+id/show_pictures"
+                    style="@style/Widget.MaterialComponents.Button.OutlinedButton"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_horizontal"
+                    android:text="@string/message_view_show_remote_images_action"
+                    app:icon="@drawable/ic_attachment_image" />
+
+            </FrameLayout>
+
+            <Space
+                android:id="@+id/line_of_death"
+                android:layout_width="match_parent"
+                android:layout_height="8dp" />
 
             <com.fsck.k9.view.ToolableViewAnimator
                 android:id="@+id/message_layout_animator"
@@ -45,7 +56,7 @@
                 android:layout_height="match_parent"
                 android:inAnimation="@anim/fade_in"
                 android:outAnimation="@anim/fade_out"
-                custom:previewInitialChild="1">
+                app:previewInitialChild="1">
 
                 <View
                     android:layout_width="wrap_content"
diff --git a/app/ui/legacy/src/main/res/layout/message_bottom_sheet.xml b/app/ui/legacy/src/main/res/layout/message_bottom_sheet.xml
new file mode 100644
index 00000000000..b97430c3e02
--- /dev/null
+++ b/app/ui/legacy/src/main/res/layout/message_bottom_sheet.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <ProgressBar
+        android:id="@+id/message_details_progress"
+        style="?android:attr/progressBarStyle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginVertical="32dp"
+        tools:visibility="gone" />
+
+    <TextView
+        android:id="@+id/message_details_error"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="16dp"
+        android:layout_marginVertical="32dp"
+        android:text="@string/message_details_loading_error"
+        android:visibility="gone"
+        tools:visibility="gone" />
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/message_details_list"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:overScrollMode="never"
+        android:visibility="gone"
+        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
+        tools:visibility="visible" />
+
+</FrameLayout>
diff --git a/app/ui/legacy/src/main/res/layout/message_details_crypto_status_item.xml b/app/ui/legacy/src/main/res/layout/message_details_crypto_status_item.xml
new file mode 100644
index 00000000000..42981ee9d40
--- /dev/null
+++ b/app/ui/legacy/src/main/res/layout/message_details_crypto_status_item.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?attr/selectableItemBackground">
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/keyline"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        app:layout_constraintGuide_begin="72dp" />
+
+    <ImageView
+        android:id="@+id/crypto_status_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:layout_marginBottom="16dp"
+        android:contentDescription="@null"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toStartOf="@+id/keyline"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:srcCompat="@drawable/status_lock_dots_3"
+        tools:tint="?attr/openpgp_green" />
+
+    <TextView
+        android:id="@+id/crypto_status_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:layout_marginEnd="16dp"
+        android:textAppearance="?attr/textAppearanceBody2"
+        app:layout_constraintBottom_toTopOf="@+id/crypto_status_description"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.0"
+        app:layout_constraintStart_toStartOf="@+id/keyline"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_chainStyle="packed"
+        tools:text="@string/crypto_msg_title_encrypted_signed_e2e" />
+
+    <TextView
+        android:id="@+id/crypto_status_description"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="16dp"
+        android:layout_marginBottom="16dp"
+        android:textAppearance="?attr/textAppearanceBody2"
+        android:textColor="?android:attr/textColorSecondary"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.0"
+        app:layout_constraintStart_toStartOf="@+id/keyline"
+        app:layout_constraintTop_toBottomOf="@+id/crypto_status_title"
+        tools:text="@string/crypto_msg_encrypted_sign_verified" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/ui/legacy/src/main/res/layout/message_details_date_item.xml b/app/ui/legacy/src/main/res/layout/message_details_date_item.xml
new file mode 100644
index 00000000000..2888a4294a7
--- /dev/null
+++ b/app/ui/legacy/src/main/res/layout/message_details_date_item.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/date"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginHorizontal="16dp"
+    android:layout_marginTop="16dp"
+    android:focusable="false"
+    android:gravity="end"
+    android:textAppearance="?attr/textAppearanceBody2"
+    tools:text="Wednesday, 11 January 2023, 10:31 pm" />
diff --git a/app/ui/legacy/src/main/res/layout/message_details_divider_item.xml b/app/ui/legacy/src/main/res/layout/message_details_divider_item.xml
new file mode 100644
index 00000000000..8a456ec9a6f
--- /dev/null
+++ b/app/ui/legacy/src/main/res/layout/message_details_divider_item.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="1px"
+    android:layout_marginHorizontal="32dp"
+    android:layout_marginVertical="8dp"
+    android:background="?attr/messageDetailsDividerColor"
+    android:focusable="false" />
diff --git a/app/ui/legacy/src/main/res/layout/message_details_participant_item.xml b/app/ui/legacy/src/main/res/layout/message_details_participant_item.xml
new file mode 100644
index 00000000000..ce99f941ffa
--- /dev/null
+++ b/app/ui/legacy/src/main/res/layout/message_details_participant_item.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/participants_container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?attr/selectableItemBackground"
+    android:gravity="center_vertical"
+    android:orientation="horizontal">
+
+    <de.hdodenhof.circleimageview.CircleImageView
+        android:id="@+id/contact_picture"
+        android:layout_width="40dp"
+        android:layout_height="40dp"
+        android:layout_marginStart="16dp"
+        android:layout_marginTop="16dp"
+        android:src="@drawable/ic_contact_picture"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <TextView
+        android:id="@+id/name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dp"
+        android:layout_marginTop="16dp"
+        android:ellipsize="end"
+        android:gravity="center_vertical"
+        android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
+        android:textColor="?android:attr/textColorPrimary"
+        app:layout_constrainedWidth="true"
+        app:layout_constraintBottom_toTopOf="@+id/email"
+        app:layout_constraintEnd_toStartOf="@+id/menu_add_contact"
+        app:layout_constraintHorizontal_bias="0.0"
+        app:layout_constraintHorizontal_chainStyle="packed"
+        app:layout_constraintStart_toEndOf="@id/contact_picture"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_chainStyle="packed"
+        app:layout_goneMarginBottom="12dp"
+        tools:text="Alice" />
+
+    <TextView
+        android:id="@+id/email"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="4dp"
+        android:layout_marginBottom="16dp"
+        android:ellipsize="end"
+        android:gravity="center_vertical"
+        android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
+        android:textColor="?android:attr/textColorSecondary"
+        app:layout_constrainedWidth="true"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toStartOf="@+id/menu_add_contact"
+        app:layout_constraintHorizontal_bias="0.0"
+        app:layout_constraintHorizontal_chainStyle="packed"
+        app:layout_constraintStart_toStartOf="@+id/name"
+        app:layout_constraintTop_toBottomOf="@+id/name"
+        tools:text="alice@domain.example" />
+
+    <ImageView
+        android:id="@+id/menu_add_contact"
+        android:layout_width="48dp"
+        android:layout_height="72dp"
+        android:background="?attr/controlBackground"
+        android:clickable="true"
+        android:contentDescription="@string/action_add_to_contacts"
+        android:focusable="true"
+        android:paddingHorizontal="12dp"
+        app:layout_constraintEnd_toStartOf="@+id/menu_compose"
+        app:layout_constraintTop_toTopOf="@+id/menu_compose"
+        app:srcCompat="?attr/messageDetailsAddContactIcon" />
+
+    <ImageView
+        android:id="@+id/menu_compose"
+        android:layout_width="48dp"
+        android:layout_height="72dp"
+        android:background="?attr/controlBackground"
+        android:clickable="true"
+        android:contentDescription="@string/compose_action"
+        android:focusable="true"
+        android:paddingHorizontal="12dp"
+        app:layout_constraintEnd_toStartOf="@id/menu_overflow"
+        app:layout_constraintTop_toTopOf="@+id/menu_overflow"
+        app:srcCompat="@drawable/ic_envelope" />
+
+    <ImageView
+        android:id="@+id/menu_overflow"
+        android:layout_width="40dp"
+        android:layout_height="72dp"
+        android:background="?attr/controlBackground"
+        android:clickable="true"
+        android:contentDescription="@string/abc_action_menu_overflow_description"
+        android:focusable="true"
+        android:paddingStart="6dp"
+        android:paddingEnd="10dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="0.0"
+        app:srcCompat="@drawable/dots_vertical" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/ui/legacy/src/main/res/layout/message_details_section_header_item.xml b/app/ui/legacy/src/main/res/layout/message_details_section_header_item.xml
new file mode 100644
index 00000000000..9e3c855a8db
--- /dev/null
+++ b/app/ui/legacy/src/main/res/layout/message_details_section_header_item.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginTop="8dp"
+    android:focusable="false"
+    android:orientation="horizontal">
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dp"
+        android:textAppearance="?attr/textAppearanceOverline"
+        tools:text="To" />
+
+    <TextView
+        android:id="@+id/extra"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="4dp"
+        android:layout_marginEnd="16dp"
+        android:textAppearance="?attr/textAppearanceOverline"
+        android:textColor="?android:attr/textColorSecondary"
+        tools:text="3" />
+
+</LinearLayout>
diff --git a/app/ui/legacy/src/main/res/layout/message_view_header.xml b/app/ui/legacy/src/main/res/layout/message_view_header.xml
index 38a67868a47..2222a7c23ed 100644
--- a/app/ui/legacy/src/main/res/layout/message_view_header.xml
+++ b/app/ui/legacy/src/main/res/layout/message_view_header.xml
@@ -1,297 +1,185 @@
 <?xml version="1.0" encoding="utf-8"?>
-<com.fsck.k9.view.MessageHeader
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<com.fsck.k9.view.MessageHeader xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/header_container"
     android:layout_width="match_parent"
-    android:orientation="vertical"
-    android:layout_height="wrap_content">
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
 
-    <LinearLayout
+    <androidx.constraintlayout.widget.ConstraintLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal" >
+        android:layout_height="wrap_content">
 
-        <!-- Color chip -->
-        <View
-            android:id="@+id/chip"
-            android:layout_width="8dip"
-            android:layout_height="match_parent"
-            tools:background="#FF1976D2" />
+        <Space
+            android:id="@+id/margin_top"
+            android:layout_width="match_parent"
+            android:layout_height="16dp"
+            app:layout_constraintTop_toTopOf="parent" />
 
-        <LinearLayout
+        <com.google.android.material.chip.Chip
+            android:id="@+id/chip"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="16dp"
+            android:layout_marginTop="8dp"
+            android:clickable="false"
+            android:textColor="@android:color/white"
+            android:textSize="14sp"
+            app:layout_constraintBottom_toTopOf="@+id/subject"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:chipBackgroundColor="#1976D2"
+            tools:text="Account name" />
+
+        <androidx.constraintlayout.widget.Barrier
+            android:id="@+id/top_margin_barrier"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="vertical">
-
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:orientation="horizontal"
-                android:padding="8dp"
-                >
-
-                <TextView
-                    android:id="@+id/subject"
-                    android:layout_width="0dp"
-                    android:layout_height="wrap_content"
-                    android:layout_weight="1"
-                    android:layout_marginTop="4dp"
-                    android:layout_marginBottom="4dp"
-                    android:ellipsize="end"
-                    android:maxLines="3"
-                    android:textColor="?android:attr/textColorPrimary"
-                    android:textAppearance="@style/TextAppearance.K9.MediumSmall"
-                    tools:text="(no subject)"
-                    />
-
-                <ImageView
-                    android:id="@+id/crypto_status_icon"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="center"
-                    android:padding="6dp"
-                    app:srcCompat="@drawable/status_lock_disabled"
-                    app:tint="?attr/openpgp_grey"
-                    android:background="?selectableItemBackground"
-                    android:visibility="gone"
-                    />
-
-                <CheckBox
-                    android:id="@+id/flagged"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="center_vertical"
-                    android:focusable="false"
-                    android:checked="false"
-                    style="@style/MessageStarStyle"
-                    />
-
-            </LinearLayout>
-
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:orientation="horizontal" >
-
-
-                <RelativeLayout
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:orientation="vertical" >
-
-                    <com.fsck.k9.ui.ContactBadge
-                        android:id="@+id/contact_badge"
-                        android:layout_width="40dp"
-                        android:layout_height="40dp"
-                        android:layout_marginStart="8dp"
-                        android:layout_marginTop="8dp"
-                        tools:src="@drawable/ic_contact_picture"/>
-
-                    <!-- State icons -->
-                    <LinearLayout
-                        android:id="@+id/icon_container"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_marginTop="10dip"
-                        android:layout_marginBottom="2dip"
-                        android:layout_below="@+id/contact_badge"
-                        android:layout_centerHorizontal="true"
-                        android:orientation="vertical" >
-
-                        <View
-                            android:id="@+id/answered"
-                            android:layout_width="32sp"
-                            android:layout_height="32sp"
-                            android:paddingStart="0dp"
-                            android:paddingEnd="2dp"
-                            android:background="?attr/messageListAnswered" />
-
-                        <View
-                            android:id="@+id/forwarded"
-                            android:layout_width="22sp"
-                            android:layout_height="22sp"
-                            android:paddingStart="0dp"
-                            android:paddingEnd="4dp"
-                            android:background="?attr/messageListForwarded" />
-
-                    </LinearLayout>
-
-                </RelativeLayout>
+            app:barrierDirection="bottom"
+            app:constraint_referenced_ids="chip,margin_top" />
 
-                <RelativeLayout
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:padding="6dip"
-                    android:layout_marginStart="2dp" >
-
-                    <!-- From -->
-                    <TextView
-                        android:id="@+id/from"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_alignParentTop="true"
-                        android:layout_alignParentStart="true"
-                        android:layout_toStartOf="@+id/status_icon_strip"
-                        android:layout_alignBottom="@+id/status_icon_strip"
-                        android:paddingTop="0dp"
-                        android:paddingStart="0dp"
-                        android:paddingEnd="6dp"
-                        android:singleLine="true"
-                        android:ellipsize="end"
-                        android:textColor="?android:attr/textColorPrimary"
-                        android:textAppearance="@style/TextAppearance.K9.MediumSmall"
-                        android:textStyle="bold"
-                        android:text="@string/general_no_sender"
-                        android:gravity="center_vertical"
-                        />
-
-                    <!-- Sender -->
-                    <TextView
-                        android:id="@+id/sender"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_alignParentStart="true"
-                        android:layout_toStartOf="@+id/status_icon_strip"
-                        android:paddingTop="0dp"
-                        android:layout_below="@+id/from"
-                        android:ellipsize="end"
-                        android:textColor="?android:attr/textColorPrimary"
-                        android:textAppearance="@style/TextAppearance.K9.Small"
-                        android:textStyle="bold"
-                        android:visibility="gone"
-                        android:gravity="center_vertical"
-                        />
-                    
-                    <!-- To -->
-                    <TextView
-                        android:id="@+id/to_label"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_alignStart="@+id/from"
-                        android:layout_alignBaseline="@+id/to"
-                        android:paddingTop="2dp"
-                        android:paddingStart="0dp"
-                        android:paddingEnd="4dp"
-                        android:text="@string/message_to_label"
-                        android:textColor="?android:attr/textColorPrimary"
-                        android:textAppearance="@style/TextAppearance.K9.MediumSmall"
-                        android:textStyle="bold" />
-
-                    <TextView
-                        android:id="@+id/to"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_toEndOf="@+id/to_label"
-                        android:layout_below="@+id/sender"
-                        android:maxLines="2"
-                        android:ellipsize="end"
-                        android:paddingTop="2dp"
-                        android:textColor="?android:attr/textColorSecondary"
-                        android:textAppearance="@style/TextAppearance.K9.MediumSmall" />
-                    
-                    <!-- CC -->
-                    <TextView
-                        android:id="@+id/cc_label"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_below="@+id/to_label"
-                        android:layout_alignStart="@+id/to_label"
-                        android:layout_alignBaseline="@+id/cc"
-                        android:paddingTop="2dp"
-                        android:paddingStart="0dp"
-                        android:paddingEnd="4dp"
-                        android:text="@string/message_view_cc_label"
-                        android:textColor="?android:attr/textColorPrimary"
-                        android:textStyle="bold"
-                        android:textAppearance="@style/TextAppearance.K9.MediumSmall" />
-                    
-                    <TextView
-                        android:id="@+id/cc"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_toEndOf="@+id/cc_label"
-                        android:layout_below="@+id/to"
-                        android:maxLines="2"
-                        android:ellipsize="end"
-                        android:paddingTop="2dp"
-                        android:textColor="?android:attr/textColorSecondary"
-                        android:textAppearance="@style/TextAppearance.K9.MediumSmall" />
-
-                    <!-- BCC -->
-                    <TextView
-                        android:id="@+id/bcc_label"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_below="@+id/cc_label"
-                        android:layout_alignStart="@+id/cc_label"
-                        android:layout_alignBaseline="@+id/bcc"
-                        android:paddingTop="2dp"
-                        android:paddingStart="0dp"
-                        android:paddingEnd="4dp"
-                        android:text="@string/message_view_bcc_label"
-                        android:textColor="?android:attr/textColorPrimary"
-                        android:textStyle="bold"
-                        android:textAppearance="@style/TextAppearance.K9.MediumSmall" />
-
-                    <TextView
-                        android:id="@+id/bcc"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_toEndOf="@+id/bcc_label"
-                        android:layout_below="@+id/cc"
-                        android:maxLines="2"
-                        android:ellipsize="end"
-                        android:paddingTop="2dp"
-                        android:textColor="?android:attr/textColorSecondary"
-                        android:textAppearance="@style/TextAppearance.K9.MediumSmall" />
-                    
-                    <!-- Date -->
-                    <TextView
-                        android:id="@+id/date"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_below="@id/bcc"
-                        android:layout_alignParentEnd="true"
-                        android:paddingTop="8dp"
-                        android:singleLine="true"
-                        android:ellipsize="none"
-                        android:textAppearance="@style/TextAppearance.K9.Small"
-                        android:textColor="?android:attr/textColorSecondary" />
-
-                    <LinearLayout
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_alignParentTop="true"
-                        android:layout_alignParentEnd="true"
-                        android:layout_centerVertical="true"
-                        android:id="@+id/status_icon_strip"
-                        >
-
-                        <ImageView
-                            android:id="@+id/icon_single_message_options"
-                            android:layout_width="wrap_content"
-                            android:layout_height="wrap_content"
-                            app:srcCompat="?iconActionSingleMessageOptions"
-                            android:padding="8dp"
-                            android:background="?selectableItemBackground"
-                            />
-
-                    </LinearLayout>
-
-                </RelativeLayout>
-
-            </LinearLayout>
-
-        </LinearLayout>
-
-    </LinearLayout>
-
-    <View
-        android:layout_height="1dip"
+        <com.fsck.k9.ui.helper.BottomBaselineTextView
+            android:id="@+id/subject"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/chip"
+            android:layout_marginStart="16dp"
+            android:layout_marginEnd="8dp"
+            android:layout_marginBottom="16dp"
+            android:ellipsize="end"
+            android:maxLines="3"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textSize="20sp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@+id/flagged"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/top_margin_barrier"
+            app:layout_constraintVertical_bias="0.0"
+            tools:text="Message subject" />
+
+        <ImageView
+            android:id="@+id/flagged"
+            android:layout_width="44dp"
+            android:layout_height="48dp"
+            android:background="?attr/controlBackground"
+            android:baseline="33dp"
+            android:paddingHorizontal="10dp"
+            android:src="@drawable/btn_select_star"
+            app:layout_constraintBaseline_toBaselineOf="@+id/subject"
+            app:layout_constraintEnd_toEndOf="parent" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/participants_container"
         android:layout_width="match_parent"
-        android:layout_marginBottom="4dip"
-        android:background="@drawable/divider_horizontal_email" />
+        android:layout_height="wrap_content"
+        android:background="?attr/messageHeaderBackground"
+        android:clickable="true"
+        android:focusable="true"
+        android:foreground="?attr/selectableItemBackground"
+        android:gravity="center_vertical"
+        android:orientation="horizontal">
+
+        <de.hdodenhof.circleimageview.CircleImageView
+            android:id="@+id/contact_picture"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_margin="16dp"
+            android:layout_marginTop="16dp"
+            android:layout_marginBottom="16dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:src="@drawable/ic_contact_picture" />
+
+        <TextView
+            android:id="@+id/from"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="16dp"
+            android:layout_marginTop="16dp"
+            android:layout_marginEnd="8dp"
+            android:ellipsize="end"
+            android:gravity="center_vertical"
+            android:maxLines="1"
+            android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
+            android:textColor="?android:attr/textColorPrimary"
+            app:layout_constrainedWidth="true"
+            app:layout_constraintEnd_toStartOf="@+id/date"
+            app:layout_constraintHorizontal_bias="0.0"
+            app:layout_constraintHorizontal_chainStyle="packed"
+            app:layout_constraintStart_toEndOf="@id/contact_picture"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:text="Sender name" />
+
+        <TextView
+            android:id="@+id/date"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="4dp"
+            android:ellipsize="none"
+            android:singleLine="true"
+            android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
+            android:textColor="?android:attr/textColorSecondary"
+            app:layout_constraintBaseline_toBaselineOf="@+id/from"
+            app:layout_constraintEnd_toStartOf="@+id/menu_primary_action"
+            app:layout_constraintStart_toEndOf="@id/from"
+            tools:text="Sep 19" />
+
+        <ImageView
+            android:id="@+id/crypto_status_icon"
+            android:layout_width="18sp"
+            android:layout_height="18sp"
+            android:layout_marginStart="16dp"
+            android:visibility="gone"
+            app:layout_constraintBottom_toBottomOf="@+id/recipients"
+            app:layout_constraintStart_toEndOf="@id/contact_picture"
+            app:srcCompat="@drawable/status_lock_disabled"
+            app:tint="?attr/openpgp_grey"
+            tools:visibility="visible" />
+
+        <com.fsck.k9.ui.messageview.RecipientNamesView
+            android:id="@+id/recipients"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="4dp"
+            android:layout_marginEnd="4dp"
+            android:layout_marginBottom="16dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@+id/menu_primary_action"
+            app:layout_constraintStart_toEndOf="@id/crypto_status_icon"
+            app:layout_constraintTop_toBottomOf="@+id/from"
+            app:layout_constraintVertical_bias="0.0"
+            app:layout_goneMarginStart="16dp" />
+
+        <ImageView
+            android:id="@+id/menu_primary_action"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:background="?attr/controlBackground"
+            android:paddingHorizontal="12dp"
+            app:layout_constraintEnd_toStartOf="@id/menu_overflow"
+            app:layout_constraintTop_toTopOf="@+id/menu_overflow"
+            app:srcCompat="?iconActionSingleMessageOptions" />
+
+        <ImageView
+            android:id="@+id/menu_overflow"
+            android:layout_width="40dp"
+            android:layout_height="48dp"
+            android:layout_marginTop="16dp"
+            android:layout_marginBottom="16dp"
+            android:background="?attr/controlBackground"
+            android:clickable="true"
+            android:focusable="true"
+            android:paddingStart="6dp"
+            android:paddingEnd="10dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:srcCompat="@drawable/dots_vertical" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
 
 </com.fsck.k9.view.MessageHeader>
diff --git a/app/ui/legacy/src/main/res/layout/recipient_names.xml b/app/ui/legacy/src/main/res/layout/recipient_names.xml
new file mode 100644
index 00000000000..c83217443dc
--- /dev/null
+++ b/app/ui/legacy/src/main/res/layout/recipient_names.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:parentTag="LinearLayout"
+    tools:orientation="horizontal">
+
+    <TextView
+        android:id="@+id/recipient_names"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:ellipsize="end"
+        android:maxLines="1"
+        android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
+        android:textColor="?android:attr/textColorSecondary"
+        tools:text="to me" />
+
+    <TextView
+        android:id="@+id/recipient_count"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="4dp"
+        android:singleLine="true"
+        android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
+        android:textColor="?attr/colorAccent"
+        tools:text="+2" />
+
+</merge>
diff --git a/app/ui/legacy/src/main/res/menu/participant_overflow_menu.xml b/app/ui/legacy/src/main/res/menu/participant_overflow_menu.xml
new file mode 100644
index 00000000000..b79ab3cc82f
--- /dev/null
+++ b/app/ui/legacy/src/main/res/menu/participant_overflow_menu.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/copy_email_address"
+        android:title="@string/action_copy_email_address" />
+    <item
+        android:id="@+id/copy_name_and_email_address"
+        android:title="@string/action_copy_name_and_email_address" />
+</menu>
diff --git a/app/ui/legacy/src/main/res/values-ar/strings.xml b/app/ui/legacy/src/main/res/values-ar/strings.xml
index 9b893ade78e..af84072fa62 100644
--- a/app/ui/legacy/src/main/res/values-ar/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ar/strings.xml
@@ -208,7 +208,6 @@
   <string name="message_view_cc_label">نسخة كربونية :</string>
   <string name="message_view_bcc_label">نسخة مخفية:</string>
   <string name="message_view_status_attachment_not_saved">غير قادر على حفظ المُرفَق.</string>
-  <string name="message_view_show_pictures_action">اظهر الصور</string>
   <string name="message_view_no_viewer">يتعذر إيجاب تطبيق لعرض <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">تنزيل الرسالة كاملةً</string>
   <string name="message_view_sender_label">عبر %1$s</string>
@@ -599,9 +598,6 @@
   <string name="font_size_message_list_preview">معاينة</string>
   <string name="font_size_message_view">رسائل</string>
   <string name="font_size_message_view_sender">مُرسِل</string>
-  <string name="font_size_message_view_to">إلى</string>
-  <string name="font_size_message_view_cc">نسخة</string>
-  <string name="font_size_message_view_additional_headers">ترويسات إضافية</string>
   <string name="font_size_message_view_subject">العنوان</string>
   <string name="font_size_message_view_date">الوقت والتاريخ</string>
   <string name="font_size_message_view_content">نصّ الرسالة</string>
diff --git a/app/ui/legacy/src/main/res/values-be/strings.xml b/app/ui/legacy/src/main/res/values-be/strings.xml
index 5fd4171bf7b..0e9e4a1bf0e 100644
--- a/app/ui/legacy/src/main/res/values-be/strings.xml
+++ b/app/ui/legacy/src/main/res/values-be/strings.xml
@@ -208,7 +208,6 @@ K-9 Mail - шматфункцыянальны свабодны паштовы к
   <string name="message_view_cc_label">Копія:</string>
   <string name="message_view_bcc_label">Схаваная копія:</string>
   <string name="message_view_status_attachment_not_saved">Немагчыма захаваць далучаныя файлы.</string>
-  <string name="message_view_show_pictures_action">Прагляд малюнкаў</string>
   <string name="message_view_no_viewer">Няма праграмы для прагляду <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Спампаваць ліст цалкам</string>
   <string name="message_view_sender_label">ад %1$s</string>
@@ -662,10 +661,6 @@ K-9 Mail - шматфункцыянальны свабодны паштовы к
   <string name="font_size_message_list_preview">Папярэдні прагляд</string>
   <string name="font_size_message_view">Паведамленне</string>
   <string name="font_size_message_view_sender">Адпраўнік</string>
-  <string name="font_size_message_view_to">Каму</string>
-  <string name="font_size_message_view_cc">Копія</string>
-  <string name="font_size_message_view_bcc">Схаваная копія</string>
-  <string name="font_size_message_view_additional_headers">Дадатковыя загалоўкі</string>
   <string name="font_size_message_view_subject">Тэма</string>
   <string name="font_size_message_view_date">Час і дата</string>
   <string name="font_size_message_view_content">Цела ліста</string>
diff --git a/app/ui/legacy/src/main/res/values-bg/strings.xml b/app/ui/legacy/src/main/res/values-bg/strings.xml
index 96e0df918f9..8b62dddf923 100644
--- a/app/ui/legacy/src/main/res/values-bg/strings.xml
+++ b/app/ui/legacy/src/main/res/values-bg/strings.xml
@@ -214,7 +214,6 @@ K-9 Mail е мощен, безплатен имейл клиент за Андр
   <string name="message_view_cc_label">Копие:</string>
   <string name="message_view_bcc_label">Bcc:</string>
   <string name="message_view_status_attachment_not_saved">Не може да запази прикачения файл.</string>
-  <string name="message_view_show_pictures_action">Покажи картинките</string>
   <string name="message_view_no_viewer">Не може да намери програма за <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Свали цялото съобщение</string>
   <string name="message_view_sender_label">през %1$s</string>
@@ -659,10 +658,6 @@ K-9 Mail е мощен, безплатен имейл клиент за Андр
   <string name="font_size_message_list_preview">Преглед</string>
   <string name="font_size_message_view">Съобщения</string>
   <string name="font_size_message_view_sender">Изпращач</string>
-  <string name="font_size_message_view_to">До</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Bcc</string>
-  <string name="font_size_message_view_additional_headers">Допълнителни header-и</string>
   <string name="font_size_message_view_subject">Тема</string>
   <string name="font_size_message_view_date">Дата и час</string>
   <string name="font_size_message_view_content">Тяло на съобщението</string>
diff --git a/app/ui/legacy/src/main/res/values-br/strings.xml b/app/ui/legacy/src/main/res/values-br/strings.xml
index 911ec1a7776..588a1878e7b 100644
--- a/app/ui/legacy/src/main/res/values-br/strings.xml
+++ b/app/ui/legacy/src/main/res/values-br/strings.xml
@@ -195,7 +195,6 @@ Danevellit beugoù, kenlabourit war keweriusterioù nevez ha savit goulennoù wa
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Bcc:</string>
   <string name="message_view_status_attachment_not_saved">N\'haller ket enrollañ ar stagadenn.</string>
-  <string name="message_view_show_pictures_action">Diskouez ar skeudennoù</string>
   <string name="message_view_no_viewer">N’haller ket kavout ur gwelerez evit <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Pellgargañ ar gemennadenn glok</string>
   <string name="message_view_sender_label">dre %1$s</string>
@@ -630,10 +629,6 @@ Danevellit beugoù, kenlabourit war keweriusterioù nevez ha savit goulennoù wa
   <string name="font_size_message_list_preview">Alberz</string>
   <string name="font_size_message_view">Kemennadennoù</string>
   <string name="font_size_message_view_sender">Kaser</string>
-  <string name="font_size_message_view_to">Da</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Bcc</string>
-  <string name="font_size_message_view_additional_headers">Talbennoù ouzhpenn</string>
   <string name="font_size_message_view_subject">Danvez</string>
   <string name="font_size_message_view_date">Eur ha deiziad</string>
   <string name="font_size_message_view_content">Korf ar gemennadenn</string>
diff --git a/app/ui/legacy/src/main/res/values-ca/strings.xml b/app/ui/legacy/src/main/res/values-ca/strings.xml
index 8dbbfef4c6c..6aa690c0eb0 100644
--- a/app/ui/legacy/src/main/res/values-ca/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ca/strings.xml
@@ -224,7 +224,6 @@ Si us plau, envieu informes d\'errors, contribuïu-hi amb noves millores i feu p
   <string name="message_view_cc_label">A/c:</string>
   <string name="message_view_bcc_label">Bcc:</string>
   <string name="message_view_status_attachment_not_saved">No s\'ha pogut desar l\'adjunt.</string>
-  <string name="message_view_show_pictures_action">Mostra les imatges</string>
   <string name="message_view_no_viewer">No s\'ha pogut trobar un visualitzador per <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Baixa el missatge complet</string>
   <string name="message_view_sender_label">mitjançant %1$s</string>
@@ -724,10 +723,6 @@ Si us plau, envieu informes d\'errors, contribuïu-hi amb noves millores i feu p
   <string name="font_size_message_list_preview">Vista prèvia</string>
   <string name="font_size_message_view">Missatges</string>
   <string name="font_size_message_view_sender">Remitent</string>
-  <string name="font_size_message_view_to">A</string>
-  <string name="font_size_message_view_cc">A/c</string>
-  <string name="font_size_message_view_bcc">C/o</string>
-  <string name="font_size_message_view_additional_headers">Capçaleres addicionals</string>
   <string name="font_size_message_view_subject">Assumpte</string>
   <string name="font_size_message_view_date">Data i hora</string>
   <string name="font_size_message_view_content">Cos del missatge</string>
diff --git a/app/ui/legacy/src/main/res/values-cs/strings.xml b/app/ui/legacy/src/main/res/values-cs/strings.xml
index 4908795b6fe..97f6462335e 100644
--- a/app/ui/legacy/src/main/res/values-cs/strings.xml
+++ b/app/ui/legacy/src/main/res/values-cs/strings.xml
@@ -228,7 +228,6 @@ Hlášení o chyb, úpravy pro nové funkce a dotazy zadávejte prostřednictví
   <string name="message_view_cc_label">Kopie:</string>
   <string name="message_view_bcc_label">Skrytá kopie (Bcc):</string>
   <string name="message_view_status_attachment_not_saved">Přílohu se nedaří uložit.</string>
-  <string name="message_view_show_pictures_action">Zobrazovat obrázky</string>
   <string name="message_view_no_viewer">Nelze nalézt prohlížeč pro <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Stáhnout úplnou zprávu</string>
   <string name="message_view_sender_label">přes %1$s</string>
@@ -721,10 +720,6 @@ Hlášení o chyb, úpravy pro nové funkce a dotazy zadávejte prostřednictví
   <string name="font_size_message_list_preview">Náhled</string>
   <string name="font_size_message_view">Zpráva</string>
   <string name="font_size_message_view_sender">Odesílatel</string>
-  <string name="font_size_message_view_to">Příjemce (Komu)</string>
-  <string name="font_size_message_view_cc">Příjemce (Kopie)</string>
-  <string name="font_size_message_view_bcc">Skrytá kopie</string>
-  <string name="font_size_message_view_additional_headers">Další záhlaví</string>
   <string name="font_size_message_view_subject">Předmět</string>
   <string name="font_size_message_view_date">Datum a čas</string>
   <string name="font_size_message_view_content">Tělo</string>
diff --git a/app/ui/legacy/src/main/res/values-cy/strings.xml b/app/ui/legacy/src/main/res/values-cy/strings.xml
index b0888279c6f..27957d4821f 100644
--- a/app/ui/legacy/src/main/res/values-cy/strings.xml
+++ b/app/ui/legacy/src/main/res/values-cy/strings.xml
@@ -229,7 +229,6 @@ Plîs rho wybod am unrhyw wallau, syniadau am nodweddion newydd, neu ofyn cwesti
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Bcc:</string>
   <string name="message_view_status_attachment_not_saved">Methu â chadw\'r atodiad.</string>
-  <string name="message_view_show_pictures_action">Dangos delweddau</string>
   <string name="message_view_no_viewer">Methwyd cael hyd i ap er mwyn darllen <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Lawrlwytho\'r neges gyfan</string>
   <string name="message_view_sender_label">trwy %1$s</string>
@@ -723,10 +722,6 @@ Plîs rho wybod am unrhyw wallau, syniadau am nodweddion newydd, neu ofyn cwesti
   <string name="font_size_message_list_preview">Rhagolwg</string>
   <string name="font_size_message_view">Negeseuon</string>
   <string name="font_size_message_view_sender">Anfonwr</string>
-  <string name="font_size_message_view_to">At</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Bcc</string>
-  <string name="font_size_message_view_additional_headers">Penynnau ychwanegol</string>
   <string name="font_size_message_view_subject">Pwnc</string>
   <string name="font_size_message_view_date">Amser a dyddiad</string>
   <string name="font_size_message_view_content">Corff neges</string>
diff --git a/app/ui/legacy/src/main/res/values-da/strings.xml b/app/ui/legacy/src/main/res/values-da/strings.xml
index 0d95102a480..ebc212b6802 100644
--- a/app/ui/legacy/src/main/res/values-da/strings.xml
+++ b/app/ui/legacy/src/main/res/values-da/strings.xml
@@ -218,7 +218,6 @@ Rapporter venligst fejl, forslag til nye funktioner eller stil spørgsmål på:
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Bcc:</string>
   <string name="message_view_status_attachment_not_saved">Kunne ikke gemme vedhæftning.</string>
-  <string name="message_view_show_pictures_action">Vis billeder</string>
   <string name="message_view_no_viewer">Kan ikke finde program som kan vise <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Hent hele meddelelsen</string>
   <string name="message_view_sender_label">via %1$s</string>
@@ -688,10 +687,6 @@ Rapporter venligst fejl, forslag til nye funktioner eller stil spørgsmål på:
   <string name="font_size_message_list_preview">Forhåndsvisning</string>
   <string name="font_size_message_view">Meddelelse</string>
   <string name="font_size_message_view_sender">Afsender</string>
-  <string name="font_size_message_view_to">Modtagere (Til)</string>
-  <string name="font_size_message_view_cc">Modtagere (Cc)</string>
-  <string name="font_size_message_view_bcc">Bcc</string>
-  <string name="font_size_message_view_additional_headers">Yderligere headere</string>
   <string name="font_size_message_view_subject">Emne</string>
   <string name="font_size_message_view_date">Tid og dato</string>
   <string name="font_size_message_view_content">Indhold</string>
diff --git a/app/ui/legacy/src/main/res/values-de/strings.xml b/app/ui/legacy/src/main/res/values-de/strings.xml
index af5f959d537..6f161377312 100644
--- a/app/ui/legacy/src/main/res/values-de/strings.xml
+++ b/app/ui/legacy/src/main/res/values-de/strings.xml
@@ -223,7 +223,6 @@ Bitte senden Sie Fehlerberichte, Ideen für neue Funktionen und stellen Sie Frag
   <string name="message_view_cc_label">CC:</string>
   <string name="message_view_bcc_label">BCC:</string>
   <string name="message_view_status_attachment_not_saved">Anhang konnte nicht gespeichert werden</string>
-  <string name="message_view_show_pictures_action">Bilder herunterladen</string>
   <string name="message_view_no_viewer">Es wurde kein Anzeigeprogramm für <xliff:g id="mimetype">%s</xliff:g> gefunden.</string>
   <string name="message_view_download_remainder">Gesamte Nachricht herunterladen</string>
   <string name="message_view_sender_label">über %1$s</string>
@@ -724,10 +723,6 @@ Bitte senden Sie Fehlerberichte, Ideen für neue Funktionen und stellen Sie Frag
   <string name="font_size_message_list_preview">Vorschau</string>
   <string name="font_size_message_view">Nachrichtenanzeige</string>
   <string name="font_size_message_view_sender">Absender</string>
-  <string name="font_size_message_view_to">Empfänger (To)</string>
-  <string name="font_size_message_view_cc">Empfänger (CC)</string>
-  <string name="font_size_message_view_bcc">Blindkopie (BCC)</string>
-  <string name="font_size_message_view_additional_headers">Zusätzliche Header</string>
   <string name="font_size_message_view_subject">Betreff</string>
   <string name="font_size_message_view_date">Uhrzeit und Datum</string>
   <string name="font_size_message_view_content">Nachrichtentext</string>
diff --git a/app/ui/legacy/src/main/res/values-el/strings.xml b/app/ui/legacy/src/main/res/values-el/strings.xml
index d7ed0e2417b..fc16199e6b2 100644
--- a/app/ui/legacy/src/main/res/values-el/strings.xml
+++ b/app/ui/legacy/src/main/res/values-el/strings.xml
@@ -225,7 +225,6 @@
   <string name="message_view_cc_label">Κοιν:</string>
   <string name="message_view_bcc_label">Κρυφή κοιν:</string>
   <string name="message_view_status_attachment_not_saved">Αδυναμία αποθήκευσης συνημμένου.</string>
-  <string name="message_view_show_pictures_action">Εμφάνιση εικόνων</string>
   <string name="message_view_no_viewer">Δεν υπάρχει πρόγραμμα προβολής για <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Λήψη πλήρους μηνύματος</string>
   <string name="message_view_sender_label">μέσω %1$s</string>
@@ -719,10 +718,6 @@
   <string name="font_size_message_list_preview">Προεπισκόπηση</string>
   <string name="font_size_message_view">Μηνύματα</string>
   <string name="font_size_message_view_sender">Αποστολέας</string>
-  <string name="font_size_message_view_to">Προς</string>
-  <string name="font_size_message_view_cc">Κοινοποίηση</string>
-  <string name="font_size_message_view_bcc">Κρυφή Κοιν:</string>
-  <string name="font_size_message_view_additional_headers">Επιπλέον επικεφαλίδες</string>
   <string name="font_size_message_view_subject">Θέμα</string>
   <string name="font_size_message_view_date">Ώρα και Ημερομηνία</string>
   <string name="font_size_message_view_content">Σώμα μηνύματος</string>
diff --git a/app/ui/legacy/src/main/res/values-eo/strings.xml b/app/ui/legacy/src/main/res/values-eo/strings.xml
index 893780e5b64..ef96153416d 100644
--- a/app/ui/legacy/src/main/res/values-eo/strings.xml
+++ b/app/ui/legacy/src/main/res/values-eo/strings.xml
@@ -212,7 +212,6 @@ Bonvolu raporti erarojn, kontribui novajn eblojn kaj peti pri novaj funkcioj per
   <string name="message_view_cc_label">Kopio:</string>
   <string name="message_view_bcc_label">Kaŝkopio:</string>
   <string name="message_view_status_attachment_not_saved">Ne povis konservi kunsendaĵon.</string>
-  <string name="message_view_show_pictures_action">Montri bildojn</string>
   <string name="message_view_no_viewer">Ne povas trovi vidigilon por <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Elŝuti tutan mesaĝon</string>
   <string name="message_view_sender_label">per %1$s</string>
@@ -677,10 +676,6 @@ Bonvolu raporti erarojn, kontribui novajn eblojn kaj peti pri novaj funkcioj per
   <string name="font_size_message_list_preview">Antaŭmontro</string>
   <string name="font_size_message_view">Mesaĝoj</string>
   <string name="font_size_message_view_sender">Sendinto</string>
-  <string name="font_size_message_view_to">Ricevonto</string>
-  <string name="font_size_message_view_cc">Ricevonto de kopio</string>
-  <string name="font_size_message_view_bcc">Kaŝkopio</string>
-  <string name="font_size_message_view_additional_headers">Aldonaj kapoj</string>
   <string name="font_size_message_view_subject">Temo</string>
   <string name="font_size_message_view_date">Tempo kaj dato</string>
   <string name="font_size_message_view_content">Mesaĝa enhavo</string>
diff --git a/app/ui/legacy/src/main/res/values-es/strings.xml b/app/ui/legacy/src/main/res/values-es/strings.xml
index 68bae6a6cb9..d719ab37ab9 100644
--- a/app/ui/legacy/src/main/res/values-es/strings.xml
+++ b/app/ui/legacy/src/main/res/values-es/strings.xml
@@ -224,7 +224,6 @@ Puedes informar de fallos, contribuir con su desarrollo y hacer preguntas en <a
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Cco:</string>
   <string name="message_view_status_attachment_not_saved">No se puede guardar el adjunto.</string>
-  <string name="message_view_show_pictures_action">Mostrar imágenes</string>
   <string name="message_view_no_viewer">Imposible encontrar visor de <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Descargar mensaje completo</string>
   <string name="message_view_sender_label">vía %1$s</string>
@@ -724,10 +723,6 @@ Puedes informar de fallos, contribuir con su desarrollo y hacer preguntas en <a
   <string name="font_size_message_list_preview">Previsualizar</string>
   <string name="font_size_message_view">Mensajes</string>
   <string name="font_size_message_view_sender">Remitente</string>
-  <string name="font_size_message_view_to">Para</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Cco</string>
-  <string name="font_size_message_view_additional_headers">Encabezados adicionales</string>
   <string name="font_size_message_view_subject">Asunto</string>
   <string name="font_size_message_view_date">Hora y fecha</string>
   <string name="font_size_message_view_content">Cuerpo de mensaje</string>
diff --git a/app/ui/legacy/src/main/res/values-et/strings.xml b/app/ui/legacy/src/main/res/values-et/strings.xml
index 232f8c9a13d..1e2718a6bc8 100644
--- a/app/ui/legacy/src/main/res/values-et/strings.xml
+++ b/app/ui/legacy/src/main/res/values-et/strings.xml
@@ -225,7 +225,6 @@ Veateated saad saata, kaastööd teha ning küsida teavet järgmisel lehel:
   <string name="message_view_cc_label">Koopia:</string>
   <string name="message_view_bcc_label">Pimekoopia:</string>
   <string name="message_view_status_attachment_not_saved">Manuse salvestamine ebaõnnestus</string>
-  <string name="message_view_show_pictures_action">Näita pilte</string>
   <string name="message_view_no_viewer">Ei leia <xliff:g id="mimetype">%s</xliff:g> kuvamiseks programmi.</string>
   <string name="message_view_download_remainder">Laadi kogu kiri alla</string>
   <string name="message_view_sender_label">%1$s kaudu</string>
@@ -722,10 +721,6 @@ Veateated saad saata, kaastööd teha ning küsida teavet järgmisel lehel:
   <string name="font_size_message_list_preview">Eelvaade</string>
   <string name="font_size_message_view">Kirjad</string>
   <string name="font_size_message_view_sender">Saatja</string>
-  <string name="font_size_message_view_to">Kellele</string>
-  <string name="font_size_message_view_cc">Koopia</string>
-  <string name="font_size_message_view_bcc">Pimekoopia</string>
-  <string name="font_size_message_view_additional_headers">Täiendavad pealkirjad</string>
   <string name="font_size_message_view_subject">Teema</string>
   <string name="font_size_message_view_date">Kellaaeg ja kuupäev</string>
   <string name="font_size_message_view_content">Kirja põhiosa</string>
diff --git a/app/ui/legacy/src/main/res/values-eu/strings.xml b/app/ui/legacy/src/main/res/values-eu/strings.xml
index 43c53b475d2..1e55f7754df 100644
--- a/app/ui/legacy/src/main/res/values-eu/strings.xml
+++ b/app/ui/legacy/src/main/res/values-eu/strings.xml
@@ -223,7 +223,6 @@ Mesedez akatsen berri emateko, ezaugarri berriak gehitzeko eta galderak egiteko
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Bcc:</string>
   <string name="message_view_status_attachment_not_saved">Ezin izan da eranskina gorde.</string>
-  <string name="message_view_show_pictures_action">Erakutsi irudiak</string>
   <string name="message_view_no_viewer">Ezin da <xliff:g id="mimetype">%s</xliff:g>-(r)entzako ikustailerik aurkitu.</string>
   <string name="message_view_download_remainder">Deskargatu mezu osoa</string>
   <string name="message_view_sender_label">%1$s-(r)en bidez</string>
@@ -723,10 +722,6 @@ Mesedez akatsen berri emateko, ezaugarri berriak gehitzeko eta galderak egiteko
   <string name="font_size_message_list_preview">Aurreikusi</string>
   <string name="font_size_message_view">Mezuak</string>
   <string name="font_size_message_view_sender">Bidaltzailea</string>
-  <string name="font_size_message_view_to">Nori</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Bcc</string>
-  <string name="font_size_message_view_additional_headers">Goiburu osagarriak</string>
   <string name="font_size_message_view_subject">Gaia</string>
   <string name="font_size_message_view_date">Ordua eta data</string>
   <string name="font_size_message_view_content">Mezuaren gorputza</string>
diff --git a/app/ui/legacy/src/main/res/values-fa/strings.xml b/app/ui/legacy/src/main/res/values-fa/strings.xml
index 77b654e2987..5477f99913f 100644
--- a/app/ui/legacy/src/main/res/values-fa/strings.xml
+++ b/app/ui/legacy/src/main/res/values-fa/strings.xml
@@ -224,7 +224,6 @@
   <string name="message_view_cc_label">رونوشت به:</string>
   <string name="message_view_bcc_label">مخفیانه به:</string>
   <string name="message_view_status_attachment_not_saved">نمی‌توان پیوست را ذخیره کرد.</string>
-  <string name="message_view_show_pictures_action">نمایش تصاویر</string>
   <string name="message_view_no_viewer">نمایش‌دهنده‌ای برای <xliff:g id="mimetype">%s</xliff:g> پیدا نشد.</string>
   <string name="message_view_download_remainder">دانلود پیام کامل</string>
   <string name="message_view_sender_label">از طریق %1$s</string>
@@ -717,10 +716,6 @@
   <string name="font_size_message_list_preview">پیش‌نمایش</string>
   <string name="font_size_message_view">پیام‌ها</string>
   <string name="font_size_message_view_sender">فرستنده</string>
-  <string name="font_size_message_view_to">به</string>
-  <string name="font_size_message_view_cc">رونوشت به</string>
-  <string name="font_size_message_view_bcc">مخفیانه به</string>
-  <string name="font_size_message_view_additional_headers">سرایندهای اضافی</string>
   <string name="font_size_message_view_subject">موضوع</string>
   <string name="font_size_message_view_date">ساعت و تاریخ</string>
   <string name="font_size_message_view_content">متن پیام</string>
diff --git a/app/ui/legacy/src/main/res/values-fi/strings.xml b/app/ui/legacy/src/main/res/values-fi/strings.xml
index aec3d318b59..b7f4ed77ff3 100644
--- a/app/ui/legacy/src/main/res/values-fi/strings.xml
+++ b/app/ui/legacy/src/main/res/values-fi/strings.xml
@@ -223,7 +223,6 @@ Ilmoita virheistä, ota osaa sovelluskehitykseen ja esitä kysymyksiä osoittees
   <string name="message_view_cc_label">Kopio:</string>
   <string name="message_view_bcc_label">Piilokopio:</string>
   <string name="message_view_status_attachment_not_saved">Liitteen tallentaminen epäonnistui.</string>
-  <string name="message_view_show_pictures_action">Näytä kuvat</string>
   <string name="message_view_no_viewer">Tiedostotyypille <xliff:g id="mimetype">%s</xliff:g> ei löydy katseluohjelmaa.</string>
   <string name="message_view_download_remainder">Lataa koko viesti</string>
   <string name="message_view_sender_label">%1$s kautta</string>
@@ -723,10 +722,6 @@ Ilmoita virheistä, ota osaa sovelluskehitykseen ja esitä kysymyksiä osoittees
   <string name="font_size_message_list_preview">Esikatselu</string>
   <string name="font_size_message_view">Viestinäkymä</string>
   <string name="font_size_message_view_sender">Lähettäjä</string>
-  <string name="font_size_message_view_to">Vastaanottaja</string>
-  <string name="font_size_message_view_cc">Kopio</string>
-  <string name="font_size_message_view_bcc">Piilokopio</string>
-  <string name="font_size_message_view_additional_headers">Tarkemmat otsikkotiedot</string>
   <string name="font_size_message_view_subject">Viestin aihe</string>
   <string name="font_size_message_view_date">Aika ja päivämäärä</string>
   <string name="font_size_message_view_content">Viestin sisältö</string>
diff --git a/app/ui/legacy/src/main/res/values-fr/strings.xml b/app/ui/legacy/src/main/res/values-fr/strings.xml
index 890b827831d..f18c8ee123f 100644
--- a/app/ui/legacy/src/main/res/values-fr/strings.xml
+++ b/app/ui/legacy/src/main/res/values-fr/strings.xml
@@ -227,7 +227,6 @@ Rapportez les bogues, recommandez de nouvelles fonctions et posez vos questions
   <string name="message_view_cc_label">Cc :</string>
   <string name="message_view_bcc_label">Cci :</string>
   <string name="message_view_status_attachment_not_saved">Impossible d’enregistrer le fichier joint.</string>
-  <string name="message_view_show_pictures_action">Afficher les images</string>
   <string name="message_view_no_viewer">Impossible de trouver un visualiseur pour <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Télécharger le courriel complet</string>
   <string name="message_view_sender_label">par %1$s</string>
@@ -727,10 +726,6 @@ Rapportez les bogues, recommandez de nouvelles fonctions et posez vos questions
   <string name="font_size_message_list_preview">Aperçu</string>
   <string name="font_size_message_view">Courriels</string>
   <string name="font_size_message_view_sender">Expéditeur</string>
-  <string name="font_size_message_view_to">À</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Cci</string>
-  <string name="font_size_message_view_additional_headers">En-têtes supplémentaires</string>
   <string name="font_size_message_view_subject">Objet</string>
   <string name="font_size_message_view_date">Horodatage</string>
   <string name="font_size_message_view_content">Corps du courriel</string>
diff --git a/app/ui/legacy/src/main/res/values-fy/strings.xml b/app/ui/legacy/src/main/res/values-fy/strings.xml
index 9ace6e5706e..b3fd82988bd 100644
--- a/app/ui/legacy/src/main/res/values-fy/strings.xml
+++ b/app/ui/legacy/src/main/res/values-fy/strings.xml
@@ -219,7 +219,6 @@ Graach flaterrapporten stjoere, bydragen foar nije funksjes en fragen stelle op
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Bcc:</string>
   <string name="message_view_status_attachment_not_saved">Kin bylage net bewarje.</string>
-  <string name="message_view_show_pictures_action">Ofbyldingen toane</string>
   <string name="message_view_no_viewer">Gjin viewer te finen foar <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Folslein berjocht downloade</string>
   <string name="message_view_sender_label">fia %1$s</string>
@@ -719,10 +718,6 @@ Graach flaterrapporten stjoere, bydragen foar nije funksjes en fragen stelle op
   <string name="font_size_message_list_preview">Foarbyld</string>
   <string name="font_size_message_view">Berjochten</string>
   <string name="font_size_message_view_sender">Ofstjoerder</string>
-  <string name="font_size_message_view_to">Oan</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Bcc</string>
-  <string name="font_size_message_view_additional_headers">Ekstra koppen</string>
   <string name="font_size_message_view_subject">Underwerp</string>
   <string name="font_size_message_view_date">Tiid en datum</string>
   <string name="font_size_message_view_content">Berichtynhâld</string>
diff --git a/app/ui/legacy/src/main/res/values-gd/strings.xml b/app/ui/legacy/src/main/res/values-gd/strings.xml
index c010b6a54c2..bffc9159784 100644
--- a/app/ui/legacy/src/main/res/values-gd/strings.xml
+++ b/app/ui/legacy/src/main/res/values-gd/strings.xml
@@ -180,7 +180,6 @@
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Bcc:</string>
   <string name="message_view_status_attachment_not_saved">Cha b’ urrainn dhuinn an ceanglachan a shàbhaladh.</string>
-  <string name="message_view_show_pictures_action">Seall na dealbhan</string>
   <string name="message_view_no_viewer">Chan urrainn dhuinn aplacaid a lorg a sheallas <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Luchdaich a-nuas an teachdaireachd shlàn</string>
   <string name="message_view_sender_label">slighe %1$s</string>
@@ -625,10 +624,6 @@
   <string name="font_size_message_list_preview">Ro-shealladh</string>
   <string name="font_size_message_view">Teachdaireachdan</string>
   <string name="font_size_message_view_sender">Seòladair</string>
-  <string name="font_size_message_view_to">Gu</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Bcc</string>
-  <string name="font_size_message_view_additional_headers">Bannan-cinn a bharrachd</string>
   <string name="font_size_message_view_subject">Cuspair</string>
   <string name="font_size_message_view_date">Ceann-là ’s àm</string>
   <string name="font_size_message_view_content">Bodhaig na teachdaireachd</string>
diff --git a/app/ui/legacy/src/main/res/values-gl-rES/strings.xml b/app/ui/legacy/src/main/res/values-gl-rES/strings.xml
index 12f4977c0b0..d46826eee9d 100644
--- a/app/ui/legacy/src/main/res/values-gl-rES/strings.xml
+++ b/app/ui/legacy/src/main/res/values-gl-rES/strings.xml
@@ -129,7 +129,6 @@
   <string name="message_view_from_format">Desde: <xliff:g id="name">%1$s</xliff:g> &lt;<xliff:g id="email">%2$s</xliff:g>&gt;</string>
   <string name="message_to_label">Para:</string>
   <string name="message_view_cc_label">Cc:</string>
-  <string name="message_view_show_pictures_action">Mostrar imaxes</string>
   <string name="message_view_no_viewer">Non é posible atopar un visor para <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Descargar mensaxe completa</string>
   <string name="from_same_sender">Máis deste remitente</string>
@@ -530,10 +529,6 @@
   <string name="font_size_message_list_preview">Previsualización</string>
   <string name="font_size_message_view">Mensaxes</string>
   <string name="font_size_message_view_sender">Remitente</string>
-  <string name="font_size_message_view_to">Para</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Bcc</string>
-  <string name="font_size_message_view_additional_headers">Cabeceiras adicionais</string>
   <string name="font_size_message_view_subject">Asunto</string>
   <string name="font_size_message_view_date">Hora e data</string>
   <string name="font_size_message_view_content">Corpo da mensaxe</string>
diff --git a/app/ui/legacy/src/main/res/values-gl/strings.xml b/app/ui/legacy/src/main/res/values-gl/strings.xml
index be2ad7feb0f..e00d2202b2c 100644
--- a/app/ui/legacy/src/main/res/values-gl/strings.xml
+++ b/app/ui/legacy/src/main/res/values-gl/strings.xml
@@ -218,7 +218,6 @@ Por favor envíen informes de fallos, contribúa con novas características e co
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Bcc:</string>
   <string name="message_view_status_attachment_not_saved">Incapaz de gardar o anexo.</string>
-  <string name="message_view_show_pictures_action">Amosar imaxe</string>
   <string name="message_view_no_viewer">Imposible atopar visor para <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Descargar mensaxe completa</string>
   <string name="message_view_sender_label">vía %1$s</string>
@@ -683,10 +682,6 @@ Por favor envíen informes de fallos, contribúa con novas características e co
   <string name="font_size_message_list_preview">Vista previa</string>
   <string name="font_size_message_view">mensaxes</string>
   <string name="font_size_message_view_sender">Remitente</string>
-  <string name="font_size_message_view_to">Para</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">CCo</string>
-  <string name="font_size_message_view_additional_headers">Encabezados adicionáis</string>
   <string name="font_size_message_view_subject">Asunto</string>
   <string name="font_size_message_view_date">Data e Hora</string>
   <string name="font_size_message_view_content">Corpo da mensaxe</string>
diff --git a/app/ui/legacy/src/main/res/values-hr/strings.xml b/app/ui/legacy/src/main/res/values-hr/strings.xml
index 36468732288..c83e6920cbd 100644
--- a/app/ui/legacy/src/main/res/values-hr/strings.xml
+++ b/app/ui/legacy/src/main/res/values-hr/strings.xml
@@ -168,7 +168,6 @@
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Bcc:</string>
   <string name="message_view_status_attachment_not_saved">Nije moguće spremiti privitak.</string>
-  <string name="message_view_show_pictures_action">Prikaži slike</string>
   <string name="message_view_no_viewer">Ne mogu pronaći preglednik za <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Preuzmi cijelu poruku</string>
   <string name="message_view_sender_label">putem %1$s</string>
@@ -621,10 +620,6 @@
   <string name="font_size_message_list_preview">Pedpregled</string>
   <string name="font_size_message_view">Poruke</string>
   <string name="font_size_message_view_sender">Pošiljatelj</string>
-  <string name="font_size_message_view_to">Za</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Bcc</string>
-  <string name="font_size_message_view_additional_headers">Dodatna zaglavlja</string>
   <string name="font_size_message_view_subject">Subjekt</string>
   <string name="font_size_message_view_date">Vrijeme i datum</string>
   <string name="font_size_message_view_content">Tijelo poruke</string>
diff --git a/app/ui/legacy/src/main/res/values-hu/strings.xml b/app/ui/legacy/src/main/res/values-hu/strings.xml
index a64ac9baccf..46aeaece89f 100644
--- a/app/ui/legacy/src/main/res/values-hu/strings.xml
+++ b/app/ui/legacy/src/main/res/values-hu/strings.xml
@@ -222,7 +222,6 @@ Hibajelentések beküldésével közreműködhet az új funkciókban, és kérd
   <string name="message_view_cc_label">Másolat:</string>
   <string name="message_view_bcc_label">Titkos másolat:</string>
   <string name="message_view_status_attachment_not_saved">Nem lehet elmenteni a mellékletet.</string>
-  <string name="message_view_show_pictures_action">Fényképek megjelenítése</string>
   <string name="message_view_no_viewer">Nem sikerült megjelenítőt találni ehhez: <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Teljes üzenet letöltése</string>
   <string name="message_view_sender_label">ezen keresztül: %1$s</string>
@@ -715,10 +714,6 @@ Hibajelentések beküldésével közreműködhet az új funkciókban, és kérd
   <string name="font_size_message_list_preview">Előnézet</string>
   <string name="font_size_message_view">Üzenetek</string>
   <string name="font_size_message_view_sender">Feladó</string>
-  <string name="font_size_message_view_to">Címzett</string>
-  <string name="font_size_message_view_cc">Másolat</string>
-  <string name="font_size_message_view_bcc">Titkos másolat</string>
-  <string name="font_size_message_view_additional_headers">További fejlécek</string>
   <string name="font_size_message_view_subject">Tárgy</string>
   <string name="font_size_message_view_date">Idő és dátum</string>
   <string name="font_size_message_view_content">Üzenet törzse</string>
diff --git a/app/ui/legacy/src/main/res/values-in/strings.xml b/app/ui/legacy/src/main/res/values-in/strings.xml
index f63dc9d09f7..d0f4bc68b9c 100644
--- a/app/ui/legacy/src/main/res/values-in/strings.xml
+++ b/app/ui/legacy/src/main/res/values-in/strings.xml
@@ -203,7 +203,6 @@ Kirimkan laporan bug, kontribusikan fitur baru dan ajukan pertanyaan di
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Bcc:</string>
   <string name="message_view_status_attachment_not_saved">Tidak dapat menyimpan lampiran.</string>
-  <string name="message_view_show_pictures_action">Tampilkan gambar</string>
   <string name="message_view_no_viewer">Tidak bisa menemukan penampil untuk <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Unduh seluruh pesan</string>
   <string name="message_view_sender_label">melalui %1$s</string>
@@ -657,10 +656,6 @@ Kirimkan laporan bug, kontribusikan fitur baru dan ajukan pertanyaan di
   <string name="font_size_message_list_preview">Pratinjau</string>
   <string name="font_size_message_view">Pesan</string>
   <string name="font_size_message_view_sender">Pengirim</string>
-  <string name="font_size_message_view_to">Kepada</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Bcc</string>
-  <string name="font_size_message_view_additional_headers">Header tambahan</string>
   <string name="font_size_message_view_subject">Subyek</string>
   <string name="font_size_message_view_date">Tanggal dan waktu</string>
   <string name="font_size_message_view_content">Isi pesan</string>
diff --git a/app/ui/legacy/src/main/res/values-is/strings.xml b/app/ui/legacy/src/main/res/values-is/strings.xml
index aba75875de1..5155956c364 100644
--- a/app/ui/legacy/src/main/res/values-is/strings.xml
+++ b/app/ui/legacy/src/main/res/values-is/strings.xml
@@ -224,7 +224,6 @@ Sendu inn villuskýrslur, leggðu fram nýja eiginleika og spurðu spurninga á
   <string name="message_view_cc_label">Afrit:</string>
   <string name="message_view_bcc_label">Falið afrit:</string>
   <string name="message_view_status_attachment_not_saved">Tókst ekki að vista viðhengi</string>
-  <string name="message_view_show_pictures_action">Sýna myndir</string>
   <string name="message_view_no_viewer">Gat ekki fundið neitt skoðunarforrit fyrir <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Niðurhala heil skilaboð</string>
   <string name="message_view_sender_label">í gegnum %1$s</string>
@@ -724,10 +723,6 @@ Sendu inn villuskýrslur, leggðu fram nýja eiginleika og spurðu spurninga á
   <string name="font_size_message_list_preview">Forskoðun</string>
   <string name="font_size_message_view">Skilaboð</string>
   <string name="font_size_message_view_sender">Sendandi</string>
-  <string name="font_size_message_view_to">Til</string>
-  <string name="font_size_message_view_cc">Afrit</string>
-  <string name="font_size_message_view_bcc">Falið afrit</string>
-  <string name="font_size_message_view_additional_headers">Viðbótarhausar</string>
   <string name="font_size_message_view_subject">Viðfangsefni</string>
   <string name="font_size_message_view_date">Tími og dagsetning</string>
   <string name="font_size_message_view_content">Efni skilaboða</string>
diff --git a/app/ui/legacy/src/main/res/values-it/strings.xml b/app/ui/legacy/src/main/res/values-it/strings.xml
index 88eff877bd8..dbf001cf47b 100644
--- a/app/ui/legacy/src/main/res/values-it/strings.xml
+++ b/app/ui/legacy/src/main/res/values-it/strings.xml
@@ -227,7 +227,6 @@ Invia segnalazioni di bug, contribuisci con nuove funzionalità e poni domande s
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Ccn:</string>
   <string name="message_view_status_attachment_not_saved">Impossibile salvare l\'allegato.</string>
-  <string name="message_view_show_pictures_action">Mostra immagini</string>
   <string name="message_view_no_viewer">Impossibile trovare un visualizzatore per <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Scarica messaggio completo</string>
   <string name="message_view_sender_label">tramite %1$s</string>
@@ -720,10 +719,6 @@ Invia segnalazioni di bug, contribuisci con nuove funzionalità e poni domande s
   <string name="font_size_message_list_preview">Anteprima</string>
   <string name="font_size_message_view">Messaggi</string>
   <string name="font_size_message_view_sender">Mittente</string>
-  <string name="font_size_message_view_to">A</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Ccn</string>
-  <string name="font_size_message_view_additional_headers">Intestazioni aggiuntive</string>
   <string name="font_size_message_view_subject">Oggetto</string>
   <string name="font_size_message_view_date">Ora e data</string>
   <string name="font_size_message_view_content">Corpo del messaggio</string>
diff --git a/app/ui/legacy/src/main/res/values-iw/strings.xml b/app/ui/legacy/src/main/res/values-iw/strings.xml
index d5eee1e9d3a..f247040f81b 100644
--- a/app/ui/legacy/src/main/res/values-iw/strings.xml
+++ b/app/ui/legacy/src/main/res/values-iw/strings.xml
@@ -182,7 +182,6 @@
   <string name="message_view_from_format">מ: <xliff:g id="name">%1$s</xliff:g> &lt;<xliff:g id="email">%2$s</xliff:g>&gt;</string>
   <string name="message_to_label">ל:</string>
   <string name="message_view_cc_label">עותק:</string>
-  <string name="message_view_show_pictures_action">הצג תמונות</string>
   <string name="message_view_no_viewer">לא ניתן למצוא את מציג בשביל <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">הורד את ההודעה המלאה</string>
   <string name="message_view_sender_label">באמצעות %1$s</string>
@@ -532,10 +531,6 @@
   <string name="font_size_message_list_preview">תצוגה מקדימה</string>
   <string name="font_size_message_view">הודעות</string>
   <string name="font_size_message_view_sender">שולח</string>
-  <string name="font_size_message_view_to">ל</string>
-  <string name="font_size_message_view_cc">עותק</string>
-  <string name="font_size_message_view_bcc">עותק מוסתר</string>
-  <string name="font_size_message_view_additional_headers">כותרות נוספות</string>
   <string name="font_size_message_view_subject">כותרת</string>
   <string name="font_size_message_view_date">שעה ותאריך</string>
   <string name="font_size_message_view_content">גוף ההודעה</string>
diff --git a/app/ui/legacy/src/main/res/values-ja/strings.xml b/app/ui/legacy/src/main/res/values-ja/strings.xml
index 1f1077e038b..06507b6f5af 100644
--- a/app/ui/legacy/src/main/res/values-ja/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ja/strings.xml
@@ -222,7 +222,6 @@ K-9 は大多数のメールクライアントと同様に、ほとんどのフ
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Bcc:</string>
   <string name="message_view_status_attachment_not_saved">添付ファイルを保存できません。</string>
-  <string name="message_view_show_pictures_action">画像表示</string>
   <string name="message_view_no_viewer"><xliff:g id="mimetype">%s</xliff:g>のビューワーが見つかりません</string>
   <string name="message_view_download_remainder">すべてダウンロード</string>
   <string name="message_view_sender_label">%1$s 経由</string>
@@ -722,10 +721,6 @@ K-9 は大多数のメールクライアントと同様に、ほとんどのフ
   <string name="font_size_message_list_preview">プレビュー</string>
   <string name="font_size_message_view">メッセージ表示</string>
   <string name="font_size_message_view_sender">送信者</string>
-  <string name="font_size_message_view_to">To</string>
-  <string name="font_size_message_view_cc">CC</string>
-  <string name="font_size_message_view_bcc">Bcc</string>
-  <string name="font_size_message_view_additional_headers">追加ヘッダ</string>
   <string name="font_size_message_view_subject">件名</string>
   <string name="font_size_message_view_date">日時</string>
   <string name="font_size_message_view_content">本文</string>
diff --git a/app/ui/legacy/src/main/res/values-ko/strings.xml b/app/ui/legacy/src/main/res/values-ko/strings.xml
index 97798f26ee3..8a0540131be 100644
--- a/app/ui/legacy/src/main/res/values-ko/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ko/strings.xml
@@ -162,7 +162,6 @@
   <string name="message_to_label">수신자:</string>
   <string name="message_view_cc_label">참조:</string>
   <string name="message_view_bcc_label">Bcc:</string>
-  <string name="message_view_show_pictures_action">그림 보기</string>
   <string name="message_view_no_viewer"><xliff:g id="mimetype">%s</xliff:g>에 대한 뷰어를 찾을 수 없습니다.</string>
   <string name="message_view_download_remainder">메시지의 남은 부분 다운로드</string>
   <string name="message_view_sender_label">%1$s을 통하여</string>
@@ -576,10 +575,6 @@
   <string name="font_size_message_list_preview">미리 보기</string>
   <string name="font_size_message_view">메시지</string>
   <string name="font_size_message_view_sender">보낸이</string>
-  <string name="font_size_message_view_to">받는이</string>
-  <string name="font_size_message_view_cc">참조</string>
-  <string name="font_size_message_view_bcc">숨은 참조</string>
-  <string name="font_size_message_view_additional_headers">추가 헤더 정보</string>
   <string name="font_size_message_view_subject">제목</string>
   <string name="font_size_message_view_date">시간 및 날짜</string>
   <string name="font_size_message_view_content">내용</string>
diff --git a/app/ui/legacy/src/main/res/values-lt/strings.xml b/app/ui/legacy/src/main/res/values-lt/strings.xml
index 51ac72e2a9c..b04da1b84a1 100644
--- a/app/ui/legacy/src/main/res/values-lt/strings.xml
+++ b/app/ui/legacy/src/main/res/values-lt/strings.xml
@@ -226,7 +226,6 @@ Pateikite pranešimus apie klaidas, prisidėkite prie naujų funkcijų kūrimo i
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Bcc:</string>
   <string name="message_view_status_attachment_not_saved">Nepavyko išsaugoti priedo.</string>
-  <string name="message_view_show_pictures_action">Rodyti paveikslėlius</string>
   <string name="message_view_no_viewer">Nepavyko rasto <xliff:g id="mimetype">%s</xliff:g> peržiūros programos.</string>
   <string name="message_view_download_remainder">Parsiųsti visą laišką</string>
   <string name="message_view_sender_label">per %1$s</string>
@@ -716,10 +715,6 @@ Pateikite pranešimus apie klaidas, prisidėkite prie naujų funkcijų kūrimo i
   <string name="font_size_message_list_preview">Peržiūra</string>
   <string name="font_size_message_view">Laiškai</string>
   <string name="font_size_message_view_sender">Siuntėjas</string>
-  <string name="font_size_message_view_to">Kam</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Slapta kopija</string>
-  <string name="font_size_message_view_additional_headers">Papildomos antraštės</string>
   <string name="font_size_message_view_subject">Tema</string>
   <string name="font_size_message_view_date">Laikas ir data</string>
   <string name="font_size_message_view_content">Pagrindinė laiško dalis</string>
diff --git a/app/ui/legacy/src/main/res/values-lv/strings.xml b/app/ui/legacy/src/main/res/values-lv/strings.xml
index b916fc05db9..28f972a2cf6 100644
--- a/app/ui/legacy/src/main/res/values-lv/strings.xml
+++ b/app/ui/legacy/src/main/res/values-lv/strings.xml
@@ -226,7 +226,6 @@ pat <xliff:g id="messages_to_load">%d</xliff:g> vairāk</string>
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Neredzamais:</string>
   <string name="message_view_status_attachment_not_saved">Nevar saglabāt pielikumu.</string>
-  <string name="message_view_show_pictures_action">Rādīt bildes</string>
   <string name="message_view_no_viewer">Nevar atrast programmu, lai atvērtu <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">lejupielādēt pilnu vēstuli</string>
   <string name="message_view_sender_label">caur %1$s</string>
@@ -726,10 +725,6 @@ pat <xliff:g id="messages_to_load">%d</xliff:g> vairāk</string>
   <string name="font_size_message_list_preview">Priekšskatījums</string>
   <string name="font_size_message_view">Vēstules</string>
   <string name="font_size_message_view_sender">Sūtītājs</string>
-  <string name="font_size_message_view_to">Kam</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Neredzamais</string>
-  <string name="font_size_message_view_additional_headers">Izvērsta vēstules papildinformācija</string>
   <string name="font_size_message_view_subject">Temats</string>
   <string name="font_size_message_view_date">Laiks un datums</string>
   <string name="font_size_message_view_content">Vēstules pamatteksts</string>
diff --git a/app/ui/legacy/src/main/res/values-ml/strings.xml b/app/ui/legacy/src/main/res/values-ml/strings.xml
index 60d4c079ca9..a07eaa05cf7 100644
--- a/app/ui/legacy/src/main/res/values-ml/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ml/strings.xml
@@ -218,7 +218,6 @@
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Bcc:</string>
   <string name="message_view_status_attachment_not_saved">അറ്റാച്ചുമെന്റ് സേവ് ചെയ്യാനായില്ല.</string>
-  <string name="message_view_show_pictures_action">ചിത്രങ്ങൾ കാണിക്കുക</string>
   <string name="message_view_no_viewer"><xliff:g id="mimetype">%s</xliff:g> എന്നതിനുള്ള വീക്ഷണസഹായസാമഗ്രി കണ്ടെത്താനായില്ല.</string>
   <string name="message_view_download_remainder">പൂർണ്ണ സന്ദേശം ഡൗൺ‌ലോഡു ചെയ്യുക</string>
   <string name="message_view_sender_label">%1$s വഴി</string>
@@ -685,10 +684,6 @@
   <string name="font_size_message_list_preview">പ്രിവ്യൂ</string>
   <string name="font_size_message_view">സന്ദേശങ്ങൾ</string>
   <string name="font_size_message_view_sender">അയച്ചയാൾ</string>
-  <string name="font_size_message_view_to">To</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Bcc</string>
-  <string name="font_size_message_view_additional_headers">അധിക തലക്കെട്ടുകൾ</string>
   <string name="font_size_message_view_subject">വിഷയം</string>
   <string name="font_size_message_view_date">സമയവും തീയതിയും</string>
   <string name="font_size_message_view_content">സന്ദേശ ബോഡി</string>
diff --git a/app/ui/legacy/src/main/res/values-nb/strings.xml b/app/ui/legacy/src/main/res/values-nb/strings.xml
index 28459d513cb..42a9a60c5a9 100644
--- a/app/ui/legacy/src/main/res/values-nb/strings.xml
+++ b/app/ui/legacy/src/main/res/values-nb/strings.xml
@@ -204,7 +204,6 @@ til <xliff:g id="messages_to_load">%d</xliff:g> flere</string>
   <string name="message_view_cc_label">Kopi:</string>
   <string name="message_view_bcc_label">Blindkopi:</string>
   <string name="message_view_status_attachment_not_saved">Klarte ikke lagre vedlegg</string>
-  <string name="message_view_show_pictures_action">Vis bilder</string>
   <string name="message_view_no_viewer">Finner ikke visningsprogram for <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Last ned komplett melding</string>
   <string name="message_view_sender_label">via %1$s</string>
@@ -656,10 +655,6 @@ til <xliff:g id="messages_to_load">%d</xliff:g> flere</string>
   <string name="font_size_message_list_preview">Forhåndsvisning</string>
   <string name="font_size_message_view">Meldinger</string>
   <string name="font_size_message_view_sender">Avsender</string>
-  <string name="font_size_message_view_to">Til</string>
-  <string name="font_size_message_view_cc">Kopi</string>
-  <string name="font_size_message_view_bcc">Blindkopi</string>
-  <string name="font_size_message_view_additional_headers">Flere meldingshoder</string>
   <string name="font_size_message_view_subject">Emne</string>
   <string name="font_size_message_view_date">Tid og dato</string>
   <string name="font_size_message_view_content">Meldingstekst</string>
diff --git a/app/ui/legacy/src/main/res/values-nl/strings.xml b/app/ui/legacy/src/main/res/values-nl/strings.xml
index 1cdaa6d0b5b..014eaab9418 100644
--- a/app/ui/legacy/src/main/res/values-nl/strings.xml
+++ b/app/ui/legacy/src/main/res/values-nl/strings.xml
@@ -219,7 +219,6 @@ Graag foutrapporten sturen, bijdragen voor nieuwe functies en vragen stellen op
   <string name="message_view_cc_label">CC:</string>
   <string name="message_view_bcc_label">BCC:</string>
   <string name="message_view_status_attachment_not_saved">Kan bijlage niet opslaan.</string>
-  <string name="message_view_show_pictures_action">Afbeeldingen tonen</string>
   <string name="message_view_no_viewer">Niet in staat viewer te vinden voor <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Compleet bericht downloaden</string>
   <string name="message_view_sender_label">via %1$s</string>
@@ -719,10 +718,6 @@ Graag foutrapporten sturen, bijdragen voor nieuwe functies en vragen stellen op
   <string name="font_size_message_list_preview">Voorbeeld</string>
   <string name="font_size_message_view">Berichten</string>
   <string name="font_size_message_view_sender">Afzender</string>
-  <string name="font_size_message_view_to">Bericht ontvanger (Aan)</string>
-  <string name="font_size_message_view_cc">Bericht ontvanger (CC)</string>
-  <string name="font_size_message_view_bcc">Bericht ontvanger (BCC)</string>
-  <string name="font_size_message_view_additional_headers">Extra koppen</string>
   <string name="font_size_message_view_subject">Onderwerp</string>
   <string name="font_size_message_view_date">Tijd en datum</string>
   <string name="font_size_message_view_content">Berichtinhoud</string>
diff --git a/app/ui/legacy/src/main/res/values-pl/strings.xml b/app/ui/legacy/src/main/res/values-pl/strings.xml
index aeaa0231c98..b85b5ead465 100644
--- a/app/ui/legacy/src/main/res/values-pl/strings.xml
+++ b/app/ui/legacy/src/main/res/values-pl/strings.xml
@@ -228,7 +228,6 @@ Wysłane z urządzenia Android za pomocą K-9 Mail. Proszę wybaczyć moją zwi
   <string name="message_view_cc_label">DW:</string>
   <string name="message_view_bcc_label">UDW:</string>
   <string name="message_view_status_attachment_not_saved">Nie można zapisać załącznika.</string>
-  <string name="message_view_show_pictures_action">Wyświetl grafikę</string>
   <string name="message_view_no_viewer">Nie moge znaleźć aplikacji do wyświetlenia pliku <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Pobierz całą wiadomość</string>
   <string name="message_view_sender_label">przez %1$s</string>
@@ -728,10 +727,6 @@ Wysłane z urządzenia Android za pomocą K-9 Mail. Proszę wybaczyć moją zwi
   <string name="font_size_message_list_preview">Podgląd</string>
   <string name="font_size_message_view">Wiadomości</string>
   <string name="font_size_message_view_sender">Nadawca</string>
-  <string name="font_size_message_view_to">Adresat (Do:)</string>
-  <string name="font_size_message_view_cc">Odbiorca kopii (DW:)</string>
-  <string name="font_size_message_view_bcc">UDW</string>
-  <string name="font_size_message_view_additional_headers">Dodatkowe nagłówki</string>
   <string name="font_size_message_view_subject">Temat</string>
   <string name="font_size_message_view_date">Czas i data</string>
   <string name="font_size_message_view_content">Treść wiadomości</string>
diff --git a/app/ui/legacy/src/main/res/values-pt-rBR/strings.xml b/app/ui/legacy/src/main/res/values-pt-rBR/strings.xml
index 2b574b7a382..8b39587fe2a 100644
--- a/app/ui/legacy/src/main/res/values-pt-rBR/strings.xml
+++ b/app/ui/legacy/src/main/res/values-pt-rBR/strings.xml
@@ -225,7 +225,6 @@ Por favor encaminhe relatórios de bugs, contribua com novos recursos e tire dú
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Cco:</string>
   <string name="message_view_status_attachment_not_saved">Não foi possível salvar o anexo.</string>
-  <string name="message_view_show_pictures_action">Exibir imagens</string>
   <string name="message_view_no_viewer">Não foi possível encontrar um visualizador para <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Baixar a mensagem completa</string>
   <string name="message_view_sender_label">via %1$s</string>
@@ -725,10 +724,6 @@ Por favor encaminhe relatórios de bugs, contribua com novos recursos e tire dú
   <string name="font_size_message_list_preview">Visualização</string>
   <string name="font_size_message_view">Mensagens</string>
   <string name="font_size_message_view_sender">Remetente</string>
-  <string name="font_size_message_view_to">Para</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Cco</string>
-  <string name="font_size_message_view_additional_headers">Cabeçalhos adicionais</string>
   <string name="font_size_message_view_subject">Assunto</string>
   <string name="font_size_message_view_date">Data e hora</string>
   <string name="font_size_message_view_content">Corpo da mensagem</string>
diff --git a/app/ui/legacy/src/main/res/values-pt-rPT/strings.xml b/app/ui/legacy/src/main/res/values-pt-rPT/strings.xml
index 3cce8cd83d1..c87a90eeccd 100644
--- a/app/ui/legacy/src/main/res/values-pt-rPT/strings.xml
+++ b/app/ui/legacy/src/main/res/values-pt-rPT/strings.xml
@@ -227,7 +227,6 @@ Por favor envie relatórios de falhas, contribua com novas funcionalidades e col
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Bcc:</string>
   <string name="message_view_status_attachment_not_saved">Não foi possível guardar o anexo</string>
-  <string name="message_view_show_pictures_action">Mostrar imagens</string>
   <string name="message_view_no_viewer">Incapaz de encontrar visualizador para <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Transferir mensagem completa</string>
   <string name="message_view_sender_label">via %1$s</string>
@@ -722,10 +721,6 @@ Por favor envie relatórios de falhas, contribua com novas funcionalidades e col
   <string name="font_size_message_list_preview">Pré-visualização</string>
   <string name="font_size_message_view">Mensagens</string>
   <string name="font_size_message_view_sender">Remetente</string>
-  <string name="font_size_message_view_to">Para</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Bcc</string>
-  <string name="font_size_message_view_additional_headers">Cabeçalhos adicionais</string>
   <string name="font_size_message_view_subject">Assunto</string>
   <string name="font_size_message_view_date">Data e hora</string>
   <string name="font_size_message_view_content">Corpo da mensagem</string>
diff --git a/app/ui/legacy/src/main/res/values-ro/strings.xml b/app/ui/legacy/src/main/res/values-ro/strings.xml
index af4d82ceccb..13f51b47a3d 100644
--- a/app/ui/legacy/src/main/res/values-ro/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ro/strings.xml
@@ -226,7 +226,6 @@ cel mult încă <xliff:g id="messages_to_load">%d</xliff:g></string>
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Bcc:</string>
   <string name="message_view_status_attachment_not_saved">Nu se poate salva atașamentul.</string>
-  <string name="message_view_show_pictures_action">Arată pozele</string>
   <string name="message_view_no_viewer">Nu se poate găsi vizualizator pentru <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Descarcă mesajul complet</string>
   <string name="message_view_sender_label">via %1$s</string>
@@ -728,10 +727,6 @@ Uneori datorită faptului că cineva încearcă să te atace pe tine sau serveru
   <string name="font_size_message_list_preview">Previzualizare</string>
   <string name="font_size_message_view">Mesaje</string>
   <string name="font_size_message_view_sender">Expeditor</string>
-  <string name="font_size_message_view_to">Către</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Bcc</string>
-  <string name="font_size_message_view_additional_headers">Antete adiționale</string>
   <string name="font_size_message_view_subject">Subiect</string>
   <string name="font_size_message_view_date">Timp și dată</string>
   <string name="font_size_message_view_content">Conținut mesaj</string>
diff --git a/app/ui/legacy/src/main/res/values-ru/strings.xml b/app/ui/legacy/src/main/res/values-ru/strings.xml
index 0d2728ffae2..e47d83c293f 100644
--- a/app/ui/legacy/src/main/res/values-ru/strings.xml
+++ b/app/ui/legacy/src/main/res/values-ru/strings.xml
@@ -227,7 +227,6 @@ K-9 Mail — почтовый клиент для Android.
   <string name="message_view_cc_label">Копия:</string>
   <string name="message_view_bcc_label">Скрытая копия:</string>
   <string name="message_view_status_attachment_not_saved">Не получается сохранить вложение.</string>
-  <string name="message_view_show_pictures_action">Изображения</string>
   <string name="message_view_no_viewer">Отсутствует просмотрщик <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Загрузить полностью</string>
   <string name="message_view_sender_label">от %1$s</string>
@@ -727,10 +726,6 @@ K-9 Mail — почтовый клиент для Android.
   <string name="font_size_message_list_preview">Просмотр</string>
   <string name="font_size_message_view">Сообщение</string>
   <string name="font_size_message_view_sender">Отправитель</string>
-  <string name="font_size_message_view_to">Кому</string>
-  <string name="font_size_message_view_cc">Копия</string>
-  <string name="font_size_message_view_bcc">Скрытая</string>
-  <string name="font_size_message_view_additional_headers">Все заголовки</string>
   <string name="font_size_message_view_subject">Тема</string>
   <string name="font_size_message_view_date">Дата и время</string>
   <string name="font_size_message_view_content">Тело сообщения</string>
diff --git a/app/ui/legacy/src/main/res/values-sk/strings.xml b/app/ui/legacy/src/main/res/values-sk/strings.xml
index fe9f9298c07..719f2753859 100644
--- a/app/ui/legacy/src/main/res/values-sk/strings.xml
+++ b/app/ui/legacy/src/main/res/values-sk/strings.xml
@@ -209,7 +209,6 @@ Prosím, nahlasujte prípadné chyby, prispievajte novými funkciami a pýtajte
   <string name="message_view_cc_label">Kópia:</string>
   <string name="message_view_bcc_label">Skrytá kópia (Bcc):</string>
   <string name="message_view_status_attachment_not_saved">Prílohu sa nepodarilo uložiť.</string>
-  <string name="message_view_show_pictures_action">Zobraziť obrázky</string>
   <string name="message_view_no_viewer">Nemožno nájsť prehliadač pre <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Stiahnuť kompletnú správu</string>
   <string name="message_view_sender_label">cez %1$s</string>
@@ -663,10 +662,6 @@ Prosím, nahlasujte prípadné chyby, prispievajte novými funkciami a pýtajte
   <string name="font_size_message_list_preview">Náhľad</string>
   <string name="font_size_message_view">Správy</string>
   <string name="font_size_message_view_sender">Odosielateľ</string>
-  <string name="font_size_message_view_to">Komu</string>
-  <string name="font_size_message_view_cc">Kópia</string>
-  <string name="font_size_message_view_bcc">Skrytá kópia</string>
-  <string name="font_size_message_view_additional_headers">Prídavné hlavičky</string>
   <string name="font_size_message_view_subject">Skrytá kópia</string>
   <string name="font_size_message_view_date">Čas a dátum</string>
   <string name="font_size_message_view_content">Telo správy</string>
diff --git a/app/ui/legacy/src/main/res/values-sl/strings.xml b/app/ui/legacy/src/main/res/values-sl/strings.xml
index fe8e6c9a9b5..cf202e27ee9 100644
--- a/app/ui/legacy/src/main/res/values-sl/strings.xml
+++ b/app/ui/legacy/src/main/res/values-sl/strings.xml
@@ -228,7 +228,6 @@ dodatnih <xliff:g id="messages_to_load">%d</xliff:g> sporočil</string>
   <string name="message_view_cc_label">Kp:</string>
   <string name="message_view_bcc_label">Skp:</string>
   <string name="message_view_status_attachment_not_saved">Priloge ni mogoče shraniti.</string>
-  <string name="message_view_show_pictures_action">Pokaži slike</string>
   <string name="message_view_no_viewer">Ni mogoče najti pregledovalnika za vrsto <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Prejmi celotno sporočilo</string>
   <string name="message_view_sender_label">prek %1$s</string>
@@ -721,10 +720,6 @@ dodatnih <xliff:g id="messages_to_load">%d</xliff:g> sporočil</string>
   <string name="font_size_message_list_preview">Predogled</string>
   <string name="font_size_message_view">Sporočila</string>
   <string name="font_size_message_view_sender">Pošiljatelj</string>
-  <string name="font_size_message_view_to">Za</string>
-  <string name="font_size_message_view_cc">Kp</string>
-  <string name="font_size_message_view_bcc">Skp</string>
-  <string name="font_size_message_view_additional_headers">Dodatni zapisi glave</string>
   <string name="font_size_message_view_subject">Zadeva</string>
   <string name="font_size_message_view_date">Čas in datum</string>
   <string name="font_size_message_view_content">Telo sporočila</string>
diff --git a/app/ui/legacy/src/main/res/values-sq/strings.xml b/app/ui/legacy/src/main/res/values-sq/strings.xml
index 3a9d0d59708..88343b926ce 100644
--- a/app/ui/legacy/src/main/res/values-sq/strings.xml
+++ b/app/ui/legacy/src/main/res/values-sq/strings.xml
@@ -225,7 +225,6 @@ Ju lutemi, parashtrim njoftimesh për të meta, kontribute për veçori të reaj
   <string name="message_view_cc_label">Cc:</string>
   <string name="message_view_bcc_label">Bcc:</string>
   <string name="message_view_status_attachment_not_saved">S\’arrihet të ruhet bashkëngjitje</string>
-  <string name="message_view_show_pictures_action">Shfaqi fotot</string>
   <string name="message_view_no_viewer">S\’arrihet të gjendet parës për <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Shkarko mesazhin e plotë</string>
   <string name="message_view_sender_label">përmes %1$s</string>
@@ -724,10 +723,6 @@ Ju lutemi, parashtrim njoftimesh për të meta, kontribute për veçori të reaj
   <string name="font_size_message_list_preview">Paraparje</string>
   <string name="font_size_message_view">Mesazhe</string>
   <string name="font_size_message_view_sender">Dërgues</string>
-  <string name="font_size_message_view_to">Për</string>
-  <string name="font_size_message_view_cc">Cc</string>
-  <string name="font_size_message_view_bcc">Bcc</string>
-  <string name="font_size_message_view_additional_headers">Krye shtesë</string>
   <string name="font_size_message_view_subject">Subjekt</string>
   <string name="font_size_message_view_date">Kohë dhe datë</string>
   <string name="font_size_message_view_content">Trup i mesazhit</string>
diff --git a/app/ui/legacy/src/main/res/values-sr/strings.xml b/app/ui/legacy/src/main/res/values-sr/strings.xml
index abcb90f1fee..a3dfec1c620 100644
--- a/app/ui/legacy/src/main/res/values-sr/strings.xml
+++ b/app/ui/legacy/src/main/res/values-sr/strings.xml
@@ -207,7 +207,6 @@
   <string name="message_view_cc_label">Коп:</string>
   <string name="message_view_bcc_label">СКоп:</string>
   <string name="message_view_status_attachment_not_saved">Не могу да сачувам прилог.</string>
-  <string name="message_view_show_pictures_action">Приказуј слике</string>
   <string name="message_view_no_viewer">Не могу да нађем прегледач за <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Преузми читаву поруку</string>
   <string name="message_view_sender_label">проследи %1$s</string>
@@ -661,10 +660,6 @@
   <string name="font_size_message_list_preview">Преглед</string>
   <string name="font_size_message_view">Поруке</string>
   <string name="font_size_message_view_sender">Пошиљалац</string>
-  <string name="font_size_message_view_to">За</string>
-  <string name="font_size_message_view_cc">Коп</string>
-  <string name="font_size_message_view_bcc">СКоп</string>
-  <string name="font_size_message_view_additional_headers">Додатна заглавља</string>
   <string name="font_size_message_view_subject">Тема</string>
   <string name="font_size_message_view_date">Време и датум</string>
   <string name="font_size_message_view_content">Тело поруке</string>
diff --git a/app/ui/legacy/src/main/res/values-sv/strings.xml b/app/ui/legacy/src/main/res/values-sv/strings.xml
index 5edc809a522..9ce5bddbc73 100644
--- a/app/ui/legacy/src/main/res/values-sv/strings.xml
+++ b/app/ui/legacy/src/main/res/values-sv/strings.xml
@@ -224,7 +224,6 @@ Skicka in felrapporter, bidra med nya funktioner och ställ frågor på
   <string name="message_view_cc_label">Kopia:</string>
   <string name="message_view_bcc_label">Blindkopia:</string>
   <string name="message_view_status_attachment_not_saved">Det går inte att spara bilaga.</string>
-  <string name="message_view_show_pictures_action">Visa bilder</string>
   <string name="message_view_no_viewer">Kan inte hitta visare för <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Hämta hela meddelandet</string>
   <string name="message_view_sender_label">via %1$s</string>
@@ -724,10 +723,6 @@ Skicka in felrapporter, bidra med nya funktioner och ställ frågor på
   <string name="font_size_message_list_preview">Förhandsgranskning</string>
   <string name="font_size_message_view">Meddelanden</string>
   <string name="font_size_message_view_sender">Avsändare</string>
-  <string name="font_size_message_view_to">Till</string>
-  <string name="font_size_message_view_cc">Kopia</string>
-  <string name="font_size_message_view_bcc">Blindkopia</string>
-  <string name="font_size_message_view_additional_headers">Ytterligare meddelanderubriker</string>
   <string name="font_size_message_view_subject">Ämne</string>
   <string name="font_size_message_view_date">Tid och datum</string>
   <string name="font_size_message_view_content">Innehåll</string>
diff --git a/app/ui/legacy/src/main/res/values-tr/strings.xml b/app/ui/legacy/src/main/res/values-tr/strings.xml
index bc067ceb76a..305203425a8 100644
--- a/app/ui/legacy/src/main/res/values-tr/strings.xml
+++ b/app/ui/legacy/src/main/res/values-tr/strings.xml
@@ -217,7 +217,6 @@ Microsoft Exchange ile konuşurken kimi tuhaflıklar yaşadığını lütfen akl
   <string name="message_view_cc_label">Bilgi:</string>
   <string name="message_view_bcc_label">Gizli Kopya:</string>
   <string name="message_view_status_attachment_not_saved">Eklenti kaydedilemedi</string>
-  <string name="message_view_show_pictures_action">Resimleri göster</string>
   <string name="message_view_no_viewer"><xliff:g id="mimetype">%s</xliff:g> için görüntüleyici bulunamadı.</string>
   <string name="message_view_download_remainder">Mesajın tamamını indir</string>
   <string name="message_view_sender_label">%1$skanalıyla</string>
@@ -699,10 +698,6 @@ Microsoft Exchange ile konuşurken kimi tuhaflıklar yaşadığını lütfen akl
   <string name="font_size_message_list_preview">Önizleme</string>
   <string name="font_size_message_view">Mesajlar</string>
   <string name="font_size_message_view_sender">Gönderen</string>
-  <string name="font_size_message_view_to">Kime</string>
-  <string name="font_size_message_view_cc">Bilgi</string>
-  <string name="font_size_message_view_bcc">Gizli Kopya</string>
-  <string name="font_size_message_view_additional_headers">Ek başlıklar</string>
   <string name="font_size_message_view_subject">Konu</string>
   <string name="font_size_message_view_date">Saat ve Tarih</string>
   <string name="font_size_message_view_content">Mesaj gövdesi</string>
diff --git a/app/ui/legacy/src/main/res/values-uk/strings.xml b/app/ui/legacy/src/main/res/values-uk/strings.xml
index fded41c9208..091f893c880 100644
--- a/app/ui/legacy/src/main/res/values-uk/strings.xml
+++ b/app/ui/legacy/src/main/res/values-uk/strings.xml
@@ -229,7 +229,6 @@ K-9 Mail — це вільний клієнт електронної пошти
   <string name="message_view_cc_label">Копія:</string>
   <string name="message_view_bcc_label">Прихована копія:</string>
   <string name="message_view_status_attachment_not_saved">Не вдалося зберегти вкладення.</string>
-  <string name="message_view_show_pictures_action">Показати зображення</string>
   <string name="message_view_no_viewer">Не знайдено додаток для перегляду <xliff:g id="mimetype">%s</xliff:g>.</string>
   <string name="message_view_download_remainder">Завантажити усе повідомлення</string>
   <string name="message_view_sender_label">за допомогою %1$s</string>
@@ -722,10 +721,6 @@ K-9 Mail — це вільний клієнт електронної пошти
   <string name="font_size_message_list_preview">Попередній перегляд</string>
   <string name="font_size_message_view">Повідомлення</string>
   <string name="font_size_message_view_sender">Відправник</string>
-  <string name="font_size_message_view_to">Кому</string>
-  <string name="font_size_message_view_cc">Копія</string>
-  <string name="font_size_message_view_bcc">Прихована копія</string>
-  <string name="font_size_message_view_additional_headers">Додаткові заголовки</string>
   <string name="font_size_message_view_subject">Тема</string>
   <string name="font_size_message_view_date">Час і дата</string>
   <string name="font_size_message_view_content">Тіло повідомлення</string>
diff --git a/app/ui/legacy/src/main/res/values-zh-rCN/strings.xml b/app/ui/legacy/src/main/res/values-zh-rCN/strings.xml
index 686850249a6..e448914e764 100644
--- a/app/ui/legacy/src/main/res/values-zh-rCN/strings.xml
+++ b/app/ui/legacy/src/main/res/values-zh-rCN/strings.xml
@@ -222,7 +222,6 @@ K-9 Mail 是 Android 上一款功能强大的免费邮件客户端。
   <string name="message_view_cc_label">抄送：</string>
   <string name="message_view_bcc_label">密送:</string>
   <string name="message_view_status_attachment_not_saved">无法保存附件。</string>
-  <string name="message_view_show_pictures_action">显示图片</string>
   <string name="message_view_no_viewer">无法打开<xliff:g id="mimetype">%s</xliff:g>。找不到可以阅读该文件的程序。</string>
   <string name="message_view_download_remainder">下载完整邮件</string>
   <string name="message_view_sender_label">通过  %1$s</string>
@@ -722,10 +721,6 @@ K-9 Mail 是 Android 上一款功能强大的免费邮件客户端。
   <string name="font_size_message_list_preview">预览</string>
   <string name="font_size_message_view">邮件</string>
   <string name="font_size_message_view_sender">发件人</string>
-  <string name="font_size_message_view_to">收件人</string>
-  <string name="font_size_message_view_cc">抄送</string>
-  <string name="font_size_message_view_bcc">秘送</string>
-  <string name="font_size_message_view_additional_headers">额外的邮件信息</string>
   <string name="font_size_message_view_subject">主题</string>
   <string name="font_size_message_view_date">日期</string>
   <string name="font_size_message_view_content">邮件内容</string>
diff --git a/app/ui/legacy/src/main/res/values-zh-rTW/strings.xml b/app/ui/legacy/src/main/res/values-zh-rTW/strings.xml
index 50d3d623c64..4ecf8cf46fe 100644
--- a/app/ui/legacy/src/main/res/values-zh-rTW/strings.xml
+++ b/app/ui/legacy/src/main/res/values-zh-rTW/strings.xml
@@ -222,7 +222,6 @@ K-9 Mail 是 Android 上一款功能強大，免費的電子郵件用戶端。
   <string name="message_view_cc_label">副本：</string>
   <string name="message_view_bcc_label">密件副本：</string>
   <string name="message_view_status_attachment_not_saved">無法存檔附件。</string>
-  <string name="message_view_show_pictures_action">顯示圖片</string>
   <string name="message_view_no_viewer">無法開啟<xliff:g id="mimetype">%s</xliff:g>。找不到可以開啟該附件的程式。</string>
   <string name="message_view_download_remainder">下載完整郵件</string>
   <string name="message_view_sender_label">透過 %1$s</string>
@@ -722,10 +721,6 @@ K-9 Mail 是 Android 上一款功能強大，免費的電子郵件用戶端。
   <string name="font_size_message_list_preview">預覽</string>
   <string name="font_size_message_view">郵件</string>
   <string name="font_size_message_view_sender">寄件人</string>
-  <string name="font_size_message_view_to">收件人</string>
-  <string name="font_size_message_view_cc">副本</string>
-  <string name="font_size_message_view_bcc">密件副本</string>
-  <string name="font_size_message_view_additional_headers">額外的郵件訊息</string>
   <string name="font_size_message_view_subject">主題</string>
   <string name="font_size_message_view_date">日期</string>
   <string name="font_size_message_view_content">郵件本文內容</string>
diff --git a/app/ui/legacy/src/main/res/values/attrs.xml b/app/ui/legacy/src/main/res/values/attrs.xml
index c17bff604ef..001cd401c3f 100644
--- a/app/ui/legacy/src/main/res/values/attrs.xml
+++ b/app/ui/legacy/src/main/res/values/attrs.xml
@@ -3,6 +3,8 @@
 
     <declare-styleable name="K9Styles">
         <attr name="toolbarColor" format="reference|color" />
+        <attr name="messageHeaderBackground" format="reference|color" />
+        <attr name="extraMessageHeaderBackground" format="reference|color" />
         <attr name="bottomBarBackground" format="reference|color" />
         <attr name="floatingActionButtonBackgroundColor" format="reference|color" />
         <attr name="floatingActionButtonForegroundColor" format="reference|color" />
@@ -107,7 +109,8 @@
         <attr name="messageStar" format="reference"/>
         <attr name="messageStarColor" format="color"/>
         <attr name="messageViewBackgroundColor" format="reference|color"/>
-        <attr name="messageComposeAddContactImage" format="reference"/>
+        <attr name="messageDetailsAddContactIcon" format="reference"/>
+        <attr name="messageDetailsDividerColor" format="reference|color"/>
         <attr name="attachmentCardBackground" format="reference|color"/>
         <attr name="composerBackgroundColor" format="color"/>
         <attr name="contactPictureFallbackDefaultBackgroundColor" format="reference|color"/>
diff --git a/app/ui/legacy/src/main/res/values/message_details_ids.xml b/app/ui/legacy/src/main/res/values/message_details_ids.xml
new file mode 100644
index 00000000000..aab6b828f6b
--- /dev/null
+++ b/app/ui/legacy/src/main/res/values/message_details_ids.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <item type="id" name="message_details_date"/>
+    <item type="id" name="message_details_crypto_status"/>
+    <item type="id" name="message_details_section_header"/>
+    <item type="id" name="message_details_participant"/>
+    <item type="id" name="message_details_divider"/>
+</resources>
diff --git a/app/ui/legacy/src/main/res/values/strings.xml b/app/ui/legacy/src/main/res/values/strings.xml
index 9742d7a9dad..5b3476a1f67 100644
--- a/app/ui/legacy/src/main/res/values/strings.xml
+++ b/app/ui/legacy/src/main/res/values/strings.xml
@@ -264,7 +264,17 @@ Please submit bug reports, contribute new features and ask questions at
     <string name="message_view_cc_label">Cc:</string>
     <string name="message_view_bcc_label">Bcc:</string>
     <string name="message_view_status_attachment_not_saved">Unable to save attachment.</string>
-    <string name="message_view_show_pictures_action">Show pictures</string>
+
+    <!-- Displayed in the message view screen in front of the recipient list. For most languages this should end with a space. -->
+    <string name="message_view_recipient_prefix">"to "</string>
+    <!-- Displayed in the message view screen in front of the number of additional recipients (the ones that are not displayed). -->
+    <string name="message_view_additional_recipient_prefix">+</string>
+    <!-- Displayed in the message view screen if the message was addressed to the user. Make sure this works with 'message_view_recipient_prefix'. In English it's "to me". -->
+    <string name="message_view_me_text">me</string>
+
+    <!-- Text of the button that is displayed below the message header when the message body references one or more remote images -->
+    <string name="message_view_show_remote_images_action">Show remote images</string>
+
     <string name="message_view_no_viewer">Unable to find viewer for <xliff:g id="mimetype">%s</xliff:g>.</string>
     <string name="message_view_download_remainder">Download complete message</string>
     <string name="message_view_sender_label">via %1$s</string>
@@ -868,10 +878,7 @@ Please submit bug reports, contribute new features and ask questions at
 
     <string name="font_size_message_view">Messages</string>
     <string name="font_size_message_view_sender">Sender</string>
-    <string name="font_size_message_view_to">To</string>
-    <string name="font_size_message_view_cc">Cc</string>
-    <string name="font_size_message_view_bcc">Bcc</string>
-    <string name="font_size_message_view_additional_headers">Additional headers</string>
+    <string name="font_size_message_view_recipients">Recipients</string>
     <string name="font_size_message_view_subject">Subject</string>
     <string name="font_size_message_view_date">Time and date</string>
     <string name="font_size_message_view_content">Message body</string>
@@ -1300,4 +1307,36 @@ You can keep this message and use it as a backup for your secret key. If you wan
     <string name="message_list_error_title">Error</string>
     <!-- Displayed instead of the message list when a folder couldn't be found, e.g. due to an outdated home screen shortcut. -->
     <string name="message_list_error_folder_not_found">Folder not found</string>
+
+    <!-- Title of the toolbar that is displayed when the message details bottom sheet has been expanded -->
+    <string name="message_details_toolbar_title">Message details</string>
+    <!-- Displayed when an email is missing the 'Date' header field and no date can be displayed in the message details bottom sheet. -->
+    <string name="message_details_missing_date">Missing \'Date\' header</string>
+    <!-- Title of the "From" section in the message details bottom sheet. This should probably be the same as the string 'recipient_from'. -->
+    <string name="message_details_from_section_title">From</string>
+    <!-- Title of the "Sender" section in the message details bottom sheet. This refers to the 'Sender' header. Be careful with translations. -->
+    <string name="message_details_sender_section_title">Sender</string>
+    <!-- Title of the "Reply to" section in the message details bottom sheet. This should probably be the same as the string 'reply_to_label'. -->
+    <string name="message_details_replyto_section_title">Reply to</string>
+    <!-- Title of the "To" section in the message details bottom sheet. This should probably be the same as the string 'recipient_to'. -->
+    <string name="message_details_to_section_title">To</string>
+    <!-- Title of the "Cc" section in the message details bottom sheet. This should probably be the same as the string 'recipient_cc'. -->
+    <string name="message_details_cc_section_title">Cc</string>
+    <!-- Title of the "Bcc" section in the message details bottom sheet. This should probably be the same as the string 'recipient_bcc'. -->
+    <string name="message_details_bcc_section_title">Bcc</string>
+
+    <!-- Displayed when loading the message details has failed -->
+    <string name="message_details_loading_error">An error occurred while loading the message details.</string>
+
+    <!-- Name of the action to add a person to contacts. Usually displayed in a menu or in a tooltip when long-pressing the associated icon. -->
+    <string name="action_add_to_contacts">Add to contacts</string>
+    <!-- Name of the action to copy an email address to the clipboard. Usually displayed in a menu or in a tooltip when long-pressing the associated icon. -->
+    <string name="action_copy_email_address">Copy email address</string>
+    <!-- Name of the action to copy a name and email address to the clipboard. Usually displayed in a menu or in a tooltip when long-pressing the associated icon. -->
+    <string name="action_copy_name_and_email_address">Copy name and email address</string>
+
+    <!-- A user visible label for the email address copied to the clipboard -->
+    <string name="clipboard_label_email_address">Email address</string>
+    <!-- A user visible label for the name and email address copied to the clipboard -->
+    <string name="clipboard_label_name_and_email_address">Name and email address</string>
 </resources>
diff --git a/app/ui/legacy/src/main/res/values/themes.xml b/app/ui/legacy/src/main/res/values/themes.xml
index 55c4b43a111..2a64b98cc8a 100644
--- a/app/ui/legacy/src/main/res/values/themes.xml
+++ b/app/ui/legacy/src/main/res/values/themes.xml
@@ -21,6 +21,8 @@
         <item name="colorSecondary">@color/material_pink_500</item>
         <item name="colorSecondaryVariant">@color/material_pink_300</item>
         <item name="colorOnSecondary">#ffffff</item>
+        <item name="messageHeaderBackground">@color/material_gray_200</item>
+        <item name="extraMessageHeaderBackground">@color/material_gray_100</item>
         <item name="bottomBarBackground">@color/material_gray_50</item>
         <item name="floatingActionButtonBackgroundColor">?attr/colorPrimary</item>
         <item name="floatingActionButtonForegroundColor">?attr/colorOnPrimary</item>
@@ -131,7 +133,8 @@
         <item name="messageStar">@drawable/btn_check_star</item>
         <item name="messageStarColor">#fbbc04</item>
         <item name="messageViewBackgroundColor">#ffffffff</item>
-        <item name="messageComposeAddContactImage">@drawable/ic_person_plus</item>
+        <item name="messageDetailsAddContactIcon">@drawable/ic_person_add</item>
+        <item name="messageDetailsDividerColor">#ffcccccc</item>
         <item name="attachmentCardBackground">#e8e8e8</item>
         <item name="contactPictureFallbackDefaultBackgroundColor">#ffababab</item>
         <item name="contactPictureFallbackBackgroundColors">@array/contact_picture_fallback_background_colors_light</item>
@@ -181,6 +184,8 @@
         <item name="colorPrimaryVariant">@color/material_gray_50</item>
         <item name="colorSecondary">@color/material_pink_300</item>
         <item name="colorSecondaryVariant">@color/material_pink_500</item>
+        <item name="messageHeaderBackground">@color/material_gray_900</item>
+        <item name="extraMessageHeaderBackground">@color/material_gray_900</item>
         <item name="bottomBarBackground">@color/material_gray_900</item>
         <item name="floatingActionButtonBackgroundColor">?attr/colorPrimary</item>
         <item name="floatingActionButtonForegroundColor">?attr/colorOnPrimary</item>
@@ -291,7 +296,8 @@
         <item name="messageStar">@drawable/btn_check_star</item>
         <item name="messageStarColor">#fdd663</item>
         <item name="messageViewBackgroundColor">#000000</item>
-        <item name="messageComposeAddContactImage">@drawable/ic_person_plus</item>
+        <item name="messageDetailsAddContactIcon">@drawable/ic_person_add</item>
+        <item name="messageDetailsDividerColor">#ff555555</item>
         <item name="attachmentCardBackground">#313131</item>
         <item name="contactTokenBackgroundColor">#313131</item>
         <item name="contactPictureFallbackDefaultBackgroundColor">#ff606060</item>
diff --git a/app/ui/legacy/src/main/res/xml/general_settings.xml b/app/ui/legacy/src/main/res/xml/general_settings.xml
index 958130a0a79..a197ce2373b 100644
--- a/app/ui/legacy/src/main/res/xml/general_settings.xml
+++ b/app/ui/legacy/src/main/res/xml/general_settings.xml
@@ -105,44 +105,20 @@
                     android:key="message_view_fonts">
 
                     <ListPreference
-                        android:key="message_view_sender_font"
-                        android:title="@string/font_size_message_view_sender"
-                        app:useSimpleSummaryProvider="true"
-                        android:entries="@array/font_entries"
-                        android:entryValues="@array/font_values"
-                        android:dialogTitle="@string/font_size_message_view_sender" />
-
-                    <ListPreference
-                        android:key="message_view_to_font"
-                        android:title="@string/font_size_message_view_to"
-                        app:useSimpleSummaryProvider="true"
-                        android:entries="@array/font_entries"
-                        android:entryValues="@array/font_values"
-                        android:dialogTitle="@string/font_size_message_view_to" />
-
-                    <ListPreference
-                        android:key="message_view_cc_font"
-                        android:title="@string/font_size_message_view_cc"
-                        app:useSimpleSummaryProvider="true"
-                        android:entries="@array/font_entries"
-                        android:entryValues="@array/font_values"
-                        android:dialogTitle="@string/font_size_message_view_cc" />
-
-                    <ListPreference
-                        android:key="message_view_bcc_font"
-                        android:title="@string/font_size_message_view_bcc"
+                        android:key="message_view_subject_font"
+                        android:title="@string/font_size_message_view_subject"
                         app:useSimpleSummaryProvider="true"
                         android:entries="@array/font_entries"
                         android:entryValues="@array/font_values"
-                        android:dialogTitle="@string/font_size_message_view_bcc" />
+                        android:dialogTitle="@string/font_size_message_view_subject" />
 
                     <ListPreference
-                        android:key="message_view_subject_font"
-                        android:title="@string/font_size_message_view_subject"
+                        android:key="message_view_sender_font"
+                        android:title="@string/font_size_message_view_sender"
                         app:useSimpleSummaryProvider="true"
                         android:entries="@array/font_entries"
                         android:entryValues="@array/font_values"
-                        android:dialogTitle="@string/font_size_message_view_subject" />
+                        android:dialogTitle="@string/font_size_message_view_sender" />
 
                     <ListPreference
                         android:key="message_view_date_font"
@@ -153,12 +129,12 @@
                         android:dialogTitle="@string/font_size_message_view_date" />
 
                     <ListPreference
-                        android:key="message_view_additional_headers_font"
-                        android:title="@string/font_size_message_view_additional_headers"
+                        android:key="message_view_recipients_font"
+                        android:title="@string/font_size_message_view_recipients"
                         app:useSimpleSummaryProvider="true"
                         android:entries="@array/font_entries"
                         android:entryValues="@array/font_values"
-                        android:dialogTitle="@string/font_size_message_view_additional_headers" />
+                        android:dialogTitle="@string/font_size_message_view_recipients" />
 
                     <SeekBarPreference
                         android:key="message_view_content_font_slider"
diff --git a/backend/demo/src/main/resources/contents.json b/backend/demo/src/main/resources/contents.json
index 87879b8cc2f..03a7c033a17 100644
--- a/backend/demo/src/main/resources/contents.json
+++ b/backend/demo/src/main/resources/contents.json
@@ -2,7 +2,7 @@
   "inbox": {
     "name": "Inbox",
     "type": "INBOX",
-    "messageServerIds": ["intro"]
+    "messageServerIds": ["intro", "many_recipients"]
   },
   "trash": {
     "name": "Trash",
diff --git a/backend/demo/src/main/resources/inbox/many_recipients.eml b/backend/demo/src/main/resources/inbox/many_recipients.eml
new file mode 100644
index 00000000000..a0bd8317d89
--- /dev/null
+++ b/backend/demo/src/main/resources/inbox/many_recipients.eml
@@ -0,0 +1,42 @@
+MIME-Version: 1.0
+From: "Alice" <from1@k9mail.example>, "Bob" <from2@k9mail.example>
+Sender: "Bernd" <sender@k9mail.example>
+Reply-To: <reply-to@k9mail.example>
+Date: Mon, 23 Jan 2023 12:00:00 +0100
+Message-ID: <inbox-2@k9mail.example>
+Subject: Message details demo
+To: "User 1" <to1@k9mail.example>,
+ "User 2" <to2@k9mail.example>,
+ "User 3" <to3@k9mail.example>,
+ "User 4" <to4@k9mail.example>,
+ "User 5" <to5@k9mail.example>,
+ "User 6" <to6@k9mail.example>,
+ "User 7" <to7@k9mail.example>,
+ "User 8" <to8@k9mail.example>,
+ "User 9" <to9@k9mail.example>,
+ "User 10" <to10@k9mail.example>,
+ "User 11" <to11@k9mail.example>,
+ "User 12" <to12@k9mail.example>,
+ "User 13" <to13@k9mail.example>,
+ "User 14" <to14@k9mail.example>,
+ "User 15" <to15@k9mail.example>,
+ "User 16" <to16@k9mail.example>,
+ "User 17" <to17@k9mail.example>,
+ "User 18" <to18@k10mail.example>,
+ "User 19" <to19@k11mail.example>,
+ "User 20" <to20@k12mail.example>
+Cc: "Copy 1" <cc1@k9mail.example>,
+ "Copy 2" <cc2@k9mail.example>,
+ "Copy 3" <cc3@k9mail.example>
+Bcc: "Blind 1" <bcc1@k9mail.example>,
+ "Blind 2" <bcc2@k9mail.example>,
+ "Blind 3" <bcc3@k9mail.example>
+Content-Type: text/plain; charset=UTF-8
+
+This message contains…
+- multiple addresses in the From: header
+- a Sender: header
+- a Reply-To: header
+- multiple addresses in the To: header
+- multiple addresses in the Cc: header
+- multiple addresses in the Bcc: header
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 58a888ddd35..a74271c2ef2 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
@@ -538,7 +538,7 @@ internal class RealImapFolder(
             fetchFields.add("RFC822.SIZE")
             fetchFields.add(
                 "BODY.PEEK[HEADER.FIELDS (date subject from content-type to cc " +
-                    "reply-to message-id references in-reply-to list-unsubscribe " +
+                    "reply-to message-id references in-reply-to list-unsubscribe sender " +
                     K9MailLib.IDENTITY_HEADER + " " + K9MailLib.CHAT_HEADER + ")]"
             )
         }
diff --git a/settings.gradle b/settings.gradle
index 12697df787c..f8ae7c284a4 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -31,6 +31,7 @@ include ':app:autodiscovery:thunderbird'
 include ':app:html-cleaner'
 include ':ui-utils:LinearLayoutManager'
 include ':ui-utils:ItemTouchHelper'
+include ':ui-utils:ToolbarBottomSheet'
 include ':mail:common'
 include ':mail:testing'
 include ':mail:protocols:imap'
diff --git a/ui-utils/ToolbarBottomSheet/build.gradle b/ui-utils/ToolbarBottomSheet/build.gradle
new file mode 100644
index 00000000000..370922cd798
--- /dev/null
+++ b/ui-utils/ToolbarBottomSheet/build.gradle
@@ -0,0 +1,10 @@
+apply plugin: 'com.android.library'
+apply plugin: 'org.jetbrains.kotlin.android'
+
+dependencies {
+    api libs.android.material
+}
+
+android {
+    namespace 'app.k9mail.ui.utils.bottomsheet'
+}
diff --git a/ui-utils/ToolbarBottomSheet/src/main/java/app/k9mail/ui/utils/bottomsheet/LayoutAwareBottomSheetBehavior.kt b/ui-utils/ToolbarBottomSheet/src/main/java/app/k9mail/ui/utils/bottomsheet/LayoutAwareBottomSheetBehavior.kt
new file mode 100644
index 00000000000..5928e4c51a6
--- /dev/null
+++ b/ui-utils/ToolbarBottomSheet/src/main/java/app/k9mail/ui/utils/bottomsheet/LayoutAwareBottomSheetBehavior.kt
@@ -0,0 +1,52 @@
+package app.k9mail.ui.utils.bottomsheet
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import androidx.coordinatorlayout.widget.CoordinatorLayout
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
+
+/**
+ * Work around the fact that [BottomSheetCallback.onLayout] is not public.
+ */
+class LayoutAwareBottomSheetBehavior<V : View>(context: Context, attrs: AttributeSet?) :
+    BottomSheetBehavior<V>(context, attrs) {
+
+    private val callbacks = mutableSetOf<LayoutAwareBottomSheetCallback>()
+
+    internal fun addBottomSheetCallback(callback: LayoutAwareBottomSheetCallback) {
+        callbacks.add(callback)
+        super.addBottomSheetCallback(callback)
+    }
+
+    internal fun removeBottomSheetCallback(callback: LayoutAwareBottomSheetCallback) {
+        super.removeBottomSheetCallback(callback)
+        callbacks.remove(callback)
+    }
+
+    override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean {
+        val layoutResult = super.onLayoutChild(parent, child, layoutDirection)
+
+        for (callback in callbacks) {
+            callback.onBottomSheetLayout(child)
+        }
+
+        return layoutResult
+    }
+
+    companion object {
+        fun <V : View> from(view: V): LayoutAwareBottomSheetBehavior<V> {
+            val params = view.layoutParams
+            require(params is CoordinatorLayout.LayoutParams) { "The view is not a child of CoordinatorLayout" }
+
+            val behavior = params.behavior
+            require(behavior is LayoutAwareBottomSheetBehavior<*>) {
+                "The view is not associated with ToolbarBottomSheetBehavior"
+            }
+
+            @Suppress("UNCHECKED_CAST")
+            return behavior as LayoutAwareBottomSheetBehavior<V>
+        }
+    }
+}
diff --git a/ui-utils/ToolbarBottomSheet/src/main/java/app/k9mail/ui/utils/bottomsheet/LayoutAwareBottomSheetCallback.kt b/ui-utils/ToolbarBottomSheet/src/main/java/app/k9mail/ui/utils/bottomsheet/LayoutAwareBottomSheetCallback.kt
new file mode 100644
index 00000000000..6b50e57f207
--- /dev/null
+++ b/ui-utils/ToolbarBottomSheet/src/main/java/app/k9mail/ui/utils/bottomsheet/LayoutAwareBottomSheetCallback.kt
@@ -0,0 +1,13 @@
+package app.k9mail.ui.utils.bottomsheet
+
+import android.view.View
+import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
+
+/**
+ * Extends [BottomSheetCallback] so we can notify listeners of layout events.
+ *
+ * See [LayoutAwareBottomSheetBehavior].
+ */
+internal abstract class LayoutAwareBottomSheetCallback : BottomSheetCallback() {
+    abstract fun onBottomSheetLayout(bottomSheet: View)
+}
diff --git a/ui-utils/ToolbarBottomSheet/src/main/java/app/k9mail/ui/utils/bottomsheet/ToolbarBottomSheetDialog.kt b/ui-utils/ToolbarBottomSheet/src/main/java/app/k9mail/ui/utils/bottomsheet/ToolbarBottomSheetDialog.kt
new file mode 100644
index 00000000000..159f74ce477
--- /dev/null
+++ b/ui-utils/ToolbarBottomSheet/src/main/java/app/k9mail/ui/utils/bottomsheet/ToolbarBottomSheetDialog.kt
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2023 K-9 Mail contributors
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package app.k9mail.ui.utils.bottomsheet
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.os.Build.VERSION
+import android.os.Build.VERSION_CODES
+import android.os.Bundle
+import android.util.TypedValue
+import android.view.View
+import android.view.ViewGroup
+import android.view.Window
+import android.view.WindowManager
+import android.widget.FrameLayout
+import androidx.annotation.LayoutRes
+import androidx.annotation.StyleRes
+import androidx.appcompat.app.AppCompatDialog
+import androidx.appcompat.widget.Toolbar
+import androidx.coordinatorlayout.widget.CoordinatorLayout
+import androidx.core.view.AccessibilityDelegateCompat
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.core.view.WindowInsetsControllerCompat
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
+import androidx.core.view.doOnLayout
+import androidx.core.view.isInvisible
+import androidx.core.view.isVisible
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
+import com.google.android.material.color.MaterialColors
+import com.google.android.material.shape.MaterialShapeDrawable
+
+/**
+ * Modal bottom sheet that displays a toolbar when it is fully expanded. Only meant to be used with
+ * [ToolbarBottomSheetDialogFragment].
+ *
+ * Based on [com.google.android.material.bottomsheet.BottomSheetDialog].
+ */
+class ToolbarBottomSheetDialog internal constructor(context: Context, @StyleRes theme: Int) :
+    AppCompatDialog(context, getThemeResId(context, theme)) {
+
+    private var internalBehavior: LayoutAwareBottomSheetBehavior<FrameLayout>? = null
+    val behavior: LayoutAwareBottomSheetBehavior<*>
+        get() {
+            if (internalBehavior == null) {
+                ensureContainerAndBehavior()
+            }
+
+            return internalBehavior ?: error("Missing ToolbarBottomSheetBehavior")
+        }
+
+    private var container: FrameLayout? = null
+    private var coordinator: CoordinatorLayout? = null
+    private var bottomSheet: FrameLayout? = null
+
+    var toolbar: Toolbar? = null
+        private set
+
+    /**
+     * `true` if dismissing will perform the swipe down animation on the bottom sheet, rather than the window animation
+     * for the dialog.
+     */
+    var isDismissWithAnimation = false
+
+    private var toolbarVisibilityCallback: LayoutAwareBottomSheetCallback? = null
+
+    init {
+        // We hide the title bar for any style configuration. Otherwise, there will be a gap above the bottom sheet
+        // when it is expanded.
+        supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setCancelable(true)
+
+        val window = window
+        if (window != null) {
+            // The status bar should always be transparent because of the window animation.
+            window.statusBarColor = Color.TRANSPARENT
+            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
+
+            if (VERSION.SDK_INT < VERSION_CODES.M) {
+                // It can be transparent for API 23 and above because we will handle switching the status
+                // bar icons to light or dark as appropriate. For API 21 and API 22 we just set the
+                // translucent status bar.
+                window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
+            }
+
+            window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
+        }
+    }
+
+    override fun setContentView(@LayoutRes layoutResID: Int) {
+        error("Not supported")
+    }
+
+    override fun setContentView(view: View) {
+        super.setContentView(wrapInBottomSheet(view))
+    }
+
+    override fun setContentView(view: View, params: ViewGroup.LayoutParams?) {
+        error("Not supported")
+    }
+
+    override fun onStart() {
+        super.onStart()
+
+        internalBehavior?.let { behavior ->
+            if (behavior.state == BottomSheetBehavior.STATE_HIDDEN) {
+                behavior.state = BottomSheetBehavior.STATE_COLLAPSED
+            }
+        }
+    }
+
+    override fun onAttachedToWindow() {
+        super.onAttachedToWindow()
+
+        val window = window
+        if (window != null) {
+            container?.fitsSystemWindows = false
+            coordinator?.fitsSystemWindows = false
+
+            val flags = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+            window.decorView.systemUiVisibility = flags
+        }
+    }
+
+    /**
+     * This function can be called from a few different use cases, including Swiping the dialog down or calling
+     * `dismiss()` from a `BottomSheetDialogFragment`, tapping outside a dialog, etc…
+     *
+     * The default animation to dismiss this dialog is a fade-out transition through a
+     * windowAnimation. Call `setDismissWithAnimation(true)` if you want to utilize the BottomSheet animation
+     * instead.
+     *
+     * If this function is called from a swipe down interaction, or dismissWithAnimation is false,
+     * then keep the default behavior.
+     *
+     * Else, since this is a terminal event which will finish this dialog, we override the attached
+     * [BottomSheetCallback] to call this function, after [BottomSheetBehavior.STATE_HIDDEN] is set. This
+     * will enforce the swipe down animation before canceling this dialog.
+     */
+    override fun cancel() {
+        val behavior = this.behavior
+
+        if (!isDismissWithAnimation || behavior.state == BottomSheetBehavior.STATE_HIDDEN) {
+            super.cancel()
+        } else {
+            behavior.setState(BottomSheetBehavior.STATE_HIDDEN)
+        }
+    }
+
+    private fun ensureContainerAndBehavior(): FrameLayout? {
+        if (container == null) {
+            val container = View.inflate(context, R.layout.design_bottom_sheet_dialog, null) as FrameLayout
+            this.container = container
+
+            coordinator = container.findViewById(R.id.coordinator) ?: error("View not found")
+            val bottomSheet = container.findViewById<FrameLayout>(R.id.design_bottom_sheet) ?: error("View not found")
+            this.bottomSheet = bottomSheet
+
+            toolbar = bottomSheet.findViewById(R.id.toolbar)
+
+            val behavior = LayoutAwareBottomSheetBehavior.from(bottomSheet).apply {
+                addBottomSheetCallback(cancelDialogCallback)
+                isHideable = true
+            }
+            this.internalBehavior = behavior
+
+            container.doOnLayout {
+                behavior.peekHeight = container.height / 2
+            }
+
+            bottomSheet.doOnLayout {
+                // Don't draw the toolbar underneath the status bar if the bottom sheet doesn't cover the whole screen
+                // anyway.
+                if (bottomSheet.width < container.width) {
+                    container.fitsSystemWindows = true
+                    coordinator?.fitsSystemWindows = true
+                    setToolbarVisibilityCallback(topInset = 0)
+                } else {
+                    container.fitsSystemWindows = false
+                    coordinator?.fitsSystemWindows = false
+                }
+            }
+        }
+
+        return container
+    }
+
+    @SuppressLint("ClickableViewAccessibility")
+    private fun wrapInBottomSheet(view: View): View {
+        ensureContainerAndBehavior()
+
+        val container = checkNotNull(container)
+        val coordinator = checkNotNull(coordinator)
+        val bottomSheet = checkNotNull(bottomSheet)
+
+        ViewCompat.setOnApplyWindowInsetsListener(bottomSheet) { _, windowInsets ->
+            val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
+            val topInset = insets.top
+
+            setToolbarVisibilityCallback(topInset)
+
+            WindowInsetsCompat.CONSUMED
+        }
+
+        while (bottomSheet.childCount > NUMBER_OF_OWN_BOTTOM_SHEET_CHILDREN) {
+            bottomSheet.removeViewAt(0)
+        }
+
+        bottomSheet.addView(view, 0)
+
+        // We treat the CoordinatorLayout as outside the dialog though it is technically inside
+        coordinator.findViewById<View>(R.id.touch_outside).setOnClickListener {
+            if (isShowing) {
+                cancel()
+            }
+        }
+
+        // Handle accessibility events
+        ViewCompat.setAccessibilityDelegate(
+            bottomSheet,
+            object : AccessibilityDelegateCompat() {
+                override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
+                    super.onInitializeAccessibilityNodeInfo(host, info)
+
+                    info.addAction(AccessibilityNodeInfoCompat.ACTION_DISMISS)
+                    info.isDismissable = true
+                }
+
+                override fun performAccessibilityAction(host: View, action: Int, args: Bundle?): Boolean {
+                    if (action == AccessibilityNodeInfoCompat.ACTION_DISMISS) {
+                        cancel()
+                        return true
+                    }
+
+                    return super.performAccessibilityAction(host, action, args)
+                }
+            }
+        )
+
+        bottomSheet.setOnTouchListener { _, _ ->
+            // Consume the event and prevent it from falling through
+            true
+        }
+
+        return container
+    }
+
+    private fun setToolbarVisibilityCallback(topInset: Int) {
+        val window = checkNotNull(window)
+        val bottomSheet = checkNotNull(bottomSheet)
+        val toolbar = checkNotNull(toolbar)
+        val behavior = checkNotNull(internalBehavior)
+
+        toolbarVisibilityCallback?.let { oldCallback ->
+            behavior.removeBottomSheetCallback(oldCallback)
+        }
+
+        val windowInsetsController = WindowCompat.getInsetsController(window, bottomSheet)
+        val newCallback = ToolbarVisibilityCallback(windowInsetsController, toolbar, topInset, behavior)
+        behavior.addBottomSheetCallback(newCallback)
+
+        this.toolbarVisibilityCallback = newCallback
+    }
+
+    fun removeDefaultCallback() {
+        checkNotNull(internalBehavior).removeBottomSheetCallback(cancelDialogCallback)
+    }
+
+    private val cancelDialogCallback: BottomSheetCallback = object : BottomSheetCallback() {
+        override fun onStateChanged(bottomSheet: View, @BottomSheetBehavior.State newState: Int) {
+            if (newState == BottomSheetBehavior.STATE_HIDDEN) {
+                cancel()
+            }
+        }
+
+        override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit
+    }
+
+    private class ToolbarVisibilityCallback(
+        private val windowInsetsController: WindowInsetsControllerCompat,
+        private val toolbar: Toolbar,
+        private val topInset: Int,
+        private val behavior: BottomSheetBehavior<FrameLayout>
+    ) : LayoutAwareBottomSheetCallback() {
+
+        private val lightStatusBar: Boolean = windowInsetsController.isAppearanceLightStatusBars
+        private var lightToolbar = false
+
+        init {
+            // Try to find the background color to automatically change the status bar icons so they will
+            // still be visible when the bottomsheet slides underneath the status bar.
+            val toolbarBackground = toolbar.background
+            val backgroundTint = if (toolbarBackground is MaterialShapeDrawable) {
+                toolbarBackground.fillColor
+            } else {
+                ViewCompat.getBackgroundTintList(toolbar)
+            }
+
+            lightToolbar = if (backgroundTint != null) {
+                // First check for a tint
+                MaterialColors.isColorLight(backgroundTint.defaultColor)
+            } else if (toolbar.background is ColorDrawable) {
+                // Then check for the background color
+                MaterialColors.isColorLight((toolbar.background as ColorDrawable).color)
+            } else {
+                // Otherwise don't change the status bar color
+                lightStatusBar
+            }
+        }
+
+        override fun onStateChanged(bottomSheet: View, newState: Int) {
+            handleToolbarVisibility(bottomSheet)
+        }
+
+        override fun onSlide(bottomSheet: View, slideOffset: Float) {
+            handleToolbarVisibility(bottomSheet)
+        }
+
+        override fun onBottomSheetLayout(bottomSheet: View) {
+            handleToolbarVisibility(bottomSheet)
+        }
+
+        private fun handleToolbarVisibility(bottomSheet: View) {
+            setToolbarState(bottomSheet)
+            setStatusBarColor(bottomSheet)
+        }
+
+        private fun setToolbarState(bottomSheet: View) {
+            val top = bottomSheet.top
+
+            val collapsedOffset = (bottomSheet.parent as View).height / 2
+            if (top >= collapsedOffset || behavior.expandedOffset > 0) {
+                toolbar.isInvisible = true
+                bottomSheet.setPadding(0, 0, 0, 0)
+                return
+            }
+
+            val toolbarHeight = toolbar.height - toolbar.paddingTop
+            val toolbarHeightAndInset = toolbarHeight + topInset
+            val expandedPercentage =
+                ((collapsedOffset - top).toFloat() / (collapsedOffset - behavior.expandedOffset)).coerceAtMost(1f)
+
+            // Add top padding to bottom sheet to make room for the toolbar
+            val paddingTop = (toolbarHeightAndInset * expandedPercentage).toInt().coerceAtLeast(0)
+            bottomSheet.setPadding(0, paddingTop, 0, 0)
+
+            // Start showing the toolbar when the bottom sheet is a toolbar height away from the top of the screen.
+            // This value was chosen rather arbitrarily because it looked nice enough.
+            val toolbarPercentage =
+                ((toolbarHeight - top).toFloat() / (toolbarHeight - behavior.expandedOffset)).coerceAtLeast(0f)
+
+            if (toolbarPercentage > 0) {
+                toolbar.isVisible = true
+
+                // Set the toolbar's top padding so the toolbar covers the bottom sheet's whole top padding
+                val toolbarPaddingTop = (paddingTop - toolbarHeight).coerceAtLeast(0)
+                toolbar.setPadding(0, toolbarPaddingTop, 0, 0)
+
+                // Translate the toolbar view so it is drawn on top of the bottom sheet's top padding
+                toolbar.translationY = -paddingTop.toFloat()
+
+                toolbar.alpha = toolbarPercentage
+            } else {
+                toolbar.isInvisible = true
+            }
+        }
+
+        private fun setStatusBarColor(bottomSheet: View) {
+            val toolbarLightThreshold = topInset / 2
+            if (bottomSheet.top < toolbarLightThreshold) {
+                windowInsetsController.isAppearanceLightStatusBars = lightToolbar
+            } else if (bottomSheet.top != 0) {
+                windowInsetsController.isAppearanceLightStatusBars = lightStatusBar
+            }
+        }
+    }
+
+    companion object {
+        private const val NUMBER_OF_OWN_BOTTOM_SHEET_CHILDREN = 1
+
+        private fun getThemeResId(context: Context, themeId: Int): Int {
+            if (themeId != 0) return themeId
+
+            val outValue = TypedValue()
+
+            val wasAttributeResolved = context.theme.resolveAttribute(
+                com.google.android.material.R.attr.bottomSheetDialogTheme,
+                outValue,
+                true
+            )
+
+            return if (wasAttributeResolved) {
+                outValue.resourceId
+            } else {
+                error("Missing bottomSheetDialogTheme attribute in theme")
+            }
+        }
+    }
+}
diff --git a/ui-utils/ToolbarBottomSheet/src/main/java/app/k9mail/ui/utils/bottomsheet/ToolbarBottomSheetDialogFragment.kt b/ui-utils/ToolbarBottomSheet/src/main/java/app/k9mail/ui/utils/bottomsheet/ToolbarBottomSheetDialogFragment.kt
new file mode 100644
index 00000000000..d218f7f5100
--- /dev/null
+++ b/ui-utils/ToolbarBottomSheet/src/main/java/app/k9mail/ui/utils/bottomsheet/ToolbarBottomSheetDialogFragment.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023 K-9 Mail contributors
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package app.k9mail.ui.utils.bottomsheet
+
+import android.app.Dialog
+import android.os.Bundle
+import android.view.View
+import androidx.appcompat.app.AppCompatDialogFragment
+import androidx.appcompat.widget.Toolbar
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+
+/**
+ * Modal bottom sheet that displays a toolbar when it is fully expanded.
+ *
+ * Based on [com.google.android.material.bottomsheet.BottomSheetDialogFragment].
+ */
+open class ToolbarBottomSheetDialogFragment : AppCompatDialogFragment() {
+    /**
+     * Tracks if we are waiting for a dismissAllowingStateLoss or a regular dismiss once the BottomSheet is hidden and
+     * onStateChanged() is called.
+     */
+    private var waitingForDismissAllowingStateLoss = false
+
+    val toolbar: Toolbar?
+        get() = dialog?.toolbar
+
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        return ToolbarBottomSheetDialog(requireContext(), theme)
+    }
+
+    override fun dismiss() {
+        if (!tryDismissWithAnimation(false)) {
+            super.dismiss()
+        }
+    }
+
+    override fun dismissAllowingStateLoss() {
+        if (!tryDismissWithAnimation(true)) {
+            super.dismissAllowingStateLoss()
+        }
+    }
+
+    override fun getDialog(): ToolbarBottomSheetDialog? {
+        return super.getDialog() as ToolbarBottomSheetDialog?
+    }
+
+    /**
+     * Tries to dismiss the dialog fragment with the bottom sheet animation. Returns true if possible, false otherwise.
+     */
+    private fun tryDismissWithAnimation(allowingStateLoss: Boolean): Boolean {
+        val dialog = dialog
+        if (dialog != null) {
+            val behavior = dialog.behavior
+            if (behavior.isHideable && dialog.isDismissWithAnimation) {
+                dismissWithAnimation(behavior, allowingStateLoss)
+                return true
+            }
+        }
+
+        return false
+    }
+
+    private fun dismissWithAnimation(behavior: BottomSheetBehavior<*>, allowingStateLoss: Boolean) {
+        waitingForDismissAllowingStateLoss = allowingStateLoss
+
+        if (behavior.state == BottomSheetBehavior.STATE_HIDDEN) {
+            dismissAfterAnimation()
+        } else {
+            dialog?.removeDefaultCallback()
+            behavior.addBottomSheetCallback(BottomSheetDismissCallback())
+            behavior.setState(BottomSheetBehavior.STATE_HIDDEN)
+        }
+    }
+
+    private fun dismissAfterAnimation() {
+        if (waitingForDismissAllowingStateLoss) {
+            super.dismissAllowingStateLoss()
+        } else {
+            super.dismiss()
+        }
+    }
+
+    private inner class BottomSheetDismissCallback : BottomSheetBehavior.BottomSheetCallback() {
+        override fun onStateChanged(bottomSheet: View, newState: Int) {
+            if (newState == BottomSheetBehavior.STATE_HIDDEN) {
+                dismissAfterAnimation()
+            }
+        }
+
+        override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit
+    }
+}
diff --git a/ui-utils/ToolbarBottomSheet/src/main/res/layout/design_bottom_sheet_dialog.xml b/ui-utils/ToolbarBottomSheet/src/main/res/layout/design_bottom_sheet_dialog.xml
new file mode 100644
index 00000000000..23da0891fd2
--- /dev/null
+++ b/ui-utils/ToolbarBottomSheet/src/main/res/layout/design_bottom_sheet_dialog.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 K-9 Mail contributors
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fitsSystemWindows="true">
+
+    <androidx.coordinatorlayout.widget.CoordinatorLayout
+        android:id="@+id/coordinator"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:fitsSystemWindows="true">
+
+        <View
+            android:id="@+id/touch_outside"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:focusable="false"
+            android:importantForAccessibility="no"
+            android:soundEffectsEnabled="false"
+            tools:ignore="UnusedAttribute" />
+
+        <FrameLayout
+            android:id="@+id/design_bottom_sheet"
+            style="?attr/bottomSheetStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal|top"
+            android:clipChildren="false"
+            android:clipToPadding="false"
+            app:layout_behavior="app.k9mail.ui.utils.bottomsheet.LayoutAwareBottomSheetBehavior">
+
+            <com.google.android.material.appbar.MaterialToolbar
+                android:id="@+id/toolbar"
+                style="?attr/toolbarStyle"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:visibility="invisible" />
+
+        </FrameLayout>
+
+    </androidx.coordinatorlayout.widget.CoordinatorLayout>
+
+</FrameLayout>


Test patch:
diff --git a/app/core/src/test/java/com/fsck/k9/helper/RealAddressFormatterTest.kt b/app/core/src/test/java/com/fsck/k9/helper/RealAddressFormatterTest.kt
new file mode 100644
index 00000000000..726ee33e6a5
--- /dev/null
+++ b/app/core/src/test/java/com/fsck/k9/helper/RealAddressFormatterTest.kt
@@ -0,0 +1,206 @@
+package com.fsck.k9.helper
+
+import android.graphics.Color
+import android.text.Spannable
+import android.text.style.ForegroundColorSpan
+import androidx.core.text.getSpans
+import com.fsck.k9.Account
+import com.fsck.k9.Identity
+import com.fsck.k9.RobolectricTest
+import com.fsck.k9.mail.Address
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+private const val IDENTITY_ADDRESS = "me@domain.example"
+private const val ME_TEXT = "me"
+
+class RealAddressFormatterTest : RobolectricTest() {
+    private val contactNameProvider = object : ContactNameProvider {
+        override fun getNameForAddress(address: String): String? {
+            return when (address) {
+                "user1@domain.example" -> "Contact One"
+                "spoof@domain.example" -> "contact@important.example"
+                else -> null
+            }
+        }
+    }
+
+    private val account = Account("uuid").apply {
+        identities += Identity(email = IDENTITY_ADDRESS)
+    }
+
+    @Test
+    fun `single identity`() {
+        val addressFormatter = createAddressFormatter()
+
+        val displayName = addressFormatter.getDisplayName(Address(IDENTITY_ADDRESS, "irrelevant"))
+
+        assertThat(displayName).isEqualTo(ME_TEXT)
+    }
+
+    @Test
+    fun `multiple identities`() {
+        val account = Account("uuid").apply {
+            identities += Identity(description = "My identity", email = IDENTITY_ADDRESS)
+            identities += Identity(email = "another.one@domain.example")
+        }
+        val addressFormatter = createAddressFormatter(account)
+
+        val displayName = addressFormatter.getDisplayName(Address(IDENTITY_ADDRESS, "irrelevant"))
+
+        assertThat(displayName).isEqualTo("My identity")
+    }
+
+    @Test
+    fun `identity without a description`() {
+        val account = Account("uuid").apply {
+            identities += Identity(name = "My name", email = IDENTITY_ADDRESS)
+            identities += Identity(email = "another.one@domain.example")
+        }
+        val addressFormatter = createAddressFormatter(account)
+
+        val displayName = addressFormatter.getDisplayName(Address(IDENTITY_ADDRESS, "irrelevant"))
+
+        assertThat(displayName).isEqualTo("My name")
+    }
+
+    @Test
+    fun `identity without a description and name`() {
+        val account = Account("uuid").apply {
+            identities += Identity(email = IDENTITY_ADDRESS)
+            identities += Identity(email = "another.one@domain.example")
+        }
+        val addressFormatter = createAddressFormatter(account)
+
+        val displayName = addressFormatter.getDisplayName(Address(IDENTITY_ADDRESS, "irrelevant"))
+
+        assertThat(displayName).isEqualTo(IDENTITY_ADDRESS)
+    }
+
+    @Test
+    fun `email address without display name`() {
+        val addressFormatter = createAddressFormatter()
+
+        val displayName = addressFormatter.getDisplayName(Address("alice@domain.example"))
+
+        assertThat(displayName).isEqualTo("alice@domain.example")
+    }
+
+    @Test
+    fun `email address with display name`() {
+        val addressFormatter = createAddressFormatter()
+
+        val displayName = addressFormatter.getDisplayName(Address("alice@domain.example", "Alice"))
+
+        assertThat(displayName).isEqualTo("Alice")
+    }
+
+    @Test
+    fun `don't look up contact when showContactNames = false`() {
+        val addressFormatter = createAddressFormatter(showContactNames = false)
+
+        val displayName = addressFormatter.getDisplayName(Address("user1@domain.example", "User 1"))
+
+        assertThat(displayName).isEqualTo("User 1")
+    }
+
+    @Test
+    fun `contact lookup`() {
+        val addressFormatter = createAddressFormatter()
+
+        val displayName = addressFormatter.getDisplayName(Address("user1@domain.example"))
+
+        assertThat(displayName).isEqualTo("Contact One")
+    }
+
+    @Test
+    fun `contact lookup despite display name`() {
+        val addressFormatter = createAddressFormatter()
+
+        val displayName = addressFormatter.getDisplayName(Address("user1@domain.example", "User 1"))
+
+        assertThat(displayName).isEqualTo("Contact One")
+    }
+
+    @Test
+    fun `colored contact name`() {
+        val addressFormatter = createAddressFormatter(contactNameColor = Color.RED)
+
+        val displayName = addressFormatter.getDisplayName(Address("user1@domain.example"))
+
+        assertThat(displayName.toString()).isEqualTo("Contact One")
+        assertThat(displayName).isInstanceOf(Spannable::class.java)
+        val spans = (displayName as Spannable).getSpans<ForegroundColorSpan>(0, displayName.length)
+        assertThat(spans.map { it.foregroundColor }).containsExactly(Color.RED)
+    }
+
+    @Test
+    fun `email address with display name but not showing correspondent names`() {
+        val addressFormatter = createAddressFormatter(showCorrespondentNames = false)
+
+        val displayName = addressFormatter.getDisplayName(Address("alice@domain.example", "Alice"))
+
+        assertThat(displayName).isEqualTo("alice@domain.example")
+    }
+
+    @Test
+    fun `do not show display name that looks like an email address`() {
+        val addressFormatter = createAddressFormatter()
+
+        val displayName = addressFormatter.getDisplayName(Address("mallory@domain.example", "potus@whitehouse.gov"))
+
+        assertThat(displayName).isEqualTo("mallory@domain.example")
+    }
+
+    @Test
+    fun `do show display name that contains an @ preceded by an opening parenthesis`() {
+        val addressFormatter = createAddressFormatter()
+
+        val displayName = addressFormatter.getDisplayName(Address("gitlab@gitlab.example", "username (@username)"))
+
+        assertThat(displayName).isEqualTo("username (@username)")
+    }
+
+    @Test
+    fun `do show display name that starts with an @`() {
+        val addressFormatter = createAddressFormatter()
+
+        val displayName = addressFormatter.getDisplayName(Address("address@domain.example", "@username"))
+
+        assertThat(displayName).isEqualTo("@username")
+    }
+
+    @Test
+    fun `spoof prevention doesn't apply to contact names`() {
+        val addressFormatter = createAddressFormatter()
+
+        val displayName = addressFormatter.getDisplayName(Address("spoof@domain.example", "contact@important.example"))
+
+        assertThat(displayName).isEqualTo("contact@important.example")
+    }
+
+    @Test
+    fun `display name matches me text`() {
+        val addressFormatter = createAddressFormatter()
+
+        val displayName = addressFormatter.getDisplayName(Address("someone_named_me@domain.example", "ME"))
+
+        assertThat(displayName).isEqualTo("someone_named_me@domain.example")
+    }
+
+    private fun createAddressFormatter(
+        account: Account = this.account,
+        showCorrespondentNames: Boolean = true,
+        showContactNames: Boolean = true,
+        contactNameColor: Int? = null
+    ): RealAddressFormatter {
+        return RealAddressFormatter(
+            contactNameProvider = contactNameProvider,
+            account = account,
+            showCorrespondentNames = showCorrespondentNames,
+            showContactNames = showContactNames,
+            contactNameColor = contactNameColor,
+            meText = ME_TEXT
+        )
+    }
+}
diff --git a/app/core/src/test/java/com/fsck/k9/message/ReplyActionStrategyTest.kt b/app/core/src/test/java/com/fsck/k9/message/ReplyActionStrategyTest.kt
new file mode 100644
index 00000000000..e366859072e
--- /dev/null
+++ b/app/core/src/test/java/com/fsck/k9/message/ReplyActionStrategyTest.kt
@@ -0,0 +1,111 @@
+package com.fsck.k9.message
+
+import com.fsck.k9.Account
+import com.fsck.k9.Identity
+import com.fsck.k9.helper.ReplyToParser
+import com.fsck.k9.mail.buildMessage
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+private const val IDENTITY_EMAIL_ADDRESS = "myself@domain.example"
+
+class ReplyActionStrategyTest {
+    private val account = createAccount()
+    private val replyActionStrategy = ReplyActionStrategy(ReplyToParser())
+
+    @Test
+    fun `message sent to only our identity`() {
+        val message = buildMessage {
+            header("From", "sender@domain.example")
+            header("To", IDENTITY_EMAIL_ADDRESS)
+        }
+
+        val replyActions = replyActionStrategy.getReplyActions(account, message)
+
+        assertThat(replyActions.defaultAction).isEqualTo(ReplyAction.REPLY)
+        assertThat(replyActions.additionalActions).isEmpty()
+    }
+
+    @Test
+    fun `message sent to our identity and others`() {
+        val message = buildMessage {
+            header("From", "sender@domain.example")
+            header("To", "$IDENTITY_EMAIL_ADDRESS, other@domain.example")
+        }
+
+        val replyActions = replyActionStrategy.getReplyActions(account, message)
+
+        assertThat(replyActions.defaultAction).isEqualTo(ReplyAction.REPLY_ALL)
+        assertThat(replyActions.additionalActions).containsExactly(ReplyAction.REPLY)
+    }
+
+    @Test
+    fun `message sent to our identity and others (CC)`() {
+        val message = buildMessage {
+            header("From", "sender@domain.example")
+            header("Cc", "$IDENTITY_EMAIL_ADDRESS, other@domain.example")
+        }
+
+        val replyActions = replyActionStrategy.getReplyActions(account, message)
+
+        assertThat(replyActions.defaultAction).isEqualTo(ReplyAction.REPLY_ALL)
+        assertThat(replyActions.additionalActions).containsExactly(ReplyAction.REPLY)
+    }
+
+    @Test
+    fun `message sent to our identity and others (To+CC)`() {
+        val message = buildMessage {
+            header("From", "sender@domain.example")
+            header("To", IDENTITY_EMAIL_ADDRESS)
+            header("Cc", "other@domain.example")
+        }
+
+        val replyActions = replyActionStrategy.getReplyActions(account, message)
+
+        assertThat(replyActions.defaultAction).isEqualTo(ReplyAction.REPLY_ALL)
+        assertThat(replyActions.additionalActions).containsExactly(ReplyAction.REPLY)
+    }
+
+    @Test
+    fun `message sent to our identity and others (CC+To)`() {
+        val message = buildMessage {
+            header("From", "sender@domain.example")
+            header("To", "other@domain.example")
+            header("Cc", IDENTITY_EMAIL_ADDRESS)
+        }
+
+        val replyActions = replyActionStrategy.getReplyActions(account, message)
+
+        assertThat(replyActions.defaultAction).isEqualTo(ReplyAction.REPLY_ALL)
+        assertThat(replyActions.additionalActions).containsExactly(ReplyAction.REPLY)
+    }
+
+    @Test
+    fun `message where neither sender nor recipient addresses belong to account`() {
+        val message = buildMessage {
+            header("From", "sender@domain.example")
+            header("To", "recipient@domain.example")
+        }
+
+        val replyActions = replyActionStrategy.getReplyActions(account, message)
+
+        assertThat(replyActions.defaultAction).isEqualTo(ReplyAction.REPLY_ALL)
+        assertThat(replyActions.additionalActions).containsExactly(ReplyAction.REPLY)
+    }
+
+    @Test
+    fun `message without any sender or recipient headers`() {
+        val message = buildMessage {}
+
+        val replyActions = replyActionStrategy.getReplyActions(account, message)
+
+        assertThat(replyActions.defaultAction).isNull()
+        assertThat(replyActions.additionalActions).isEmpty()
+    }
+
+    private fun createAccount(): Account {
+        return Account("00000000-0000-4000-0000-000000000000").apply {
+            identities += Identity(name = "Myself", email = IDENTITY_EMAIL_ADDRESS)
+        }
+    }
+}
diff --git a/app/storage/src/test/java/com/fsck/k9/storage/messages/RetrieveMessageOperationsTest.kt b/app/storage/src/test/java/com/fsck/k9/storage/messages/RetrieveMessageOperationsTest.kt
index 09dccb31d70..09246c3cb30 100644
--- a/app/storage/src/test/java/com/fsck/k9/storage/messages/RetrieveMessageOperationsTest.kt
+++ b/app/storage/src/test/java/com/fsck/k9/storage/messages/RetrieveMessageOperationsTest.kt
@@ -170,6 +170,34 @@ class RetrieveMessageOperationsTest : RobolectricTest() {
         )
     }
 
+    @Test
+    fun `get some headers`() {
+        val messagePartId = sqliteDatabase.createMessagePart(
+            header = """
+                From: <alice@domain.example>
+                To: Bob <bob@domain.example>
+                Date: Thu, 01 Apr 2021 01:23:45 +0200
+                Subject: Test
+                Message-Id: <20210401012345.123456789A@domain.example>
+            """.trimIndent().crlf()
+        )
+        sqliteDatabase.createMessage(folderId = 1, uid = "uid1", messagePartId = messagePartId)
+
+        val headers = retrieveMessageOperations.getHeaders(
+            folderId = 1,
+            messageServerId = "uid1",
+            headerNames = setOf("from", "to", "message-id")
+        )
+
+        assertThat(headers).isEqualTo(
+            listOf(
+                Header("From", "<alice@domain.example>"),
+                Header("To", "Bob <bob@domain.example>"),
+                Header("Message-Id", "<20210401012345.123456789A@domain.example>")
+            )
+        )
+    }
+
     @Test
     fun `get oldest message date`() {
         sqliteDatabase.createMessage(folderId = 1, date = 42)
diff --git a/app/ui/legacy/src/test/java/com/fsck/k9/ui/messageview/DisplayRecipientsExtractorTest.kt b/app/ui/legacy/src/test/java/com/fsck/k9/ui/messageview/DisplayRecipientsExtractorTest.kt
new file mode 100644
index 00000000000..7c7300817d3
--- /dev/null
+++ b/app/ui/legacy/src/test/java/com/fsck/k9/ui/messageview/DisplayRecipientsExtractorTest.kt
@@ -0,0 +1,148 @@
+package com.fsck.k9.ui.messageview
+
+import com.fsck.k9.Account
+import com.fsck.k9.Identity
+import com.fsck.k9.helper.AddressFormatter
+import com.fsck.k9.mail.Address
+import com.fsck.k9.mail.buildMessage
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+private const val IDENTITY_ADDRESS = "me@domain.example"
+
+class DisplayRecipientsExtractorTest {
+    private val account = Account("uuid").apply {
+        identities += Identity(
+            email = IDENTITY_ADDRESS
+        )
+    }
+
+    private val addressFormatter = object : AddressFormatter {
+        override fun getDisplayName(address: Address): CharSequence {
+            return when (address.address) {
+                IDENTITY_ADDRESS -> "me"
+                "user1@domain.example" -> "Contact One"
+                "user2@domain.example" -> "Contact Two"
+                "user3@domain.example" -> "Contact Three"
+                "user4@domain.example" -> "Contact Four"
+                else -> address.personal ?: address.address
+            }
+        }
+    }
+
+    private val displayRecipientsExtractor = DisplayRecipientsExtractor(
+        addressFormatter,
+        maxNumberOfDisplayRecipients = 5
+    )
+
+    @Test
+    fun `single recipient is identity address`() {
+        val message = buildMessage {
+            header("To", "Test User <$IDENTITY_ADDRESS>")
+        }
+
+        val displayRecipients = displayRecipientsExtractor.extractDisplayRecipients(message, account)
+
+        assertThat(displayRecipients).isEqualTo(
+            DisplayRecipients(recipientNames = listOf("me"), numberOfRecipients = 1)
+        )
+    }
+
+    @Test
+    fun `single recipient is a contact`() {
+        val message = buildMessage {
+            header("To", "User 1 <user1@domain.example>")
+        }
+
+        val displayRecipients = displayRecipientsExtractor.extractDisplayRecipients(message, account)
+
+        assertThat(displayRecipients).isEqualTo(
+            DisplayRecipients(recipientNames = listOf("Contact One"), numberOfRecipients = 1)
+        )
+    }
+
+    @Test
+    fun `single recipient is not a contact`() {
+        val message = buildMessage {
+            header("To", "Alice <alice@domain.example>")
+        }
+
+        val displayRecipients = displayRecipientsExtractor.extractDisplayRecipients(message, account)
+
+        assertThat(displayRecipients).isEqualTo(
+            DisplayRecipients(recipientNames = listOf("Alice"), numberOfRecipients = 1)
+        )
+    }
+
+    @Test
+    fun `single recipient without name and not a contact`() {
+        val message = buildMessage {
+            header("To", "alice@domain.example")
+        }
+
+        val displayRecipients = displayRecipientsExtractor.extractDisplayRecipients(message, account)
+
+        assertThat(displayRecipients).isEqualTo(
+            DisplayRecipients(recipientNames = listOf("alice@domain.example"), numberOfRecipients = 1)
+        )
+    }
+
+    @Test
+    fun `three unknown recipients`() {
+        val message = buildMessage {
+            header("To", "Unknown 1 <unknown1@domain.example>, Unknown 2 <unknown2@domain.example>")
+            header("Cc", "Unknown 3 <unknown3@domain.example>")
+        }
+
+        val displayRecipients = displayRecipientsExtractor.extractDisplayRecipients(message, account)
+
+        assertThat(displayRecipients).isEqualTo(
+            DisplayRecipients(
+                recipientNames = listOf("Unknown 1", "Unknown 2", "Unknown 3"),
+                numberOfRecipients = 3
+            )
+        )
+    }
+
+    @Test
+    fun `recipients spread across To and Cc header`() {
+        val message = buildMessage {
+            header("To", "user1@domain.example, Alice <alice@domain.example>, $IDENTITY_ADDRESS")
+            header("Cc", "user2@domain.example, User 4 <user4@domain.example>, someone.else@domain.example")
+            header("Bcc", "hidden@domain.example")
+        }
+
+        val displayRecipients = displayRecipientsExtractor.extractDisplayRecipients(message, account)
+
+        assertThat(displayRecipients).isEqualTo(
+            DisplayRecipients(
+                recipientNames = listOf("me", "Contact One", "Alice", "Contact Two", "Contact Four"),
+                numberOfRecipients = 7
+            )
+        )
+    }
+
+    @Test
+    fun `100 recipients, AddressFormatter_getDisplayName() should only be called maxNumberOfDisplayRecipients times`() {
+        val recipients = (1..100).joinToString(separator = ", ") { "unknown$it@domain.example" }
+        val message = buildMessage {
+            header("To", recipients)
+        }
+        var numberOfTimesCalled = 0
+        val addressFormatter = object : AddressFormatter {
+            override fun getDisplayName(address: Address): CharSequence {
+                numberOfTimesCalled++
+                return address.address
+            }
+        }
+        val displayRecipientsExtractor = DisplayRecipientsExtractor(
+            addressFormatter,
+            maxNumberOfDisplayRecipients = 5
+        )
+
+        val displayRecipients = displayRecipientsExtractor.extractDisplayRecipients(message, account)
+
+        assertThat(displayRecipients.numberOfRecipients).isEqualTo(100)
+        assertThat(numberOfTimesCalled).isEqualTo(5)
+    }
+}
diff --git a/app/ui/legacy/src/test/java/com/fsck/k9/ui/messageview/RecipientLayoutCreatorTest.kt b/app/ui/legacy/src/test/java/com/fsck/k9/ui/messageview/RecipientLayoutCreatorTest.kt
new file mode 100644
index 00000000000..18add6b7fc2
--- /dev/null
+++ b/app/ui/legacy/src/test/java/com/fsck/k9/ui/messageview/RecipientLayoutCreatorTest.kt
@@ -0,0 +1,146 @@
+package com.fsck.k9.ui.messageview
+
+import com.fsck.k9.RobolectricTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class RecipientLayoutCreatorTest : RobolectricTest() {
+    private val textMeasure = object : TextMeasure {
+        override fun measureRecipientNames(text: CharSequence): Int {
+            return measureText(text)
+        }
+
+        override fun measureRecipientCount(text: CharSequence): Int {
+            return measureText(text)
+        }
+
+        private fun measureText(text: CharSequence): Int {
+            return text.length
+        }
+    }
+
+    private val recipientLayoutCreator = RecipientLayoutCreator(
+        textMeasure = textMeasure,
+        maxNumberOfRecipientNames = 5,
+        recipientsPrefix = "to ",
+        additionalRecipientSpacing = 1,
+        additionalRecipientsPrefix = "+"
+    )
+
+    @Test(expected = IllegalArgumentException::class)
+    fun `empty recipient list should throw`() {
+        recipientLayoutCreator.createRecipientLayout(
+            recipientNames = emptyList(),
+            totalNumberOfRecipients = 0,
+            availableWidth = 100
+        )
+    }
+
+    @Test
+    fun `single recipient name that fits available width`() {
+        val result = recipientLayoutCreator.createRecipientLayout(
+            recipientNames = listOf("me"),
+            totalNumberOfRecipients = 1,
+            availableWidth = 10
+        )
+
+        assertThat(result.recipientNames.toString()).isEqualTo("to me")
+        assertThat(result.additionalRecipients).isNull()
+    }
+
+    @Test
+    fun `single recipient name that doesn't fit available width`() {
+        val result = recipientLayoutCreator.createRecipientLayout(
+            recipientNames = listOf("me"),
+            totalNumberOfRecipients = 1,
+            availableWidth = 1
+        )
+
+        assertThat(result.recipientNames.toString()).isEqualTo("to me")
+        assertThat(result.additionalRecipients).isNull()
+    }
+
+    @Test
+    fun `two recipient names where first one doesn't fit available width`() {
+        val result = recipientLayoutCreator.createRecipientLayout(
+            recipientNames = listOf("Alice", "Bob"),
+            totalNumberOfRecipients = 2,
+            availableWidth = 5
+        )
+
+        assertThat(result.recipientNames.toString()).isEqualTo("to Alice")
+        assertThat(result.additionalRecipients).isEqualTo("+1")
+    }
+
+    @Test
+    fun `two recipient names where both fit available width`() {
+        val result = recipientLayoutCreator.createRecipientLayout(
+            recipientNames = listOf("Alice", "Bob"),
+            totalNumberOfRecipients = 2,
+            availableWidth = 13
+        )
+
+        assertThat(result.recipientNames.toString()).isEqualTo("to Alice, Bob")
+        assertThat(result.additionalRecipients).isNull()
+    }
+
+    @Test
+    fun `three recipient names where only first one fits available width`() {
+        val result = recipientLayoutCreator.createRecipientLayout(
+            recipientNames = listOf("Alice", "Bob", "Charly"),
+            totalNumberOfRecipients = 3,
+            availableWidth = 13
+        )
+
+        assertThat(result.recipientNames.toString()).isEqualTo("to Alice")
+        assertThat(result.additionalRecipients).isEqualTo("+2")
+    }
+
+    @Test
+    fun `three recipient names where only first two fit available width`() {
+        val result = recipientLayoutCreator.createRecipientLayout(
+            recipientNames = listOf("Alice", "Bob", "Charly"),
+            totalNumberOfRecipients = 3,
+            availableWidth = 16
+        )
+
+        assertThat(result.recipientNames.toString()).isEqualTo("to Alice, Bob")
+        assertThat(result.additionalRecipients).isEqualTo("+1")
+    }
+
+    @Test
+    fun `three recipient names where all fit available width`() {
+        val result = recipientLayoutCreator.createRecipientLayout(
+            recipientNames = listOf("Alice", "Bob", "Charly"),
+            totalNumberOfRecipients = 3,
+            availableWidth = 100
+        )
+
+        assertThat(result.recipientNames.toString()).isEqualTo("to Alice, Bob, Charly")
+        assertThat(result.additionalRecipients).isNull()
+    }
+
+    @Test
+    fun `five recipient names and additional recipients where only two fit available width`() {
+        val result = recipientLayoutCreator.createRecipientLayout(
+            recipientNames = listOf("One", "Two", "Three", "Four", "Five"),
+            totalNumberOfRecipients = 10,
+            availableWidth = 20
+        )
+
+        assertThat(result.recipientNames.toString()).isEqualTo("to One, Two")
+        assertThat(result.additionalRecipients).isEqualTo("+8")
+    }
+
+    @Test
+    fun `five recipient names and additional recipients where all five fit available width`() {
+        val result = recipientLayoutCreator.createRecipientLayout(
+            recipientNames = listOf("One", "Two", "Three", "Four", "Five"),
+            totalNumberOfRecipients = 10,
+            availableWidth = 100
+        )
+
+        assertThat(result.recipientNames.toString()).isEqualTo("to One, Two, Three, Four, Five")
+        assertThat(result.additionalRecipients).isEqualTo("+5")
+    }
+}
diff --git a/app/ui/legacy/src/test/java/com/fsck/k9/view/MessageHeaderTest.kt b/app/ui/legacy/src/test/java/com/fsck/k9/view/MessageHeaderTest.kt
deleted file mode 100644
index 0802c749e9a..00000000000
--- a/app/ui/legacy/src/test/java/com/fsck/k9/view/MessageHeaderTest.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.fsck.k9.view
-
-import com.fsck.k9.RobolectricTest
-import com.fsck.k9.mail.Address
-import com.fsck.k9.mail.internet.MimeMessage
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-
-class MessageHeaderTest : RobolectricTest() {
-
-    @Test
-    fun shouldShowSender_withSender_shouldReturnTrue() {
-        val message = createMessage("from@example1.com", "sender@example2.com")
-
-        val showSender = MessageHeader.shouldShowSender(message)
-
-        assertThat(showSender).isTrue()
-    }
-
-    @Test
-    fun shouldShowSender_withoutSender_shouldReturnFalse() {
-        val message = createMessage("from@example1.com")
-
-        val showSender = MessageHeader.shouldShowSender(message)
-
-        assertThat(showSender).isFalse()
-    }
-
-    private fun createMessage(from: String, sender: String? = null) = MimeMessage().apply {
-        setFrom(from.toAddress())
-        setSender(sender?.toAddress())
-    }
-
-    private fun String.toAddress() = Address.parse(this)[0]
-}
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 0ff0ada4a95..b1beda25bdc 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
@@ -698,7 +698,7 @@ class RealImapFolderTest {
         verify(imapConnection).sendCommand(
             "UID FETCH 1 (UID INTERNALDATE RFC822.SIZE BODY.PEEK[HEADER.FIELDS " +
                 "(date subject from content-type to cc reply-to message-id references in-reply-to list-unsubscribe " +
-                "X-K9mail-Identity Chat-Version)])",
+                "sender X-K9mail-Identity Chat-Version)])",
             false
         )
     }