Merge branch 'feat/authenticity_badge' into 'develop'

Feat/authenticity badge

See merge request android/mail/proton-mail-android!1231
This commit is contained in:
Zorica Stojchevska 2023-01-23 20:26:50 +00:00
commit 19d6e4a036
25 changed files with 729 additions and 76 deletions

View File

@ -0,0 +1,557 @@
{
"formatVersion": 1,
"database": {
"version": 20,
"identityHash": "06f71940b640092ab44e6d8578410899",
"entities": [
{
"tableName": "attachmentv3",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`attachment_id` TEXT, `file_name` TEXT NOT NULL, `mime_type` TEXT, `file_size` INTEGER NOT NULL, `key_packets` TEXT, `message_id` TEXT NOT NULL, `uploaded` INTEGER NOT NULL, `uploading` INTEGER NOT NULL, `signature` TEXT, `headers` TEXT, `is_inline` INTEGER NOT NULL, `file_path` TEXT, `mime_data` BLOB, `_id` INTEGER PRIMARY KEY AUTOINCREMENT)",
"fields": [
{
"fieldPath": "attachmentId",
"columnName": "attachment_id",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "fileName",
"columnName": "file_name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "mimeType",
"columnName": "mime_type",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "fileSize",
"columnName": "file_size",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "keyPackets",
"columnName": "key_packets",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "messageId",
"columnName": "message_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "isUploaded",
"columnName": "uploaded",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isUploading",
"columnName": "uploading",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "signature",
"columnName": "signature",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "headers",
"columnName": "headers",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "inline",
"columnName": "is_inline",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "filePath",
"columnName": "file_path",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "mimeData",
"columnName": "mime_data",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "dbId",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"_id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_attachmentv3_attachment_id",
"unique": true,
"columnNames": [
"attachment_id"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_attachmentv3_attachment_id` ON `${TABLE_NAME}` (`attachment_id`)"
}
],
"foreignKeys": []
},
{
"tableName": "conversations",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`ID` TEXT NOT NULL, `Order` INTEGER NOT NULL, `UserID` TEXT NOT NULL, `Subject` TEXT NOT NULL, `Senders` TEXT NOT NULL, `Recipients` TEXT NOT NULL, `NumMessages` INTEGER NOT NULL, `NumUnread` INTEGER NOT NULL, `NumAttachments` INTEGER NOT NULL, `ExpirationTime` INTEGER NOT NULL, `Size` INTEGER NOT NULL, `Labels` TEXT NOT NULL, PRIMARY KEY(`ID`))",
"fields": [
{
"fieldPath": "id",
"columnName": "ID",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "order",
"columnName": "Order",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "UserID",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "subject",
"columnName": "Subject",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "senders",
"columnName": "Senders",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "recipients",
"columnName": "Recipients",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "numMessages",
"columnName": "NumMessages",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "numUnread",
"columnName": "NumUnread",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "numAttachments",
"columnName": "NumAttachments",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "expirationTime",
"columnName": "ExpirationTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "size",
"columnName": "Size",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "labels",
"columnName": "Labels",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"ID"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_conversations_ID",
"unique": true,
"columnNames": [
"ID"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_conversations_ID` ON `${TABLE_NAME}` (`ID`)"
}
],
"foreignKeys": []
},
{
"tableName": "messagev3",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`ID` TEXT, `ConversationID` TEXT, `Subject` TEXT, `Unread` INTEGER NOT NULL, `Type` INTEGER NOT NULL, `Time` INTEGER NOT NULL, `Size` INTEGER NOT NULL, `Location` INTEGER NOT NULL, `FolderLocation` TEXT, `Starred` INTEGER, `NumAttachments` INTEGER NOT NULL, `IsEncrypted` INTEGER NOT NULL, `ExpirationTime` INTEGER NOT NULL, `IsReplied` INTEGER, `IsRepliedAll` INTEGER, `IsForwarded` INTEGER, `Body` TEXT, `IsDownloaded` INTEGER NOT NULL, `AddressID` TEXT, `InlineResponse` INTEGER NOT NULL, `NewServerId` TEXT, `MIMEType` TEXT, `SpamScore` INTEGER NOT NULL, `AccessTime` INTEGER NOT NULL, `Header` TEXT, `ParsedHeaders` TEXT, `LabelIDs` TEXT NOT NULL, `ToList` TEXT NOT NULL, `ReplyTos` TEXT NOT NULL, `CCList` TEXT NOT NULL, `BCCList` TEXT NOT NULL, `Flags` INTEGER NOT NULL DEFAULT 0, `Order` INTEGER NOT NULL DEFAULT 9223372036854775807, `_id` INTEGER PRIMARY KEY AUTOINCREMENT, `Sender_SenderName` TEXT, `Sender_SenderSerialized` TEXT, `Sender_IsProton` INTEGER DEFAULT 0)",
"fields": [
{
"fieldPath": "messageId",
"columnName": "ID",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "conversationId",
"columnName": "ConversationID",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "subject",
"columnName": "Subject",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "Unread",
"columnName": "Unread",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "Type",
"columnName": "Type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "Time",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "totalSize",
"columnName": "Size",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "location",
"columnName": "Location",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "folderLocation",
"columnName": "FolderLocation",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isStarred",
"columnName": "Starred",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "numAttachments",
"columnName": "NumAttachments",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "messageEncryption",
"columnName": "IsEncrypted",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "expirationTime",
"columnName": "ExpirationTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isReplied",
"columnName": "IsReplied",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "isRepliedAll",
"columnName": "IsRepliedAll",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "isForwarded",
"columnName": "IsForwarded",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "messageBody",
"columnName": "Body",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isDownloaded",
"columnName": "IsDownloaded",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "addressID",
"columnName": "AddressID",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isInline",
"columnName": "InlineResponse",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "localId",
"columnName": "NewServerId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "mimeType",
"columnName": "MIMEType",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "spamScore",
"columnName": "SpamScore",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "accessTime",
"columnName": "AccessTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "header",
"columnName": "Header",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "parsedHeaders",
"columnName": "ParsedHeaders",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "allLabelIDs",
"columnName": "LabelIDs",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "toList",
"columnName": "ToList",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "replyTos",
"columnName": "ReplyTos",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "ccList",
"columnName": "CCList",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "bccList",
"columnName": "BCCList",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "flags",
"columnName": "Flags",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "order",
"columnName": "Order",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "9223372036854775807"
},
{
"fieldPath": "dbId",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "sender.name",
"columnName": "Sender_SenderName",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "sender.emailAddress",
"columnName": "Sender_SenderSerialized",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "sender.isProton",
"columnName": "Sender_IsProton",
"affinity": "INTEGER",
"notNull": false,
"defaultValue": "0"
}
],
"primaryKey": {
"columnNames": [
"_id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_messagev3_ID",
"unique": true,
"columnNames": [
"ID"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_messagev3_ID` ON `${TABLE_NAME}` (`ID`)"
},
{
"name": "index_messagev3_Location",
"unique": false,
"columnNames": [
"Location"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_messagev3_Location` ON `${TABLE_NAME}` (`Location`)"
},
{
"name": "index_messagev3_ConversationID",
"unique": false,
"columnNames": [
"ConversationID"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_messagev3_ConversationID` ON `${TABLE_NAME}` (`ConversationID`)"
}
],
"foreignKeys": []
},
{
"tableName": "message_preference",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`ID` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `message_id` TEXT NOT NULL, `view_in_dark_mode` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "ID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "messageId",
"columnName": "message_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "viewInDarkMode",
"columnName": "view_in_dark_mode",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"ID"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "UnreadCounter",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_id` TEXT NOT NULL, `type` TEXT NOT NULL, `label_id` TEXT NOT NULL, `unread_count` INTEGER NOT NULL, PRIMARY KEY(`user_id`, `label_id`, `type`))",
"fields": [
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "labelId",
"columnName": "label_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "unreadCount",
"columnName": "unread_count",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"user_id",
"label_id",
"type"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '06f71940b640092ab44e6d8578410899')"
]
}
}

View File

@ -19,6 +19,7 @@
package ch.protonmail.android.api.models.messages.receive
import ch.protonmail.android.data.local.model.MessageSender
import me.proton.core.util.kotlin.toBoolean
import javax.inject.Inject
class MessageSenderFactory @Inject constructor() {
@ -33,6 +34,6 @@ class MessageSenderFactory @Inject constructor() {
fun createMessageSender(serverMessageSender: ServerMessageSender): MessageSender {
val name = serverMessageSender.name
val emailAddress = requireNotNull(serverMessageSender.address)
return MessageSender(name, emailAddress)
return MessageSender(name, emailAddress, serverMessageSender.isProton.toBoolean())
}
}

View File

@ -28,7 +28,10 @@ data class ServerMessageSender(
val name: String? = null,
@SerializedName(Fields.Message.Sender.ADDRESS)
val address: String
val address: String,
@SerializedName("IsProton")
val isProton: Int = 0
) : Serializable, Comparable<ServerMessageSender> {

View File

@ -71,6 +71,7 @@ import kotlinx.coroutines.runBlocking
import me.proton.core.domain.entity.UserId
import me.proton.core.usersettings.domain.usecase.GetUserSettings
import me.proton.core.util.kotlin.EMPTY_STRING
import me.proton.core.util.kotlin.toBoolean
import timber.log.Timber
class EventHandler @AssistedInject constructor(
@ -390,7 +391,7 @@ class EventHandler @AssistedInject constructor(
}
val sender = newMessage.Sender
if (sender != null) {
message.sender = MessageSender(sender.name, sender.address)
message.sender = MessageSender(sender.name, sender.address, sender.isProton.toBoolean())
}
val toList = newMessage.ToList
if (toList != null) {

View File

@ -48,8 +48,9 @@ import me.proton.core.data.room.db.CommonConverters
AutoMigration(from = 16, to = 17),
AutoMigration(from = 17, to = 19),
AutoMigration(from = 18, to = 19),
AutoMigration(from = 19, to = 20)
],
version = 19
version = 20
)
@TypeConverters(
value = [

View File

@ -95,6 +95,7 @@ const val COLUMN_MESSAGE_PREFIX_SENDER = "Sender_"
const val COLUMN_MESSAGE_REPLY_TOS = "ReplyTos"
const val COLUMN_MESSAGE_SENDER_EMAIL = "SenderSerialized"
const val COLUMN_MESSAGE_SENDER_NAME = "SenderName"
const val COLUMN_MESSAGE_SENDER_IS_PROTON = "IsProton"
const val COLUMN_MESSAGE_SIZE = "Size"
const val COLUMN_MESSAGE_SPAM_SCORE = "SpamScore"
const val COLUMN_MESSAGE_SUBJECT = "Subject"
@ -217,7 +218,7 @@ data class Message @JvmOverloads constructor(
@ColumnInfo(name = COLUMN_MESSAGE_ORDER, defaultValue = Long.MAX_VALUE.toString())
val order: Long = Long.MAX_VALUE
) : Serializable {
) : Serializable {
@Ignore
var attachments = listOf<Attachment>()

View File

@ -29,10 +29,15 @@ data class MessageSender constructor(
@ColumnInfo(name = COLUMN_MESSAGE_SENDER_EMAIL)
@SerializedName("Address")
var emailAddress: String?
var emailAddress: String?,
@ColumnInfo(name = COLUMN_MESSAGE_SENDER_IS_PROTON, defaultValue = "0")
val isProton: Boolean = false
) : Serializable, Comparable<MessageSender> {
constructor(name: String?, emailAddress: String?) : this(name, emailAddress, false)
override fun toString(): String =
"$name $emailAddress"

View File

@ -31,5 +31,7 @@ class CorrespondentToMessageSenderMapper @Inject constructor() :
Mapper<Correspondent, MessageSender> {
fun toDatabaseModel(correspondent: Correspondent): MessageSender =
MessageSender(name = correspondent.name, emailAddress = correspondent.address)
MessageSender(
name = correspondent.name, emailAddress = correspondent.address, isProton = correspondent.isProton
)
}

View File

@ -21,5 +21,6 @@ package ch.protonmail.android.mailbox.domain.model
data class Correspondent(
val name: String,
val address: String
val address: String,
val isProton: Boolean = false
)

View File

@ -64,7 +64,8 @@ class MailboxItemUiModelMapper @Inject constructor(
messageLabels = message.buildLabelChipFromMessageLabels(allLabels),
allLabelsIds = message.allLabelsIds(),
isDraft = message.isDraft(),
isScheduled = message.isScheduled
isScheduled = message.isScheduled,
isProton = message.sender?.isProton ?: false
)
@JvmName("messagesToUiModels")
@ -94,7 +95,8 @@ class MailboxItemUiModelMapper @Inject constructor(
messageLabels = conversation.buildLabelChipFromMessageLabels(allLabels),
allLabelsIds = conversation.allLabelsIds(),
isDraft = conversation.containsSingleDraftMessage(),
isScheduled = conversation.labels.any { it.id == MessageLocationType.ALL_SCHEDULED.asLabelIdString() }
isScheduled = conversation.labels.any { it.id == MessageLocationType.ALL_SCHEDULED.asLabelIdString() },
isProton = conversation.messages?.any { it.sender.isProton } ?: false
)
@JvmName("conversationsToUiModels")

View File

@ -42,7 +42,8 @@ data class MailboxItemUiModel(
val messageLabels: List<LabelChipUiModel>,
val allLabelsIds: List<LabelId>,
val isDraft: Boolean,
val isScheduled: Boolean
val isScheduled: Boolean,
val isProton: Boolean
) {
class DiffCallback : DiffUtil.ItemCallback<MailboxItemUiModel>() {

View File

@ -392,7 +392,8 @@ class MessageRepository @Inject constructor(
val senderContactName = contactEmails.find { it.email == sender.emailAddress }?.name
return MessageSender(
if (!senderContactName.isNullOrEmpty()) senderContactName else sender.name,
sender.emailAddress
sender.emailAddress,
sender.isProton
)
}

View File

@ -52,6 +52,7 @@ import ch.protonmail.android.ui.view.SingleLineLabelChipGroupView
import ch.protonmail.android.utils.DateUtil
import ch.protonmail.android.utils.UiUtil
import ch.protonmail.android.views.messagesList.SenderInitialView
import com.google.android.material.chip.Chip
private const val HYPHEN = "-"
@ -72,6 +73,7 @@ class MessageDetailsHeaderView @JvmOverloads constructor(
private val senderInitialView: SenderInitialView
private val senderNameTextView: TextView
private val senderEmailTextView: TextView
private val authenticityBadge: Chip
private val recipientsCollapsedTextView: TextView
private val toExpandedTextView: TextView
@ -121,6 +123,7 @@ class MessageDetailsHeaderView @JvmOverloads constructor(
senderInitialView = binding.senderInitialView
senderNameTextView = binding.senderNameTextView
senderEmailTextView = binding.senderEmailTextView
authenticityBadge = binding.authenticityBadgeChip
recipientsCollapsedTextView = binding.recipientsCollapsedTextView
toExpandedTextView = binding.toExpandedTextView
@ -192,6 +195,8 @@ class MessageDetailsHeaderView @JvmOverloads constructor(
senderEmailTextView.text = context.getString(R.string.recipient_email_format, message.senderEmail)
senderEmailTextView.setOnClickListener(getOnSenderClickListener(message.senderEmail))
authenticityBadge.isVisible = message.sender?.isProton ?: false
labelsCollapsedGroupView.setLabels(nonExclusiveLabels)
labelsExpandedGroupView.setLabels(nonExclusiveLabels)
collapsedHeaderGroup.addView(labelsCollapsedGroupView)

View File

@ -173,6 +173,7 @@ class MailboxItemView @JvmOverloads constructor(
sending_uploading_progress_bar.isVisible = isBeingSent || areAttachmentsBeingUploaded
attachment_image_view.isVisible = mailboxItem.hasAttachments
star_image_view.isVisible = mailboxItem.isStarred
authenticity_badge_chip.isVisible = mailboxItem.isProton
empty_space_view.isVisible = attachment_image_view.isVisible || star_image_view.isVisible

View File

@ -19,6 +19,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?attr/proton_interaction_norm" />
<corners
android:bottomRightRadius="@dimen/size_corner_xxs"
android:topRightRadius="@dimen/size_corner_xxs" />
android:bottomRightRadius="@dimen/size_corner_xxxs"
android:topRightRadius="@dimen/size_corner_xxxs" />
</shape>

View File

@ -19,6 +19,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?attr/proton_interaction_norm" />
<corners
android:bottomLeftRadius="@dimen/size_corner_xxs"
android:topLeftRadius="@dimen/size_corner_xxs" />
android:bottomLeftRadius="@dimen/size_corner_xxxs"
android:topLeftRadius="@dimen/size_corner_xxxs" />
</shape>

View File

@ -19,5 +19,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/interaction_weak"/>
<corners android:radius="@dimen/size_corner_xxs" />
<corners android:radius="@dimen/size_corner_xxxs" />
</shape>

View File

@ -49,10 +49,10 @@
android:layout_height="@dimen/message_details_header_small_icon_size"
android:layout_marginEnd="@dimen/message_details_header_margin_normal"
app:srcCompat="@drawable/ic_proton_arrow_up_and_left"
app:layout_constraintBottom_toBottomOf="@id/senderNameTextView"
app:layout_constraintBottom_toBottomOf="@id/senderNameWrapper"
app:layout_constraintLeft_toRightOf="@id/senderInitialBarrier"
app:layout_constraintRight_toLeftOf="@id/repliedAllImageView"
app:layout_constraintTop_toTopOf="@id/senderNameTextView"
app:layout_constraintTop_toTopOf="@id/senderNameWrapper"
app:tint="@color/icon_norm"
tools:src="@drawable/ic_proton_arrow_up_and_left" />
@ -62,9 +62,9 @@
android:layout_height="@dimen/message_details_header_small_icon_size"
android:layout_marginEnd="@dimen/message_details_header_margin_normal"
app:srcCompat="@drawable/ic_proton_arrows_up_and_left"
app:layout_constraintBottom_toBottomOf="@id/senderNameTextView"
app:layout_constraintBottom_toBottomOf="@id/senderNameWrapper"
app:layout_constraintLeft_toRightOf="@id/repliedImageView"
app:layout_constraintTop_toTopOf="@id/senderNameTextView"
app:layout_constraintTop_toTopOf="@id/senderNameWrapper"
app:tint="@color/icon_norm"
tools:src="@drawable/ic_proton_arrows_up_and_left" />
@ -74,27 +74,58 @@
android:layout_height="@dimen/message_details_header_small_icon_size"
android:layout_marginEnd="@dimen/message_details_header_margin_normal"
app:srcCompat="@drawable/ic_proton_arrow_right"
app:layout_constraintBottom_toBottomOf="@id/senderNameTextView"
app:layout_constraintBottom_toBottomOf="@id/senderNameWrapper"
app:layout_constraintLeft_toRightOf="@id/repliedAllImageView"
app:layout_constraintTop_toTopOf="@id/senderNameTextView"
app:layout_constraintTop_toTopOf="@id/senderNameWrapper"
app:tint="@color/icon_norm"
tools:src="@drawable/ic_proton_arrow_right" />
<TextView
android:id="@+id/senderNameTextView"
style="@style/Proton.Text.Default"
android:layout_width="wrap_content"
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/senderNameWrapper"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="@id/senderInitialView"
app:layout_constraintEnd_toStartOf="@id/collapsedMessageViews"
android:layout_marginEnd="@dimen/message_details_header_margin_small"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constrainedWidth="true"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/forwardedImageView"
app:layout_constraintTop_toTopOf="@id/senderInitialView"
tools:text="@tools:sample/full_names" />
app:layout_constraintEnd_toStartOf="@id/collapsedMessageViews">
<TextView
android:id="@+id/senderNameTextView"
style="@style/Proton.Text.Default"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:ellipsize="end"
android:maxLines="1"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constrainedWidth="true"
app:layout_constraintStart_toStartOf="parent"
tools:text="@tools:sample/full_names" />
<com.google.android.material.chip.Chip
android:id="@+id/authenticity_badge_chip"
android:layout_width="wrap_content"
android:layout_height="@dimen/message_details_header_small_icon_size"
android:layout_marginStart="@dimen/padding_xs"
android:background="@color/background_secondary"
android:clickable="false"
android:contentDescription="@string/is_proton"
android:text="@string/is_proton_official_badge"
android:textAppearance="@style/Proton.Text.Overline"
android:textColor="?attr/proton_text_accent"
android:visibility="gone"
app:chipCornerRadius="@dimen/size_corner_xxs"
app:chipMinTouchTargetSize="0dp"
app:layout_constraintBottom_toBottomOf="@id/senderNameWrapper"
app:layout_constraintStart_toEndOf="@id/senderNameTextView"
app:layout_constraintTop_toTopOf="@id/senderNameWrapper"
app:rippleColor="@android:color/transparent"
app:textEndPadding="@dimen/padding_xs"
app:textStartPadding="@dimen/padding_xs" />
</androidx.constraintlayout.widget.ConstraintLayout>
<ch.protonmail.android.details.presentation.view.CollapsedMessageViews
android:id="@+id/collapsedMessageViews"
@ -102,11 +133,11 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/message_details_header_margin_extra_small"
android:layout_marginEnd="@dimen/message_details_header_margin_small"
app:layout_constraintBottom_toBottomOf="@id/senderNameTextView"
app:layout_constraintBottom_toBottomOf="@id/senderNameWrapper"
app:layout_constraintEnd_toStartOf="@id/messageDetailsIcons"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/senderNameTextView"
app:layout_constraintTop_toTopOf="@id/senderNameTextView" />
app:layout_constraintStart_toEndOf="@id/senderNameWrapper"
app:layout_constraintTop_toTopOf="@id/senderNameWrapper" />
<ch.protonmail.android.details.presentation.view.MessageDetailsHeaderIcons
android:id="@+id/messageDetailsIcons"
@ -114,9 +145,9 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/message_details_header_margin_small"
android:layout_marginEnd="@dimen/message_details_header_margin_small"
app:layout_constraintBottom_toBottomOf="@id/senderNameTextView"
app:layout_constraintBottom_toBottomOf="@id/senderNameWrapper"
app:layout_constraintEnd_toStartOf="@id/lockIconTextView"
app:layout_constraintTop_toTopOf="@id/senderNameTextView" />
app:layout_constraintTop_toTopOf="@id/senderNameWrapper" />
<TextView
android:id="@+id/lockIconTextView"
@ -126,9 +157,9 @@
android:layout_marginEnd="@dimen/message_details_header_margin_normal"
android:layout_marginTop="@dimen/message_details_header_margin_small"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="@id/senderNameTextView"
app:layout_constraintBottom_toBottomOf="@id/senderNameWrapper"
app:layout_constraintEnd_toStartOf="@id/locationImageView"
app:layout_constraintTop_toTopOf="@id/senderNameTextView"
app:layout_constraintTop_toTopOf="@id/senderNameWrapper"
tools:text="@string/lock_default" />
<ImageView
@ -136,9 +167,9 @@
android:layout_width="@dimen/message_details_header_small_icon_size"
android:layout_height="@dimen/message_details_header_small_icon_size"
android:layout_marginEnd="@dimen/message_details_header_margin_normal"
app:layout_constraintBottom_toBottomOf="@id/senderNameTextView"
app:layout_constraintBottom_toBottomOf="@id/senderNameWrapper"
app:layout_constraintEnd_toStartOf="@id/timeDateTextView"
app:layout_constraintTop_toTopOf="@id/senderNameTextView"
app:layout_constraintTop_toTopOf="@id/senderNameWrapper"
app:tint="@color/icon_weak"
tools:src="@drawable/ic_proton_inbox" />
@ -147,9 +178,9 @@
style="@style/Proton.Text.Caption.Weak"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="@id/senderNameTextView"
app:layout_constraintBottom_toBottomOf="@id/senderNameWrapper"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/senderNameTextView"
app:layout_constraintTop_toTopOf="@id/senderNameWrapper"
tools:text="@tools:sample/date/ddmmyy" />
<TextView
@ -161,7 +192,7 @@
android:layout_marginEnd="@dimen/message_details_header_margin_normal"
app:layout_constraintLeft_toRightOf="@id/senderInitialBarrier"
app:layout_constraintRight_toLeftOf="@id/expandCollapseChevronImageView"
app:layout_constraintTop_toBottomOf="@id/senderNameTextView"
app:layout_constraintTop_toBottomOf="@id/senderNameWrapper"
tools:text="&lt;alice@pm.me&gt;" />
<ImageView

View File

@ -45,6 +45,6 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="@color/sunglow" />
app:tint="@color/notification_warning" />
</merge>

View File

@ -92,32 +92,62 @@
android:layout_height="@dimen/mailbox_list_item_icon_size"
android:layout_marginEnd="@dimen/mailbox_list_item_views_margin"
app:layout_constraintStart_toEndOf="@id/forward_image_view"
app:layout_constraintEnd_toStartOf="@id/correspondents_text_view"
app:layout_constraintTop_toTopOf="@id/correspondents_text_view"
app:layout_constraintBottom_toBottomOf="@id/correspondents_text_view"
app:layout_constraintEnd_toStartOf="@id/correspondents_wrapper_view"
app:layout_constraintTop_toTopOf="@id/correspondents_wrapper_view"
app:layout_constraintBottom_toBottomOf="@id/correspondents_wrapper_view"
app:srcCompat="@drawable/ic_proton_pen" />
<TextView
android:id="@+id/correspondents_text_view"
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/correspondents_wrapper_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/mailbox_list_item_views_margin"
app:layout_constraintStart_toEndOf="@id/draft_image_view"
app:layout_constraintEnd_toStartOf="@id/time_date_text_view"
app:layout_constraintTop_toTopOf="parent"
android:maxLines="1"
android:ellipsize="end"
android:textAppearance="@style/Proton.Text.Default"
tools:text="@tools:sample/full_names" />
app:layout_constraintStart_toEndOf="@id/draft_image_view"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/correspondents_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
app:layout_constraintStart_toStartOf="parent"
android:textAppearance="@style/Proton.Text.Default"
app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/full_names" />
<com.google.android.material.chip.Chip
android:id="@+id/authenticity_badge_chip"
android:layout_width="wrap_content"
android:layout_height="@dimen/mailbox_list_item_icon_size"
android:layout_marginStart="@dimen/padding_xs"
android:background="@color/background_secondary"
android:clickable="false"
android:contentDescription="@string/is_proton"
android:text="@string/is_proton_official_badge"
android:textAppearance="@style/Proton.Text.Overline"
android:textColor="?attr/proton_text_accent"
android:visibility="gone"
app:chipCornerRadius="@dimen/size_corner_xxs"
app:chipMinTouchTargetSize="0dp"
app:layout_constraintBottom_toBottomOf="@id/correspondents_text_view"
app:layout_constraintStart_toEndOf="@id/correspondents_text_view"
app:layout_constraintTop_toTopOf="@id/correspondents_text_view"
app:rippleColor="@android:color/transparent"
app:textEndPadding="@dimen/padding_xs"
app:textStartPadding="@dimen/padding_xs" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/time_date_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/correspondents_text_view"
app:layout_constraintStart_toEndOf="@id/correspondents_wrapper_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/correspondents_text_view"
app:layout_constraintBottom_toBottomOf="@id/correspondents_text_view"
app:layout_constraintTop_toTopOf="@id/correspondents_wrapper_view"
app:layout_constraintBottom_toBottomOf="@id/correspondents_wrapper_view"
android:textAppearance="@style/Proton.Text.Caption.Weak"
tools:text="@tools:sample/date/ddmmyy" />
@ -128,8 +158,8 @@
android:orientation="horizontal"
app:layout_constraintStart_toEndOf="@id/sender_initial_barrier"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/correspondents_text_view" />
app:layout_constraintTop_toBottomOf="@id/correspondents_wrapper_view" />
<ImageView
android:id="@+id/first_location_image_view"
android:layout_width="@dimen/mailbox_list_item_icon_size"
@ -180,7 +210,7 @@
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/third_location_image_view"
app:layout_constraintEnd_toStartOf="@id/messages_number_text_view"
app:layout_constraintTop_toBottomOf="@id/correspondents_text_view"
app:layout_constraintTop_toBottomOf="@id/correspondents_wrapper_view"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constrainedWidth="true"
android:maxLines="1"

View File

@ -27,8 +27,9 @@
<dimen name="size_icon_s">16dp</dimen>
<dimen name="size_icon_m">24dp</dimen>
<dimen name="size_icon_l">32dp</dimen>
<dimen name="size_corner_xxxs">4dp</dimen>
<dimen name="size_corner_xxs">6dp</dimen>
<dimen name="size_corner_xs">8dp</dimen>
<dimen name="size_corner_xxs">4dp</dimen>
<dimen name="size_badge">24dp</dimen>
<dimen name="size_corner_s">16dp</dimen>

View File

@ -62,6 +62,8 @@
<string name="mailbox_updated_just_now">Updated just now</string>
<string name="mailbox_updated_recently">Updated recently</string>
<string name="mailbox_updating">Updating…</string>
<string name="is_proton">This is a Proton message</string>
<string name="is_proton_official_badge">Official</string>
<!-- endregion -->
<!-- region Details -->
<string name="details_open_in_proton_calendar_title">Open in Proton Calendar</string>

View File

@ -95,8 +95,8 @@ import me.proton.core.domain.arch.ResponseSource
import me.proton.core.domain.entity.UserId
import me.proton.core.test.android.ArchTest
import me.proton.core.test.kotlin.CoroutinesTest
import me.proton.core.util.kotlin.EMPTY_STRING
import me.proton.core.test.kotlin.TestDispatcherProvider
import me.proton.core.util.kotlin.EMPTY_STRING
import java.util.UUID
import kotlin.test.AfterTest
import kotlin.test.BeforeTest

View File

@ -73,7 +73,7 @@ class MailboxItemUiModelMapperTest {
// given
val message = Message().apply {
messageId = TEST_ITEM_ID
sender = MessageSender(TEST_SENDER_NAME, TEST_SENDER_ADDRESS)
sender = MessageSender(TEST_SENDER_NAME, TEST_SENDER_ADDRESS, true)
subject = TEST_SUBJECT
time = TEST_MESSAGE_TIME_SEC
numAttachments = 1
@ -107,7 +107,8 @@ class MailboxItemUiModelMapperTest {
messageLabels = emptyList(),
allLabelsIds = emptyList(),
isDraft = false,
isScheduled = false
isScheduled = false,
isProton = true
)
// when
@ -150,10 +151,10 @@ class MailboxItemUiModelMapperTest {
@Test
fun `message sender has name from correspondent address`() = runBlockingTest {
// given
val sender = MessageSender(EMPTY_STRING, TEST_EMAIL_ADDRESS)
val sender = MessageSender(EMPTY_STRING, TEST_EMAIL_ADDRESS, true)
val message = buildMessage(sender)
val expected = buildMailboxItemUiModel(
correspondentsNames = sender.emailAddress!!, messageData = buildMessageData()
correspondentsNames = sender.emailAddress!!, messageData = buildMessageData(), isProton = true
)
// when
@ -166,9 +167,11 @@ class MailboxItemUiModelMapperTest {
@Test
fun `message sender has name from correspondent name`() = runBlockingTest {
// given
val sender = MessageSender(TEST_CORRESPONDENT_NAME, TEST_EMAIL_ADDRESS)
val sender = MessageSender(TEST_CORRESPONDENT_NAME, TEST_EMAIL_ADDRESS, true)
val message = buildMessage(sender)
val expected = buildMailboxItemUiModel(correspondentsNames = sender.name!!, messageData = buildMessageData())
val expected = buildMailboxItemUiModel(
correspondentsNames = sender.name!!, messageData = buildMessageData(), isProton = true
)
// when
val result = mapper.toUiModel(message, INBOX.asLabelId(), allLabels = emptyList())
@ -180,10 +183,10 @@ class MailboxItemUiModelMapperTest {
@Test
fun `message has sender as correspondents names for inbox folder`() = runBlockingTest {
// given
val sender = MessageSender(TEST_CORRESPONDENT_NAME, TEST_EMAIL_ADDRESS)
val sender = MessageSender(TEST_CORRESPONDENT_NAME, TEST_EMAIL_ADDRESS, true)
val message = buildMessage(sender = sender)
val expected = buildMailboxItemUiModel(
correspondentsNames = TEST_CORRESPONDENT_NAME, messageData = buildMessageData()
correspondentsNames = TEST_CORRESPONDENT_NAME, messageData = buildMessageData(), isProton = true
)
// when
@ -320,7 +323,8 @@ class MailboxItemUiModelMapperTest {
messageLabels = emptyList(),
allLabelsIds = emptyList(),
isDraft = false,
isScheduled = false
isScheduled = false,
isProton = false
)
// when
@ -544,7 +548,7 @@ class MailboxItemUiModelMapperTest {
}
fun buildMessage(
sender: MessageSender = MessageSender(EMPTY_STRING, EMPTY_STRING),
sender: MessageSender = MessageSender(EMPTY_STRING, EMPTY_STRING, false),
toRecipients: List<MessageRecipient> = emptyList(),
ccRecipients: List<MessageRecipient> = emptyList(),
bccRecipients: List<MessageRecipient> = emptyList(),
@ -588,6 +592,7 @@ class MailboxItemUiModelMapperTest {
messageData: MessageData? = null,
messageLabels: List<LabelChipUiModel> = emptyList(),
allLabelsIds: List<LabelId> = emptyList(),
isProton: Boolean = false
) = MailboxItemUiModel(
itemId = EMPTY_STRING,
correspondentsNames = correspondentsNames,
@ -602,7 +607,8 @@ class MailboxItemUiModelMapperTest {
messageLabels = messageLabels,
allLabelsIds = allLabelsIds,
isDraft = isDraft,
isScheduled = isScheduled
isScheduled = isScheduled,
isProton = isProton
)
fun buildMessageData() = MessageData(

View File

@ -641,7 +641,8 @@ class MailboxViewModelTest : ArchTest by ArchTest(),
messageLabels = emptyList(),
allLabelsIds = emptyList(),
isDraft = false,
isScheduled = false
isScheduled = false,
isProton = false
)