From 01e2f4a9b3052feab7409f01017083a512965348 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Thu, 30 Apr 2026 16:48:29 +0200 Subject: [PATCH 01/40] feat: Create reusable function to handle both disabled states with and without actions --- .../mail/utils/extensions/ViewExt.kt | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt new file mode 100644 index 0000000000..dfa4bfeb8b --- /dev/null +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt @@ -0,0 +1,85 @@ +/* + * Infomaniak Mail - Android + * Copyright (C) 2026 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.utils.extensions + +import android.content.res.ColorStateList +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import androidx.core.content.ContextCompat +import com.google.android.material.appbar.MaterialToolbar +import com.google.android.material.button.MaterialButton +import com.infomaniak.mail.R + +fun View.setSendingButtonListener( + buttonState: SendingButtonState, + onActionBlocked: () -> Unit, + onActionExecute: () -> Unit +) { + + if (buttonState != SendingButtonState.Send) { + this.applyDisabledColor(buttonState) + } + + this.setOnClickListener { + when (buttonState) { + SendingButtonState.Send -> onActionExecute() + SendingButtonState.SendingBlocked -> onActionBlocked() + SendingButtonState.Disabled -> Unit + } + } +} + + +private fun View.applyDisabledColor(buttonState: SendingButtonState) { + + val color = ContextCompat.getColor(context, R.color.disabledIconColor) + when (this) { + is MaterialButton -> { + this.iconTint = ColorStateList.valueOf(color) + this.setTextColor(color) + } + + is MaterialToolbar -> { + this.navigationIcon?.setTint(color) + // this.menu.forEach { item -> + // item.icon?.setTint(color) + // } + this.setTitleTextColor(color) + } + + // is ActionItemView -> { + // this.setIconColor(color) + // } + + is ImageView -> { + this.setColorFilter(color) + } + + is TextView -> { + this.setTextColor(color) + } + } +} + + +enum class SendingButtonState { + Send, + SendingBlocked, + Disabled +} From edd6304ba0449af05d67a66c16b929332eea7493 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Thu, 30 Apr 2026 16:49:04 +0200 Subject: [PATCH 02/40] feat: Add permission field --- .../main/java/com/infomaniak/mail/data/cache/RealmDatabase.kt | 2 +- .../infomaniak/mail/data/models/mailbox/MailboxPermissions.kt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) 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 14433036e4..2d8fc47b63 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 @@ -199,7 +199,7 @@ object RealmDatabase { //region Configurations versions const val USER_INFO_SCHEMA_VERSION = 5L - const val MAILBOX_INFO_SCHEMA_VERSION = 18L + const val MAILBOX_INFO_SCHEMA_VERSION = 19L const val MAILBOX_CONTENT_SCHEMA_VERSION = 36L //endregion diff --git a/app/src/main/java/com/infomaniak/mail/data/models/mailbox/MailboxPermissions.kt b/app/src/main/java/com/infomaniak/mail/data/models/mailbox/MailboxPermissions.kt index ff97fd1712..6ba0f8e936 100644 --- a/app/src/main/java/com/infomaniak/mail/data/models/mailbox/MailboxPermissions.kt +++ b/app/src/main/java/com/infomaniak/mail/data/models/mailbox/MailboxPermissions.kt @@ -34,6 +34,9 @@ class MailboxPermissions : EmbeddedRealmObject { var canConfigureMailFolders: Boolean = false @SerialName("can_restore_emails") var canRestoreEmails: Boolean = false + @SerialName("can_send_emails") + var canSendEmails: Boolean = false + companion object } From d4a6c5049d1847e2709951e22da8438aa158f24e Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Thu, 30 Apr 2026 17:25:28 +0200 Subject: [PATCH 03/40] feat: Use new function to disabeld new message button --- .../mail/ui/main/folder/ThreadListFragment.kt | 16 ++++++++++++---- .../infomaniak/mail/utils/extensions/ViewExt.kt | 7 ++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt index 1b4bea9979..e7f6ed8c71 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt @@ -102,6 +102,7 @@ import com.infomaniak.mail.utils.UiUtils.formatUnreadCount import com.infomaniak.mail.utils.Utils import com.infomaniak.mail.utils.Utils.isPermanentDeleteFolder import com.infomaniak.mail.utils.Utils.runCatchingRealm +import com.infomaniak.mail.utils.extensions.SendingButtonState import com.infomaniak.mail.utils.extensions.addStickyDateDecoration import com.infomaniak.mail.utils.extensions.applySideAndBottomSystemInsets import com.infomaniak.mail.utils.extensions.applyStatusBarInsets @@ -110,6 +111,7 @@ import com.infomaniak.mail.utils.extensions.bindAlertToViewLifecycle import com.infomaniak.mail.utils.extensions.observeNotNull import com.infomaniak.mail.utils.extensions.safeArea import com.infomaniak.mail.utils.extensions.safeNavigateToNewMessageActivity +import com.infomaniak.mail.utils.extensions.setSendingClickListener import com.infomaniak.mail.utils.extensions.shareString import com.infomaniak.mail.utils.extensions.toDate import dagger.hilt.android.AndroidEntryPoint @@ -469,10 +471,16 @@ class ThreadListFragment : TwoPaneFragment(), PickerEmojiObserver { isHandled } - newMessageFab.setOnClickListener { - trackNewMessageEvent(MatomoName.OpenFromFab) - safeNavigateToNewMessageActivity() - } + newMessageFab.setSendingClickListener( + buttonState = mailbox(). SendingButtonState.SendingBlocked, + onActionBlocked = { + snackbarManager.setValue(getString(R.string.snackbarAdminDisabledMessageSending)) + }, + onActionExecute = { + trackNewMessageEvent(MatomoName.OpenFromFab) + safeNavigateToNewMessageActivity() + } + ) threadsList.scrollListener = object : OnListScrollListener { override fun onListScrollStateChanged(scrollState: ScrollState) = Unit diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt index dfa4bfeb8b..0c786d92e8 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt @@ -26,7 +26,7 @@ import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.button.MaterialButton import com.infomaniak.mail.R -fun View.setSendingButtonListener( +fun View.setSendingClickListener( buttonState: SendingButtonState, onActionBlocked: () -> Unit, onActionExecute: () -> Unit @@ -51,8 +51,9 @@ private fun View.applyDisabledColor(buttonState: SendingButtonState) { val color = ContextCompat.getColor(context, R.color.disabledIconColor) when (this) { is MaterialButton -> { - this.iconTint = ColorStateList.valueOf(color) - this.setTextColor(color) + // this.iconTint = ColorStateList.valueOf(color) + // this.setTextColor(color) + this.backgroundTintList = ColorStateList.valueOf(color) } is MaterialToolbar -> { From 3f1738ddd43237d3d07afc8cb9e6e63f352335c9 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Fri, 1 May 2026 09:00:48 +0200 Subject: [PATCH 04/40] feat: Use the canSendEmails parameter to dynamically change the button --- .../mail/ui/main/folder/ThreadListFragment.kt | 10 ++++----- .../mail/utils/extensions/ViewExt.kt | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt index e7f6ed8c71..a1ed737bfa 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt @@ -102,16 +102,15 @@ import com.infomaniak.mail.utils.UiUtils.formatUnreadCount import com.infomaniak.mail.utils.Utils import com.infomaniak.mail.utils.Utils.isPermanentDeleteFolder import com.infomaniak.mail.utils.Utils.runCatchingRealm -import com.infomaniak.mail.utils.extensions.SendingButtonState import com.infomaniak.mail.utils.extensions.addStickyDateDecoration import com.infomaniak.mail.utils.extensions.applySideAndBottomSystemInsets import com.infomaniak.mail.utils.extensions.applyStatusBarInsets import com.infomaniak.mail.utils.extensions.applyWindowInsetsListener import com.infomaniak.mail.utils.extensions.bindAlertToViewLifecycle +import com.infomaniak.mail.utils.extensions.bindSendingClickListener import com.infomaniak.mail.utils.extensions.observeNotNull import com.infomaniak.mail.utils.extensions.safeArea import com.infomaniak.mail.utils.extensions.safeNavigateToNewMessageActivity -import com.infomaniak.mail.utils.extensions.setSendingClickListener import com.infomaniak.mail.utils.extensions.shareString import com.infomaniak.mail.utils.extensions.toDate import dagger.hilt.android.AndroidEntryPoint @@ -471,8 +470,9 @@ class ThreadListFragment : TwoPaneFragment(), PickerEmojiObserver { isHandled } - newMessageFab.setSendingClickListener( - buttonState = mailbox(). SendingButtonState.SendingBlocked, + newMessageFab.bindSendingClickListener( + lifecycleOwner = viewLifecycleOwner, + permissionLive = mainViewModel.currentPermissionsLive, onActionBlocked = { snackbarManager.setValue(getString(R.string.snackbarAdminDisabledMessageSending)) }, @@ -579,7 +579,7 @@ class ThreadListFragment : TwoPaneFragment(), PickerEmojiObserver { private fun handleAccountSwipe(isSwipeDown: Boolean) = lifecycleScope.launch { val accounts = switchUserViewModel.accounts.first() if (accounts.isEmpty()) return@launch - + val currentIndex = accounts.indexOfFirst { it.id == AccountUtils.currentUserId } if (currentIndex == -1) return@launch diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt index 0c786d92e8..1a4e68ed38 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt @@ -22,9 +22,30 @@ import android.view.View import android.widget.ImageView import android.widget.TextView import androidx.core.content.ContextCompat +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LiveData import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.button.MaterialButton import com.infomaniak.mail.R +import com.infomaniak.mail.data.models.mailbox.MailboxPermissions + +fun View.bindSendingClickListener( + lifecycleOwner: LifecycleOwner, + permissionLive: LiveData, + onActionBlocked: () -> Unit, + onActionExecute: () -> Unit +) { + permissionLive.observe(lifecycleOwner) { permissions -> + val canSendEmails = permissions?.canSendEmails ?: true + val buttonState = if (canSendEmails) SendingButtonState.Send else SendingButtonState.SendingBlocked + + this.setSendingClickListener( + buttonState = buttonState, + onActionBlocked = onActionBlocked, + onActionExecute = onActionExecute + ) + } +} fun View.setSendingClickListener( buttonState: SendingButtonState, From f35e60605d51decc29b5d6e5d1943795c014af29 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Fri, 1 May 2026 11:27:19 +0200 Subject: [PATCH 05/40] feat: Block actions and disabled state color on BottomQuickActionBar --- .../com/infomaniak/mail/ui/MainViewModel.kt | 8 ++++ .../mail/ui/main/thread/ThreadFragment.kt | 39 +++++++++++++++---- .../mail/utils/extensions/ViewExt.kt | 38 ++++++++---------- .../mail/views/BottomQuickActionBarView.kt | 11 ++++++ 4 files changed, 66 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt index 70049571a8..e134c8033e 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -388,6 +388,14 @@ class MainViewModel @Inject constructor( } ?: appContext.launchNoValidMailboxesActivity() } + fun executeIfCanSendEmails( + onBlocked: () -> Unit, + onExecute: () -> Unit + ) { + val canSend = currentPermissionsLive.value?.canSendEmails ?: true + if (canSend) onExecute() else onBlocked() + } + fun dismissCurrentMailboxNotifications() = viewModelScope.launch(ioCoroutineContext) { currentMailbox.value?.let { appContext.cancelNotification(it.notificationGroupId) 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 13b61e4200..2cfbce61b4 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 @@ -551,6 +551,13 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { localSettings, ) quickActionBar.init(if (shouldDisplayScheduledDraftActions) R.menu.scheduled_draft_menu else R.menu.message_menu) + + val canSendEmails = mainViewModel.currentPermissionsLive.value?.canSendEmails ?: true + if (!canSendEmails) { + quickActionBar.applyDisabledColorByMenuId(R.id.quickActionReply) + quickActionBar.applyDisabledColorByMenuId(R.id.quickActionForward) + } + thread.snoozeEndDate?.let { snoozeEndDate -> val formattedDate = context.formatDayOfWeekAdaptiveYear(snoozeEndDate.toDate()) @@ -820,14 +827,6 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { quickActionBar.setOnItemClickListener { menuId -> when (menuId) { - R.id.quickActionReply -> { - trackThreadActionsEvent(MatomoName.Reply) - threadViewModel.clickOnQuickActionBar(menuId) - } - R.id.quickActionForward -> { - trackThreadActionsEvent(MatomoName.Forward) - threadViewModel.clickOnQuickActionBar(menuId) - } R.id.quickActionArchive -> { descriptionDialog.archiveWithConfirmationPopup(folderRole, count = 1) { trackThreadActionsEvent(MatomoName.Archive, isFromArchive) @@ -844,6 +843,30 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { trackThreadActionsEvent(MatomoName.OpenBottomSheet) threadViewModel.clickOnQuickActionBar(menuId) } + + R.id.quickActionReply, R.id.quickActionForward -> { + + val executeAction: () -> Unit = { + when (menuId) { + R.id.quickActionReply -> { + trackThreadActionsEvent(MatomoName.Reply) + } + R.id.quickActionForward -> { + trackThreadActionsEvent(MatomoName.Forward) + } + } + threadViewModel.clickOnQuickActionBar(menuId) + } + + val blockAction = { + snackbarManager.setValue(getString(R.string.snackbarAdminDisabledMessageSending)) + } + + mainViewModel.executeIfCanSendEmails( + onBlocked = blockAction, + onExecute = executeAction + ) + } } } } diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt index 1a4e68ed38..c5e8f7f484 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt @@ -18,14 +18,15 @@ package com.infomaniak.mail.utils.extensions import android.content.res.ColorStateList +import android.util.Log import android.view.View import android.widget.ImageView import android.widget.TextView import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LiveData -import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.button.MaterialButton +import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.infomaniak.mail.R import com.infomaniak.mail.data.models.mailbox.MailboxPermissions @@ -54,7 +55,7 @@ fun View.setSendingClickListener( ) { if (buttonState != SendingButtonState.Send) { - this.applyDisabledColor(buttonState) + this.applyDisabledColor() } this.setOnClickListener { @@ -67,35 +68,28 @@ fun View.setSendingClickListener( } -private fun View.applyDisabledColor(buttonState: SendingButtonState) { - +fun View.applyDisabledColor() { val color = ContextCompat.getColor(context, R.color.disabledIconColor) + when (this) { - is MaterialButton -> { - // this.iconTint = ColorStateList.valueOf(color) - // this.setTextColor(color) + is ExtendedFloatingActionButton -> { this.backgroundTintList = ColorStateList.valueOf(color) } - is MaterialToolbar -> { - this.navigationIcon?.setTint(color) - // this.menu.forEach { item -> - // item.icon?.setTint(color) - // } - this.setTitleTextColor(color) + is MaterialButton -> { + this.setTextColor(color) + this.iconTint = ColorStateList.valueOf(color) } - // is ActionItemView -> { - // this.setIconColor(color) + // is ImageView -> { + // this.setColorFilter(color) + // } + // + // is TextView -> { + // this.setTextColor(color) // } - is ImageView -> { - this.setColorFilter(color) - } - - is TextView -> { - this.setTextColor(color) - } + else -> Log.i("elouan", "not supported : $this") } } diff --git a/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt b/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt index c258ec4a1d..728c9ef92d 100644 --- a/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt +++ b/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt @@ -26,6 +26,7 @@ import android.view.WindowInsets import android.widget.ActionMenuView import android.widget.FrameLayout import androidx.annotation.DrawableRes +import androidx.annotation.IdRes import androidx.annotation.MenuRes import androidx.annotation.StringRes import androidx.core.content.res.getResourceIdOrThrow @@ -37,6 +38,7 @@ import com.google.android.material.button.MaterialButton import com.infomaniak.core.legacy.utils.getAttributes import com.infomaniak.mail.R import com.infomaniak.mail.databinding.ViewBottomQuickActionBarBinding +import com.infomaniak.mail.utils.extensions.applyDisabledColor import com.infomaniak.mail.utils.extensions.applySideAndBottomSystemInsets class BottomQuickActionBarView @JvmOverloads constructor( @@ -83,6 +85,15 @@ class BottomQuickActionBarView @JvmOverloads constructor( } } + fun applyDisabledColorByMenuId(@IdRes menuId: Int) { + for (index in 0 until menu.size) { + if (menu[index].itemId == menuId) { + buttons[index].applyDisabledColor() + return + } + } + } + fun changeIcon(index: Int, @DrawableRes icon: Int) { buttons[index].setIconResource(icon) } From 05d4d7dd4d3f5f55e03cbce005e077191a5df9e2 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Fri, 1 May 2026 14:27:30 +0200 Subject: [PATCH 06/40] feat: Apply disabled state to send action in BottomQuickActionBar and MailActionsBottomSheetDialog, and update XML for grey color --- .../com/infomaniak/mail/ui/MainViewModel.kt | 8 ----- .../mail/ui/main/thread/ThreadFragment.kt | 32 ++++--------------- .../ui/main/thread/actions/ActionItemView.kt | 8 +++++ .../actions/MailActionsBottomSheetDialog.kt | 9 ++++++ .../ui/main/thread/actions/MainActionsView.kt | 6 ++++ .../mail/views/BottomQuickActionBarView.kt | 4 +-- .../information_block_background_disabled.xml | 21 ++++++++++++ .../res/layout/item_bottom_sheet_action.xml | 3 +- app/src/main/res/layout/view_main_actions.xml | 20 +++++++----- 9 files changed, 67 insertions(+), 44 deletions(-) create mode 100644 app/src/main/res/color/information_block_background_disabled.xml diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt index e134c8033e..70049571a8 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -388,14 +388,6 @@ class MainViewModel @Inject constructor( } ?: appContext.launchNoValidMailboxesActivity() } - fun executeIfCanSendEmails( - onBlocked: () -> Unit, - onExecute: () -> Unit - ) { - val canSend = currentPermissionsLive.value?.canSendEmails ?: true - if (canSend) onExecute() else onBlocked() - } - fun dismissCurrentMailboxNotifications() = viewModelScope.launch(ioCoroutineContext) { currentMailbox.value?.let { appContext.cancelNotification(it.notificationGroupId) 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 2cfbce61b4..7ea478c1d1 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 @@ -554,8 +554,8 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { val canSendEmails = mainViewModel.currentPermissionsLive.value?.canSendEmails ?: true if (!canSendEmails) { - quickActionBar.applyDisabledColorByMenuId(R.id.quickActionReply) - quickActionBar.applyDisabledColorByMenuId(R.id.quickActionForward) + quickActionBar.disableByMenuId(R.id.quickActionReply) + quickActionBar.disableByMenuId(R.id.quickActionForward) } @@ -843,29 +843,11 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { trackThreadActionsEvent(MatomoName.OpenBottomSheet) threadViewModel.clickOnQuickActionBar(menuId) } - - R.id.quickActionReply, R.id.quickActionForward -> { - - val executeAction: () -> Unit = { - when (menuId) { - R.id.quickActionReply -> { - trackThreadActionsEvent(MatomoName.Reply) - } - R.id.quickActionForward -> { - trackThreadActionsEvent(MatomoName.Forward) - } - } - threadViewModel.clickOnQuickActionBar(menuId) - } - - val blockAction = { - snackbarManager.setValue(getString(R.string.snackbarAdminDisabledMessageSending)) - } - - mainViewModel.executeIfCanSendEmails( - onBlocked = blockAction, - onExecute = executeAction - ) + R.id.quickActionReply -> { + trackThreadActionsEvent(MatomoName.Reply) + } + R.id.quickActionForward -> { + trackThreadActionsEvent(MatomoName.Forward) } } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ActionItemView.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ActionItemView.kt index 113e932b8f..ea2561fe7e 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ActionItemView.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ActionItemView.kt @@ -93,6 +93,14 @@ class ActionItemView @JvmOverloads constructor( binding.root.setOnClickListener(onClickListener) } + override fun setEnabled(enabled: Boolean) { + super.setEnabled(enabled) + binding.root.isEnabled = enabled + + binding.icon.isEnabled = enabled + binding.title.isEnabled = enabled + } + fun setIconResource(@DrawableRes iconResourceId: Int) = binding.icon.setImageResource(iconResourceId) private fun setIconTint(color: ColorStateList) { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt index 37be708118..ca3e8b693b 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt @@ -17,6 +17,7 @@ */ package com.infomaniak.mail.ui.main.thread.actions +import android.R.attr.text import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -115,6 +116,14 @@ abstract class MailActionsBottomSheetDialog : ActionsBottomSheetDialog() { R.id.actionDelete -> onClickListener.onDelete() } } + + val canSend = mainViewModel.currentPermissionsLive.value?.canSendEmails ?: true + if (!canSend) { + binding.mainActions.disableByMenuId(R.id.actionReply) + binding.mainActions.disableByMenuId(R.id.actionReplyAll) + binding.mainActions.disableByMenuId(R.id.actionForward) + binding.addReaction.isEnabled = false + } } private fun setShareTrailingContent() { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MainActionsView.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MainActionsView.kt index 65dabacb47..415883018d 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MainActionsView.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MainActionsView.kt @@ -116,6 +116,12 @@ class MainActionsView @JvmOverloads constructor( return -1 } + fun disableByMenuId(@IdRes menuId: Int) { + val index = getIndexOfMenuItem(menuId) + buttons[index].isEnabled = false + textViews[index].isEnabled = false + } + private fun setAction(index: Int, drawable: Drawable?, title: String) { buttons[index].apply { icon = drawable diff --git a/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt b/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt index 728c9ef92d..ab75927b9e 100644 --- a/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt +++ b/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt @@ -85,10 +85,10 @@ class BottomQuickActionBarView @JvmOverloads constructor( } } - fun applyDisabledColorByMenuId(@IdRes menuId: Int) { + fun disableByMenuId(@IdRes menuId: Int) { for (index in 0 until menu.size) { if (menu[index].itemId == menuId) { - buttons[index].applyDisabledColor() + disable(index) return } } diff --git a/app/src/main/res/color/information_block_background_disabled.xml b/app/src/main/res/color/information_block_background_disabled.xml new file mode 100644 index 0000000000..f59db336d3 --- /dev/null +++ b/app/src/main/res/color/information_block_background_disabled.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/app/src/main/res/layout/item_bottom_sheet_action.xml b/app/src/main/res/layout/item_bottom_sheet_action.xml index 1d26d9dd90..47adb04db3 100644 --- a/app/src/main/res/layout/item_bottom_sheet_action.xml +++ b/app/src/main/res/layout/item_bottom_sheet_action.xml @@ -46,7 +46,7 @@ android:layout_marginVertical="@dimen/marginStandardMedium" android:layout_marginEnd="@dimen/marginStandardMedium" android:contentDescription="@string/contentDescriptionSchedule" - app:tint="?attr/colorPrimary" + app:tint="@color/primary_color_disabled" tools:src="@drawable/ic_afternoon_schedule" /> diff --git a/app/src/main/res/layout/view_main_actions.xml b/app/src/main/res/layout/view_main_actions.xml index 9527230b9c..f7d71387be 100644 --- a/app/src/main/res/layout/view_main_actions.xml +++ b/app/src/main/res/layout/view_main_actions.xml @@ -26,13 +26,13 @@ style="@style/IconButton" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:backgroundTint="?attr/colorPrimaryContainer" + android:backgroundTint="@color/information_block_background_disabled" android:minWidth="0dp" android:minHeight="0dp" android:padding="20dp" app:cornerRadius="8dp" app:iconSize="24dp" - app:iconTint="?attr/colorOnPrimaryContainer" + app:iconTint="@color/primary_color_disabled" app:layout_constraintEnd_toStartOf="@id/button2" app:layout_constraintHorizontal_chainStyle="spread" app:layout_constraintStart_toStartOf="parent" @@ -47,6 +47,7 @@ android:focusable="false" android:gravity="center_horizontal" android:paddingTop="@dimen/marginStandardVerySmall" + android:textColor="@color/primary_color_disabled" app:layout_constraintEnd_toEndOf="@id/button1" app:layout_constraintStart_toStartOf="@id/button1" app:layout_constraintTop_toBottomOf="@id/button1" @@ -57,13 +58,13 @@ style="@style/IconButton" android:layout_width="0dp" android:layout_height="0dp" - android:backgroundTint="?attr/colorPrimaryContainer" + android:backgroundTint="@color/information_block_background_disabled" android:minWidth="0dp" android:minHeight="0dp" android:padding="20dp" app:cornerRadius="8dp" app:iconSize="24dp" - app:iconTint="?attr/colorOnPrimaryContainer" + app:iconTint="@color/primary_color_disabled" app:layout_constraintBottom_toBottomOf="@id/button1" app:layout_constraintDimensionRatio="1" app:layout_constraintEnd_toStartOf="@id/button3" @@ -79,6 +80,7 @@ android:focusable="false" android:gravity="center_horizontal" android:paddingTop="@dimen/marginStandardVerySmall" + android:textColor="@color/primary_color_disabled" app:layout_constraintEnd_toEndOf="@id/button2" app:layout_constraintStart_toStartOf="@id/button2" app:layout_constraintTop_toBottomOf="@id/button2" @@ -89,13 +91,13 @@ style="@style/IconButton" android:layout_width="0dp" android:layout_height="0dp" - android:backgroundTint="?attr/colorPrimaryContainer" + android:backgroundTint="@color/information_block_background_disabled" android:minWidth="0dp" android:minHeight="0dp" android:padding="20dp" app:cornerRadius="8dp" app:iconSize="24dp" - app:iconTint="?attr/colorOnPrimaryContainer" + app:iconTint="@color/primary_color_disabled" app:layout_constraintBottom_toBottomOf="@id/button1" app:layout_constraintDimensionRatio="1" app:layout_constraintEnd_toStartOf="@id/button4" @@ -111,6 +113,7 @@ android:focusable="false" android:gravity="center_horizontal" android:paddingTop="@dimen/marginStandardVerySmall" + android:textColor="@color/primary_color_disabled" app:layout_constraintEnd_toEndOf="@id/button3" app:layout_constraintStart_toStartOf="@id/button3" app:layout_constraintTop_toBottomOf="@id/button3" @@ -121,13 +124,13 @@ style="@style/IconButton" android:layout_width="0dp" android:layout_height="0dp" - android:backgroundTint="?attr/colorPrimaryContainer" + android:backgroundTint="@color/information_block_background_disabled" android:minWidth="0dp" android:minHeight="0dp" android:padding="20dp" app:cornerRadius="8dp" app:iconSize="24dp" - app:iconTint="?attr/colorOnPrimaryContainer" + app:iconTint="@color/primary_color_disabled" app:layout_constraintBottom_toBottomOf="@id/button1" app:layout_constraintDimensionRatio="1" app:layout_constraintEnd_toEndOf="parent" @@ -143,6 +146,7 @@ android:focusable="false" android:gravity="center_horizontal" android:paddingTop="@dimen/marginStandardVerySmall" + android:textColor="@color/primary_color_disabled" app:layout_constraintEnd_toEndOf="@id/button4" app:layout_constraintStart_toStartOf="@id/button4" app:layout_constraintTop_toBottomOf="@id/button4" From f4a82cd958722435df9db2b947dd11a57024d17f Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Fri, 1 May 2026 14:32:13 +0200 Subject: [PATCH 07/40] feat: Disable writeEmail on DetailedContactBottomSheetDialog --- .../mail/ui/main/thread/DetailedContactBottomSheetDialog.kt | 6 ++++++ 1 file changed, 6 insertions(+) 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 ff4856d0d6..c629aac6a6 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 @@ -27,6 +27,7 @@ import androidx.navigation.fragment.navArgs import com.infomaniak.core.legacy.utils.safeBinding import com.infomaniak.mail.MatomoMail.MatomoName import com.infomaniak.mail.MatomoMail.trackContactActionsEvent +import com.infomaniak.mail.R import com.infomaniak.mail.databinding.BottomSheetDetailedContactBinding import com.infomaniak.mail.ui.MainViewModel import com.infomaniak.mail.ui.main.SnackbarManager @@ -61,6 +62,11 @@ class DetailedContactBottomSheetDialog : ActionsBottomSheetDialog() { contactDetails.setCorrespondent(navigationArgs.recipient, bimi) setupListeners() + + val canSend = mainViewModel.currentPermissionsLive.value?.canSendEmails ?: true + if (!canSend) { + binding.writeMail.isEnabled = false + } } private fun setupListeners() = with(binding) { From e354b5c7af999e2a6502b5654fdad534b74b41b8 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Mon, 4 May 2026 08:36:16 +0200 Subject: [PATCH 08/40] feat: Remove reply button in notification --- .../mail/utils/NotificationUtils.kt | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/utils/NotificationUtils.kt b/app/src/main/java/com/infomaniak/mail/utils/NotificationUtils.kt index 6fe3d8cac4..6e5ba31c65 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/NotificationUtils.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/NotificationUtils.kt @@ -52,6 +52,7 @@ import com.infomaniak.mail.receivers.NotificationActionsReceiver.Companion.EXTRA import com.infomaniak.mail.receivers.NotificationActionsReceiver.Companion.UNDO_ACTION import com.infomaniak.mail.ui.LaunchActivity import com.infomaniak.mail.ui.LaunchActivityArgs +import com.infomaniak.mail.ui.MainViewModel import com.infomaniak.mail.utils.AvatarTypeUtils.getAvatarType import com.infomaniak.mail.utils.extensions.MergedContactDictionary import io.realm.kotlin.Realm @@ -250,7 +251,7 @@ class NotificationUtils @Inject constructor( setContentTitle(null) setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY) } else { - addActions(payload) + addActions(mailbox, payload) } val avatarType = getAvatarType( @@ -299,7 +300,7 @@ class NotificationUtils @Inject constructor( } @SuppressLint("WrongConstant") - private fun NotificationCompat.Builder.addActions(payload: NotificationPayload) { + private fun NotificationCompat.Builder.addActions(mailbox: Mailbox, payload: NotificationPayload) { fun createBroadcastAction(@StringRes title: Int, intent: Intent): NotificationCompat.Action { val requestCode = UUID.randomUUID().hashCode() @@ -339,21 +340,26 @@ class NotificationUtils @Inject constructor( title = R.string.actionDelete, intent = createBroadcastIntent(DELETE_ACTION), ) - val replyAction = createActivityAction( - title = R.string.actionReply, - activity = LaunchActivity::class.java, - args = LaunchActivityArgs( - userId = payload.userId, - mailboxId = payload.mailboxId, - replyToMessageUid = payload.messageUid, - draftMode = DraftMode.REPLY, - notificationId = payload.notificationId, - ).toBundle(), - ) addAction(archiveAction) addAction(deleteAction) - addAction(replyAction) + + val canSendEmails = mailbox.permissions?.canSendEmails ?: true + if (canSendEmails){ + val replyAction = createActivityAction( + title = R.string.actionReply, + activity = LaunchActivity::class.java, + args = LaunchActivityArgs( + userId = payload.userId, + mailboxId = payload.mailboxId, + replyToMessageUid = payload.messageUid, + draftMode = DraftMode.REPLY, + notificationId = payload.notificationId, + ).toBundle(), + ) + + addAction(replyAction) + } } suspend fun updateUserAndMailboxes(mailboxController: MailboxController, tag: String) { From 2b317d9bbf312f365266ae01665ccddbdbbbdae7 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Mon, 4 May 2026 15:04:40 +0200 Subject: [PATCH 09/40] refactor: Add get function for canSendEmails --- app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt | 3 +++ .../mail/ui/main/thread/DetailedContactBottomSheetDialog.kt | 3 +-- .../com/infomaniak/mail/ui/main/thread/ThreadFragment.kt | 5 ++--- .../ui/main/thread/actions/MailActionsBottomSheetDialog.kt | 3 +-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt index 70049571a8..aac5cda1eb 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -196,6 +196,9 @@ class MainViewModel @Inject constructor( val autoAdvanceThreadsUids = SingleLiveEvent>() + val canSendEmails: Boolean + get() = currentPermissionsLive.value?.canSendEmails ?: true + val mailboxesLive = mailboxController.getMailboxesAsync(AccountUtils.currentUserId).asLiveData(ioCoroutineContext) //region Multi selection 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 c629aac6a6..50ea35b485 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 @@ -63,8 +63,7 @@ class DetailedContactBottomSheetDialog : ActionsBottomSheetDialog() { setupListeners() - val canSend = mainViewModel.currentPermissionsLive.value?.canSendEmails ?: true - if (!canSend) { + if (!mainViewModel.canSendEmails) { binding.writeMail.isEnabled = false } } 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 7ea478c1d1..2239db0e4c 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 @@ -551,9 +551,8 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { localSettings, ) quickActionBar.init(if (shouldDisplayScheduledDraftActions) R.menu.scheduled_draft_menu else R.menu.message_menu) - - val canSendEmails = mainViewModel.currentPermissionsLive.value?.canSendEmails ?: true - if (!canSendEmails) { + + if (!mainViewModel.canSendEmails) { quickActionBar.disableByMenuId(R.id.quickActionReply) quickActionBar.disableByMenuId(R.id.quickActionForward) } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt index ca3e8b693b..8aef3b623d 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt @@ -117,8 +117,7 @@ abstract class MailActionsBottomSheetDialog : ActionsBottomSheetDialog() { } } - val canSend = mainViewModel.currentPermissionsLive.value?.canSendEmails ?: true - if (!canSend) { + if (!mainViewModel.canSendEmails) { binding.mainActions.disableByMenuId(R.id.actionReply) binding.mainActions.disableByMenuId(R.id.actionReplyAll) binding.mainActions.disableByMenuId(R.id.actionForward) From 5e34304ae867dcf09f35853aa54232900539e3e2 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Mon, 4 May 2026 15:56:23 +0200 Subject: [PATCH 10/40] feat: Block send action in mailto links --- .../main/java/com/infomaniak/mail/ui/MainActivity.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt index 159c244d08..00c51daaf3 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt @@ -636,9 +636,13 @@ class MainActivity : BaseActivity() { } fun navigateToNewMessageActivity(args: Bundle? = null) { - val intent = Intent(this, NewMessageActivity::class.java) - args?.let(intent::putExtras) - newMessageActivityResultLauncher.launch(intent) + if (!mainViewModel.canSendEmails) { + snackbarManager.setValue(getString(R.string.snackbarAdminDisabledMessageSending)) + }else{ + val intent = Intent(this, NewMessageActivity::class.java) + args?.let(intent::putExtras) + newMessageActivityResultLauncher.launch(intent) + } } fun navigateToSyncAutoConfigActivity() { From 59fc05b99a8a12b203eb08b7a25fefd5ae2e7765 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Mon, 4 May 2026 16:04:33 +0200 Subject: [PATCH 11/40] feat: Block the continue button in the SelectMailbox view --- .../selectMailbox/SelectMailboxScreen.kt | 5 ++++- .../selectMailbox/SelectMailboxViewModel.kt | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/selectMailbox/SelectMailboxScreen.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/selectMailbox/SelectMailboxScreen.kt index 7ee461a861..a3443edd32 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/selectMailbox/SelectMailboxScreen.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/selectMailbox/SelectMailboxScreen.kt @@ -237,7 +237,10 @@ private fun ContinueButton( modifier = modifier, title = stringResource(R.string.buttonContinue), onClick = { selectedMailbox?.let { selectedMailboxUi -> onContinueWithMailbox(selectedMailboxUi) } }, - enabled = { uiState() is SelectMailboxViewModel.SelectedMailboxState }, + enabled = { + uiState() is SelectMailboxViewModel.SelectedMailboxState && + (uiState() as? SelectMailboxViewModel.SelectedMailboxState)?.mailboxUi?.mailboxUi?.canSendEmails == true + }, showIndeterminateProgress = { uiState() is DefaultScreen.FetchingNewMailbox }, ) } diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/selectMailbox/SelectMailboxViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/selectMailbox/SelectMailboxViewModel.kt index 15d06042c1..cf0783f767 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/selectMailbox/SelectMailboxViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/selectMailbox/SelectMailboxViewModel.kt @@ -65,7 +65,11 @@ class SelectMailboxViewModel @Inject constructor( initials = user.getInitials(), fullName = user.displayName ?: user.run { "$firstname $lastname" }, mailboxesUi = mailboxController.getMailboxes(user.id).map { mailbox -> - MailboxUi(mailboxId = mailbox.mailboxId, emailIdn = mailbox.emailIdn) + MailboxUi( + mailboxId = mailbox.mailboxId, + emailIdn = mailbox.emailIdn, + canSendEmails = mailbox.permissions?.canSendEmails ?: true + ) } ) } @@ -85,7 +89,11 @@ class SelectMailboxViewModel @Inject constructor( if (defaultMailbox != null) { val selectedMailbox = SelectedMailboxUi( userId = currentUser.id, - mailboxUi = MailboxUi(mailboxId = defaultMailbox.mailboxId, emailIdn = defaultMailbox.emailIdn), + mailboxUi = MailboxUi( + mailboxId = defaultMailbox.mailboxId, + emailIdn = defaultMailbox.emailIdn, + canSendEmails = defaultMailbox.permissions?.canSendEmails ?: true + ), avatarUrl = currentUser.avatar, initials = currentUser.getInitials() ) @@ -126,7 +134,8 @@ class SelectMailboxViewModel @Inject constructor( data class MailboxUi( val mailboxId: Int, - val emailIdn: String + val emailIdn: String, + val canSendEmails: Boolean = true, ) data class SelectedMailboxUi( From 1bf1af8dd6fcd3d8820d32f27ecf4c7cd205034b Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Mon, 4 May 2026 17:02:42 +0200 Subject: [PATCH 12/40] feat: Disabled dropDownMenuItem --- .../compose/AccountMailboxesDropdown.kt | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/selectMailbox/compose/AccountMailboxesDropdown.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/selectMailbox/compose/AccountMailboxesDropdown.kt index 604238b1cf..699034c650 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/selectMailbox/compose/AccountMailboxesDropdown.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/selectMailbox/compose/AccountMailboxesDropdown.kt @@ -35,6 +35,7 @@ import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.MenuDefaults import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -166,19 +167,15 @@ private fun AccountDropdownMenu( ) { userWithMailboxes.mailboxesUi.forEach { mailbox -> DropdownMenuItem( - text = { - Row(verticalAlignment = Alignment.CenterVertically) { - Icon( - painter = painterResource(id = R.drawable.ic_envelope), - tint = MaterialTheme.colorScheme.primary, - contentDescription = null - ) - Text( - modifier = Modifier.padding(horizontal = Margin.Small), - text = mailbox.emailIdn - ) - } + colors = MenuDefaults.itemColors(leadingIconColor = MaterialTheme.colorScheme.primary), + leadingIcon = { + Icon( + painter = painterResource(id = R.drawable.ic_envelope), + contentDescription = null + ) }, + enabled = mailbox.canSendEmails, + text = { Text(text = mailbox.emailIdn) }, onClick = { isDropDownExpanded.value = false onClickMailbox( From c0a9c2c4cc3c5e091f0f7cafa0db7e0824cc91ab Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Tue, 5 May 2026 11:11:05 +0200 Subject: [PATCH 13/40] feat: Redirect to MainActivity when triggering mailto from outside kMail with a single mailbox --- .../mail/ui/newMessage/NewMessageActivity.kt | 26 ++++++++++++------- .../mail/ui/newMessage/NewMessageViewModel.kt | 8 ++++++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt index 206d19f54c..159ffb70a8 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt @@ -30,6 +30,7 @@ import androidx.core.content.pm.ShortcutManagerCompat import androidx.lifecycle.lifecycleScope import androidx.navigation.NavDestination import androidx.navigation.fragment.NavHostFragment +import com.infomaniak.core.ui.showToast import com.infomaniak.mail.BuildConfig import com.infomaniak.mail.MatomoMail.trackDestination import com.infomaniak.mail.R @@ -107,25 +108,32 @@ class NewMessageActivity : BaseActivity() { } private fun setupNagGraphStartDestination() { - val navGraph = navController.navInflater.inflate(R.navigation.new_message_navigation) lifecycleScope.launch { - when (intent.action) { + val (graphResId, startDestinationId) = when (intent.action) { Intent.ACTION_SEND, Intent.ACTION_SEND_MULTIPLE, Intent.ACTION_VIEW, Intent.ACTION_SENDTO -> { - navGraph.setStartDestination( - if (newMessageViewModel.hasMultiMailboxes()) { - R.id.selectMailboxFragment - } else { - R.id.newMessageFragment + when { + newMessageViewModel.hasMultiMailboxes() -> { + Pair(R.navigation.new_message_navigation, R.id.selectMailboxFragment) } - ) + newMessageViewModel.canSendMails() -> { + Pair(R.navigation.new_message_navigation, R.id.newMessageFragment) + } + else -> { + applicationContext.showToast(R.string.snackbarAdminDisabledMessageSending) + Pair(R.navigation.main_navigation, R.id.mainActivity) + } + } } else -> { - navGraph.setStartDestination(R.id.newMessageFragment) + Pair(R.navigation.new_message_navigation, R.id.newMessageFragment) } } + + val navGraph = navController.navInflater.inflate(graphResId) + navGraph.setStartDestination(startDestinationId) navController.graph = navGraph } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt index 81c7bbcd48..0a4ac1640c 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt @@ -319,6 +319,14 @@ class NewMessageViewModel @Inject constructor( }.size > 1 } + suspend fun canSendMails(): Boolean = withContext(ioDispatcher) { + val mailbox = AccountUtils.getAllUsersSync().flatMap { user -> + mailboxController.getMailboxes(user.id) + }.first() + + mailbox.permissions?.canSendEmails ?: true + } + fun initDraftAndViewModel(intent: Intent): LiveData = liveData(ioCoroutineContext) { val realm = mailboxContentRealm() From e7424208b49daff24a81d00c31fdef623d94002e Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Tue, 5 May 2026 14:52:19 +0200 Subject: [PATCH 14/40] feat: Display snackbar on mailto link when only one mailbox is configured --- .../java/com/infomaniak/mail/ui/MainActivity.kt | 16 +++++++++++++++- .../mail/ui/newMessage/NewMessageActivity.kt | 13 +++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt index 00c51daaf3..2216164968 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt @@ -18,6 +18,7 @@ package com.infomaniak.mail.ui import android.annotation.SuppressLint +import android.app.ComponentCaller import android.content.Intent import android.os.Bundle import android.os.CountDownTimer @@ -234,6 +235,8 @@ class MainActivity : BaseActivity() { syncDiscoveryManager.init(::showSyncDiscovery) observeNotificationToRefresh() + + handleAdminDisabledSendingSnackbarIfNeeded(intent) } private fun handleMenuDrawerEdgeToEdge() { @@ -438,6 +441,16 @@ class MainActivity : BaseActivity() { notificationManagerCompat.cancel(GENERIC_NEW_MAILS_NOTIFICATION_ID) } + override fun onNewIntent(intent: Intent, caller: ComponentCaller) { + super.onNewIntent(intent, caller) + handleAdminDisabledSendingSnackbarIfNeeded(intent) + } + + private fun handleAdminDisabledSendingSnackbarIfNeeded(intent: Intent) { + if (!intent.getBooleanExtra(EXTRA_SHOW_ADMIN_DISABLED_SENDING_SNACKBAR, false)) return + snackbarManager.setValue(getString(R.string.snackbarAdminDisabledMessageSending)) + } + private fun handleOnBackPressed() = with(binding) { fun closeDrawer() { @@ -638,7 +651,7 @@ class MainActivity : BaseActivity() { fun navigateToNewMessageActivity(args: Bundle? = null) { if (!mainViewModel.canSendEmails) { snackbarManager.setValue(getString(R.string.snackbarAdminDisabledMessageSending)) - }else{ + } else { val intent = Intent(this, NewMessageActivity::class.java) args?.let(intent::putExtras) newMessageActivityResultLauncher.launch(intent) @@ -679,6 +692,7 @@ class MainActivity : BaseActivity() { const val SYNC_AUTO_CONFIG_KEY = "syncAutoConfigKey" const val SYNC_AUTO_CONFIG_SUCCESS = "syncAutoConfigSuccess" const val SYNC_AUTO_CONFIG_ALREADY_SYNC = "syncAutoConfigAlreadySync" + const val EXTRA_SHOW_ADMIN_DISABLED_SENDING_SNACKBAR = "show_admin_disabled_sending_snackbar" private const val DEFAULT_APP_REVIEW_LAUNCHES = 50 private const val MAX_APP_REVIEW_LAUNCHES = 500 diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt index 159ffb70a8..47b1d96389 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt @@ -30,7 +30,6 @@ import androidx.core.content.pm.ShortcutManagerCompat import androidx.lifecycle.lifecycleScope import androidx.navigation.NavDestination import androidx.navigation.fragment.NavHostFragment -import com.infomaniak.core.ui.showToast import com.infomaniak.mail.BuildConfig import com.infomaniak.mail.MatomoMail.trackDestination import com.infomaniak.mail.R @@ -39,6 +38,7 @@ import com.infomaniak.mail.data.models.draft.Draft.DraftAction import com.infomaniak.mail.databinding.ActivityNewMessageBinding import com.infomaniak.mail.ui.BaseActivity import com.infomaniak.mail.ui.LaunchActivity +import com.infomaniak.mail.ui.MainActivity import com.infomaniak.mail.ui.main.SnackbarManager import com.infomaniak.mail.utils.AccountUtils import com.infomaniak.mail.utils.SentryDebug @@ -93,7 +93,6 @@ class NewMessageActivity : BaseActivity() { finish() } - setupSnackbar() setupNavController() setupFeatureFlagIfMailTo() } @@ -122,8 +121,12 @@ class NewMessageActivity : BaseActivity() { Pair(R.navigation.new_message_navigation, R.id.newMessageFragment) } else -> { - applicationContext.showToast(R.string.snackbarAdminDisabledMessageSending) - Pair(R.navigation.main_navigation, R.id.mainActivity) + Intent(this@NewMessageActivity, MainActivity::class.java).apply { + putExtra(MainActivity.EXTRA_SHOW_ADMIN_DISABLED_SENDING_SNACKBAR, true) + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) + }.also(::startActivity) + finish() + return@launch } } } @@ -135,6 +138,8 @@ class NewMessageActivity : BaseActivity() { val navGraph = navController.navInflater.inflate(graphResId) navGraph.setStartDestination(startDestinationId) navController.graph = navGraph + + setupSnackbar() } } From d8831c74845221542eaeb3994fd7fd0ea680ce74 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Tue, 5 May 2026 17:51:17 +0200 Subject: [PATCH 15/40] feat: Disabled reply button in ThreadAdapter --- .../com/infomaniak/mail/ui/MainViewModel.kt | 11 ++++++++--- .../mail/ui/main/thread/ThreadAdapter.kt | 19 +++++++++++++++++-- .../mail/ui/main/thread/ThreadFragment.kt | 9 +++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt index aac5cda1eb..bba8528894 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -196,9 +196,6 @@ class MainViewModel @Inject constructor( val autoAdvanceThreadsUids = SingleLiveEvent>() - val canSendEmails: Boolean - get() = currentPermissionsLive.value?.canSendEmails ?: true - val mailboxesLive = mailboxController.getMailboxesAsync(AccountUtils.currentUserId).asLiveData(ioCoroutineContext) //region Multi selection @@ -276,6 +273,14 @@ class MainViewModel @Inject constructor( val currentPermissionsLive = _currentMailboxObjectId.flatMapLatest { it?.let(permissionsController::getPermissionsAsync) ?: emptyFlow() }.asLiveData(ioCoroutineContext) + + val canSendEmailsLive: LiveData = currentPermissionsLive.map { permissions -> + permissions?.canSendEmails ?: true + } + + val canSendEmails: Boolean + get() = currentPermissionsLive.value?.canSendEmails ?: true + //endregion //region Current Folder 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 d17e4b03f2..2e7ab7d85d 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,6 +120,8 @@ class ThreadAdapter( private val manuallyAllowedMessagesUids = mutableSetOf() private lateinit var recyclerView: RecyclerView + + private var canSendEmails: Boolean = true private val webViewUtils by lazy { WebViewUtils(recyclerView.context) } private val scaledTouchSlop by lazy { ViewConfiguration.get(recyclerView.context).scaledTouchSlop } @@ -194,10 +196,17 @@ class ThreadAdapter( is NotifyType.MessagesCollapseStateChanged -> { holder.handleMessagesCollapseStatePayload(item.message, isCollapsible = payload.isCollapsible) } + NotifyType.UpdatePermissions -> holder.handlePermissionsPayload(canSendEmails) } } }.getOrDefault(Unit) + fun updateEmailsPermission(canSend: Boolean) { + if (this.canSendEmails != canSend) { + this.canSendEmails = canSend + notifyItemRangeChanged(0, itemCount, NotifyType.UpdatePermissions) + } + } private fun MessageViewHolder.handleToggleLightModePayload(messageUid: String) = with(threadAdapterState) { isThemeTheSameMap[messageUid] = !isThemeTheSameMap[messageUid]!! toggleContentAndQuoteTheme(messageUid) @@ -246,7 +255,7 @@ class ThreadAdapter( } if (item is MessageUi) { - (holder as MessageViewHolder).bindMail(item, position) + (holder as MessageViewHolder).bindMail(item, position, canSendEmails) } else { (holder as SuperCollapsedBlockViewHolder).bindSuperCollapsedBlock(item as SuperCollapsedBlock) } @@ -262,7 +271,7 @@ class ThreadAdapter( } } - private fun MessageViewHolder.bindMail(messageUi: MessageUi, position: Int) { + private fun MessageViewHolder.bindMail(messageUi: MessageUi, position: Int, canSendEmails: Boolean) { initMapForNewMessage(messageUi.message, position) @@ -274,6 +283,11 @@ class ThreadAdapter( bindEmojiReactions(messageUi) onExpandOrCollapseMessage(messageUi.message, shouldTrack = false) + handlePermissionsPayload(canSendEmails) + } + + private fun MessageViewHolder.handlePermissionsPayload(canSend: Boolean) { + binding.replyButton.isEnabled = canSend } private fun MessageViewHolder.bindCalendarEvent(message: Message) { @@ -960,6 +974,7 @@ class ThreadAdapter( data object OnlyRebindCalendarAttendance : NotifyType data object OnlyRebindEmojiReactions : NotifyType data object UnsubscribeRebind : NotifyType + data object UpdatePermissions : NotifyType @JvmInline value class MessagesCollapseStateChanged(val isCollapsible: Boolean) : NotifyType } 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 2239db0e4c..2fe38289b0 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 @@ -33,8 +33,10 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle import androidx.lifecycle.distinctUntilChanged import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationPolicy import androidx.recyclerview.widget.SimpleItemAnimator import androidx.work.Data @@ -220,6 +222,7 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { permissionUtils.registerDownloadManagerPermission(fragment = this) observeLightThemeToggle() + observeCanSendEmails() observeThreadLive() observeMessagesLive() observeMessagesAreCollapsibles() @@ -527,6 +530,12 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { mainViewModel.toggleLightThemeForMessage.observe(viewLifecycleOwner, threadAdapter::toggleLightMode) } + private fun observeCanSendEmails() { + mainViewModel.canSendEmailsLive.observe(viewLifecycleOwner) { canSend -> + threadAdapter.updateEmailsPermission(canSend) + } + } + private fun observeThreadLive() = with(binding) { threadViewModel.threadLive.observe(viewLifecycleOwner) { thread -> From 70f7c12d10826bb2f2f13a35778bc43d6b9bdb6f Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Wed, 6 May 2026 09:21:36 +0200 Subject: [PATCH 16/40] refactor: Remove unused code and restore code accidentally deleted --- .../mail/ui/main/folder/ThreadListFragment.kt | 2 +- .../mail/ui/main/thread/ThreadFragment.kt | 2 ++ .../mail/utils/extensions/ViewExt.kt | 31 +++---------------- 3 files changed, 8 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt index a1ed737bfa..174b46d12d 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt @@ -472,7 +472,7 @@ class ThreadListFragment : TwoPaneFragment(), PickerEmojiObserver { newMessageFab.bindSendingClickListener( lifecycleOwner = viewLifecycleOwner, - permissionLive = mainViewModel.currentPermissionsLive, + canSendEmailsLive = mainViewModel.canSendEmailsLive, onActionBlocked = { snackbarManager.setValue(getString(R.string.snackbarAdminDisabledMessageSending)) }, 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 2fe38289b0..3ebc56361f 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 @@ -853,9 +853,11 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { } R.id.quickActionReply -> { trackThreadActionsEvent(MatomoName.Reply) + threadViewModel.clickOnQuickActionBar(menuId) } R.id.quickActionForward -> { trackThreadActionsEvent(MatomoName.Forward) + threadViewModel.clickOnQuickActionBar(menuId) } } } diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt index c5e8f7f484..c00180617e 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt @@ -28,16 +28,16 @@ import androidx.lifecycle.LiveData import com.google.android.material.button.MaterialButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.infomaniak.mail.R +import com.infomaniak.mail.data.api.ApiRoutes.permissions import com.infomaniak.mail.data.models.mailbox.MailboxPermissions fun View.bindSendingClickListener( lifecycleOwner: LifecycleOwner, - permissionLive: LiveData, + canSendEmailsLive: LiveData, onActionBlocked: () -> Unit, onActionExecute: () -> Unit ) { - permissionLive.observe(lifecycleOwner) { permissions -> - val canSendEmails = permissions?.canSendEmails ?: true + canSendEmailsLive.observe(lifecycleOwner) { canSendEmails -> val buttonState = if (canSendEmails) SendingButtonState.Send else SendingButtonState.SendingBlocked this.setSendingClickListener( @@ -62,7 +62,6 @@ fun View.setSendingClickListener( when (buttonState) { SendingButtonState.Send -> onActionExecute() SendingButtonState.SendingBlocked -> onActionBlocked() - SendingButtonState.Disabled -> Unit } } } @@ -71,31 +70,11 @@ fun View.setSendingClickListener( fun View.applyDisabledColor() { val color = ContextCompat.getColor(context, R.color.disabledIconColor) - when (this) { - is ExtendedFloatingActionButton -> { - this.backgroundTintList = ColorStateList.valueOf(color) - } - - is MaterialButton -> { - this.setTextColor(color) - this.iconTint = ColorStateList.valueOf(color) - } - - // is ImageView -> { - // this.setColorFilter(color) - // } - // - // is TextView -> { - // this.setTextColor(color) - // } - - else -> Log.i("elouan", "not supported : $this") - } + if (this is ExtendedFloatingActionButton) this.backgroundTintList = ColorStateList.valueOf(color) } enum class SendingButtonState { Send, - SendingBlocked, - Disabled + SendingBlocked } From 430a332cd1de92c0e48927717feaa6c3cc81f114 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Wed, 6 May 2026 11:16:53 +0200 Subject: [PATCH 17/40] fix: Correct current mailbox selection --- .../mail/ui/newMessage/NewMessageActivity.kt | 43 +++++++++---------- .../mail/ui/newMessage/NewMessageViewModel.kt | 9 ++-- 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt index 47b1d96389..b69902d585 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt @@ -79,7 +79,6 @@ class NewMessageActivity : BaseActivity() { ShortcutManagerCompat.reportShortcutUsed(this@NewMessageActivity, Shortcuts.NEW_MESSAGE.id) setContentView(binding.root) addTwoFactorAuthOverlay() - setupNagGraphStartDestination() if (!isAuth()) { finish() @@ -93,6 +92,7 @@ class NewMessageActivity : BaseActivity() { finish() } + setupNavGraphStartDestination() setupNavController() setupFeatureFlagIfMailTo() } @@ -106,36 +106,25 @@ class NewMessageActivity : BaseActivity() { return true } - private fun setupNagGraphStartDestination() { + private fun setupNavGraphStartDestination() { lifecycleScope.launch { - val (graphResId, startDestinationId) = when (intent.action) { + val isSendIntent = intent.action in setOf( Intent.ACTION_SEND, Intent.ACTION_SEND_MULTIPLE, Intent.ACTION_VIEW, - Intent.ACTION_SENDTO -> { - when { - newMessageViewModel.hasMultiMailboxes() -> { - Pair(R.navigation.new_message_navigation, R.id.selectMailboxFragment) - } - newMessageViewModel.canSendMails() -> { - Pair(R.navigation.new_message_navigation, R.id.newMessageFragment) - } - else -> { - Intent(this@NewMessageActivity, MainActivity::class.java).apply { - putExtra(MainActivity.EXTRA_SHOW_ADMIN_DISABLED_SENDING_SNACKBAR, true) - addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) - }.also(::startActivity) - finish() - return@launch - } - } - } + Intent.ACTION_SENDTO + ) + + val startDestinationId = when { + isSendIntent && newMessageViewModel.hasMultiMailboxes() -> R.id.selectMailboxFragment + newMessageViewModel.canSendEmails() -> R.id.newMessageFragment else -> { - Pair(R.navigation.new_message_navigation, R.id.newMessageFragment) + handleDisabledSending() + return@launch } } - val navGraph = navController.navInflater.inflate(graphResId) + val navGraph = navController.navInflater.inflate(R.navigation.new_message_navigation) navGraph.setStartDestination(startDestinationId) navController.graph = navGraph @@ -143,6 +132,14 @@ class NewMessageActivity : BaseActivity() { } } + private fun handleDisabledSending() { + Intent(this@NewMessageActivity, MainActivity::class.java).apply { + putExtra(MainActivity.EXTRA_SHOW_ADMIN_DISABLED_SENDING_SNACKBAR, true) + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) + }.also(::startActivity) + finish() + } + private fun setupSnackbar() { fun getAnchor(): View? = when (navController.currentDestination?.id) { R.id.newMessageFragment -> findViewById(R.id.editorToolbar) diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt index 0a4ac1640c..8c481b4002 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt @@ -319,12 +319,9 @@ class NewMessageViewModel @Inject constructor( }.size > 1 } - suspend fun canSendMails(): Boolean = withContext(ioDispatcher) { - val mailbox = AccountUtils.getAllUsersSync().flatMap { user -> - mailboxController.getMailboxes(user.id) - }.first() - - mailbox.permissions?.canSendEmails ?: true + suspend fun canSendEmails(): Boolean { + val mailbox = mailboxController.getMailbox(AccountUtils.currentUserId, AccountUtils.currentMailboxId) + return mailbox?.permissions?.canSendEmails ?: false } fun initDraftAndViewModel(intent: Intent): LiveData = liveData(ioCoroutineContext) { From 82adc3d4063233d74e8f838555e1293d0e284074 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Wed, 6 May 2026 11:18:07 +0200 Subject: [PATCH 18/40] fix: Set canSendEmails to true by default --- .../infomaniak/mail/data/models/mailbox/MailboxPermissions.kt | 2 +- .../com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/data/models/mailbox/MailboxPermissions.kt b/app/src/main/java/com/infomaniak/mail/data/models/mailbox/MailboxPermissions.kt index 6ba0f8e936..831ec29d86 100644 --- a/app/src/main/java/com/infomaniak/mail/data/models/mailbox/MailboxPermissions.kt +++ b/app/src/main/java/com/infomaniak/mail/data/models/mailbox/MailboxPermissions.kt @@ -35,7 +35,7 @@ class MailboxPermissions : EmbeddedRealmObject { @SerialName("can_restore_emails") var canRestoreEmails: Boolean = false @SerialName("can_send_emails") - var canSendEmails: Boolean = false + var canSendEmails: Boolean = true companion object diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt index 8c481b4002..8307b1a69c 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageViewModel.kt @@ -321,7 +321,7 @@ class NewMessageViewModel @Inject constructor( suspend fun canSendEmails(): Boolean { val mailbox = mailboxController.getMailbox(AccountUtils.currentUserId, AccountUtils.currentMailboxId) - return mailbox?.permissions?.canSendEmails ?: false + return mailbox?.permissions?.canSendEmails ?: true } fun initDraftAndViewModel(intent: Intent): LiveData = liveData(ioCoroutineContext) { From f20fa184b876ea0b497a87486d1de682851eee8b Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Wed, 6 May 2026 11:21:46 +0200 Subject: [PATCH 19/40] feat: Add onNewIntent support for Android versions below 15 --- app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt index 2216164968..b8b6dfa021 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt @@ -441,8 +441,17 @@ class MainActivity : BaseActivity() { notificationManagerCompat.cancel(GENERIC_NEW_MAILS_NOTIFICATION_ID) } + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + handleNewIntent(intent) + } + override fun onNewIntent(intent: Intent, caller: ComponentCaller) { super.onNewIntent(intent, caller) + handleNewIntent(intent) + } + + private fun handleNewIntent(intent: Intent) { handleAdminDisabledSendingSnackbarIfNeeded(intent) } From 6b07acbbfb53dfb5d907980437d3695f68457f7b Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Wed, 6 May 2026 12:49:57 +0200 Subject: [PATCH 20/40] refactor: Reduce cognitive complexity --- .../mail/ui/main/thread/ThreadFragment.kt | 77 ++++++++++--------- 1 file changed, 42 insertions(+), 35 deletions(-) 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 3ebc56361f..6e9932264e 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 @@ -536,52 +536,59 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { } } - private fun observeThreadLive() = with(binding) { - + private fun observeThreadLive() { threadViewModel.threadLive.observe(viewLifecycleOwner) { thread -> - if (thread == null) { twoPaneViewModel.closeThread() return@observe } - iconFavorite.apply { - setIconResource(if (thread.isFavorite) R.drawable.ic_star_filled else R.drawable.ic_star) - val color = if (thread.isFavorite) { - context.getColor(R.color.favoriteYellow) - } else { - context.getAttributeColor(RAndroid.attr.colorPrimary) - } - iconTint = ColorStateList.valueOf(color) - } + updateFavoriteIcon(thread.isFavorite) + setupQuickActionBar(thread) + setupSnoozeAlert(thread) + } + } - val shouldDisplayScheduledDraftActions = thread.containsOnlyScheduledDrafts( - mainViewModel.featureFlagsLive.value, - localSettings, - ) - quickActionBar.init(if (shouldDisplayScheduledDraftActions) R.menu.scheduled_draft_menu else R.menu.message_menu) + private fun updateFavoriteIcon(isFavorite: Boolean) = with(binding.iconFavorite) { + val iconRes = if (isFavorite) R.drawable.ic_star_filled else R.drawable.ic_star + val color = if (isFavorite) { + context.getColor(R.color.favoriteYellow) + } else { + context.getAttributeColor(RAndroid.attr.colorPrimary) + } - if (!mainViewModel.canSendEmails) { - quickActionBar.disableByMenuId(R.id.quickActionReply) - quickActionBar.disableByMenuId(R.id.quickActionForward) - } + setIconResource(iconRes) + iconTint = ColorStateList.valueOf(color) + } + private fun setupQuickActionBar(thread: Thread) = with(binding.quickActionBar) { + val shouldDisplayScheduledDraftActions = thread.containsOnlyScheduledDrafts( + mainViewModel.featureFlagsLive.value, + localSettings + ) - thread.snoozeEndDate?.let { snoozeEndDate -> - val formattedDate = context.formatDayOfWeekAdaptiveYear(snoozeEndDate.toDate()) - snoozeAlert.setDescription(getString(R.string.snoozeAlertTitle, formattedDate)) - } + init(if (shouldDisplayScheduledDraftActions) R.menu.scheduled_draft_menu else R.menu.message_menu) - snoozeAlert.apply { - onAction1 { - trackSnoozeEvent(MatomoName.ModifySnooze) - navigateToSnoozeBottomSheet(SnoozeScheduleType.Modify(thread.uid)) - } - onAction2 { - trackSnoozeEvent(MatomoName.CancelSnooze) - unsnoozeThread(thread) - } - } + if (!mainViewModel.canSendEmails) { + disableByMenuId(R.id.quickActionReply) + disableByMenuId(R.id.quickActionForward) + } + } + + private fun setupSnoozeAlert(thread: Thread) = with(binding.snoozeAlert) { + thread.snoozeEndDate?.let { snoozeEndDate -> + val formattedDate = context.formatDayOfWeekAdaptiveYear(snoozeEndDate.toDate()) + setDescription(getString(R.string.snoozeAlertTitle, formattedDate)) + } + + onAction1 { + trackSnoozeEvent(MatomoName.ModifySnooze) + navigateToSnoozeBottomSheet(SnoozeScheduleType.Modify(thread.uid)) + } + + onAction2 { + trackSnoozeEvent(MatomoName.CancelSnooze) + unsnoozeThread(thread) } } From eb7fc3537be03c434e89199683027fb81f8f44f1 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Wed, 6 May 2026 13:06:55 +0200 Subject: [PATCH 21/40] feat: Block reactions in item_message --- .../infomaniak/emojicomponents/views/EmojiReactionsView.kt | 5 +++++ .../java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/EmojiComponents/src/main/java/com/infomaniak/emojicomponents/views/EmojiReactionsView.kt b/EmojiComponents/src/main/java/com/infomaniak/emojicomponents/views/EmojiReactionsView.kt index 64bf9e5a5c..9c66ef9fc2 100644 --- a/EmojiComponents/src/main/java/com/infomaniak/emojicomponents/views/EmojiReactionsView.kt +++ b/EmojiComponents/src/main/java/com/infomaniak/emojicomponents/views/EmojiReactionsView.kt @@ -80,6 +80,11 @@ class EmojiReactionsView @JvmOverloads constructor( addView(composeView) } + override fun setEnabled(enabled: Boolean) { + super.setEnabled(enabled) + setAddReactionEnabledState(enabled) + } + private fun TypedArray.getDimensionOrNull(@StyleableRes index: Int): Float? { return if (hasValue(index)) getDimension(index, -1f) else null } 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 2e7ab7d85d..c6bfdaafc1 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 @@ -286,8 +286,9 @@ class ThreadAdapter( handlePermissionsPayload(canSendEmails) } - private fun MessageViewHolder.handlePermissionsPayload(canSend: Boolean) { - binding.replyButton.isEnabled = canSend + private fun MessageViewHolder.handlePermissionsPayload(canSendEmails: Boolean) = with(binding) { + replyButton.isEnabled = canSendEmails + emojiReactions.isEnabled = canSendEmails } private fun MessageViewHolder.bindCalendarEvent(message: Message) { From 12a00992daedd1dfa2a9e8acbaf0e8bc5315b7ee Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Wed, 6 May 2026 13:38:49 +0200 Subject: [PATCH 22/40] refactor: Clean code --- .../com/infomaniak/mail/ui/main/thread/ThreadFragment.kt | 2 -- .../ui/main/thread/actions/MailActionsBottomSheetDialog.kt | 1 - .../mail/ui/main/thread/actions/MainActionsView.kt | 1 + .../mail/ui/newMessage/selectMailbox/SelectMailboxScreen.kt | 5 +---- .../java/com/infomaniak/mail/utils/NotificationUtils.kt | 3 +-- .../java/com/infomaniak/mail/utils/extensions/ViewExt.kt | 6 ------ .../com/infomaniak/mail/views/BottomQuickActionBarView.kt | 1 - 7 files changed, 3 insertions(+), 16 deletions(-) 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 6e9932264e..4f8e734f02 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 @@ -33,10 +33,8 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle import androidx.lifecycle.distinctUntilChanged import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationPolicy import androidx.recyclerview.widget.SimpleItemAnimator import androidx.work.Data diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt index 8aef3b623d..07a4548fe3 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt @@ -17,7 +17,6 @@ */ package com.infomaniak.mail.ui.main.thread.actions -import android.R.attr.text import android.os.Bundle import android.view.LayoutInflater import android.view.View diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MainActionsView.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MainActionsView.kt index 415883018d..87f5d0c88c 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MainActionsView.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MainActionsView.kt @@ -118,6 +118,7 @@ class MainActionsView @JvmOverloads constructor( fun disableByMenuId(@IdRes menuId: Int) { val index = getIndexOfMenuItem(menuId) + if (index < 0) return buttons[index].isEnabled = false textViews[index].isEnabled = false } diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/selectMailbox/SelectMailboxScreen.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/selectMailbox/SelectMailboxScreen.kt index a3443edd32..b9bd3d6750 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/selectMailbox/SelectMailboxScreen.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/selectMailbox/SelectMailboxScreen.kt @@ -237,10 +237,7 @@ private fun ContinueButton( modifier = modifier, title = stringResource(R.string.buttonContinue), onClick = { selectedMailbox?.let { selectedMailboxUi -> onContinueWithMailbox(selectedMailboxUi) } }, - enabled = { - uiState() is SelectMailboxViewModel.SelectedMailboxState && - (uiState() as? SelectMailboxViewModel.SelectedMailboxState)?.mailboxUi?.mailboxUi?.canSendEmails == true - }, + enabled = { selectedMailbox?.mailboxUi?.canSendEmails == true }, showIndeterminateProgress = { uiState() is DefaultScreen.FetchingNewMailbox }, ) } diff --git a/app/src/main/java/com/infomaniak/mail/utils/NotificationUtils.kt b/app/src/main/java/com/infomaniak/mail/utils/NotificationUtils.kt index 6e5ba31c65..a2bb8919ae 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/NotificationUtils.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/NotificationUtils.kt @@ -52,7 +52,6 @@ import com.infomaniak.mail.receivers.NotificationActionsReceiver.Companion.EXTRA import com.infomaniak.mail.receivers.NotificationActionsReceiver.Companion.UNDO_ACTION import com.infomaniak.mail.ui.LaunchActivity import com.infomaniak.mail.ui.LaunchActivityArgs -import com.infomaniak.mail.ui.MainViewModel import com.infomaniak.mail.utils.AvatarTypeUtils.getAvatarType import com.infomaniak.mail.utils.extensions.MergedContactDictionary import io.realm.kotlin.Realm @@ -345,7 +344,7 @@ class NotificationUtils @Inject constructor( addAction(deleteAction) val canSendEmails = mailbox.permissions?.canSendEmails ?: true - if (canSendEmails){ + if (canSendEmails) { val replyAction = createActivityAction( title = R.string.actionReply, activity = LaunchActivity::class.java, diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt index c00180617e..960d93bb15 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt @@ -18,18 +18,12 @@ package com.infomaniak.mail.utils.extensions import android.content.res.ColorStateList -import android.util.Log import android.view.View -import android.widget.ImageView -import android.widget.TextView import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LiveData -import com.google.android.material.button.MaterialButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.infomaniak.mail.R -import com.infomaniak.mail.data.api.ApiRoutes.permissions -import com.infomaniak.mail.data.models.mailbox.MailboxPermissions fun View.bindSendingClickListener( lifecycleOwner: LifecycleOwner, diff --git a/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt b/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt index ab75927b9e..bee5418c21 100644 --- a/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt +++ b/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt @@ -38,7 +38,6 @@ import com.google.android.material.button.MaterialButton import com.infomaniak.core.legacy.utils.getAttributes import com.infomaniak.mail.R import com.infomaniak.mail.databinding.ViewBottomQuickActionBarBinding -import com.infomaniak.mail.utils.extensions.applyDisabledColor import com.infomaniak.mail.utils.extensions.applySideAndBottomSystemInsets class BottomQuickActionBarView @JvmOverloads constructor( From 470d320f2085f12b20276b3a59f10b35f033e88b Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Wed, 6 May 2026 13:45:10 +0200 Subject: [PATCH 23/40] fix: Reset extra when it is processed --- app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt index b8b6dfa021..42e9d80990 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt @@ -458,6 +458,8 @@ class MainActivity : BaseActivity() { private fun handleAdminDisabledSendingSnackbarIfNeeded(intent: Intent) { if (!intent.getBooleanExtra(EXTRA_SHOW_ADMIN_DISABLED_SENDING_SNACKBAR, false)) return snackbarManager.setValue(getString(R.string.snackbarAdminDisabledMessageSending)) + intent.removeExtra(EXTRA_SHOW_ADMIN_DISABLED_SENDING_SNACKBAR) + setIntent(intent) } private fun handleOnBackPressed() = with(binding) { From d4164c323dbd1629f5c9dd88fc356bb6ce6aeabe Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Wed, 6 May 2026 17:08:31 +0200 Subject: [PATCH 24/40] fix: Add observe for update UI dynamically --- .../com/infomaniak/mail/ui/MainViewModel.kt | 10 ++++--- .../DetailedContactBottomSheetDialog.kt | 9 +++++++ .../mail/ui/main/thread/ThreadFragment.kt | 15 +++++++++++ .../actions/MailActionsBottomSheetDialog.kt | 27 +++++++++++++++---- .../ui/main/thread/actions/MainActionsView.kt | 7 +++++ .../mail/utils/extensions/ViewExt.kt | 15 +++++++---- 6 files changed, 70 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt index bba8528894..0072ef0fe7 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -130,6 +130,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first @@ -274,9 +275,12 @@ class MainViewModel @Inject constructor( it?.let(permissionsController::getPermissionsAsync) ?: emptyFlow() }.asLiveData(ioCoroutineContext) - val canSendEmailsLive: LiveData = currentPermissionsLive.map { permissions -> - permissions?.canSendEmails ?: true - } + val canSendEmailsLive: LiveData = _currentMailboxObjectId.flatMapLatest { mailboxObjectId -> + mailboxObjectId?.let { id -> + mailboxController.getMailboxAsync(id).map { it.obj?.permissions?.canSendEmails ?: true } + } ?: emptyFlow() + }.distinctUntilChanged() + .asLiveData(ioCoroutineContext) val canSendEmails: Boolean get() = currentPermissionsLive.value?.canSendEmails ?: true 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 50ea35b485..bf61b655c6 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 @@ -18,6 +18,7 @@ package com.infomaniak.mail.ui.main.thread import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -66,6 +67,7 @@ class DetailedContactBottomSheetDialog : ActionsBottomSheetDialog() { if (!mainViewModel.canSendEmails) { binding.writeMail.isEnabled = false } + observeCanSendEmails() } private fun setupListeners() = with(binding) { @@ -86,4 +88,11 @@ class DetailedContactBottomSheetDialog : ActionsBottomSheetDialog() { copyRecipientEmailToClipboard(navigationArgs.recipient, snackbarManager) } } + + private fun observeCanSendEmails(){ + mainViewModel.canSendEmailsLive.observe(viewLifecycleOwner) { canSend -> + Log.i("elouan", "observeCanSendEmails : ${canSend}") + binding.writeMail.isEnabled = canSend + } + } } 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 4f8e734f02..5d2089ad50 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 @@ -23,6 +23,7 @@ import android.graphics.drawable.InsetDrawable import android.os.Build.VERSION.SDK_INT import android.os.Bundle import android.text.method.LinkMovementMethod +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -38,6 +39,7 @@ import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationPolicy import androidx.recyclerview.widget.SimpleItemAnimator import androidx.work.Data +import com.google.android.gms.tasks.Tasks.call import com.infomaniak.core.fragmentnavigation.safelyNavigate import com.infomaniak.core.ksuite.data.KSuite import com.infomaniak.core.legacy.utils.context @@ -530,7 +532,20 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { private fun observeCanSendEmails() { mainViewModel.canSendEmailsLive.observe(viewLifecycleOwner) { canSend -> + Log.i("elouan", "ThreadFragment : ${canSend}") threadAdapter.updateEmailsPermission(canSend) + updateQuickActionBarSendingState(canSend) + } + } + + private fun updateQuickActionBarSendingState(canSend: Boolean) = with(binding.quickActionBar) { + Log.i("elouan", "updateQuickActionBarSendingState canSend : ${canSend}") + if (canSend) { + enable(R.id.quickActionReply) + enable(R.id.quickActionForward) + } else { + disableByMenuId(R.id.quickActionReply) + disableByMenuId(R.id.quickActionForward) } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt index 07a4548fe3..f04fe86f36 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt @@ -18,6 +18,7 @@ package com.infomaniak.mail.ui.main.thread.actions import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -116,12 +117,28 @@ abstract class MailActionsBottomSheetDialog : ActionsBottomSheetDialog() { } } - if (!mainViewModel.canSendEmails) { - binding.mainActions.disableByMenuId(R.id.actionReply) - binding.mainActions.disableByMenuId(R.id.actionReplyAll) - binding.mainActions.disableByMenuId(R.id.actionForward) - binding.addReaction.isEnabled = false + Log.i("elouan", "call updateEmailActionState : ${mainViewModel.canSendEmails}") + updateEmailActionsState(mainViewModel.canSendEmails) + observeCanSendEmails() + } + + private fun observeCanSendEmails() { + mainViewModel.canSendEmailsLive.observe(viewLifecycleOwner) { canSend -> + Log.i("elouan", "mailActionsBottomSheetDialog canSend : ${canSend}") + updateEmailActionsState(canSend) + } + } + + private fun updateEmailActionsState(canSendEmails: Boolean) { + Log.i("elouan", "updateEmailActionsState : ${canSendEmails}") + listOf(R.id.actionReply, R.id.actionReplyAll, R.id.actionForward).forEach { id -> + if (canSendEmails) { + binding.mainActions.enableByMenuId(id) + } else { + binding.mainActions.disableByMenuId(id) + } } + binding.addReaction.isEnabled = canSendEmails } private fun setShareTrailingContent() { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MainActionsView.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MainActionsView.kt index 87f5d0c88c..98102dd7b1 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MainActionsView.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MainActionsView.kt @@ -123,6 +123,13 @@ class MainActionsView @JvmOverloads constructor( textViews[index].isEnabled = false } + fun enableByMenuId(@IdRes menuId: Int) { + val index = getIndexOfMenuItem(menuId) + if (index < 0) return + buttons[index].isEnabled = true + textViews[index].isEnabled = true + } + private fun setAction(index: Int, drawable: Drawable?, title: String) { buttons[index].apply { icon = drawable diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt index 960d93bb15..7327c6f0e8 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt @@ -18,12 +18,14 @@ package com.infomaniak.mail.utils.extensions import android.content.res.ColorStateList +import android.util.Log import android.view.View import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LiveData import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.infomaniak.mail.R +import androidx.appcompat.R as RAndroid fun View.bindSendingClickListener( lifecycleOwner: LifecycleOwner, @@ -32,6 +34,7 @@ fun View.bindSendingClickListener( onActionExecute: () -> Unit ) { canSendEmailsLive.observe(lifecycleOwner) { canSendEmails -> + Log.i("elouan", "ThreadListFragment : ${canSendEmails}") val buttonState = if (canSendEmails) SendingButtonState.Send else SendingButtonState.SendingBlocked this.setSendingClickListener( @@ -48,9 +51,7 @@ fun View.setSendingClickListener( onActionExecute: () -> Unit ) { - if (buttonState != SendingButtonState.Send) { - this.applyDisabledColor() - } + this.applyColor(buttonState) this.setOnClickListener { when (buttonState) { @@ -61,8 +62,12 @@ fun View.setSendingClickListener( } -fun View.applyDisabledColor() { - val color = ContextCompat.getColor(context, R.color.disabledIconColor) +fun View.applyColor(buttonState: SendingButtonState) { + val color = if (buttonState == SendingButtonState.SendingBlocked) { + ContextCompat.getColor(context, R.color.disabledIconColor) + } else { + context.getAttributeColor(RAndroid.attr.colorPrimary) + } if (this is ExtendedFloatingActionButton) this.backgroundTintList = ColorStateList.valueOf(color) } From 04006747570638e9ea1ec9a29e2dc0578be8cc77 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Wed, 6 May 2026 18:11:01 +0200 Subject: [PATCH 25/40] feat: Use Flow to dynamically update the UI when permissions change --- .../com/infomaniak/mail/ui/MainViewModel.kt | 25 ++++++++++++------- .../mail/ui/main/folder/ThreadListFragment.kt | 2 +- .../DetailedContactBottomSheetDialog.kt | 8 +++--- .../mail/ui/main/thread/ThreadFragment.kt | 11 +++----- .../actions/MailActionsBottomSheetDialog.kt | 7 ++---- .../mail/utils/extensions/ViewExt.kt | 9 +++---- .../mail/views/BottomQuickActionBarView.kt | 9 +++++++ 7 files changed, 39 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt index 0072ef0fe7..18709701a8 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -127,14 +127,15 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapNotNull @@ -275,12 +276,15 @@ class MainViewModel @Inject constructor( it?.let(permissionsController::getPermissionsAsync) ?: emptyFlow() }.asLiveData(ioCoroutineContext) - val canSendEmailsLive: LiveData = _currentMailboxObjectId.flatMapLatest { mailboxObjectId -> - mailboxObjectId?.let { id -> - mailboxController.getMailboxAsync(id).map { it.obj?.permissions?.canSendEmails ?: true } - } ?: emptyFlow() - }.distinctUntilChanged() - .asLiveData(ioCoroutineContext) + val canSendEmailsFlow: SharedFlow = _currentMailboxObjectId.flatMapLatest { + it?.let(permissionsController::getPermissionsAsync) ?: flowOf(null) + }.map { permissions -> + permissions?.canSendEmails ?: true + }.shareIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(), + replay = 1 + ) val canSendEmails: Boolean get() = currentPermissionsLive.value?.canSendEmails ?: true @@ -496,8 +500,11 @@ class MainViewModel @Inject constructor( SentryLog.d(TAG, "Force refresh Permissions") with(ApiRepository.getPermissions(mailbox.accessId, mailbox.hostingId)) { if (isSuccess()) { - mailboxController.updateMailbox(mailbox.objectId) { - it.permissions = data + mailboxController.updateMailbox(mailbox.objectId) { localMailbox -> + localMailbox.permissions = null + } + mailboxController.updateMailbox(mailbox.objectId) { localMailbox -> + localMailbox.permissions = data } } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt index 174b46d12d..930b676ef7 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt @@ -472,7 +472,7 @@ class ThreadListFragment : TwoPaneFragment(), PickerEmojiObserver { newMessageFab.bindSendingClickListener( lifecycleOwner = viewLifecycleOwner, - canSendEmailsLive = mainViewModel.canSendEmailsLive, + canSendEmailsFlow = mainViewModel.canSendEmailsFlow, onActionBlocked = { snackbarManager.setValue(getString(R.string.snackbarAdminDisabledMessageSending)) }, 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 bf61b655c6..25f1089ee5 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 @@ -18,17 +18,16 @@ package com.infomaniak.mail.ui.main.thread import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.navArgs +import com.infomaniak.core.common.observe import com.infomaniak.core.legacy.utils.safeBinding import com.infomaniak.mail.MatomoMail.MatomoName import com.infomaniak.mail.MatomoMail.trackContactActionsEvent -import com.infomaniak.mail.R import com.infomaniak.mail.databinding.BottomSheetDetailedContactBinding import com.infomaniak.mail.ui.MainViewModel import com.infomaniak.mail.ui.main.SnackbarManager @@ -89,9 +88,8 @@ class DetailedContactBottomSheetDialog : ActionsBottomSheetDialog() { } } - private fun observeCanSendEmails(){ - mainViewModel.canSendEmailsLive.observe(viewLifecycleOwner) { canSend -> - Log.i("elouan", "observeCanSendEmails : ${canSend}") + private fun observeCanSendEmails() { + mainViewModel.canSendEmailsFlow.observe(viewLifecycleOwner) { canSend -> binding.writeMail.isEnabled = canSend } } 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 5d2089ad50..4363654c6d 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 @@ -23,7 +23,6 @@ import android.graphics.drawable.InsetDrawable import android.os.Build.VERSION.SDK_INT import android.os.Bundle import android.text.method.LinkMovementMethod -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -39,7 +38,7 @@ import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationPolicy import androidx.recyclerview.widget.SimpleItemAnimator import androidx.work.Data -import com.google.android.gms.tasks.Tasks.call +import com.infomaniak.core.common.observe import com.infomaniak.core.fragmentnavigation.safelyNavigate import com.infomaniak.core.ksuite.data.KSuite import com.infomaniak.core.legacy.utils.context @@ -531,18 +530,16 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { } private fun observeCanSendEmails() { - mainViewModel.canSendEmailsLive.observe(viewLifecycleOwner) { canSend -> - Log.i("elouan", "ThreadFragment : ${canSend}") + mainViewModel.canSendEmailsFlow.observe(viewLifecycleOwner) { canSend -> threadAdapter.updateEmailsPermission(canSend) updateQuickActionBarSendingState(canSend) } } private fun updateQuickActionBarSendingState(canSend: Boolean) = with(binding.quickActionBar) { - Log.i("elouan", "updateQuickActionBarSendingState canSend : ${canSend}") if (canSend) { - enable(R.id.quickActionReply) - enable(R.id.quickActionForward) + enableByMenuId(R.id.quickActionReply) + enableByMenuId(R.id.quickActionForward) } else { disableByMenuId(R.id.quickActionReply) disableByMenuId(R.id.quickActionForward) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt index f04fe86f36..41f1202d7c 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt @@ -18,12 +18,12 @@ package com.infomaniak.mail.ui.main.thread.actions import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels +import com.infomaniak.core.common.observe import com.infomaniak.core.ksuite.data.KSuite import com.infomaniak.core.legacy.utils.safeBinding import com.infomaniak.mail.MatomoMail.MatomoName @@ -117,20 +117,17 @@ abstract class MailActionsBottomSheetDialog : ActionsBottomSheetDialog() { } } - Log.i("elouan", "call updateEmailActionState : ${mainViewModel.canSendEmails}") updateEmailActionsState(mainViewModel.canSendEmails) observeCanSendEmails() } private fun observeCanSendEmails() { - mainViewModel.canSendEmailsLive.observe(viewLifecycleOwner) { canSend -> - Log.i("elouan", "mailActionsBottomSheetDialog canSend : ${canSend}") + mainViewModel.canSendEmailsFlow.observe(viewLifecycleOwner) { canSend -> updateEmailActionsState(canSend) } } private fun updateEmailActionsState(canSendEmails: Boolean) { - Log.i("elouan", "updateEmailActionsState : ${canSendEmails}") listOf(R.id.actionReply, R.id.actionReplyAll, R.id.actionForward).forEach { id -> if (canSendEmails) { binding.mainActions.enableByMenuId(id) diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt index 7327c6f0e8..dc4b73588b 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt @@ -18,23 +18,22 @@ package com.infomaniak.mail.utils.extensions import android.content.res.ColorStateList -import android.util.Log import android.view.View import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.LiveData import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton +import com.infomaniak.core.common.observe import com.infomaniak.mail.R +import kotlinx.coroutines.flow.SharedFlow import androidx.appcompat.R as RAndroid fun View.bindSendingClickListener( lifecycleOwner: LifecycleOwner, - canSendEmailsLive: LiveData, + canSendEmailsFlow: SharedFlow, onActionBlocked: () -> Unit, onActionExecute: () -> Unit ) { - canSendEmailsLive.observe(lifecycleOwner) { canSendEmails -> - Log.i("elouan", "ThreadListFragment : ${canSendEmails}") + canSendEmailsFlow.observe(lifecycleOwner) { canSendEmails -> val buttonState = if (canSendEmails) SendingButtonState.Send else SendingButtonState.SendingBlocked this.setSendingClickListener( diff --git a/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt b/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt index bee5418c21..e096181b84 100644 --- a/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt +++ b/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt @@ -93,6 +93,15 @@ class BottomQuickActionBarView @JvmOverloads constructor( } } + fun enableByMenuId(@IdRes menuId: Int) { + for (index in 0 until menu.size) { + if (menu[index].itemId == menuId) { + enable(index) + return + } + } + } + fun changeIcon(index: Int, @DrawableRes icon: Int) { buttons[index].setIconResource(icon) } From 479f2b1494dbbc06041efa85a798a012003e56e9 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Thu, 7 May 2026 10:47:44 +0200 Subject: [PATCH 26/40] refactor: Remove unused code --- app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt | 6 ------ app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt | 3 --- 2 files changed, 9 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt index 42e9d80990..ebe3d95e1c 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt @@ -18,7 +18,6 @@ package com.infomaniak.mail.ui import android.annotation.SuppressLint -import android.app.ComponentCaller import android.content.Intent import android.os.Bundle import android.os.CountDownTimer @@ -446,11 +445,6 @@ class MainActivity : BaseActivity() { handleNewIntent(intent) } - override fun onNewIntent(intent: Intent, caller: ComponentCaller) { - super.onNewIntent(intent, caller) - handleNewIntent(intent) - } - private fun handleNewIntent(intent: Intent) { handleAdminDisabledSendingSnackbarIfNeeded(intent) } diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt index 18709701a8..9872edeb15 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -500,9 +500,6 @@ class MainViewModel @Inject constructor( SentryLog.d(TAG, "Force refresh Permissions") with(ApiRepository.getPermissions(mailbox.accessId, mailbox.hostingId)) { if (isSuccess()) { - mailboxController.updateMailbox(mailbox.objectId) { localMailbox -> - localMailbox.permissions = null - } mailboxController.updateMailbox(mailbox.objectId) { localMailbox -> localMailbox.permissions = data } From fa49eafc74bc4fc439a76055a6ccc0578663e2c0 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Thu, 7 May 2026 10:54:25 +0200 Subject: [PATCH 27/40] fix: Enable EmojiReactionView only if permission is true and canBeReactedTo is true --- .../emojicomponents/views/EmojiReactionsView.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/EmojiComponents/src/main/java/com/infomaniak/emojicomponents/views/EmojiReactionsView.kt b/EmojiComponents/src/main/java/com/infomaniak/emojicomponents/views/EmojiReactionsView.kt index 9c66ef9fc2..108d0dcbda 100644 --- a/EmojiComponents/src/main/java/com/infomaniak/emojicomponents/views/EmojiReactionsView.kt +++ b/EmojiComponents/src/main/java/com/infomaniak/emojicomponents/views/EmojiReactionsView.kt @@ -53,7 +53,11 @@ class EmojiReactionsView @JvmOverloads constructor( ) : FrameLayout(context, attrs, defStyleAttr) { private val reactionsState = mutableStateListOf() - private var isAddReactionEnabled by mutableStateOf(true) + private var isViewEnabled by mutableStateOf(true) + private var isAddReactionEnabledForMessage by mutableStateOf(true) + private val isAddReactionEnabled: Boolean + get() = isViewEnabled && isAddReactionEnabledForMessage + private var addReactionClickListener: (() -> Unit)? = null private var onEmojiClickListener: ((emoji: String) -> Unit)? = null @@ -82,7 +86,7 @@ class EmojiReactionsView @JvmOverloads constructor( override fun setEnabled(enabled: Boolean) { super.setEnabled(enabled) - setAddReactionEnabledState(enabled) + isViewEnabled = enabled } private fun TypedArray.getDimensionOrNull(@StyleableRes index: Int): Float? { @@ -144,7 +148,7 @@ class EmojiReactionsView @JvmOverloads constructor( } fun setAddReactionEnabledState(isEnabled: Boolean) { - isAddReactionEnabled = isEnabled + isAddReactionEnabledForMessage = isEnabled } @Composable From 4c098803ef4b0333866bcfe90562b55c42995b72 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Thu, 7 May 2026 11:04:39 +0200 Subject: [PATCH 28/40] refactor: Clean code --- .../ui/main/thread/DetailedContactBottomSheetDialog.kt | 4 +--- .../mail/ui/main/thread/actions/ActionItemView.kt | 9 ++++----- .../thread/actions/MailActionsBottomSheetDialog.kt | 10 +++------- 3 files changed, 8 insertions(+), 15 deletions(-) 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 25f1089ee5..fffc43ca35 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 @@ -63,9 +63,7 @@ class DetailedContactBottomSheetDialog : ActionsBottomSheetDialog() { setupListeners() - if (!mainViewModel.canSendEmails) { - binding.writeMail.isEnabled = false - } + if (!mainViewModel.canSendEmails) binding.writeMail.isEnabled = false observeCanSendEmails() } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ActionItemView.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ActionItemView.kt index ea2561fe7e..df1a4e11f4 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ActionItemView.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ActionItemView.kt @@ -93,12 +93,11 @@ class ActionItemView @JvmOverloads constructor( binding.root.setOnClickListener(onClickListener) } - override fun setEnabled(enabled: Boolean) { + override fun setEnabled(enabled: Boolean) = with(binding) { super.setEnabled(enabled) - binding.root.isEnabled = enabled - - binding.icon.isEnabled = enabled - binding.title.isEnabled = enabled + root.isEnabled = enabled + icon.isEnabled = enabled + title.isEnabled = enabled } fun setIconResource(@DrawableRes iconResourceId: Int) = binding.icon.setImageResource(iconResourceId) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt index 41f1202d7c..26d460096e 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt @@ -127,15 +127,11 @@ abstract class MailActionsBottomSheetDialog : ActionsBottomSheetDialog() { } } - private fun updateEmailActionsState(canSendEmails: Boolean) { + private fun updateEmailActionsState(canSendEmails: Boolean) = with(binding) { listOf(R.id.actionReply, R.id.actionReplyAll, R.id.actionForward).forEach { id -> - if (canSendEmails) { - binding.mainActions.enableByMenuId(id) - } else { - binding.mainActions.disableByMenuId(id) - } + if (canSendEmails) mainActions.enableByMenuId(id) else mainActions.disableByMenuId(id) } - binding.addReaction.isEnabled = canSendEmails + addReaction.isEnabled = canSendEmails } private fun setShareTrailingContent() { From d9a7d8b0e44873cb4283691e971b478bb3ce4bcd Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Tue, 19 May 2026 16:34:11 +0200 Subject: [PATCH 29/40] feat: Block button on schedule alert --- .../mail/ui/main/thread/MessageAlertView.kt | 6 ++++++ .../mail/ui/main/thread/ThreadAdapter.kt | 1 + .../alert_view_button_color_disabled.xml | 21 +++++++++++++++++++ app/src/main/res/values/styles.xml | 2 +- 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/color/alert_view_button_color_disabled.xml diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/MessageAlertView.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/MessageAlertView.kt index ce348af4cd..6ae49e4ed8 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/MessageAlertView.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/MessageAlertView.kt @@ -65,6 +65,12 @@ class MessageAlertView @JvmOverloads constructor( } } + override fun setEnabled(enabled: Boolean) = with(binding) { + super.setEnabled(enabled) + action1.isEnabled = enabled + action2.isEnabled = enabled + } + fun setDescription(text: String) { binding.description.text = text } 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 c6bfdaafc1..a701d8a703 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 @@ -289,6 +289,7 @@ class ThreadAdapter( private fun MessageViewHolder.handlePermissionsPayload(canSendEmails: Boolean) = with(binding) { replyButton.isEnabled = canSendEmails emojiReactions.isEnabled = canSendEmails + scheduleAlert.isEnabled = canSendEmails } private fun MessageViewHolder.bindCalendarEvent(message: Message) { diff --git a/app/src/main/res/color/alert_view_button_color_disabled.xml b/app/src/main/res/color/alert_view_button_color_disabled.xml new file mode 100644 index 0000000000..57250a3b96 --- /dev/null +++ b/app/src/main/res/color/alert_view_button_color_disabled.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index e0338d7503..49bf3956e8 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -208,7 +208,7 @@ @dimen/marginStandardMedium @dimen/marginStandardVerySmall @dimen/marginStandardVerySmall - @color/alertViewButtonColor + @color/alert_view_button_color_disabled 12sp @dimen/smallCornerRadius @color/ripple_color_alert_view From 186b08c374dd28efde9bd16d4aed3857d32bd38d Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Fri, 22 May 2026 10:50:18 +0200 Subject: [PATCH 30/40] refactor: Update flow construction and remove usage of canSendEmails --- .../com/infomaniak/mail/ui/MainActivity.kt | 2 +- .../com/infomaniak/mail/ui/MainViewModel.kt | 21 ++++++++----------- .../DetailedContactBottomSheetDialog.kt | 2 +- .../mail/ui/main/thread/ThreadFragment.kt | 2 +- .../actions/MailActionsBottomSheetDialog.kt | 2 +- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt index ebe3d95e1c..44478ba65c 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt @@ -654,7 +654,7 @@ class MainActivity : BaseActivity() { } fun navigateToNewMessageActivity(args: Bundle? = null) { - if (!mainViewModel.canSendEmails) { + if (!mainViewModel.canSendEmailsFlow.value) { snackbarManager.setValue(getString(R.string.snackbarAdminDisabledMessageSending)) } else { val intent = Intent(this, NewMessageActivity::class.java) diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt index 9872edeb15..e3b933b21a 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -129,6 +129,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emptyFlow @@ -272,23 +273,19 @@ class MainViewModel @Inject constructor( } } - val currentPermissionsLive = _currentMailboxObjectId.flatMapLatest { - it?.let(permissionsController::getPermissionsAsync) ?: emptyFlow() - }.asLiveData(ioCoroutineContext) - - val canSendEmailsFlow: SharedFlow = _currentMailboxObjectId.flatMapLatest { + private var _currentPermissionFlow = _currentMailboxObjectId.flatMapLatest { it?.let(permissionsController::getPermissionsAsync) ?: flowOf(null) - }.map { permissions -> + } + + val currentPermissionsLive = _currentPermissionFlow.filterNotNull().asLiveData(ioCoroutineContext) + + val canSendEmailsFlow: StateFlow = _currentPermissionFlow.map { permissions -> permissions?.canSendEmails ?: true - }.shareIn( + }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(), - replay = 1 + initialValue = true ) - - val canSendEmails: Boolean - get() = currentPermissionsLive.value?.canSendEmails ?: true - //endregion //region Current Folder 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 fffc43ca35..a642e3ac02 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 @@ -63,7 +63,7 @@ class DetailedContactBottomSheetDialog : ActionsBottomSheetDialog() { setupListeners() - if (!mainViewModel.canSendEmails) binding.writeMail.isEnabled = false + if (!mainViewModel.canSendEmailsFlow.value) binding.writeMail.isEnabled = false observeCanSendEmails() } 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 4363654c6d..ab2dd3d096 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 @@ -579,7 +579,7 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { init(if (shouldDisplayScheduledDraftActions) R.menu.scheduled_draft_menu else R.menu.message_menu) - if (!mainViewModel.canSendEmails) { + if (!mainViewModel.canSendEmailsFlow.value) { disableByMenuId(R.id.quickActionReply) disableByMenuId(R.id.quickActionForward) } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt index 26d460096e..aba20a186a 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt @@ -117,7 +117,7 @@ abstract class MailActionsBottomSheetDialog : ActionsBottomSheetDialog() { } } - updateEmailActionsState(mainViewModel.canSendEmails) + updateEmailActionsState(mainViewModel.canSendEmailsFlow.value) observeCanSendEmails() } From 12cb6c977aab4d3a66268b29b00bf7d9e300c372 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Fri, 22 May 2026 11:08:43 +0200 Subject: [PATCH 31/40] refactor: Change disableByMenuId and enableByMenuId to setEnableByMenuId --- .../mail/ui/main/thread/ThreadAdapter.kt | 1 + .../mail/ui/main/thread/ThreadFragment.kt | 19 ++++++------------- .../mail/views/BottomQuickActionBarView.kt | 13 ++----------- 3 files changed, 9 insertions(+), 24 deletions(-) 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 a701d8a703..b68ab90304 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 @@ -207,6 +207,7 @@ class ThreadAdapter( notifyItemRangeChanged(0, itemCount, NotifyType.UpdatePermissions) } } + private fun MessageViewHolder.handleToggleLightModePayload(messageUid: String) = with(threadAdapterState) { isThemeTheSameMap[messageUid] = !isThemeTheSameMap[messageUid]!! toggleContentAndQuoteTheme(messageUid) 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 ab2dd3d096..ce1dbf1a38 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 @@ -537,13 +537,8 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { } private fun updateQuickActionBarSendingState(canSend: Boolean) = with(binding.quickActionBar) { - if (canSend) { - enableByMenuId(R.id.quickActionReply) - enableByMenuId(R.id.quickActionForward) - } else { - disableByMenuId(R.id.quickActionReply) - disableByMenuId(R.id.quickActionForward) - } + setEnableByMenuId(R.id.quickActionReply, enabled = canSend) + setEnableByMenuId(R.id.quickActionForward, enabled = canSend) } private fun observeThreadLive() { @@ -560,28 +555,26 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { } private fun updateFavoriteIcon(isFavorite: Boolean) = with(binding.iconFavorite) { - val iconRes = if (isFavorite) R.drawable.ic_star_filled else R.drawable.ic_star + setIconResource(if (isFavorite) R.drawable.ic_star_filled else R.drawable.ic_star) val color = if (isFavorite) { context.getColor(R.color.favoriteYellow) } else { context.getAttributeColor(RAndroid.attr.colorPrimary) } - - setIconResource(iconRes) iconTint = ColorStateList.valueOf(color) } private fun setupQuickActionBar(thread: Thread) = with(binding.quickActionBar) { val shouldDisplayScheduledDraftActions = thread.containsOnlyScheduledDrafts( mainViewModel.featureFlagsLive.value, - localSettings + localSettings, ) init(if (shouldDisplayScheduledDraftActions) R.menu.scheduled_draft_menu else R.menu.message_menu) if (!mainViewModel.canSendEmailsFlow.value) { - disableByMenuId(R.id.quickActionReply) - disableByMenuId(R.id.quickActionForward) + setEnableByMenuId(R.id.quickActionReply, enabled = false) + setEnableByMenuId(R.id.quickActionForward, enabled = false) } } diff --git a/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt b/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt index e096181b84..6584575aeb 100644 --- a/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt +++ b/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt @@ -84,19 +84,10 @@ class BottomQuickActionBarView @JvmOverloads constructor( } } - fun disableByMenuId(@IdRes menuId: Int) { + fun setEnableByMenuId(@IdRes menuId: Int, enabled: Boolean) { for (index in 0 until menu.size) { if (menu[index].itemId == menuId) { - disable(index) - return - } - } - } - - fun enableByMenuId(@IdRes menuId: Int) { - for (index in 0 until menu.size) { - if (menu[index].itemId == menuId) { - enable(index) + if (enabled) enable(index) else disable(index) return } } From 5dcead55c15f2782602852b6182a1ac3ade9e518 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Fri, 22 May 2026 11:18:01 +0200 Subject: [PATCH 32/40] refactor: Clean code --- .../java/com/infomaniak/mail/ui/MainActivity.kt | 4 ---- .../java/com/infomaniak/mail/ui/MainViewModel.kt | 1 - .../mail/ui/main/thread/ThreadFragment.kt | 16 ++++++++-------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt index 44478ba65c..16628cd1aa 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt @@ -442,10 +442,6 @@ class MainActivity : BaseActivity() { override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) - handleNewIntent(intent) - } - - private fun handleNewIntent(intent: Intent) { handleAdminDisabledSendingSnackbarIfNeeded(intent) } diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt index e3b933b21a..0fd1b3b824 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -127,7 +127,6 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.catch 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 ce1dbf1a38..3f6be0976e 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 @@ -845,6 +845,14 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { quickActionBar.setOnItemClickListener { menuId -> when (menuId) { + R.id.quickActionReply -> { + trackThreadActionsEvent(MatomoName.Reply) + threadViewModel.clickOnQuickActionBar(menuId) + } + R.id.quickActionForward -> { + trackThreadActionsEvent(MatomoName.Forward) + threadViewModel.clickOnQuickActionBar(menuId) + } R.id.quickActionArchive -> { descriptionDialog.archiveWithConfirmationPopup(folderRole, count = 1) { trackThreadActionsEvent(MatomoName.Archive, isFromArchive) @@ -861,14 +869,6 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { trackThreadActionsEvent(MatomoName.OpenBottomSheet) threadViewModel.clickOnQuickActionBar(menuId) } - R.id.quickActionReply -> { - trackThreadActionsEvent(MatomoName.Reply) - threadViewModel.clickOnQuickActionBar(menuId) - } - R.id.quickActionForward -> { - trackThreadActionsEvent(MatomoName.Forward) - threadViewModel.clickOnQuickActionBar(menuId) - } } } } From 391cfdf200dd3aae6f7e788ef2ae214a059cfb8a Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Tue, 26 May 2026 10:30:18 +0200 Subject: [PATCH 33/40] refactor: Clean code --- .../data/models/mailbox/MailboxPermissions.kt | 1 - .../com/infomaniak/mail/ui/MainViewModel.kt | 2 +- .../DetailedContactBottomSheetDialog.kt | 1 - .../mail/ui/main/thread/MessageAlertView.kt | 8 ++--- .../mail/ui/main/thread/ThreadAdapter.kt | 4 +-- .../mail/ui/main/thread/ThreadFragment.kt | 9 +++--- .../actions/MailActionsBottomSheetDialog.kt | 1 - .../mail/utils/NotificationUtils.kt | 3 +- .../mail/utils/extensions/ViewExt.kt | 30 +++++-------------- .../mail/views/BottomQuickActionBarView.kt | 5 ++-- ...ound_disabled.xml => disabled_surface.xml} | 0 app/src/main/res/layout/view_main_actions.xml | 8 ++--- 12 files changed, 27 insertions(+), 45 deletions(-) rename app/src/main/res/color/{information_block_background_disabled.xml => disabled_surface.xml} (100%) diff --git a/app/src/main/java/com/infomaniak/mail/data/models/mailbox/MailboxPermissions.kt b/app/src/main/java/com/infomaniak/mail/data/models/mailbox/MailboxPermissions.kt index 831ec29d86..311142000b 100644 --- a/app/src/main/java/com/infomaniak/mail/data/models/mailbox/MailboxPermissions.kt +++ b/app/src/main/java/com/infomaniak/mail/data/models/mailbox/MailboxPermissions.kt @@ -37,6 +37,5 @@ class MailboxPermissions : EmbeddedRealmObject { @SerialName("can_send_emails") var canSendEmails: Boolean = true - companion object } diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt index 0fd1b3b824..1b898c70ce 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -272,7 +272,7 @@ class MainViewModel @Inject constructor( } } - private var _currentPermissionFlow = _currentMailboxObjectId.flatMapLatest { + private val _currentPermissionFlow = _currentMailboxObjectId.flatMapLatest { it?.let(permissionsController::getPermissionsAsync) ?: flowOf(null) } 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 a642e3ac02..a7d540b02d 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 @@ -63,7 +63,6 @@ class DetailedContactBottomSheetDialog : ActionsBottomSheetDialog() { setupListeners() - if (!mainViewModel.canSendEmailsFlow.value) binding.writeMail.isEnabled = false observeCanSendEmails() } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/MessageAlertView.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/MessageAlertView.kt index 6ae49e4ed8..552d34fae6 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/MessageAlertView.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/MessageAlertView.kt @@ -65,10 +65,10 @@ class MessageAlertView @JvmOverloads constructor( } } - override fun setEnabled(enabled: Boolean) = with(binding) { - super.setEnabled(enabled) - action1.isEnabled = enabled - action2.isEnabled = enabled + override fun setEnabled(isEnabled: Boolean) = with(binding) { + super.setEnabled(isEnabled) + action1.isEnabled = isEnabled + action2.isEnabled = isEnabled } fun setDescription(text: String) { 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 b68ab90304..ffb1eb8322 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 @@ -202,8 +202,8 @@ class ThreadAdapter( }.getOrDefault(Unit) fun updateEmailsPermission(canSend: Boolean) { - if (this.canSendEmails != canSend) { - this.canSendEmails = canSend + if (canSendEmails != canSend) { + canSendEmails = canSend notifyItemRangeChanged(0, itemCount, NotifyType.UpdatePermissions) } } 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 3f6be0976e..17e2c19622 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 @@ -555,12 +555,13 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { } private fun updateFavoriteIcon(isFavorite: Boolean) = with(binding.iconFavorite) { - setIconResource(if (isFavorite) R.drawable.ic_star_filled else R.drawable.ic_star) - val color = if (isFavorite) { - context.getColor(R.color.favoriteYellow) + val (iconRes, color) = if (isFavorite) { + R.drawable.ic_star_filled to context.getColor(R.color.favoriteYellow) } else { - context.getAttributeColor(RAndroid.attr.colorPrimary) + R.drawable.ic_star to context.getAttributeColor(RAndroid.attr.colorPrimary) } + + setIconResource(iconRes) iconTint = ColorStateList.valueOf(color) } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt index aba20a186a..0dd491d81a 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt @@ -117,7 +117,6 @@ abstract class MailActionsBottomSheetDialog : ActionsBottomSheetDialog() { } } - updateEmailActionsState(mainViewModel.canSendEmailsFlow.value) observeCanSendEmails() } diff --git a/app/src/main/java/com/infomaniak/mail/utils/NotificationUtils.kt b/app/src/main/java/com/infomaniak/mail/utils/NotificationUtils.kt index a2bb8919ae..96fdc77be4 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/NotificationUtils.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/NotificationUtils.kt @@ -343,8 +343,7 @@ class NotificationUtils @Inject constructor( addAction(archiveAction) addAction(deleteAction) - val canSendEmails = mailbox.permissions?.canSendEmails ?: true - if (canSendEmails) { + if (mailbox.permissions?.canSendEmails != false) { val replyAction = createActivityAction( title = R.string.actionReply, activity = LaunchActivity::class.java, diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt index dc4b73588b..841c35ee56 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/ViewExt.kt @@ -35,33 +35,18 @@ fun View.bindSendingClickListener( ) { canSendEmailsFlow.observe(lifecycleOwner) { canSendEmails -> val buttonState = if (canSendEmails) SendingButtonState.Send else SendingButtonState.SendingBlocked + applyColor(buttonState) - this.setSendingClickListener( - buttonState = buttonState, - onActionBlocked = onActionBlocked, - onActionExecute = onActionExecute - ) - } -} - -fun View.setSendingClickListener( - buttonState: SendingButtonState, - onActionBlocked: () -> Unit, - onActionExecute: () -> Unit -) { - - this.applyColor(buttonState) - - this.setOnClickListener { - when (buttonState) { - SendingButtonState.Send -> onActionExecute() - SendingButtonState.SendingBlocked -> onActionBlocked() + setOnClickListener { + when (buttonState) { + SendingButtonState.Send -> onActionExecute() + SendingButtonState.SendingBlocked -> onActionBlocked() + } } } } - -fun View.applyColor(buttonState: SendingButtonState) { +private fun View.applyColor(buttonState: SendingButtonState) { val color = if (buttonState == SendingButtonState.SendingBlocked) { ContextCompat.getColor(context, R.color.disabledIconColor) } else { @@ -71,7 +56,6 @@ fun View.applyColor(buttonState: SendingButtonState) { if (this is ExtendedFloatingActionButton) this.backgroundTintList = ColorStateList.valueOf(color) } - enum class SendingButtonState { Send, SendingBlocked diff --git a/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt b/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt index 6584575aeb..149d01d29c 100644 --- a/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt +++ b/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt @@ -31,6 +31,7 @@ import androidx.annotation.MenuRes import androidx.annotation.StringRes import androidx.core.content.res.getResourceIdOrThrow import androidx.core.view.WindowInsetsCompat +import androidx.core.view.forEachIndexed import androidx.core.view.get import androidx.core.view.isGone import androidx.core.view.size @@ -85,8 +86,8 @@ class BottomQuickActionBarView @JvmOverloads constructor( } fun setEnableByMenuId(@IdRes menuId: Int, enabled: Boolean) { - for (index in 0 until menu.size) { - if (menu[index].itemId == menuId) { + menu.forEachIndexed { index, menuItem -> + if (menuItem.itemId == menuId) { if (enabled) enable(index) else disable(index) return } diff --git a/app/src/main/res/color/information_block_background_disabled.xml b/app/src/main/res/color/disabled_surface.xml similarity index 100% rename from app/src/main/res/color/information_block_background_disabled.xml rename to app/src/main/res/color/disabled_surface.xml diff --git a/app/src/main/res/layout/view_main_actions.xml b/app/src/main/res/layout/view_main_actions.xml index f7d71387be..5531abec45 100644 --- a/app/src/main/res/layout/view_main_actions.xml +++ b/app/src/main/res/layout/view_main_actions.xml @@ -26,7 +26,7 @@ style="@style/IconButton" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:backgroundTint="@color/information_block_background_disabled" + android:backgroundTint="@color/disabled_surface" android:minWidth="0dp" android:minHeight="0dp" android:padding="20dp" @@ -58,7 +58,7 @@ style="@style/IconButton" android:layout_width="0dp" android:layout_height="0dp" - android:backgroundTint="@color/information_block_background_disabled" + android:backgroundTint="@color/disabled_surface" android:minWidth="0dp" android:minHeight="0dp" android:padding="20dp" @@ -91,7 +91,7 @@ style="@style/IconButton" android:layout_width="0dp" android:layout_height="0dp" - android:backgroundTint="@color/information_block_background_disabled" + android:backgroundTint="@color/disabled_surface" android:minWidth="0dp" android:minHeight="0dp" android:padding="20dp" @@ -124,7 +124,7 @@ style="@style/IconButton" android:layout_width="0dp" android:layout_height="0dp" - android:backgroundTint="@color/information_block_background_disabled" + android:backgroundTint="@color/disabled_surface" android:minWidth="0dp" android:minHeight="0dp" android:padding="20dp" From 51a7dfeed64f772a87bc9a9704fdd89f830fc3f4 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Tue, 26 May 2026 10:44:36 +0200 Subject: [PATCH 34/40] fix: Ensure correct Flow observation before launching activity --- .../java/com/infomaniak/mail/ui/MainActivity.kt | 15 +++++++++------ .../java/com/infomaniak/mail/ui/MainViewModel.kt | 2 +- .../mail/ui/main/thread/ThreadFragment.kt | 9 ++++++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt index 16628cd1aa..51ab0d136b 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt @@ -98,6 +98,7 @@ import com.infomaniak.mail.workers.DraftsActionsWorker import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine import java.text.SimpleDateFormat @@ -650,12 +651,14 @@ class MainActivity : BaseActivity() { } fun navigateToNewMessageActivity(args: Bundle? = null) { - if (!mainViewModel.canSendEmailsFlow.value) { - snackbarManager.setValue(getString(R.string.snackbarAdminDisabledMessageSending)) - } else { - val intent = Intent(this, NewMessageActivity::class.java) - args?.let(intent::putExtras) - newMessageActivityResultLauncher.launch(intent) + lifecycleScope.launch { + if (!mainViewModel.canSendEmailsFlow.first()) { + snackbarManager.setValue(getString(R.string.snackbarAdminDisabledMessageSending)) + } else { + val intent = Intent(this@MainActivity, NewMessageActivity::class.java) + args?.let(intent::putExtras) + newMessageActivityResultLauncher.launch(intent) + } } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt index 1b898c70ce..d1c78e4d33 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -282,7 +282,7 @@ class MainViewModel @Inject constructor( permissions?.canSendEmails ?: true }.stateIn( scope = viewModelScope, - started = SharingStarted.WhileSubscribed(), + started = SharingStarted.Eagerly, initialValue = true ) //endregion 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 17e2c19622..1d9486e607 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 @@ -135,6 +135,7 @@ import com.infomaniak.mail.workers.MailActionsManager import dagger.hilt.android.AndroidEntryPoint import io.sentry.Sentry import io.sentry.SentryLevel +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.serialization.json.Json import java.util.Date @@ -573,9 +574,11 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { init(if (shouldDisplayScheduledDraftActions) R.menu.scheduled_draft_menu else R.menu.message_menu) - if (!mainViewModel.canSendEmailsFlow.value) { - setEnableByMenuId(R.id.quickActionReply, enabled = false) - setEnableByMenuId(R.id.quickActionForward, enabled = false) + lifecycleScope.launch { + if (!mainViewModel.canSendEmailsFlow.first()) { + setEnableByMenuId(R.id.quickActionReply, enabled = false) + setEnableByMenuId(R.id.quickActionForward, enabled = false) + } } } From 64b508205ee129d599b8db0b5091dba854ae443a Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Tue, 26 May 2026 13:06:44 +0200 Subject: [PATCH 35/40] fix: Change setValue to postValue for coroutine compatibility --- app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt index 51ab0d136b..5d1ffc4d7b 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt @@ -653,7 +653,7 @@ class MainActivity : BaseActivity() { fun navigateToNewMessageActivity(args: Bundle? = null) { lifecycleScope.launch { if (!mainViewModel.canSendEmailsFlow.first()) { - snackbarManager.setValue(getString(R.string.snackbarAdminDisabledMessageSending)) + snackbarManager.postValue(getString(R.string.snackbarAdminDisabledMessageSending)) } else { val intent = Intent(this@MainActivity, NewMessageActivity::class.java) args?.let(intent::putExtras) From baf4d50131b21fb214b479022cc7a1875ff9a64a Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Tue, 26 May 2026 15:05:43 +0200 Subject: [PATCH 36/40] fix: Use .value instead of .first because we use an eagerly collected flow --- .../java/com/infomaniak/mail/ui/MainActivity.kt | 15 ++++++--------- .../mail/ui/main/thread/ThreadFragment.kt | 9 +++------ 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt index 5d1ffc4d7b..d18cb8831f 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt @@ -98,7 +98,6 @@ import com.infomaniak.mail.workers.DraftsActionsWorker import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine import java.text.SimpleDateFormat @@ -651,14 +650,12 @@ class MainActivity : BaseActivity() { } fun navigateToNewMessageActivity(args: Bundle? = null) { - lifecycleScope.launch { - if (!mainViewModel.canSendEmailsFlow.first()) { - snackbarManager.postValue(getString(R.string.snackbarAdminDisabledMessageSending)) - } else { - val intent = Intent(this@MainActivity, NewMessageActivity::class.java) - args?.let(intent::putExtras) - newMessageActivityResultLauncher.launch(intent) - } + if (!mainViewModel.canSendEmailsFlow.value) { + snackbarManager.postValue(getString(R.string.snackbarAdminDisabledMessageSending)) + } else { + val intent = Intent(this, NewMessageActivity::class.java) + args?.let(intent::putExtras) + newMessageActivityResultLauncher.launch(intent) } } 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 1d9486e607..17e2c19622 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 @@ -135,7 +135,6 @@ import com.infomaniak.mail.workers.MailActionsManager import dagger.hilt.android.AndroidEntryPoint import io.sentry.Sentry import io.sentry.SentryLevel -import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.serialization.json.Json import java.util.Date @@ -574,11 +573,9 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { init(if (shouldDisplayScheduledDraftActions) R.menu.scheduled_draft_menu else R.menu.message_menu) - lifecycleScope.launch { - if (!mainViewModel.canSendEmailsFlow.first()) { - setEnableByMenuId(R.id.quickActionReply, enabled = false) - setEnableByMenuId(R.id.quickActionForward, enabled = false) - } + if (!mainViewModel.canSendEmailsFlow.value) { + setEnableByMenuId(R.id.quickActionReply, enabled = false) + setEnableByMenuId(R.id.quickActionForward, enabled = false) } } From 539db399070be390c56ea62d7a1008751760fa32 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Tue, 26 May 2026 15:06:35 +0200 Subject: [PATCH 37/40] fix: Correct crash when opening an unread thread --- .../java/com/infomaniak/mail/ui/main/thread/ThreadFragment.kt | 3 ++- .../com/infomaniak/mail/views/BottomQuickActionBarView.kt | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) 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 17e2c19622..2a679258a1 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 @@ -221,7 +221,6 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { permissionUtils.registerDownloadManagerPermission(fragment = this) observeLightThemeToggle() - observeCanSendEmails() observeThreadLive() observeMessagesLive() observeMessagesAreCollapsibles() @@ -243,6 +242,8 @@ class ThreadFragment : Fragment(), PickerEmojiObserver { observeReportDisplayProblemResult() observeMessageOfUserToBlock() + + observeCanSendEmails() } private fun handleEdgeToEdge() = with(binding) { diff --git a/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt b/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt index 149d01d29c..cdf67bafb1 100644 --- a/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt +++ b/app/src/main/java/com/infomaniak/mail/views/BottomQuickActionBarView.kt @@ -62,12 +62,14 @@ class BottomQuickActionBarView @JvmOverloads constructor( val menuResId = menuRes ?: runCatching { getResourceIdOrThrow(R.styleable.BottomQuickActionBarView_menu) }.getOrNull() ?: return@getAttributes + menu.clear() MenuInflater(context).inflate(menuResId, menu) buttons.forEachIndexed { index, button -> if (index >= menu.size) { button.isGone = true } else { + button.isGone = false with(menu[index]) { button.icon = icon button.text = title @@ -103,10 +105,12 @@ class BottomQuickActionBarView @JvmOverloads constructor( } fun enable(index: Int) { + if (index !in buttons.indices) return buttons[index].isEnabled = true } fun disable(index: Int) { + if (index !in buttons.indices) return buttons[index].isEnabled = false } From 2bd5d3a7ceba8c69959c3bdf561ce51253cc0034 Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Wed, 27 May 2026 10:30:20 +0200 Subject: [PATCH 38/40] fix: Correct bug where second mailto link triggers no action --- app/src/main/AndroidManifest.xml | 1 + .../com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0d942337cb..03b6b74d7e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -161,6 +161,7 @@ android:name=".ui.newMessage.NewMessageActivity" android:configChanges="orientation|screenSize" android:exported="true" + android:launchMode="singleTask" android:windowSoftInputMode="adjustResize"> diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt index b69902d585..40a219c5b4 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt @@ -135,7 +135,7 @@ class NewMessageActivity : BaseActivity() { private fun handleDisabledSending() { Intent(this@NewMessageActivity, MainActivity::class.java).apply { putExtra(MainActivity.EXTRA_SHOW_ADMIN_DISABLED_SENDING_SNACKBAR, true) - addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK }.also(::startActivity) finish() } From 363d98608e0491bbbc8cba2f4db8fb0f2494830f Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Wed, 27 May 2026 12:31:14 +0200 Subject: [PATCH 39/40] fix: Disable onEmojiClick --- .../com/infomaniak/emojicomponents/views/EmojiReactionsView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EmojiComponents/src/main/java/com/infomaniak/emojicomponents/views/EmojiReactionsView.kt b/EmojiComponents/src/main/java/com/infomaniak/emojicomponents/views/EmojiReactionsView.kt index 108d0dcbda..fb5efbe4d9 100644 --- a/EmojiComponents/src/main/java/com/infomaniak/emojicomponents/views/EmojiReactionsView.kt +++ b/EmojiComponents/src/main/java/com/infomaniak/emojicomponents/views/EmojiReactionsView.kt @@ -113,7 +113,7 @@ class EmojiReactionsView @JvmOverloads constructor( EmojiReactions( reactions = { reactionsState }, - onEmojiClicked = { emoji -> onEmojiClickListener?.invoke(emoji) }, + onEmojiClicked = { emoji -> if (isAddReactionEnabled) onEmojiClickListener?.invoke(emoji) }, shape = chipCornerRadius?.let { RoundedCornerShape(it) } ?: InputChipDefaults.shape, addReactionIcon = addReactionIcon, isAddReactionEnabled = { isAddReactionEnabled }, From 80fc3bb9e8c011ab54d5ddba576868ed3d5f6a8e Mon Sep 17 00:00:00 2001 From: Elouan BOITEUX Date: Wed, 27 May 2026 15:22:22 +0200 Subject: [PATCH 40/40] fix: Remove singleTask --- app/src/main/AndroidManifest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 03b6b74d7e..0d942337cb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -161,7 +161,6 @@ android:name=".ui.newMessage.NewMessageActivity" android:configChanges="orientation|screenSize" android:exported="true" - android:launchMode="singleTask" android:windowSoftInputMode="adjustResize">