From 69691763e77ff1d925a68a24aefc5f6be2d9fce2 Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Fri, 14 Feb 2025 16:00:21 +0100 Subject: [PATCH 1/7] feat(MyKSuiteStorageBanner): Add the storage banner when mailbox is near full --- .../mail/ui/main/folder/ThreadListFragment.kt | 16 ++++ .../views/itemViews/MyKSuiteStorageBanner.kt | 93 +++++++++++++++++++ .../main/res/layout/fragment_thread_list.xml | 5 + .../layout/view_banner_my_ksuite_storage.xml | 83 +++++++++++++++++ app/src/main/res/values-de/strings.xml | 5 + app/src/main/res/values-es/strings.xml | 5 + app/src/main/res/values-fr/strings.xml | 5 + app/src/main/res/values-it/strings.xml | 5 + app/src/main/res/values-night/colors.xml | 8 ++ app/src/main/res/values/colors.xml | 12 +++ app/src/main/res/values/strings.xml | 5 + 11 files changed, 242 insertions(+) create mode 100644 app/src/main/java/com/infomaniak/mail/views/itemViews/MyKSuiteStorageBanner.kt create mode 100644 app/src/main/res/layout/view_banner_my_ksuite_storage.xml 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 681bdf9259..61505889f3 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 @@ -82,6 +82,7 @@ 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.* +import com.infomaniak.mail.views.itemViews.MyKSuiteStorageBanner.StorageLevel import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import java.util.Date @@ -146,6 +147,7 @@ class ThreadListFragment : TwoPaneFragment() { setupListeners() setupUserAvatar() setupUnreadCountChip() + setupMyKSuiteStorageBanner() threadListMultiSelection.initMultiSelection( mainViewModel = mainViewModel, @@ -527,6 +529,20 @@ class ThreadListFragment : TwoPaneFragment() { } } + private fun setupMyKSuiteStorageBanner() = with(binding.myKSuiteStorageBanner) { + mainViewModel.currentQuotasLive.observe(viewLifecycleOwner) { quotas -> + val mailboxFullness = quotas?.getProgress() ?: return@observe + + storageLevel = when { + mailboxFullness >= 100 -> StorageLevel.Full + mailboxFullness > 85 -> StorageLevel.Warning + else -> StorageLevel.Normal + } + + setupListener(onCloseButtonClicked = { isGone = true }) + } + } + private fun observeNetworkStatus() { viewLifecycleOwner.lifecycleScope.launch { mainViewModel.isNetworkAvailable.collect { isNetworkAvailable -> diff --git a/app/src/main/java/com/infomaniak/mail/views/itemViews/MyKSuiteStorageBanner.kt b/app/src/main/java/com/infomaniak/mail/views/itemViews/MyKSuiteStorageBanner.kt new file mode 100644 index 0000000000..a098c3c417 --- /dev/null +++ b/app/src/main/java/com/infomaniak/mail/views/itemViews/MyKSuiteStorageBanner.kt @@ -0,0 +1,93 @@ +/* + * Infomaniak Mail - Android + * Copyright (C) 2025 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.views.itemViews + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import androidx.annotation.ColorRes +import androidx.annotation.StringRes +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.res.ResourcesCompat +import androidx.core.view.isGone +import androidx.core.view.isVisible +import com.infomaniak.mail.R +import com.infomaniak.mail.databinding.ViewBannerMyKsuiteStorageBinding + +class MyKSuiteStorageBanner @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, +) : ConstraintLayout(context, attrs, defStyleAttr) { + + private val binding by lazy { ViewBannerMyKsuiteStorageBinding.inflate(LayoutInflater.from(context), this, true) } + + var storageLevel = StorageLevel.Normal + set(value) { + binding.root.isGone = value == StorageLevel.Normal + if (value != StorageLevel.Normal) setStorageLevelUi(value) + field = value + } + + + fun setupListener(onCloseButtonClicked: () -> Unit) { + binding.closeButton.setOnClickListener { onCloseButtonClicked() } + } + + private fun setStorageLevelUi(newStorageLevel: StorageLevel) = with(binding) { + if (newStorageLevel == storageLevel) return@with + + root.setBackgroundColor(context.getColor(newStorageLevel.backgroundColorRes)) + title.text = context.getText(newStorageLevel.titleRes) + title.setTextColor(context.getColor(newStorageLevel.titleColorRes)) + description.text = context.getText(newStorageLevel.descriptionRes) + alertIcon.setColorFilter(context.getColor(newStorageLevel.iconColorRes)) + + closeButton.isVisible = newStorageLevel == StorageLevel.Warning + } + + enum class StorageLevel( + @ColorRes val titleColorRes: Int, + @ColorRes val backgroundColorRes: Int, + @ColorRes val iconColorRes: Int, + @StringRes val titleRes: Int, + @StringRes val descriptionRes: Int, + ) { + Normal( + titleColorRes = ResourcesCompat.ID_NULL, + backgroundColorRes = ResourcesCompat.ID_NULL, + iconColorRes = ResourcesCompat.ID_NULL, + titleRes = ResourcesCompat.ID_NULL, + descriptionRes = ResourcesCompat.ID_NULL, + ), + Warning( + titleColorRes = R.color.warningBannerTitleColor, + backgroundColorRes = R.color.warningBannerBackgroundColor, + iconColorRes = R.color.warningBannerIconColor, + titleRes = R.string.myKSuiteQuotasAlertTitle, + descriptionRes = R.string.myKSuiteQuotasAlertDescription, + ), + Full( + titleColorRes = R.color.errorBannerTitleColor, + backgroundColorRes = R.color.errorBannerBackgroundColor, + iconColorRes = R.color.errorBannerIconColor, + titleRes = R.string.myKSuiteQuotasAlertFullTitle, + descriptionRes = R.string.myKSuiteQuotasAlertFullDescription, + ), + } +} diff --git a/app/src/main/res/layout/fragment_thread_list.xml b/app/src/main/res/layout/fragment_thread_list.xml index 694a1b38bd..52320933c0 100644 --- a/app/src/main/res/layout/fragment_thread_list.xml +++ b/app/src/main/res/layout/fragment_thread_list.xml @@ -207,6 +207,11 @@ android:layout_height="match_parent" android:orientation="vertical"> + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index eb7fb9489a..377d1badc1 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -139,6 +139,7 @@ Alle herunterladen Rückmeldung Ordner + Kostenlos ausprobieren Hilfe Einen Hyperlink hinzufügen E-Mails importieren @@ -358,6 +359,10 @@ %d ausgewählt %d ausgewählt + Wenn Sie dieses Limit erreicht haben, können Sie keine Nachrichten mehr senden oder empfangen. Geben Sie Speicherplatz frei oder wechseln Sie zu my kSuite+. + Geben Sie Speicherplatz frei oder wechseln Sie zu my kSuite+. + Empfang und Versand von Nachrichten blockiert + Speichergrenze fast erreicht Kommunizieren und speichern Sie Ihre Dokumente und Fotos in einem werbefreien Ökosystem, das Ihre Privatsphäre respektiert 15 GB kDrive-Speicher 20 GB Mail-Speicher diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 199bf034db..19e56fe386 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -139,6 +139,7 @@ Descargar todo Comentarios Carpetas + Pruébelo gratis Ayuda Añadir un hipervínculo Importar correos electrónicos @@ -358,6 +359,10 @@ %d seleccionado %d seleccionados + Una vez alcanzado este límite, ya no podrás enviar ni recibir mensajes. Libera espacio o actualiza a my kSuite+. + Libera espacio o actualízate a my kSuite+. + Recepción y envío de mensajes bloqueados + Límite de almacenamiento casi alcanzado Comunica y almacena tus documentos y fotos en un ecosistema sin publicidad que respeta tu privacidad 15 GB de almacenamiento kDrive 20 GB de almacenamiento de correo diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 935691ff4d..ebf8ee6a6c 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -141,6 +141,7 @@ Tout télécharger Feedback Dossiers + Essayer gratuitement Aide Ajouter un lien hypertexte Importer des e-mails @@ -366,6 +367,10 @@ %d sélectionnés %d de sélectionnés + Une fois atteinte, vous ne pourrez plus recevoir ou envoyer de messages. Libérez de l’espace ou faites évoluer votre offre vers my kSuite+. + Libérer de l’espace ou faites évoluer votre offre vers my kSuite+. + Réception et envoi de messages bloqués + Limite de stockage presque atteinte Communiquez et stockez vos documents et photos dans un écosystème sans publicité qui respecte votre vie privée 15 Go de stockage kDrive 20 Go de stockage Mail diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 3565c730a4..f94b48d8fc 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -139,6 +139,7 @@ Scarica tutti Feedback Cartelle + Prova gratis Aiuto Aggiungi un collegamento ipertestuale Importazione di e-mail @@ -358,6 +359,10 @@ %d selezionato %d selezionati + Una volta raggiunto questo limite, non sarà più possibile inviare o ricevere messaggi. Liberate spazio o passate a my kSuite+. + Liberate spazio o passate alla my kSuite+. + Blocco della ricezione e dell’invio di messaggi + Limite di memoria quasi raggiunto Comunicare e archiviare documenti e foto in un ecosistema privo di pubblicità che rispetta la vostra privacy 15 GB di memoria kDrive 20 GB di memoria per la posta diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 5ec10453cf..2b45e5f42e 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -161,6 +161,14 @@ #E4E4E4 #626262 + + @color/orange_ultra_light + @color/orange_ultra_dark + @color/orange_dark + @color/red_ultra_light + @color/red_ultra_dark + @color/red_dark + @color/shark @color/mouse diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index fe594c820d..0349f5beb5 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -59,12 +59,16 @@ #FFDF9E #FF9802 #FFC166 + #FFE7A5 #FF8500 #FFAA4C + #823F0C #FF540B #FD9459 + #FFCCC8 #F44336 #FC8878 + #82211A #3EBF4D #78D383 #009688 @@ -273,6 +277,14 @@ #F5F5F5 + + @color/orange_ultra_dark + @color/orange_ultra_light + #B25D00 + @color/red_ultra_dark + @color/red_ultra_light + #D71E04 + @color/mouse @color/elephant diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fd94b6f8c9..5af14b2186 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -145,6 +145,7 @@ Download all Feedback Folders + Try for free Help Add a hyperlink Import emails @@ -364,6 +365,10 @@ %d selected %d selected + Once you have reached this limit, you will no longer be able to send or receive messages. Free up space or upgrade to my kSuite+. + Free up space or upgrade to my kSuite+. + Receiving and sending messages blocked + Storage limit almost reached Communicate and store your documents and photos in an ad-free ecosystem that respects your privacy 15 GB of kDrive storage 20 GB of free mail storage From 68b4e14f2f790bfcefd8fb39f4f2661c90ff1b6e Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Fri, 14 Feb 2025 16:08:14 +0100 Subject: [PATCH 2/7] refactor(MyKSuiteStorageBanner): Remove the divider as it's not really useful --- .../res/layout/view_banner_my_ksuite_storage.xml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/app/src/main/res/layout/view_banner_my_ksuite_storage.xml b/app/src/main/res/layout/view_banner_my_ksuite_storage.xml index 2c86a09de3..8021861814 100644 --- a/app/src/main/res/layout/view_banner_my_ksuite_storage.xml +++ b/app/src/main/res/layout/view_banner_my_ksuite_storage.xml @@ -20,6 +20,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:paddingVertical="@dimen/marginStandardSmall" android:visibility="gone" tools:background="@color/warningBannerBackgroundColor" tools:visibility="visible"> @@ -42,7 +43,6 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="@dimen/alternativeMargin" - android:layout_marginTop="@dimen/marginStandardSmall" android:layout_marginEnd="@dimen/marginStandardVerySmall" app:layout_constraintEnd_toStartOf="@id/closeButton" app:layout_constraintStart_toEndOf="@id/alertIcon" @@ -66,18 +66,10 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="@dimen/marginStandardVerySmall" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@id/title" app:layout_constraintStart_toStartOf="@id/title" app:layout_constraintTop_toBottomOf="@id/title" tools:text="@string/myKSuiteQuotasAlertDescription" /> - - From 663373f290edaba3b1f70e06143f58dfb0df77b3 Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Fri, 14 Feb 2025 17:11:30 +0100 Subject: [PATCH 3/7] refactor(MyKSuiteStorageBanner): Only display the banner once every 10 launches if user closes it --- .../com/infomaniak/mail/MainApplication.kt | 2 ++ .../com/infomaniak/mail/data/LocalSettings.kt | 7 +++++++ .../mail/ui/main/folder/ThreadListFragment.kt | 20 +++++++++++++++---- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/MainApplication.kt b/app/src/main/java/com/infomaniak/mail/MainApplication.kt index 0132ccba5b..231bc3eaee 100644 --- a/app/src/main/java/com/infomaniak/mail/MainApplication.kt +++ b/app/src/main/java/com/infomaniak/mail/MainApplication.kt @@ -130,6 +130,8 @@ open class MainApplication : Application(), ImageLoaderFactory, DefaultLifecycle configureInfomaniakCore() notificationUtils.initNotificationChannel() configureHttpClient() + + localSettings.storageBannerDisplayAppLaunches++ } override fun onStart(owner: LifecycleOwner) { diff --git a/app/src/main/java/com/infomaniak/mail/data/LocalSettings.kt b/app/src/main/java/com/infomaniak/mail/data/LocalSettings.kt index c57d4f0816..71d680d655 100644 --- a/app/src/main/java/com/infomaniak/mail/data/LocalSettings.kt +++ b/app/src/main/java/com/infomaniak/mail/data/LocalSettings.kt @@ -70,6 +70,8 @@ class LocalSettings private constructor(context: Context) : SharedValues { var showWebViewOutdated by sharedValue("showWebViewOutdatedKey", true) var accessTokenApiCallRecord by sharedValue("accessTokenApiCallRecordKey", null) var lastSelectedScheduleEpoch by sharedValue("lastSelectedScheduleEpochKey", null) + var storageBannerDisplayAppLaunches by sharedValue("storageBannerDisplayAppLaunchesKey", 0) + var hasClosedStorageBanner by sharedValue("hasClosedStorageBannerKey", false) fun removeSettings() = sharedPreferences.transaction { clear() } @@ -93,6 +95,11 @@ class LocalSettings private constructor(context: Context) : SharedValues { firebaseRegisteredUsers = mutableSetOf() } + fun resetStorageBannerSettings() { + hasClosedStorageBanner = true + storageBannerDisplayAppLaunches = 0 + } + enum class EmailForwarding(@StringRes val localisedNameRes: Int) { IN_BODY(R.string.settingsTransferInBody), AS_ATTACHMENT(R.string.settingsTransferAsAttachment), 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 61505889f3..af5988be3a 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 @@ -529,17 +529,29 @@ class ThreadListFragment : TwoPaneFragment() { } } - private fun setupMyKSuiteStorageBanner() = with(binding.myKSuiteStorageBanner) { + private fun setupMyKSuiteStorageBanner() = with(localSettings) { mainViewModel.currentQuotasLive.observe(viewLifecycleOwner) { quotas -> val mailboxFullness = quotas?.getProgress() ?: return@observe - storageLevel = when { + binding.myKSuiteStorageBanner.storageLevel = when { mailboxFullness >= 100 -> StorageLevel.Full - mailboxFullness > 85 -> StorageLevel.Warning + mailboxFullness > 85 -> { + if (!hasClosedStorageBanner || storageBannerDisplayAppLaunches % 10 == 0) { + hasClosedStorageBanner = false + StorageLevel.Warning + } else { + StorageLevel.Normal + } + } else -> StorageLevel.Normal } - setupListener(onCloseButtonClicked = { isGone = true }) + binding.myKSuiteStorageBanner.setupListener( + onCloseButtonClicked = { + binding.myKSuiteStorageBanner.isGone = true + resetStorageBannerSettings() + } + ) } } From e2a081c5f884e6bc15d37e9985f45c3b0cec883f Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Fri, 14 Feb 2025 17:32:42 +0100 Subject: [PATCH 4/7] refactor(MyKSuiteStorageBanner): Use new design --- .../mail/views/itemViews/MyKSuiteStorageBanner.kt | 14 ++------------ .../res/layout/view_banner_my_ksuite_storage.xml | 7 +++---- app/src/main/res/values-night/colors.xml | 8 -------- app/src/main/res/values/colors.xml | 12 ------------ 4 files changed, 5 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/views/itemViews/MyKSuiteStorageBanner.kt b/app/src/main/java/com/infomaniak/mail/views/itemViews/MyKSuiteStorageBanner.kt index a098c3c417..52d7d6eb78 100644 --- a/app/src/main/java/com/infomaniak/mail/views/itemViews/MyKSuiteStorageBanner.kt +++ b/app/src/main/java/com/infomaniak/mail/views/itemViews/MyKSuiteStorageBanner.kt @@ -52,9 +52,7 @@ class MyKSuiteStorageBanner @JvmOverloads constructor( private fun setStorageLevelUi(newStorageLevel: StorageLevel) = with(binding) { if (newStorageLevel == storageLevel) return@with - root.setBackgroundColor(context.getColor(newStorageLevel.backgroundColorRes)) title.text = context.getText(newStorageLevel.titleRes) - title.setTextColor(context.getColor(newStorageLevel.titleColorRes)) description.text = context.getText(newStorageLevel.descriptionRes) alertIcon.setColorFilter(context.getColor(newStorageLevel.iconColorRes)) @@ -62,30 +60,22 @@ class MyKSuiteStorageBanner @JvmOverloads constructor( } enum class StorageLevel( - @ColorRes val titleColorRes: Int, - @ColorRes val backgroundColorRes: Int, @ColorRes val iconColorRes: Int, @StringRes val titleRes: Int, @StringRes val descriptionRes: Int, ) { Normal( - titleColorRes = ResourcesCompat.ID_NULL, - backgroundColorRes = ResourcesCompat.ID_NULL, iconColorRes = ResourcesCompat.ID_NULL, titleRes = ResourcesCompat.ID_NULL, descriptionRes = ResourcesCompat.ID_NULL, ), Warning( - titleColorRes = R.color.warningBannerTitleColor, - backgroundColorRes = R.color.warningBannerBackgroundColor, - iconColorRes = R.color.warningBannerIconColor, + iconColorRes = R.color.orangeWarning, titleRes = R.string.myKSuiteQuotasAlertTitle, descriptionRes = R.string.myKSuiteQuotasAlertDescription, ), Full( - titleColorRes = R.color.errorBannerTitleColor, - backgroundColorRes = R.color.errorBannerBackgroundColor, - iconColorRes = R.color.errorBannerIconColor, + iconColorRes = R.color.redDestructiveAction, titleRes = R.string.myKSuiteQuotasAlertFullTitle, descriptionRes = R.string.myKSuiteQuotasAlertFullDescription, ), diff --git a/app/src/main/res/layout/view_banner_my_ksuite_storage.xml b/app/src/main/res/layout/view_banner_my_ksuite_storage.xml index 8021861814..d52204d3f3 100644 --- a/app/src/main/res/layout/view_banner_my_ksuite_storage.xml +++ b/app/src/main/res/layout/view_banner_my_ksuite_storage.xml @@ -20,9 +20,9 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="@color/informationBlockBackground" android:paddingVertical="@dimen/marginStandardSmall" android:visibility="gone" - tools:background="@color/warningBannerBackgroundColor" tools:visibility="visible"> + tools:tint="@color/orange_light" /> + tools:text="@string/myKSuiteQuotasAlertTitle" /> #E4E4E4 #626262 - - @color/orange_ultra_light - @color/orange_ultra_dark - @color/orange_dark - @color/red_ultra_light - @color/red_ultra_dark - @color/red_dark - @color/shark @color/mouse diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 0349f5beb5..fe594c820d 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -59,16 +59,12 @@ #FFDF9E #FF9802 #FFC166 - #FFE7A5 #FF8500 #FFAA4C - #823F0C #FF540B #FD9459 - #FFCCC8 #F44336 #FC8878 - #82211A #3EBF4D #78D383 #009688 @@ -277,14 +273,6 @@ #F5F5F5 - - @color/orange_ultra_dark - @color/orange_ultra_light - #B25D00 - @color/red_ultra_dark - @color/red_ultra_light - #D71E04 - @color/mouse @color/elephant From 698ea686707661ff9d8110a0ede0e5109e38f714 Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Mon, 17 Feb 2025 08:58:55 +0100 Subject: [PATCH 5/7] feat(MyKSuiteStorageBanner): Block sending mail when the mailbox is full --- .../java/com/infomaniak/mail/data/models/Quotas.kt | 2 ++ .../mail/ui/main/folder/ThreadListFragment.kt | 6 +++--- .../mail/ui/newMessage/NewMessageFragment.kt | 14 ++++++++++++-- app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 1 + app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values-it/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 8 files changed, 22 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/data/models/Quotas.kt b/app/src/main/java/com/infomaniak/mail/data/models/Quotas.kt index 07a08ee67b..d3bc73cdc0 100644 --- a/app/src/main/java/com/infomaniak/mail/data/models/Quotas.kt +++ b/app/src/main/java/com/infomaniak/mail/data/models/Quotas.kt @@ -33,6 +33,8 @@ class Quotas : EmbeddedRealmObject { val size: Long get() = _size * 1_000L // Convert from KiloOctets to Octets + val isFull get() = getProgress() >= 100 + fun getText(context: Context): String { val usedSize = context.formatShortFileSize(size) 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 af5988be3a..7690ca5355 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 @@ -531,11 +531,11 @@ class ThreadListFragment : TwoPaneFragment() { private fun setupMyKSuiteStorageBanner() = with(localSettings) { mainViewModel.currentQuotasLive.observe(viewLifecycleOwner) { quotas -> - val mailboxFullness = quotas?.getProgress() ?: return@observe + if (quotas == null) return@observe binding.myKSuiteStorageBanner.storageLevel = when { - mailboxFullness >= 100 -> StorageLevel.Full - mailboxFullness > 85 -> { + quotas.isFull -> StorageLevel.Full + quotas.getProgress() > 85 -> { if (!hasClosedStorageBanner || storageBannerDisplayAppLaunches % 10 == 0) { hasClosedStorageBanner = false StorageLevel.Warning diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt index b5af49e41a..0d2663747e 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageFragment.kt @@ -718,9 +718,9 @@ class NewMessageFragment : Fragment() { sendButton.isEnabled = it } - scheduleButton.setOnClickListener { navigateToScheduleSendBottomSheet() } + scheduleButton.setOnClickListener { if (checkMailboxStorage()) navigateToScheduleSendBottomSheet() } - sendButton.setOnClickListener { tryToSendEmail() } + sendButton.setOnClickListener { if (checkMailboxStorage()) tryToSendEmail() } } private fun navigateToScheduleSendBottomSheet() { @@ -768,6 +768,16 @@ class NewMessageFragment : Fragment() { } } + private fun checkMailboxStorage(): Boolean { + val isMailboxFull = newMessageViewModel.currentMailbox.quotas?.isFull == true + if (isMailboxFull) { + trackNewMessageEvent("trySendingWithMailboxFull") + showSnackbar(R.string.myKSuiteSpaceFullAlert) + } + + return !isMailboxFull + } + private fun Activity.finishAppAndRemoveTaskIfNeeded() { if (isTaskRoot) finishAndRemoveTask() else finish() } diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 377d1badc1..c16b206a45 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -363,6 +363,7 @@ Geben Sie Speicherplatz frei oder wechseln Sie zu my kSuite+. Empfang und Versand von Nachrichten blockiert Speichergrenze fast erreicht + Es gibt nicht genügend Speicherplatz, um diese Aktion durchzuführen. Kommunizieren und speichern Sie Ihre Dokumente und Fotos in einem werbefreien Ökosystem, das Ihre Privatsphäre respektiert 15 GB kDrive-Speicher 20 GB Mail-Speicher diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 19e56fe386..d3fb7205ea 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -363,6 +363,7 @@ Libera espacio o actualízate a my kSuite+. Recepción y envío de mensajes bloqueados Límite de almacenamiento casi alcanzado + No hay espacio suficiente para llevar a cabo esta acción. Comunica y almacena tus documentos y fotos en un ecosistema sin publicidad que respeta tu privacidad 15 GB de almacenamiento kDrive 20 GB de almacenamiento de correo diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index ebf8ee6a6c..db509959b3 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -371,6 +371,7 @@ Libérer de l’espace ou faites évoluer votre offre vers my kSuite+. Réception et envoi de messages bloqués Limite de stockage presque atteinte + L’espace de stockage n’est pas suffisant pour effectuer cette action. Communiquez et stockez vos documents et photos dans un écosystème sans publicité qui respecte votre vie privée 15 Go de stockage kDrive 20 Go de stockage Mail diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index f94b48d8fc..2f7b1697bc 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -363,6 +363,7 @@ Liberate spazio o passate alla my kSuite+. Blocco della ricezione e dell’invio di messaggi Limite di memoria quasi raggiunto + Non c’è spazio sufficiente per eseguire questa azione. Comunicare e archiviare documenti e foto in un ecosistema privo di pubblicità che rispetta la vostra privacy 15 GB di memoria kDrive 20 GB di memoria per la posta diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5af14b2186..1f042ea388 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -369,6 +369,7 @@ Free up space or upgrade to my kSuite+. Receiving and sending messages blocked Storage limit almost reached + There is not enough storage space to carry out this action. Communicate and store your documents and photos in an ad-free ecosystem that respects your privacy 15 GB of kDrive storage 20 GB of free mail storage From 9e4d16013e07bc83919edc5941ac87f70c760e49 Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Thu, 20 Feb 2025 14:10:47 +0100 Subject: [PATCH 6/7] refactor(MyKSuiteStorageBanner): Create a storageBannerStatus liveData --- .../com/infomaniak/mail/ui/MainViewModel.kt | 21 ++++++++++++++ .../mail/ui/main/folder/ThreadListFragment.kt | 29 +++++-------------- .../views/itemViews/MyKSuiteStorageBanner.kt | 6 +++- 3 files changed, 34 insertions(+), 22 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 28209b3abb..a2371289ca 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -29,6 +29,7 @@ import com.infomaniak.lib.core.utils.SentryLog import com.infomaniak.lib.core.utils.SingleLiveEvent import com.infomaniak.mail.MatomoMail.trackMultiSelectionEvent import com.infomaniak.mail.R +import com.infomaniak.mail.data.LocalSettings import com.infomaniak.mail.data.api.ApiRepository import com.infomaniak.mail.data.api.ApiRoutes import com.infomaniak.mail.data.cache.RealmDatabase @@ -64,6 +65,7 @@ import com.infomaniak.mail.utils.Utils.isPermanentDeleteFolder import com.infomaniak.mail.utils.Utils.runCatchingRealm import com.infomaniak.mail.utils.extensions.* import com.infomaniak.mail.views.itemViews.AvatarMergedContactData +import com.infomaniak.mail.views.itemViews.MyKSuiteStorageBanner.StorageLevel import dagger.hilt.android.lifecycle.HiltViewModel import io.realm.kotlin.Realm import io.realm.kotlin.ext.toRealmList @@ -109,6 +111,9 @@ class MainViewModel @Inject constructor( private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher) private var refreshEverythingJob: Job? = null + @Inject + lateinit var localSettings: LocalSettings + val isDownloadingChanges: MutableLiveData = MutableLiveData(false) val isMovedToNewFolder = SingleLiveEvent() val toggleLightThemeForMessage = SingleLiveEvent() @@ -162,6 +167,22 @@ class MainViewModel @Inject constructor( it?.let(quotasController::getQuotasAsync) ?: emptyFlow() }.asLiveData(ioCoroutineContext) + val storageBannerStatus = currentQuotasLive.map { quotas -> + when { + quotas == null -> null + quotas.isFull -> StorageLevel.Full + quotas.getProgress() > StorageLevel.WARNING_THRESHOLD -> { + if (!localSettings.hasClosedStorageBanner || localSettings.storageBannerDisplayAppLaunches % 10 == 0) { + localSettings.hasClosedStorageBanner = false + StorageLevel.Warning + } else { + StorageLevel.Normal + } + } + else -> StorageLevel.Normal + } + } + val currentPermissionsLive = _currentMailboxObjectId.flatMapLatest { it?.let(permissionsController::getPermissionsAsync) ?: emptyFlow() }.asLiveData(ioCoroutineContext) 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 7690ca5355..809a03bcc2 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 @@ -82,7 +82,6 @@ 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.* -import com.infomaniak.mail.views.itemViews.MyKSuiteStorageBanner.StorageLevel import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import java.util.Date @@ -530,28 +529,16 @@ class ThreadListFragment : TwoPaneFragment() { } private fun setupMyKSuiteStorageBanner() = with(localSettings) { - mainViewModel.currentQuotasLive.observe(viewLifecycleOwner) { quotas -> - if (quotas == null) return@observe - - binding.myKSuiteStorageBanner.storageLevel = when { - quotas.isFull -> StorageLevel.Full - quotas.getProgress() > 85 -> { - if (!hasClosedStorageBanner || storageBannerDisplayAppLaunches % 10 == 0) { - hasClosedStorageBanner = false - StorageLevel.Warning - } else { - StorageLevel.Normal + mainViewModel.storageBannerStatus.observeNotNull(viewLifecycleOwner) { storageBannerStatus -> + binding.myKSuiteStorageBanner.apply { + storageLevel = storageBannerStatus + setupListener( + onCloseButtonClicked = { + binding.myKSuiteStorageBanner.isGone = true + resetStorageBannerSettings() } - } - else -> StorageLevel.Normal + ) } - - binding.myKSuiteStorageBanner.setupListener( - onCloseButtonClicked = { - binding.myKSuiteStorageBanner.isGone = true - resetStorageBannerSettings() - } - ) } } diff --git a/app/src/main/java/com/infomaniak/mail/views/itemViews/MyKSuiteStorageBanner.kt b/app/src/main/java/com/infomaniak/mail/views/itemViews/MyKSuiteStorageBanner.kt index 52d7d6eb78..85b2637dcf 100644 --- a/app/src/main/java/com/infomaniak/mail/views/itemViews/MyKSuiteStorageBanner.kt +++ b/app/src/main/java/com/infomaniak/mail/views/itemViews/MyKSuiteStorageBanner.kt @@ -78,6 +78,10 @@ class MyKSuiteStorageBanner @JvmOverloads constructor( iconColorRes = R.color.redDestructiveAction, titleRes = R.string.myKSuiteQuotasAlertFullTitle, descriptionRes = R.string.myKSuiteQuotasAlertFullDescription, - ), + ); + + companion object { + const val WARNING_THRESHOLD = 85 + } } } From f79256cde3f71b897785c1d5dc87c64ff4a37ba4 Mon Sep 17 00:00:00 2001 From: Fabian DEVEL Date: Thu, 20 Feb 2025 14:16:53 +0100 Subject: [PATCH 7/7] chore(MyKSuiteStorageBanner): Apply suggestions --- app/src/main/java/com/infomaniak/mail/data/LocalSettings.kt | 2 +- .../com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/data/LocalSettings.kt b/app/src/main/java/com/infomaniak/mail/data/LocalSettings.kt index 71d680d655..7d5288e7b4 100644 --- a/app/src/main/java/com/infomaniak/mail/data/LocalSettings.kt +++ b/app/src/main/java/com/infomaniak/mail/data/LocalSettings.kt @@ -95,7 +95,7 @@ class LocalSettings private constructor(context: Context) : SharedValues { firebaseRegisteredUsers = mutableSetOf() } - fun resetStorageBannerSettings() { + fun resetStorageBannerAppLaunches() { hasClosedStorageBanner = true storageBannerDisplayAppLaunches = 0 } 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 809a03bcc2..3276a92c94 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 @@ -535,7 +535,7 @@ class ThreadListFragment : TwoPaneFragment() { setupListener( onCloseButtonClicked = { binding.myKSuiteStorageBanner.isGone = true - resetStorageBannerSettings() + resetStorageBannerAppLaunches() } ) }