From fe6a9faeeb7d0bd198c316c275d6ab7bd9e95f49 Mon Sep 17 00:00:00 2001 From: NicolasBourdin88 Date: Mon, 13 May 2024 08:39:00 +0200 Subject: [PATCH 01/17] Setup Bimi object --- .../mail/data/cache/RealmDatabase.kt | 6 +- .../com/infomaniak/mail/data/models/Bimi.kt | 61 +++++++++++++++++++ .../mail/data/models/message/Message.kt | 2 + 3 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/infomaniak/mail/data/models/Bimi.kt diff --git a/app/src/main/java/com/infomaniak/mail/data/cache/RealmDatabase.kt b/app/src/main/java/com/infomaniak/mail/data/cache/RealmDatabase.kt index 93d7ce7cbc..a58c14a934 100644 --- a/app/src/main/java/com/infomaniak/mail/data/cache/RealmDatabase.kt +++ b/app/src/main/java/com/infomaniak/mail/data/cache/RealmDatabase.kt @@ -18,10 +18,7 @@ package com.infomaniak.mail.data.cache import android.content.Context -import com.infomaniak.mail.data.models.AppSettings -import com.infomaniak.mail.data.models.Attachment -import com.infomaniak.mail.data.models.Folder -import com.infomaniak.mail.data.models.Quotas +import com.infomaniak.mail.data.models.* import com.infomaniak.mail.data.models.addressBook.AddressBook import com.infomaniak.mail.data.models.calendar.Attendee import com.infomaniak.mail.data.models.calendar.CalendarEvent @@ -199,6 +196,7 @@ object RealmDatabase { CalendarEvent::class, Attendee::class, Signature::class, + Bimi::class, ) //endregion diff --git a/app/src/main/java/com/infomaniak/mail/data/models/Bimi.kt b/app/src/main/java/com/infomaniak/mail/data/models/Bimi.kt new file mode 100644 index 0000000000..07484287e7 --- /dev/null +++ b/app/src/main/java/com/infomaniak/mail/data/models/Bimi.kt @@ -0,0 +1,61 @@ +/* + * Infomaniak Mail - Android + * Copyright (C) 2024 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.infomaniak.mail.data.models + +import android.os.Parcel +import android.os.Parcelable +import io.realm.kotlin.types.EmbeddedRealmObject +import kotlinx.parcelize.Parceler +import kotlinx.parcelize.Parcelize +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Parcelize +@Serializable +class Bimi() : EmbeddedRealmObject, Parcelable { + @SerialName("svg_content") + var svgContentUrl: String? = null + @SerialName("is_certified") + var isCertified: Boolean = false + + constructor(svgContentUrl: String, isCertified: Boolean) : this() { + this.svgContentUrl = svgContentUrl + this.isCertified = isCertified + } + + companion object : Parceler { + + override fun create(parcel: Parcel): Bimi = with(parcel) { + val svgContentUrl = readString()!! + val isCertified = customReadBoolean() + + return Bimi(svgContentUrl, isCertified) + } + + override fun Bimi.write(parcel: Parcel, flags: Int) = with(parcel) { + writeString(svgContentUrl) + customWriteBoolean(isCertified) + } + + private fun Parcel.customWriteBoolean(value: Boolean) { + writeInt(if (value) 1 else 0) + } + + private fun Parcel.customReadBoolean(): Boolean = readInt() != 0 + } +} diff --git a/app/src/main/java/com/infomaniak/mail/data/models/message/Message.kt b/app/src/main/java/com/infomaniak/mail/data/models/message/Message.kt index b405a3efb3..7376906281 100644 --- a/app/src/main/java/com/infomaniak/mail/data/models/message/Message.kt +++ b/app/src/main/java/com/infomaniak/mail/data/models/message/Message.kt @@ -24,6 +24,7 @@ import com.infomaniak.mail.data.api.RealmInstantSerializer import com.infomaniak.mail.data.api.UnwrappingJsonListSerializer import com.infomaniak.mail.data.cache.mailboxContent.FolderController.Companion.SEARCH_FOLDER_ID import com.infomaniak.mail.data.models.Attachment +import com.infomaniak.mail.data.models.Bimi import com.infomaniak.mail.data.models.Folder import com.infomaniak.mail.data.models.calendar.CalendarEventResponse import com.infomaniak.mail.data.models.correspondent.Recipient @@ -97,6 +98,7 @@ class Message : RealmObject { var size: Int = 0 @SerialName("has_unsubscribe_link") var hasUnsubscribeLink: Boolean? = null + var bimi : Bimi? = null // TODO: Those are unused for now, but if we ever want to use them, we need to save them in `Message.keepHeavyData()`. // If we don't do it now, we'll probably forget to do it in the future. From e6022baf37afe8f29ffe701aecff0ad239e9b04c Mon Sep 17 00:00:00 2001 From: NicolasBourdin88 Date: Tue, 14 May 2024 10:07:43 +0200 Subject: [PATCH 02/17] Add bimi route --- app/src/main/java/com/infomaniak/mail/data/api/ApiRoutes.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/com/infomaniak/mail/data/api/ApiRoutes.kt b/app/src/main/java/com/infomaniak/mail/data/api/ApiRoutes.kt index 718a47d769..82b523508b 100644 --- a/app/src/main/java/com/infomaniak/mail/data/api/ApiRoutes.kt +++ b/app/src/main/java/com/infomaniak/mail/data/api/ApiRoutes.kt @@ -261,4 +261,8 @@ object ApiRoutes { fun resource(resource: String): String { return "$MAIL_API$resource" } + + fun bimi(bimi: String): String { + return "$MAIL_API$bimi" + } } From 48ee688bfd7e8d8fdaf58d805d2cc8b454c78230 Mon Sep 17 00:00:00 2001 From: NicolasBourdin88 Date: Tue, 14 May 2024 10:12:14 +0200 Subject: [PATCH 03/17] Load bimi and certified icon in thread adapter --- .../mail/ui/main/thread/ThreadAdapter.kt | 19 ++++++++++- .../com/infomaniak/mail/views/AvatarView.kt | 16 ++++++++++ app/src/main/res/drawable/ic_certified.xml | 10 ++++++ app/src/main/res/layout/item_message.xml | 32 +++++++++---------- 4 files changed, 59 insertions(+), 18 deletions(-) create mode 100644 app/src/main/res/drawable/ic_certified.xml diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt index 79062ef81e..2adb270f7d 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt @@ -40,7 +40,9 @@ import com.infomaniak.lib.core.utils.context import com.infomaniak.lib.core.utils.isNightModeEnabled import com.infomaniak.mail.MatomoMail.trackMessageEvent import com.infomaniak.mail.R +import com.infomaniak.mail.data.api.ApiRoutes import com.infomaniak.mail.data.models.Attachment +import com.infomaniak.mail.data.models.Bimi import com.infomaniak.mail.data.models.calendar.Attendee import com.infomaniak.mail.data.models.calendar.Attendee.AttendanceState import com.infomaniak.mail.data.models.correspondent.Recipient @@ -337,12 +339,16 @@ class ThreadAdapter( shortMessageDate.text = "" } else { val firstSender = message.sender - userAvatar.loadAvatar(firstSender) + expeditorName.apply { text = firstSender?.let { context.getPrettyNameAndEmail(it).first } ?: run { context.getString(R.string.unknownRecipientTitle) } setTextAppearance(R.style.BodyMedium) } + + loadAvatar(message.bimi, firstSender) + iconCertified.isVisible = message.bimi?.isCertified ?: false + shortMessageDate.text = mailFormattedDate(context, messageDate) } @@ -362,6 +368,17 @@ class ThreadAdapter( bindRecipientDetails(message, messageDate) } + private fun ItemMessageBinding.loadAvatar( + bimi: Bimi?, + firstSender: Recipient? + ) { + if (bimi == null || bimi.isCertified.not()) { + userAvatar.loadAvatar(firstSender) + } else { + userAvatar.loadBimiAvatar(ApiRoutes.bimi(bimi.svgContentUrl.orEmpty()), firstSender) + } + } + private fun ItemMessageBinding.setDetailedFieldsVisibility(message: Message) { fromGroup.isVisible = message.from.isNotEmpty() toGroup.isVisible = message.to.isNotEmpty() diff --git a/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt b/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt index fc36215830..7e01a27d44 100644 --- a/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt +++ b/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt @@ -140,6 +140,22 @@ class AvatarView @JvmOverloads constructor( binding.avatarImage.load(R.drawable.ic_unknown_user_avatar) } + fun loadBimiAvatar(urlBimi: String, correspondent: Correspondent?) = with(binding.avatarImage) { + val fakeUrlBimi = "https://upload.wikimedia.org/wikipedia/commons/4/4f/SVG_Logo.svg" + + contentDescription = correspondent?.email.orEmpty() + loadAvatar( + backgroundColor = context.getBackgroundColorBasedOnId( + correspondent?.email.orEmpty().hashCode(), + R.array.AvatarColors + ), + avatarUrl = fakeUrlBimi, + initials = correspondent?.initials.orEmpty(), + imageLoader = context.simpleImageLoader, + initialsColor = context.getColor(R.color.onColorfulBackground), + ) + } + fun setImageDrawable(drawable: Drawable?) = binding.avatarImage.setImageDrawable(drawable) private fun searchInMergedContact(correspondent: Correspondent, contacts: MergedContactDictionary): MergedContact? { diff --git a/app/src/main/res/drawable/ic_certified.xml b/app/src/main/res/drawable/ic_certified.xml new file mode 100644 index 0000000000..d6095cb502 --- /dev/null +++ b/app/src/main/res/drawable/ic_certified.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/item_message.xml b/app/src/main/res/layout/item_message.xml index 228489dcb6..3983e3a10f 100644 --- a/app/src/main/res/layout/item_message.xml +++ b/app/src/main/res/layout/item_message.xml @@ -62,27 +62,24 @@ android:ellipsize="end" android:lines="1" app:layout_constrainedWidth="true" - app:layout_constraintEnd_toStartOf="@id/shortMessageDate" + app:layout_constraintEnd_toStartOf="@id/iconCertified" app:layout_constraintHorizontal_bias="0" app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintStart_toEndOf="@id/userAvatar" app:layout_constraintTop_toTopOf="parent" tools:text="@tools:sample/full_names" /> - + + Date: Tue, 14 May 2024 10:49:08 +0200 Subject: [PATCH 04/17] Add Bimi in detailed contact bottom sheet dialog --- .../mail/ui/main/AvatarNameEmailView.kt | 18 ++++++++--- .../DetailedContactBottomSheetDialog.kt | 2 +- .../main/thread/DetailedRecipientAdapter.kt | 10 +++--- .../mail/ui/main/thread/ThreadAdapter.kt | 14 ++++---- .../mail/ui/main/thread/ThreadFragment.kt | 7 ++-- .../res/layout/view_avatar_name_email.xml | 32 +++++++++++++++---- .../main/res/navigation/main_navigation.xml | 4 +++ 7 files changed, 62 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/AvatarNameEmailView.kt b/app/src/main/java/com/infomaniak/mail/ui/main/AvatarNameEmailView.kt index 7e461e6292..1a525dd4cb 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/AvatarNameEmailView.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/AvatarNameEmailView.kt @@ -31,6 +31,7 @@ import androidx.core.view.isVisible import com.infomaniak.lib.core.utils.getAttributes import com.infomaniak.lib.core.utils.setMarginsRelative import com.infomaniak.mail.R +import com.infomaniak.mail.data.models.Bimi import com.infomaniak.mail.data.models.calendar.Attendee import com.infomaniak.mail.data.models.correspondent.Correspondent import com.infomaniak.mail.data.models.correspondent.MergedContact @@ -71,9 +72,13 @@ class AvatarNameEmailView @JvmOverloads constructor( } } - fun setCorrespondent(correspondent: Correspondent) = with(binding) { - userAvatar.loadAvatar(correspondent) - setNameAndEmail(correspondent) + fun setCorrespondent(correspondent: Correspondent, bimi: Bimi? = null) = with(binding) { + if (bimi == null || !bimi.isCertified) { + userAvatar.loadAvatar(correspondent) + } else { + userAvatar.loadBimiAvatar(bimi.svgContentUrl.orEmpty(), correspondent) + } + setNameAndEmail(correspondent, isCorrespondentCertified = bimi?.isCertified ?: false) } fun setMergedContact(mergedContact: MergedContact) = with(binding) { @@ -86,12 +91,17 @@ class AvatarNameEmailView @JvmOverloads constructor( setNameAndEmail(attendee) } - private fun ViewAvatarNameEmailBinding.setNameAndEmail(correspondent: Correspondent) { + private fun ViewAvatarNameEmailBinding.setNameAndEmail( + correspondent: Correspondent, + isCorrespondentCertified: Boolean = false + ) { val filledSingleField = fillInUserNameAndEmail(correspondent, userName, userEmail, ignoreIsMe = !processNameAndEmail) if (displayAsAttendee) { val userNameTextColor = if (filledSingleField) R.style.AvatarNameEmailSecondary else R.style.AvatarNameEmailPrimary userName.setTextAppearance(userNameTextColor) } + + iconCertified.isVisible = isCorrespondentCertified } fun setAutocompleteUnknownContact(searchQuery: String) = with(binding) { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/DetailedContactBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/DetailedContactBottomSheetDialog.kt index 58a02ccdfa..faf94479c8 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/DetailedContactBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/DetailedContactBottomSheetDialog.kt @@ -53,7 +53,7 @@ class DetailedContactBottomSheetDialog : ActionsBottomSheetDialog() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) = with(binding) { super.onViewCreated(view, savedInstanceState) - contactDetails.setCorrespondent(navigationArgs.recipient) + contactDetails.setCorrespondent(navigationArgs.recipient, navigationArgs.bimi) setupListeners() } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/DetailedRecipientAdapter.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/DetailedRecipientAdapter.kt index 7b74f47b97..251bb37705 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/DetailedRecipientAdapter.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/DetailedRecipientAdapter.kt @@ -23,17 +23,18 @@ import androidx.recyclerview.widget.RecyclerView.Adapter import androidx.recyclerview.widget.RecyclerView.ViewHolder import com.infomaniak.lib.core.utils.context import com.infomaniak.mail.MatomoMail.trackMessageEvent +import com.infomaniak.mail.data.models.Bimi import com.infomaniak.mail.data.models.correspondent.Recipient import com.infomaniak.mail.databinding.ItemDetailedContactBinding import com.infomaniak.mail.ui.main.thread.DetailedRecipientAdapter.DetailedRecipientViewHolder import com.infomaniak.mail.utils.UiUtils.fillInUserNameAndEmail class DetailedRecipientAdapter( - private val onContactClicked: ((contact: Recipient) -> Unit)?, + private val onContactClicked: ((contact: Recipient, bimi: Bimi?) -> Unit)? ) : Adapter() { private var recipients = emptyList() - + private var bimi: Bimi? = null override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DetailedRecipientViewHolder { return DetailedRecipientViewHolder(ItemDetailedContactBinding.inflate(LayoutInflater.from(parent.context), parent, false)) } @@ -45,14 +46,15 @@ class DetailedRecipientAdapter( name.setOnClickListener { context.trackMessageEvent("selectRecipient") - onContactClicked?.invoke(recipient) + onContactClicked?.invoke(recipient, bimi) } } override fun getItemCount(): Int = recipients.count() - fun updateList(newList: List) { + fun updateList(newList: List, newBimi: Bimi? = null) { recipients = newList + this.bimi = newBimi } class DetailedRecipientViewHolder(val binding: ItemDetailedContactBinding) : ViewHolder(binding.root) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt index 2adb270f7d..072ca0ae83 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt @@ -121,7 +121,7 @@ class ThreadAdapter( shouldLoadDistantResources, threadAdapterCallbacks?.onContactClicked, threadAdapterCallbacks?.onAttachmentClicked, - threadAdapterCallbacks?.onAttachmentOptionsClicked, + threadAdapterCallbacks?.onAttachmentOptionsClicked ) } else { SuperCollapsedBlockViewHolder(ItemSuperCollapsedBlockBinding.inflate(layoutInflater, parent, false)) @@ -355,7 +355,7 @@ class ThreadAdapter( val listener: OnClickListener? = message.sender?.let { recipient -> OnClickListener { context.trackMessageEvent("selectAvatar") - threadAdapterCallbacks?.onContactClicked?.invoke(recipient) + threadAdapterCallbacks?.onContactClicked?.invoke(recipient, message.bimi) } } @@ -372,7 +372,7 @@ class ThreadAdapter( bimi: Bimi?, firstSender: Recipient? ) { - if (bimi == null || bimi.isCertified.not()) { + if (bimi == null || !bimi.isCertified) { userAvatar.loadAvatar(firstSender) } else { userAvatar.loadBimiAvatar(ApiRoutes.bimi(bimi.svgContentUrl.orEmpty()), firstSender) @@ -414,7 +414,7 @@ class ThreadAdapter( private fun MessageViewHolder.bindRecipientDetails(message: Message, messageDate: Date) = with(binding) { - fromAdapter.updateList(message.from.toList()) + fromAdapter.updateList(message.from.toList(), message.bimi) toAdapter.updateList(message.to.toList()) val ccIsNotEmpty = message.cc.isNotEmpty() @@ -687,7 +687,7 @@ class ThreadAdapter( data class ThreadAdapterCallbacks( var onBodyWebViewFinishedLoading: (() -> Unit)? = null, - var onContactClicked: ((contact: Recipient) -> Unit)? = null, + var onContactClicked: ((contact: Recipient, bimi: Bimi?) -> Unit)? = null, var onDeleteDraftClicked: ((message: Message) -> Unit)? = null, var onDraftClicked: ((message: Message) -> Unit)? = null, var onAttachmentClicked: ((attachment: Attachment) -> Unit)? = null, @@ -725,9 +725,9 @@ class ThreadAdapter( private class MessageViewHolder( override val binding: ItemMessageBinding, private val shouldLoadDistantResources: Boolean, - onContactClicked: ((contact: Recipient) -> Unit)?, + onContactClicked: ((contact: Recipient, bimi: Bimi?) -> Unit)?, onAttachmentClicked: ((attachment: Attachment) -> Unit)?, - onAttachmentOptionsClicked: ((attachment: Attachment) -> Unit)?, + onAttachmentOptionsClicked: ((attachment: Attachment) -> Unit)? ) : ThreadAdapterViewHolder(binding) { val fromAdapter = DetailedRecipientAdapter(onContactClicked) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadFragment.kt index d3e5c45e7c..a0600aca51 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadFragment.kt @@ -224,10 +224,13 @@ class ThreadFragment : Fragment() { override val isCalendarEventExpandedMap by threadState::isCalendarEventExpandedMap }, threadAdapterCallbacks = ThreadAdapterCallbacks( - onContactClicked = { + onContactClicked = { recipient, bimi -> safeNavigate( resId = R.id.detailedContactBottomSheetDialog, - args = DetailedContactBottomSheetDialogArgs(it).toBundle(), + args = DetailedContactBottomSheetDialogArgs( + recipient = recipient, + bimi + ).toBundle(), ) }, onDraftClicked = { message -> diff --git a/app/src/main/res/layout/view_avatar_name_email.xml b/app/src/main/res/layout/view_avatar_name_email.xml index b2b22e3ff3..2b2540c86c 100644 --- a/app/src/main/res/layout/view_avatar_name_email.xml +++ b/app/src/main/res/layout/view_avatar_name_email.xml @@ -64,14 +64,32 @@ app:layout_constraintStart_toEndOf="@id/avatarLayout" app:layout_constraintTop_toTopOf="@id/avatarLayout"> - + android:gravity="center_vertical" + android:orientation="horizontal" + tools:ignore="UseCompoundDrawables"> + + + + + + Date: Tue, 14 May 2024 11:47:37 +0200 Subject: [PATCH 05/17] Add Bimi in thread list adapter --- .../com/infomaniak/mail/data/models/thread/Thread.kt | 7 ++++--- .../infomaniak/mail/ui/main/folder/ThreadListAdapter.kt | 9 ++++++++- .../main/java/com/infomaniak/mail/views/AvatarView.kt | 7 +++++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/data/models/thread/Thread.kt b/app/src/main/java/com/infomaniak/mail/data/models/thread/Thread.kt index ca3d2ae642..93122866aa 100644 --- a/app/src/main/java/com/infomaniak/mail/data/models/thread/Thread.kt +++ b/app/src/main/java/com/infomaniak/mail/data/models/thread/Thread.kt @@ -26,6 +26,7 @@ import com.infomaniak.mail.MatomoMail.SEARCH_FOLDER_FILTER_NAME import com.infomaniak.mail.R import com.infomaniak.mail.data.api.RealmInstantSerializer import com.infomaniak.mail.data.cache.mailboxContent.FolderController +import com.infomaniak.mail.data.models.Bimi import com.infomaniak.mail.data.models.Folder import com.infomaniak.mail.data.models.Folder.FolderRole import com.infomaniak.mail.data.models.correspondent.Recipient @@ -213,7 +214,7 @@ class Thread : RealmObject { } } - fun computeAvatarRecipient(): Recipient? = runCatching { + fun computeAvatarRecipient(): Pair = runCatching { val message = messages .lastOrNull { it.folder.role != FolderRole.SENT && it.folder.role != FolderRole.DRAFT } @@ -224,7 +225,7 @@ class Thread : RealmObject { else -> message.from } - recipients.firstOrNull() + recipients.firstOrNull() to message.bimi }.getOrElse { throwable -> Sentry.withScope { scope -> @@ -239,7 +240,7 @@ class Thread : RealmObject { Sentry.captureException(throwable) } - null + null to null } fun computeDisplayedRecipients(): RealmList = when (folder.role) { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt index 15aba5c08b..db32b14be8 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt @@ -49,6 +49,7 @@ import com.infomaniak.mail.R import com.infomaniak.mail.data.LocalSettings import com.infomaniak.mail.data.LocalSettings.SwipeAction import com.infomaniak.mail.data.LocalSettings.ThreadDensity +import com.infomaniak.mail.data.models.Bimi import com.infomaniak.mail.data.models.Folder.FolderRole import com.infomaniak.mail.data.models.correspondent.Recipient import com.infomaniak.mail.data.models.thread.Thread @@ -400,7 +401,13 @@ class ThreadListAdapter @Inject constructor( } private fun CardviewThreadItemBinding.displayAvatar(thread: Thread) { - expeditorAvatar.loadAvatar(thread.computeAvatarRecipient()) + val avatarValue = thread.computeAvatarRecipient() + + if (avatarValue.second != null && avatarValue.second?.isCertified == true) { + expeditorAvatar.loadBimiAvatar(avatarValue.second?.svgContentUrl.toString(), avatarValue.first) + } else { + expeditorAvatar.loadAvatar(avatarValue.first) + } } private fun CardviewThreadItemBinding.formatRecipientNames(recipients: List): String { diff --git a/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt b/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt index 7e01a27d44..b72924b9bd 100644 --- a/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt +++ b/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt @@ -55,9 +55,12 @@ class AvatarView @JvmOverloads constructor( private val binding by lazy { ViewAvatarBinding.inflate(LayoutInflater.from(context), this, true) } private var currentCorrespondent: Correspondent? = null + private var isBimiShow: Boolean? = null private val mergedContactObserver = Observer { contacts -> - currentCorrespondent?.let { correspondent -> loadAvatarUsingDictionary(correspondent, contacts) } + currentCorrespondent?.let { correspondent -> + if (isBimiShow == false) loadAvatarUsingDictionary(correspondent, contacts) + } } @Inject @@ -142,8 +145,8 @@ class AvatarView @JvmOverloads constructor( fun loadBimiAvatar(urlBimi: String, correspondent: Correspondent?) = with(binding.avatarImage) { val fakeUrlBimi = "https://upload.wikimedia.org/wikipedia/commons/4/4f/SVG_Logo.svg" - contentDescription = correspondent?.email.orEmpty() + isBimiShow = fakeUrlBimi.isNotEmpty() loadAvatar( backgroundColor = context.getBackgroundColorBasedOnId( correspondent?.email.orEmpty().hashCode(), From cf44a4cad746893029f9f90de321d7db051ae8f5 Mon Sep 17 00:00:00 2001 From: NicolasBourdin88 Date: Tue, 14 May 2024 12:52:28 +0200 Subject: [PATCH 06/17] Fix return null instead of null to null --- .../java/com/infomaniak/mail/data/models/thread/Thread.kt | 4 ++-- .../com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/data/models/thread/Thread.kt b/app/src/main/java/com/infomaniak/mail/data/models/thread/Thread.kt index 93122866aa..0002395790 100644 --- a/app/src/main/java/com/infomaniak/mail/data/models/thread/Thread.kt +++ b/app/src/main/java/com/infomaniak/mail/data/models/thread/Thread.kt @@ -214,7 +214,7 @@ class Thread : RealmObject { } } - fun computeAvatarRecipient(): Pair = runCatching { + fun computeAvatarRecipient(): Pair? = runCatching { val message = messages .lastOrNull { it.folder.role != FolderRole.SENT && it.folder.role != FolderRole.DRAFT } @@ -240,7 +240,7 @@ class Thread : RealmObject { Sentry.captureException(throwable) } - null to null + null } fun computeDisplayedRecipients(): RealmList = when (folder.role) { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt index db32b14be8..608aa877f5 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt @@ -49,7 +49,6 @@ import com.infomaniak.mail.R import com.infomaniak.mail.data.LocalSettings import com.infomaniak.mail.data.LocalSettings.SwipeAction import com.infomaniak.mail.data.LocalSettings.ThreadDensity -import com.infomaniak.mail.data.models.Bimi import com.infomaniak.mail.data.models.Folder.FolderRole import com.infomaniak.mail.data.models.correspondent.Recipient import com.infomaniak.mail.data.models.thread.Thread @@ -403,10 +402,10 @@ class ThreadListAdapter @Inject constructor( private fun CardviewThreadItemBinding.displayAvatar(thread: Thread) { val avatarValue = thread.computeAvatarRecipient() - if (avatarValue.second != null && avatarValue.second?.isCertified == true) { + if (avatarValue?.second != null && avatarValue.second?.isCertified == true) { expeditorAvatar.loadBimiAvatar(avatarValue.second?.svgContentUrl.toString(), avatarValue.first) } else { - expeditorAvatar.loadAvatar(avatarValue.first) + expeditorAvatar.loadAvatar(avatarValue?.first) } } From 4b62abe2236d663659ea18bfc0243df25af1d67e Mon Sep 17 00:00:00 2001 From: NicolasBourdin88 Date: Tue, 14 May 2024 13:23:30 +0200 Subject: [PATCH 07/17] Place logic load avatar logic in same place --- .../infomaniak/mail/ui/main/AvatarNameEmailView.kt | 6 +----- .../mail/ui/main/thread/ThreadAdapter.kt | 14 +------------- .../java/com/infomaniak/mail/views/AvatarView.kt | 11 +++++++++++ 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/AvatarNameEmailView.kt b/app/src/main/java/com/infomaniak/mail/ui/main/AvatarNameEmailView.kt index 1a525dd4cb..293373a8ab 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/AvatarNameEmailView.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/AvatarNameEmailView.kt @@ -73,11 +73,7 @@ class AvatarNameEmailView @JvmOverloads constructor( } fun setCorrespondent(correspondent: Correspondent, bimi: Bimi? = null) = with(binding) { - if (bimi == null || !bimi.isCertified) { - userAvatar.loadAvatar(correspondent) - } else { - userAvatar.loadBimiAvatar(bimi.svgContentUrl.orEmpty(), correspondent) - } + userAvatar.loadAvatar(correspondent, bimi) setNameAndEmail(correspondent, isCorrespondentCertified = bimi?.isCertified ?: false) } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt index 072ca0ae83..178888bc6a 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt @@ -40,7 +40,6 @@ import com.infomaniak.lib.core.utils.context import com.infomaniak.lib.core.utils.isNightModeEnabled import com.infomaniak.mail.MatomoMail.trackMessageEvent import com.infomaniak.mail.R -import com.infomaniak.mail.data.api.ApiRoutes import com.infomaniak.mail.data.models.Attachment import com.infomaniak.mail.data.models.Bimi import com.infomaniak.mail.data.models.calendar.Attendee @@ -346,7 +345,7 @@ class ThreadAdapter( setTextAppearance(R.style.BodyMedium) } - loadAvatar(message.bimi, firstSender) + userAvatar.loadAvatar(firstSender, message.bimi) iconCertified.isVisible = message.bimi?.isCertified ?: false shortMessageDate.text = mailFormattedDate(context, messageDate) @@ -368,17 +367,6 @@ class ThreadAdapter( bindRecipientDetails(message, messageDate) } - private fun ItemMessageBinding.loadAvatar( - bimi: Bimi?, - firstSender: Recipient? - ) { - if (bimi == null || !bimi.isCertified) { - userAvatar.loadAvatar(firstSender) - } else { - userAvatar.loadBimiAvatar(ApiRoutes.bimi(bimi.svgContentUrl.orEmpty()), firstSender) - } - } - private fun ItemMessageBinding.setDetailedFieldsVisibility(message: Message) { fromGroup.isVisible = message.from.isNotEmpty() toGroup.isVisible = message.to.isNotEmpty() diff --git a/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt b/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt index b72924b9bd..71c648a49c 100644 --- a/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt +++ b/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt @@ -33,8 +33,11 @@ import com.infomaniak.lib.core.utils.UtilsUi.getBackgroundColorBasedOnId import com.infomaniak.lib.core.utils.getAttributes import com.infomaniak.lib.core.utils.loadAvatar import com.infomaniak.mail.R +import com.infomaniak.mail.data.api.ApiRoutes +import com.infomaniak.mail.data.models.Bimi import com.infomaniak.mail.data.models.correspondent.Correspondent import com.infomaniak.mail.data.models.correspondent.MergedContact +import com.infomaniak.mail.data.models.correspondent.Recipient import com.infomaniak.mail.databinding.ViewAvatarBinding import com.infomaniak.mail.utils.AccountUtils import com.infomaniak.mail.utils.extensions.MergedContactDictionary @@ -186,4 +189,12 @@ class AvatarView @JvmOverloads constructor( ) } } + + fun loadAvatar(correspondent: Correspondent?, bimi: Bimi?) { + if (bimi == null || !bimi.isCertified) { + loadAvatar(correspondent) + } else { + loadBimiAvatar(ApiRoutes.bimi(bimi.svgContentUrl.orEmpty()), correspondent) + } + } } From e1362cd650234b162bce64be0d7196a2d255cf2a Mon Sep 17 00:00:00 2001 From: NicolasBourdin88 Date: Tue, 21 May 2024 13:11:40 +0200 Subject: [PATCH 08/17] Verify if bimi is show --- .../infomaniak/mail/ui/main/folder/ThreadListAdapter.kt | 5 +++-- app/src/main/java/com/infomaniak/mail/views/AvatarView.kt | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt index 608aa877f5..2721ed0d86 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt @@ -23,6 +23,7 @@ import android.graphics.Canvas import android.os.Build import android.text.Spannable import android.text.TextUtils.TruncateAt +import android.util.Log import android.view.HapticFeedbackConstants import android.view.LayoutInflater import android.view.View @@ -49,6 +50,7 @@ import com.infomaniak.mail.R import com.infomaniak.mail.data.LocalSettings import com.infomaniak.mail.data.LocalSettings.SwipeAction import com.infomaniak.mail.data.LocalSettings.ThreadDensity +import com.infomaniak.mail.data.api.ApiRoutes import com.infomaniak.mail.data.models.Folder.FolderRole import com.infomaniak.mail.data.models.correspondent.Recipient import com.infomaniak.mail.data.models.thread.Thread @@ -401,9 +403,8 @@ class ThreadListAdapter @Inject constructor( private fun CardviewThreadItemBinding.displayAvatar(thread: Thread) { val avatarValue = thread.computeAvatarRecipient() - if (avatarValue?.second != null && avatarValue.second?.isCertified == true) { - expeditorAvatar.loadBimiAvatar(avatarValue.second?.svgContentUrl.toString(), avatarValue.first) + expeditorAvatar.loadBimiAvatar(ApiRoutes.bimi(avatarValue.second?.svgContentUrl.toString()), avatarValue.first) } else { expeditorAvatar.loadAvatar(avatarValue?.first) } diff --git a/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt b/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt index 71c648a49c..d52a5ddecd 100644 --- a/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt +++ b/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt @@ -21,6 +21,7 @@ import android.content.Context import android.content.res.ColorStateList import android.graphics.drawable.Drawable import android.util.AttributeSet +import android.util.Log import android.view.LayoutInflater import android.widget.FrameLayout import android.widget.ImageView @@ -37,7 +38,6 @@ import com.infomaniak.mail.data.api.ApiRoutes import com.infomaniak.mail.data.models.Bimi import com.infomaniak.mail.data.models.correspondent.Correspondent import com.infomaniak.mail.data.models.correspondent.MergedContact -import com.infomaniak.mail.data.models.correspondent.Recipient import com.infomaniak.mail.databinding.ViewAvatarBinding import com.infomaniak.mail.utils.AccountUtils import com.infomaniak.mail.utils.extensions.MergedContactDictionary @@ -149,13 +149,14 @@ class AvatarView @JvmOverloads constructor( fun loadBimiAvatar(urlBimi: String, correspondent: Correspondent?) = with(binding.avatarImage) { val fakeUrlBimi = "https://upload.wikimedia.org/wikipedia/commons/4/4f/SVG_Logo.svg" contentDescription = correspondent?.email.orEmpty() - isBimiShow = fakeUrlBimi.isNotEmpty() + isBimiShow = urlBimi.isNotEmpty() + Log.e("Bimi", urlBimi) loadAvatar( backgroundColor = context.getBackgroundColorBasedOnId( correspondent?.email.orEmpty().hashCode(), R.array.AvatarColors ), - avatarUrl = fakeUrlBimi, + avatarUrl = urlBimi, initials = correspondent?.initials.orEmpty(), imageLoader = context.simpleImageLoader, initialsColor = context.getColor(R.color.onColorfulBackground), From 8a6dc561b240a4f384ae42bac841385d3bae8532 Mon Sep 17 00:00:00 2001 From: NicolasBourdin88 Date: Thu, 23 May 2024 10:46:06 +0200 Subject: [PATCH 09/17] Change certified icon size for dialog --- app/src/main/res/layout/item_message.xml | 4 ++-- app/src/main/res/layout/view_avatar_name_email.xml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/layout/item_message.xml b/app/src/main/res/layout/item_message.xml index 3983e3a10f..1184dfcf74 100644 --- a/app/src/main/res/layout/item_message.xml +++ b/app/src/main/res/layout/item_message.xml @@ -71,8 +71,8 @@ From 66c83e2576d2f811d9737d70aa319ef7c5bc277b Mon Sep 17 00:00:00 2001 From: NicolasBourdin88 Date: Thu, 23 May 2024 10:47:42 +0200 Subject: [PATCH 10/17] Simplify logic --- .../com/infomaniak/mail/data/models/thread/Thread.kt | 4 ++-- .../infomaniak/mail/ui/main/folder/ThreadListAdapter.kt | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/data/models/thread/Thread.kt b/app/src/main/java/com/infomaniak/mail/data/models/thread/Thread.kt index 0002395790..93122866aa 100644 --- a/app/src/main/java/com/infomaniak/mail/data/models/thread/Thread.kt +++ b/app/src/main/java/com/infomaniak/mail/data/models/thread/Thread.kt @@ -214,7 +214,7 @@ class Thread : RealmObject { } } - fun computeAvatarRecipient(): Pair? = runCatching { + fun computeAvatarRecipient(): Pair = runCatching { val message = messages .lastOrNull { it.folder.role != FolderRole.SENT && it.folder.role != FolderRole.DRAFT } @@ -240,7 +240,7 @@ class Thread : RealmObject { Sentry.captureException(throwable) } - null + null to null } fun computeDisplayedRecipients(): RealmList = when (folder.role) { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt index 2721ed0d86..35257524a7 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt @@ -23,7 +23,6 @@ import android.graphics.Canvas import android.os.Build import android.text.Spannable import android.text.TextUtils.TruncateAt -import android.util.Log import android.view.HapticFeedbackConstants import android.view.LayoutInflater import android.view.View @@ -402,11 +401,11 @@ class ThreadListAdapter @Inject constructor( } private fun CardviewThreadItemBinding.displayAvatar(thread: Thread) { - val avatarValue = thread.computeAvatarRecipient() - if (avatarValue?.second != null && avatarValue.second?.isCertified == true) { - expeditorAvatar.loadBimiAvatar(ApiRoutes.bimi(avatarValue.second?.svgContentUrl.toString()), avatarValue.first) + val (recipient, bimi) = thread.computeAvatarRecipient() + if (bimi != null && bimi.isCertified) { + expeditorAvatar.loadBimiAvatar(ApiRoutes.bimi(bimi.svgContentUrl.toString()), recipient) } else { - expeditorAvatar.loadAvatar(avatarValue?.first) + expeditorAvatar.loadAvatar(recipient) } } From 393efdd7f8d63b68dfbea2bec0bc58a79f9d4b4f Mon Sep 17 00:00:00 2001 From: NicolasBourdin88 Date: Thu, 23 May 2024 10:48:46 +0200 Subject: [PATCH 11/17] Add svg image loader to avatar view --- app/src/main/java/com/infomaniak/mail/MainApplication.kt | 4 ++++ .../java/com/infomaniak/mail/di/ApplicationModule.kt | 5 +++++ .../main/java/com/infomaniak/mail/views/AvatarView.kt | 9 +++++---- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/MainApplication.kt b/app/src/main/java/com/infomaniak/mail/MainApplication.kt index 3450bff5bd..4b94ed7cc9 100644 --- a/app/src/main/java/com/infomaniak/mail/MainApplication.kt +++ b/app/src/main/java/com/infomaniak/mail/MainApplication.kt @@ -274,6 +274,10 @@ open class MainApplication : Application(), ImageLoaderFactory, DefaultLifecycle lastAppClosingTime = null } + fun createSvgImageLoader(): ImageLoader { + return CoilUtils.newImageLoader(applicationContext, tokenInterceptorListener(), isSvg = true) + } + companion object { private const val FIRST_LAUNCH_TIME = 0L } diff --git a/app/src/main/java/com/infomaniak/mail/di/ApplicationModule.kt b/app/src/main/java/com/infomaniak/mail/di/ApplicationModule.kt index 7306e3dda2..0198213e0d 100644 --- a/app/src/main/java/com/infomaniak/mail/di/ApplicationModule.kt +++ b/app/src/main/java/com/infomaniak/mail/di/ApplicationModule.kt @@ -21,6 +21,7 @@ import android.app.Application import android.content.Context import androidx.core.app.NotificationManagerCompat import androidx.work.WorkManager +import coil.ImageLoader import com.infomaniak.lib.stores.AppUpdateScheduler import com.infomaniak.lib.stores.StoresSettingsRepository import com.infomaniak.mail.MainApplication @@ -74,4 +75,8 @@ object ApplicationModule { appContext: Context, workManager: WorkManager, ): AppUpdateScheduler = AppUpdateScheduler(appContext, workManager) + + @Provides + @Singleton + fun providesSvgImageLoader(mainApplication: MainApplication): ImageLoader = mainApplication.createSvgImageLoader() } diff --git a/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt b/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt index d52a5ddecd..22dacde7a6 100644 --- a/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt +++ b/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt @@ -21,11 +21,11 @@ import android.content.Context import android.content.res.ColorStateList import android.graphics.drawable.Drawable import android.util.AttributeSet -import android.util.Log import android.view.LayoutInflater import android.widget.FrameLayout import android.widget.ImageView import androidx.lifecycle.Observer +import coil.ImageLoader import coil.imageLoader import coil.load import com.infomaniak.lib.core.models.user.User @@ -69,6 +69,9 @@ class AvatarView @JvmOverloads constructor( @Inject lateinit var avatarMergedContactData: AvatarMergedContactData + @Inject + lateinit var svgImageLoader: ImageLoader + var strokeWidth: Float get() = binding.avatarImage.strokeWidth set(value) { @@ -147,10 +150,8 @@ class AvatarView @JvmOverloads constructor( } fun loadBimiAvatar(urlBimi: String, correspondent: Correspondent?) = with(binding.avatarImage) { - val fakeUrlBimi = "https://upload.wikimedia.org/wikipedia/commons/4/4f/SVG_Logo.svg" contentDescription = correspondent?.email.orEmpty() isBimiShow = urlBimi.isNotEmpty() - Log.e("Bimi", urlBimi) loadAvatar( backgroundColor = context.getBackgroundColorBasedOnId( correspondent?.email.orEmpty().hashCode(), @@ -158,7 +159,7 @@ class AvatarView @JvmOverloads constructor( ), avatarUrl = urlBimi, initials = correspondent?.initials.orEmpty(), - imageLoader = context.simpleImageLoader, + imageLoader = svgImageLoader, initialsColor = context.getColor(R.color.onColorfulBackground), ) } From b814ecacf3d29a3e29f72980f5bd6d5c9943c25c Mon Sep 17 00:00:00 2001 From: NicolasBourdin88 Date: Thu, 23 May 2024 11:01:09 +0200 Subject: [PATCH 12/17] Simplify logic --- .../com/infomaniak/mail/views/AvatarView.kt | 4 +-- app/src/main/res/drawable/ic_certified.xml | 25 ++++++++++++++++--- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt b/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt index 22dacde7a6..dee821b40c 100644 --- a/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt +++ b/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt @@ -58,11 +58,11 @@ class AvatarView @JvmOverloads constructor( private val binding by lazy { ViewAvatarBinding.inflate(LayoutInflater.from(context), this, true) } private var currentCorrespondent: Correspondent? = null - private var isBimiShow: Boolean? = null + private var isBimiShow: Boolean = false private val mergedContactObserver = Observer { contacts -> currentCorrespondent?.let { correspondent -> - if (isBimiShow == false) loadAvatarUsingDictionary(correspondent, contacts) + if (!isBimiShow) loadAvatarUsingDictionary(correspondent, contacts) } } diff --git a/app/src/main/res/drawable/ic_certified.xml b/app/src/main/res/drawable/ic_certified.xml index d6095cb502..5944af3f9d 100644 --- a/app/src/main/res/drawable/ic_certified.xml +++ b/app/src/main/res/drawable/ic_certified.xml @@ -1,10 +1,27 @@ + - + From 09e08ce0f43189d60184fe0934c3f086667a1a7d Mon Sep 17 00:00:00 2001 From: NicolasBourdin88 Date: Thu, 23 May 2024 12:47:27 +0200 Subject: [PATCH 13/17] Change emplacement where svg is used Change emplacement so we don't import the coil svg lib everywhere, only in mail --- app/build.gradle | 2 ++ app/src/main/java/com/infomaniak/mail/MainApplication.kt | 7 ++++++- gradle/libs.versions.toml | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 948f8432e7..c33cb42bab 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -113,6 +113,8 @@ dependencies { implementation libs.sentry.android.fragment + implementation libs.coil.svg + // Test testImplementation libs.junit androidTestImplementation libs.ext.junit diff --git a/app/src/main/java/com/infomaniak/mail/MainApplication.kt b/app/src/main/java/com/infomaniak/mail/MainApplication.kt index 4b94ed7cc9..950a645157 100644 --- a/app/src/main/java/com/infomaniak/mail/MainApplication.kt +++ b/app/src/main/java/com/infomaniak/mail/MainApplication.kt @@ -33,6 +33,7 @@ import androidx.lifecycle.lifecycleScope import androidx.work.Configuration import coil.ImageLoader import coil.ImageLoaderFactory +import coil.decode.SvgDecoder import com.facebook.stetho.Stetho import com.infomaniak.lib.core.InfomaniakCore import com.infomaniak.lib.core.auth.TokenInterceptorListener @@ -275,7 +276,11 @@ open class MainApplication : Application(), ImageLoaderFactory, DefaultLifecycle } fun createSvgImageLoader(): ImageLoader { - return CoilUtils.newImageLoader(applicationContext, tokenInterceptorListener(), isSvg = true) + return CoilUtils.newImageLoader( + applicationContext, + tokenInterceptorListener(), + customComponents = listOf(SvgDecoder.Factory()) + ) } companion object { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 914a9b9a2e..822dd52f32 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,6 +22,7 @@ sentryAndroidFragment = "7.10.0" webkit = "1.11.0" workConcurrentFutures = "1.2.0" workRuntimeKtx = "2.9.0" +coilSvg = "2.6.0" [libraries] dotsindicator = { module = "com.tbuonomo:dotsindicator", version.ref = "dotsindicator" } @@ -43,6 +44,7 @@ sentry-android-fragment = { module = "io.sentry:sentry-android-fragment", versio webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" } work-concurrent-futures = { module = "androidx.concurrent:concurrent-futures-ktx", version.ref = "workConcurrentFutures" } work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtx" } +coil-svg = { module = "io.coil-kt:coil-svg", version.ref = "coilSvg" } # Unit tests ext-junit = { module = "androidx.test.ext:junit", version.ref = "junitVersion" } junit = { module = "junit:junit", version.ref = "junit" } From badbab286e42a94a71ee97c8594f974921c1c72a Mon Sep 17 00:00:00 2001 From: NicolasBourdin88 Date: Thu, 13 Jun 2024 11:26:00 +0200 Subject: [PATCH 14/17] Update core Update core --- app/src/main/java/com/infomaniak/mail/MainApplication.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/infomaniak/mail/MainApplication.kt b/app/src/main/java/com/infomaniak/mail/MainApplication.kt index 950a645157..b324c18551 100644 --- a/app/src/main/java/com/infomaniak/mail/MainApplication.kt +++ b/app/src/main/java/com/infomaniak/mail/MainApplication.kt @@ -279,7 +279,7 @@ open class MainApplication : Application(), ImageLoaderFactory, DefaultLifecycle return CoilUtils.newImageLoader( applicationContext, tokenInterceptorListener(), - customComponents = listOf(SvgDecoder.Factory()) + customFactories = listOf(SvgDecoder.Factory()) ) } From 2df62b79938c194987a002bc41824c76af4685a9 Mon Sep 17 00:00:00 2001 From: NicolasBourdin88 Date: Mon, 17 Jun 2024 08:59:20 +0200 Subject: [PATCH 15/17] Apply suggestion from code review Apply suggestion from code review --- .../infomaniak/mail/data/cache/RealmDatabase.kt | 6 +++++- .../com/infomaniak/mail/data/models/Bimi.kt | 3 +++ .../mail/ui/main/AvatarNameEmailView.kt | 2 +- .../mail/ui/main/folder/ThreadListAdapter.kt | 2 +- .../ui/main/thread/DetailedRecipientAdapter.kt | 4 +++- .../mail/ui/main/thread/ThreadAdapter.kt | 4 ++-- .../mail/ui/main/thread/ThreadFragment.kt | 5 +---- .../com/infomaniak/mail/views/AvatarView.kt | 17 +++++++++-------- app/src/main/res/drawable/ic_certified.xml | 2 +- 9 files changed, 26 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/data/cache/RealmDatabase.kt b/app/src/main/java/com/infomaniak/mail/data/cache/RealmDatabase.kt index a58c14a934..499216453a 100644 --- a/app/src/main/java/com/infomaniak/mail/data/cache/RealmDatabase.kt +++ b/app/src/main/java/com/infomaniak/mail/data/cache/RealmDatabase.kt @@ -18,7 +18,11 @@ package com.infomaniak.mail.data.cache import android.content.Context -import com.infomaniak.mail.data.models.* +import com.infomaniak.mail.data.models.AppSettings +import com.infomaniak.mail.data.models.Attachment +import com.infomaniak.mail.data.models.Folder +import com.infomaniak.mail.data.models.Quotas +import com.infomaniak.mail.data.models.Bimi import com.infomaniak.mail.data.models.addressBook.AddressBook import com.infomaniak.mail.data.models.calendar.Attendee import com.infomaniak.mail.data.models.calendar.CalendarEvent diff --git a/app/src/main/java/com/infomaniak/mail/data/models/Bimi.kt b/app/src/main/java/com/infomaniak/mail/data/models/Bimi.kt index 07484287e7..8b318e0571 100644 --- a/app/src/main/java/com/infomaniak/mail/data/models/Bimi.kt +++ b/app/src/main/java/com/infomaniak/mail/data/models/Bimi.kt @@ -28,10 +28,13 @@ import kotlinx.serialization.Serializable @Parcelize @Serializable class Bimi() : EmbeddedRealmObject, Parcelable { + + //region Remote data @SerialName("svg_content") var svgContentUrl: String? = null @SerialName("is_certified") var isCertified: Boolean = false + //endregion constructor(svgContentUrl: String, isCertified: Boolean) : this() { this.svgContentUrl = svgContentUrl diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/AvatarNameEmailView.kt b/app/src/main/java/com/infomaniak/mail/ui/main/AvatarNameEmailView.kt index 293373a8ab..c2a3b26839 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/AvatarNameEmailView.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/AvatarNameEmailView.kt @@ -89,7 +89,7 @@ class AvatarNameEmailView @JvmOverloads constructor( private fun ViewAvatarNameEmailBinding.setNameAndEmail( correspondent: Correspondent, - isCorrespondentCertified: Boolean = false + isCorrespondentCertified: Boolean = false, ) { val filledSingleField = fillInUserNameAndEmail(correspondent, userName, userEmail, ignoreIsMe = !processNameAndEmail) if (displayAsAttendee) { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt index 35257524a7..10670ab811 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt @@ -402,7 +402,7 @@ class ThreadListAdapter @Inject constructor( private fun CardviewThreadItemBinding.displayAvatar(thread: Thread) { val (recipient, bimi) = thread.computeAvatarRecipient() - if (bimi != null && bimi.isCertified) { + if (bimi?.isCertified == true) { expeditorAvatar.loadBimiAvatar(ApiRoutes.bimi(bimi.svgContentUrl.toString()), recipient) } else { expeditorAvatar.loadAvatar(recipient) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/DetailedRecipientAdapter.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/DetailedRecipientAdapter.kt index 251bb37705..5168f58c56 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/DetailedRecipientAdapter.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/DetailedRecipientAdapter.kt @@ -30,11 +30,13 @@ import com.infomaniak.mail.ui.main.thread.DetailedRecipientAdapter.DetailedRecip import com.infomaniak.mail.utils.UiUtils.fillInUserNameAndEmail class DetailedRecipientAdapter( - private val onContactClicked: ((contact: Recipient, bimi: Bimi?) -> Unit)? + private val onContactClicked: ((contact: Recipient, bimi: Bimi?) -> Unit)?, ) : Adapter() { private var recipients = emptyList() + private var bimi: Bimi? = null + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DetailedRecipientViewHolder { return DetailedRecipientViewHolder(ItemDetailedContactBinding.inflate(LayoutInflater.from(parent.context), parent, false)) } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt index 178888bc6a..3a5678129d 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt @@ -120,7 +120,7 @@ class ThreadAdapter( shouldLoadDistantResources, threadAdapterCallbacks?.onContactClicked, threadAdapterCallbacks?.onAttachmentClicked, - threadAdapterCallbacks?.onAttachmentOptionsClicked + threadAdapterCallbacks?.onAttachmentOptionsClicked, ) } else { SuperCollapsedBlockViewHolder(ItemSuperCollapsedBlockBinding.inflate(layoutInflater, parent, false)) @@ -715,7 +715,7 @@ class ThreadAdapter( private val shouldLoadDistantResources: Boolean, onContactClicked: ((contact: Recipient, bimi: Bimi?) -> Unit)?, onAttachmentClicked: ((attachment: Attachment) -> Unit)?, - onAttachmentOptionsClicked: ((attachment: Attachment) -> Unit)? + onAttachmentOptionsClicked: ((attachment: Attachment) -> Unit)?, ) : ThreadAdapterViewHolder(binding) { val fromAdapter = DetailedRecipientAdapter(onContactClicked) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadFragment.kt index a0600aca51..7283fee300 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadFragment.kt @@ -227,10 +227,7 @@ class ThreadFragment : Fragment() { onContactClicked = { recipient, bimi -> safeNavigate( resId = R.id.detailedContactBottomSheetDialog, - args = DetailedContactBottomSheetDialogArgs( - recipient = recipient, - bimi - ).toBundle(), + args = DetailedContactBottomSheetDialogArgs(recipient, bimi).toBundle(), ) }, onDraftClicked = { message -> diff --git a/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt b/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt index dee821b40c..814e5d12e4 100644 --- a/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt +++ b/app/src/main/java/com/infomaniak/mail/views/AvatarView.kt @@ -58,11 +58,11 @@ class AvatarView @JvmOverloads constructor( private val binding by lazy { ViewAvatarBinding.inflate(LayoutInflater.from(context), this, true) } private var currentCorrespondent: Correspondent? = null - private var isBimiShow: Boolean = false + private var isBimiShown: Boolean = false private val mergedContactObserver = Observer { contacts -> currentCorrespondent?.let { correspondent -> - if (!isBimiShow) loadAvatarUsingDictionary(correspondent, contacts) + if (!isBimiShown) loadAvatarUsingDictionary(correspondent, contacts) } } @@ -149,15 +149,15 @@ class AvatarView @JvmOverloads constructor( binding.avatarImage.load(R.drawable.ic_unknown_user_avatar) } - fun loadBimiAvatar(urlBimi: String, correspondent: Correspondent?) = with(binding.avatarImage) { + fun loadBimiAvatar(bimiUrl: String, correspondent: Correspondent?) = with(binding.avatarImage) { contentDescription = correspondent?.email.orEmpty() - isBimiShow = urlBimi.isNotEmpty() + isBimiShown = bimiUrl.isNotEmpty() loadAvatar( backgroundColor = context.getBackgroundColorBasedOnId( correspondent?.email.orEmpty().hashCode(), - R.array.AvatarColors + R.array.AvatarColors, ), - avatarUrl = urlBimi, + avatarUrl = bimiUrl, initials = correspondent?.initials.orEmpty(), imageLoader = svgImageLoader, initialsColor = context.getColor(R.color.onColorfulBackground), @@ -193,10 +193,11 @@ class AvatarView @JvmOverloads constructor( } fun loadAvatar(correspondent: Correspondent?, bimi: Bimi?) { - if (bimi == null || !bimi.isCertified) { + val svgContentUrl = bimi?.svgContentUrl + if (bimi == null || !bimi.isCertified || svgContentUrl.isNullOrEmpty()) { loadAvatar(correspondent) } else { - loadBimiAvatar(ApiRoutes.bimi(bimi.svgContentUrl.orEmpty()), correspondent) + loadBimiAvatar(ApiRoutes.bimi(svgContentUrl), correspondent) } } } diff --git a/app/src/main/res/drawable/ic_certified.xml b/app/src/main/res/drawable/ic_certified.xml index 5944af3f9d..3d0defd282 100644 --- a/app/src/main/res/drawable/ic_certified.xml +++ b/app/src/main/res/drawable/ic_certified.xml @@ -1,6 +1,6 @@