Skip to content

Commit

Permalink
Added showing thread messages count + correct labels + all recipients…
Browse files Browse the repository at this point in the history
… in a thread.| #74
  • Loading branch information
DenBond7 committed Aug 5, 2024
1 parent b2d33fb commit 43a752f
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.flowcrypt.email.database.FlowCryptRoomDatabase
import com.flowcrypt.email.database.entity.AccountEntity
import com.flowcrypt.email.database.entity.AttachmentEntity
import com.flowcrypt.email.database.entity.MessageEntity
import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.getUniqueRecipients
import com.flowcrypt.email.extensions.contentId
import com.flowcrypt.email.extensions.disposition
import com.flowcrypt.email.extensions.isMimeType
Expand Down Expand Up @@ -118,6 +119,7 @@ class GmailApiHelper {
const val MESSAGE_RESPONSE_FORMAT_RAW = "raw"
const val MESSAGE_RESPONSE_FORMAT_FULL = "full"
const val MESSAGE_RESPONSE_FORMAT_MINIMAL = "minimal"
const val THREAD_RESPONSE_FORMAT_METADATA = "metadata"

private val FULL_INFO_WITHOUT_DATA = listOf(
"id",
Expand Down Expand Up @@ -395,9 +397,11 @@ class GmailApiHelper {
id = thread.id,
lastMessage = it,
messagesCount = thread.messages?.size ?: 0,
recipients = emptyList(),
recipients = thread.getUniqueRecipients(),
subject = it.snippet,
labels = ""
labels = thread.messages.flatMap { message ->
message.labelIds ?: emptyList()
}.toSortedSet()
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@ data class GmailThreadInfo(
val messagesCount: Int,
val recipients: List<InternetAddress>,
val subject: String,
val labels: String
val labels: Set<String>,
val hasAttachments: Boolean = false,
val hasPgpThings: Boolean = false,
)
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,11 @@ data class MessageEntity(
msgsList: List<com.google.api.services.gmail.model.Message>,
isNew: Boolean,
onlyPgpModeEnabled: Boolean,
draftIdsMap: Map<String, String> = emptyMap()
draftIdsMap: Map<String, String> = emptyMap(),
messageModificationAction: (
message: com.google.api.services.gmail.model.Message,
messageEntity: MessageEntity
) -> MessageEntity = { _, messageEntity -> messageEntity }
): List<MessageEntity> {
val messageEntities = mutableListOf<MessageEntity>()
val isNotificationDisabled = NotificationsSettingsFragment.NOTIFICATION_LEVEL_NEVER ==
Expand Down Expand Up @@ -348,22 +352,23 @@ data class MessageEntity(
}

val mimeMessage = GmaiAPIMimeMessage(Session.getInstance(Properties()), msg)
val messageEntityTemplate = genMsgEntity(
account = account,
accountType = accountType,
label = label,
msg = mimeMessage,
uid = msg.uid,
isNew = isNewTemp,
hasPgp = hasPgp,
hasAttachments = GmailApiHelper.getAttsInfoFromMessagePart(msg.payload).isNotEmpty()
).copy(
threadId = msg.threadId,
historyId = msg.historyId.toString(),
draftId = draftIdsMap[msg.id],
labelIds = msg.labelIds?.joinToString(separator = LABEL_IDS_SEPARATOR)
)
messageEntities.add(
genMsgEntity(
account = account,
accountType = accountType,
label = label,
msg = mimeMessage,
uid = msg.uid,
isNew = isNewTemp,
hasPgp = hasPgp,
hasAttachments = GmailApiHelper.getAttsInfoFromMessagePart(msg.payload).isNotEmpty()
).copy(
threadId = msg.threadId,
historyId = msg.historyId.toString(),
draftId = draftIdsMap[msg.id],
labelIds = msg.labelIds?.joinToString(separator = LABEL_IDS_SEPARATOR)
)
messageModificationAction.invoke(msg, messageEntityTemplate)
)
} catch (e: MessageRemovedException) {
e.printStackTrace()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* © 2016-present FlowCrypt a.s. Limitations apply. Contact [email protected]
* Contributors: denbond7
*/

package com.flowcrypt.email.extensions.com.google.api.services.gmail.model

import com.flowcrypt.email.extensions.kotlin.asInternetAddresses
import com.google.api.services.gmail.model.Thread
import jakarta.mail.internet.InternetAddress

/**
* @author Denys Bondarenko
*/
fun Thread.getUniqueRecipients(): List<InternetAddress> {
return mutableListOf<InternetAddress>().apply {
val filteredHeaders = messages?.flatMap { message ->
message?.payload?.headers?.filter { header ->
//need to check this line and test
header.name in listOf(
"From",
"To"
)//maybe need to add Cc. need to check
} ?: emptyList()
}

val mapOfUniqueRecipients = mutableMapOf<String, String>()
filteredHeaders?.forEach { header ->
header.value.asInternetAddresses().forEach { internetAddress ->
val address = internetAddress.address.lowercase()

if (!mapOfUniqueRecipients.contains(address)
|| mapOfUniqueRecipients[address].isNullOrEmpty()
) {
add(internetAddress)
mapOfUniqueRecipients[address] = internetAddress.personal
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.flowcrypt.email.api.email.IMAPStoreManager
import com.flowcrypt.email.api.email.JavaEmailConstants
import com.flowcrypt.email.api.email.gmail.GmailApiHelper
import com.flowcrypt.email.api.email.gmail.api.GmaiAPIMimeMessage
import com.flowcrypt.email.api.email.gmail.model.GmailThreadInfo
import com.flowcrypt.email.api.email.model.LocalFolder
import com.flowcrypt.email.api.email.model.MessageFlag
import com.flowcrypt.email.api.retrofit.response.base.Result
Expand All @@ -32,6 +33,7 @@ import com.flowcrypt.email.database.entity.AccountEntity
import com.flowcrypt.email.database.entity.AttachmentEntity
import com.flowcrypt.email.database.entity.LabelEntity
import com.flowcrypt.email.database.entity.MessageEntity
import com.flowcrypt.email.database.entity.MessageEntity.Companion.LABEL_IDS_SEPARATOR
import com.flowcrypt.email.extensions.com.google.api.services.gmail.model.hasPgp
import com.flowcrypt.email.extensions.kotlin.toHex
import com.flowcrypt.email.jetpack.workmanager.EmailAndNameWorker
Expand Down Expand Up @@ -417,7 +419,7 @@ class MessagesViewModel(application: Application) : AccountViewModel(application
)
)
if (end < 1) {
handleReceivedMsgs(accountEntity, localFolder, imapFolder)
handleReceivedMessages(accountEntity, localFolder, imapFolder)
} else {
val msgs: Array<Message> = if (isOnlyPgpModeEnabled == true) {
foundMsgs.copyOfRange(start - 1, end)
Expand All @@ -433,7 +435,7 @@ class MessagesViewModel(application: Application) : AccountViewModel(application
imapFolder.search(EmailUtil.genPgpThingsSearchTerm(accountEntity), msgs)
.map { imapFolder.getUID(it) }.toSet()

handleReceivedMsgs(
handleReceivedMessages(
account = accountEntity,
localFolder = localFolder,
remoteFolder = folder,
Expand Down Expand Up @@ -465,6 +467,7 @@ class MessagesViewModel(application: Application) : AccountViewModel(application
val messages: List<com.google.api.services.gmail.model.Message>
val nextPageToken: String?
val draftIdsMap: Map<String, String>
val gmailThreadInfoList: List<GmailThreadInfo>

if (accountEntity.useConversationMode) {
val threadsResponse = GmailApiHelper.loadThreads(
Expand All @@ -474,18 +477,19 @@ class MessagesViewModel(application: Application) : AccountViewModel(application
nextPageToken = if (totalItemsCount > 0) labelEntity?.nextPageToken else null
)

val gmailThreadInfoList = GmailApiHelper.loadGmailThreadInfoInParallel(
gmailThreadInfoList = GmailApiHelper.loadGmailThreadInfoInParallel(
context = getApplication(),
accountEntity = accountEntity,
threads = threadsResponse.threads,
format = GmailApiHelper.MESSAGE_RESPONSE_FORMAT_MINIMAL,
threads = threadsResponse.threads ?: emptyList(),
format = GmailApiHelper.MESSAGE_RESPONSE_FORMAT_FULL,
localFolder = localFolder
)

messages = gmailThreadInfoList.map { it.lastMessage }
nextPageToken = threadsResponse.nextPageToken
draftIdsMap = emptyMap()//todo-denbond7 fix me
} else{
} else {
gmailThreadInfoList = emptyList()
val messagesBaseInfo = GmailApiHelper.loadMsgsBaseInfo(
context = getApplication(),
accountEntity = accountEntity,
Expand Down Expand Up @@ -531,7 +535,30 @@ class MessagesViewModel(application: Application) : AccountViewModel(application
resultCode = R.id.progress_id_gmail_msgs_info
)
)
handleReceivedMsgs(accountEntity, localFolder, msgs, draftIdsMap)
handleReceivedMessages(
accountEntity,
localFolder,
msgs,
draftIdsMap
) { message, messageEntity ->
if (accountEntity.useConversationMode) {
val thread = gmailThreadInfoList.firstOrNull { it.id == message.threadId }
if (thread != null) {
messageEntity.copy(
threadMessagesCount = thread.messagesCount,
labelIds = thread.labels.joinToString(separator = LABEL_IDS_SEPARATOR),
hasAttachments = thread.hasAttachments,
fromAddresses = InternetAddress.toString(thread.recipients.toTypedArray()),
toAddresses = InternetAddress.toString(thread.recipients.toTypedArray()),
hasPgp = thread.hasPgpThings
)
} else {
messageEntity
}
} else {
messageEntity
}
}
} else {
loadMsgsFromRemoteServerLiveData.postValue(
Result.loading(
Expand All @@ -549,11 +576,15 @@ class MessagesViewModel(application: Application) : AccountViewModel(application
return@withContext Result.success(true)
}

private suspend fun handleReceivedMsgs(
private suspend fun handleReceivedMessages(
account: AccountEntity,
localFolder: LocalFolder,
msgs: List<com.google.api.services.gmail.model.Message>,
draftIdsMap: Map<String, String> = emptyMap()
draftIdsMap: Map<String, String> = emptyMap(),
messageEntityModificationAction: (
message: com.google.api.services.gmail.model.Message,
messageEntity: MessageEntity
) -> MessageEntity
) = withContext(Dispatchers.IO) {
val email = account.email
val folder = localFolder.fullName
Expand All @@ -568,7 +599,9 @@ class MessagesViewModel(application: Application) : AccountViewModel(application
isNew = false,
onlyPgpModeEnabled = isOnlyPgpModeEnabled,
draftIdsMap = draftIdsMap
)
) { message, messageEntity ->
messageEntityModificationAction.invoke(message, messageEntity)
}

roomDatabase.msgDao().insertWithReplaceSuspend(msgEntities)
GmailApiHelper.identifyAttachments(msgEntities, msgs, account, localFolder, roomDatabase)
Expand All @@ -579,7 +612,7 @@ class MessagesViewModel(application: Application) : AccountViewModel(application
)
}

private suspend fun handleReceivedMsgs(
private suspend fun handleReceivedMessages(
account: AccountEntity,
localFolder: LocalFolder,
remoteFolder: IMAPFolder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import android.graphics.Camera
import android.graphics.Color
import android.graphics.Typeface
import android.text.SpannableString
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.TextUtils
import android.text.style.AbsoluteSizeSpan
Expand Down Expand Up @@ -303,16 +304,39 @@ class MsgsPagedListAdapter(private val onMessageClickListener: OnMessageClickLis
folderType: FoldersManager.FolderType?,
messageEntity: MessageEntity,
context: Context
) = when (folderType) {
FoldersManager.FolderType.SENT -> generateAddresses(messageEntity.to)
): CharSequence {
val addresses = when (folderType) {
FoldersManager.FolderType.SENT -> generateAddresses(messageEntity.to)

FoldersManager.FolderType.DRAFTS -> generateAddresses(messageEntity.to).ifEmpty {
context.getString(R.string.no_recipients)
}
FoldersManager.FolderType.DRAFTS -> generateAddresses(messageEntity.to).ifEmpty {
context.getString(R.string.no_recipients)
}

FoldersManager.FolderType.OUTBOX -> generateOutboxStatus(context, messageEntity.msgState)
FoldersManager.FolderType.OUTBOX -> generateOutboxStatus(context, messageEntity.msgState)

else -> generateAddresses(messageEntity.from)
else -> generateAddresses(messageEntity.from)
}

return if ((messageEntity.threadMessagesCount ?: 0) > 1) {
SpannableStringBuilder(addresses).apply {
val spannableStringForThreadMessageCount = SpannableString(
"(${messageEntity.threadMessagesCount})"
).apply {
val textSize =
context.resources.getDimensionPixelSize(R.dimen.default_text_size_small)
setSpan(
AbsoluteSizeSpan(textSize),
0,
length,
Spanned.SPAN_INCLUSIVE_INCLUSIVE
)
}
append(" ")
append(spannableStringForThreadMessageCount)
}
} else {
addresses
}
}

private fun changeStatusView(messageEntity: MessageEntity) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ object AvatarGenerator {
//draw a text
textPaint.textSize = fontSize
canvas.drawText(
text.substring(0, 2).uppercase(), (bitmapWidth / 2).toFloat(),
text.takeIf { it.length > 1 }?.substring(0, 2)?.uppercase() ?: text,
(bitmapWidth / 2).toFloat(),
bitmapHeight / 2 - (textPaint.descent() + textPaint.ascent()) / 2, textPaint
)

Expand Down

0 comments on commit 43a752f

Please sign in to comment.