Loading app/core/build.gradle +1 −0 Original line number Diff line number Diff line Loading @@ -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") Loading app/core/src/main/java/com/fsck/k9/helper/Contacts.java +27 −5 Original line number Diff line number Diff line package com.fsck.k9.helper; import java.util.HashMap; import android.Manifest; import android.content.ContentResolver; import android.content.Context; Loading @@ -9,13 +11,12 @@ import android.content.pm.PackageManager; 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. Loading @@ -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 }; /** Loading @@ -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. Loading Loading @@ -169,6 +173,24 @@ public class Contacts { 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. * Loading app/core/src/main/java/com/fsck/k9/mailstore/MessageDetails.kt 0 → 100644 +22 −0 Original line number Diff line number Diff line 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 } app/core/src/main/java/com/fsck/k9/mailstore/MessageRepository.kt +58 −0 Original line number Diff line number Diff line 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", ) } } app/core/src/main/java/com/fsck/k9/mailstore/MessageStore.kt +5 −0 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading
app/core/build.gradle +1 −0 Original line number Diff line number Diff line Loading @@ -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") Loading
app/core/src/main/java/com/fsck/k9/helper/Contacts.java +27 −5 Original line number Diff line number Diff line package com.fsck.k9.helper; import java.util.HashMap; import android.Manifest; import android.content.ContentResolver; import android.content.Context; Loading @@ -9,13 +11,12 @@ import android.content.pm.PackageManager; 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. Loading @@ -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 }; /** Loading @@ -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. Loading Loading @@ -169,6 +173,24 @@ public class Contacts { 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. * Loading
app/core/src/main/java/com/fsck/k9/mailstore/MessageDetails.kt 0 → 100644 +22 −0 Original line number Diff line number Diff line 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 }
app/core/src/main/java/com/fsck/k9/mailstore/MessageRepository.kt +58 −0 Original line number Diff line number Diff line 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", ) } }
app/core/src/main/java/com/fsck/k9/mailstore/MessageStore.kt +5 −0 Original line number Diff line number Diff line Loading @@ -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. */ Loading