From 9ff2ee1901fa366aa3792383fe066f9a8339e510 Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Tue, 29 Oct 2024 13:54:08 +0100 Subject: [PATCH 01/29] refactor: Share code of 'reportDisplayProblem' --- .../com/infomaniak/mail/data/api/ApiRepository.kt | 9 +++++++++ .../java/com/infomaniak/mail/ui/MainViewModel.kt | 14 +++----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt b/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt index a43f35a862..e7ca860488 100644 --- a/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt +++ b/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt @@ -435,6 +435,15 @@ object ApiRepository : ApiRepositoryCore() { return callApi(url = ApiRoutes.shareLink(mailboxUuid, folderId, mailId), method = POST) } + fun getDownloadedAttachment(mailboxUuid: String, folderId: String, shortUid: Int): Response { + val request = Request.Builder().url(ApiRoutes.downloadMessage(mailboxUuid, folderId, shortUid)) + .headers(HttpUtils.getHeaders(null)) + .get() + .build() + + return HttpClient.okHttpClient.newCall(request).execute() + } + /** * Create batches of the given values to perform the given request * @param values Data to batch 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 eb084de327..6c1fa025f0 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -18,8 +18,10 @@ package com.infomaniak.mail.ui import android.app.Application +import android.util.Log import androidx.lifecycle.* import com.infomaniak.lib.core.models.ApiResponse +import com.infomaniak.lib.core.networking.HttpClient import com.infomaniak.lib.core.networking.HttpUtils import com.infomaniak.lib.core.networking.NetworkAvailability import com.infomaniak.lib.core.utils.ApiErrorCode.Companion.translateError @@ -924,19 +926,9 @@ class MainViewModel @Inject constructor( fun reportDisplayProblem(messageUid: String) = viewModelScope.launch(ioCoroutineContext) { val message = messageController.getMessage(messageUid) ?: return@launch - val mailbox = currentMailbox.value ?: return@launch - val userApiToken = AccountUtils.getUserById(mailbox.userId)?.apiToken?.accessToken ?: return@launch - val headers = HttpUtils.getHeaders(contentType = null).newBuilder() - .set("Authorization", "Bearer $userApiToken") - .build() - val request = Request.Builder().url(ApiRoutes.downloadMessage(mailbox.uuid, message.folderId, message.shortUid)) - .headers(headers) - .get() - .build() - - val response = AccountUtils.getHttpClient(mailbox.userId).newCall(request).execute() + val response = ApiRepository.getDownloadedAttachment(mailbox.uuid, message.folderId, message.shortUid) if (!response.isSuccessful || response.body == null) { reportDisplayProblemTrigger.postValue(Unit) From 790dc87a85972ef47d36f4acfa5055ff248591b1 Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Tue, 29 Oct 2024 14:42:32 +0100 Subject: [PATCH 02/29] feat: Add save to kDrive option --- app/src/main/java/com/infomaniak/mail/MatomoMail.kt | 1 + .../main/thread/actions/MailActionsBottomSheetDialog.kt | 3 +++ .../thread/actions/MessageActionsBottomSheetDialog.kt | 6 ++++++ .../thread/actions/ThreadActionsBottomSheetDialog.kt | 5 +++++ app/src/main/res/layout/bottom_sheet_actions_menu.xml | 9 +++++++++ 5 files changed, 24 insertions(+) diff --git a/app/src/main/java/com/infomaniak/mail/MatomoMail.kt b/app/src/main/java/com/infomaniak/mail/MatomoMail.kt index 54f5af12fc..e794108e24 100644 --- a/app/src/main/java/com/infomaniak/mail/MatomoMail.kt +++ b/app/src/main/java/com/infomaniak/mail/MatomoMail.kt @@ -56,6 +56,7 @@ object MatomoMail : MatomoCore { const val ACTION_SPAM_NAME = "spam" const val ACTION_PRINT_NAME = "print" const val ACTION_SHARE_LINK_NAME = "shareLink" + const val ACTION_SAVE_KDRIVE_NAME = "saveInkDrive" const val ACTION_POSTPONE_NAME = "postpone" const val ADD_MAILBOX_NAME = "addMailbox" const val DISCOVER_LATER = "discoverLater" 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 de26b453eb..141af2ad85 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 @@ -57,6 +57,7 @@ abstract class MailActionsBottomSheetDialog : ActionsBottomSheetDialog() { override fun onReportJunk() = Unit override fun onPrint() = Unit override fun onShare() = Unit + override fun onSaveKDrive() = Unit override fun onReportDisplayProblem() = Unit //endregion } @@ -78,6 +79,7 @@ abstract class MailActionsBottomSheetDialog : ActionsBottomSheetDialog() { reportJunk.setClosingOnClickListener(shouldCloseMultiSelection) { onClickListener.onReportJunk() } print.setClosingOnClickListener(shouldCloseMultiSelection) { onClickListener.onPrint() } share.setClosingOnClickListener(shouldCloseMultiSelection) { onClickListener.onShare() } + saveKDrive.setClosingOnClickListener(shouldCloseMultiSelection) { onClickListener.onSaveKDrive() } reportDisplayProblem.setClosingOnClickListener(shouldCloseMultiSelection) { onClickListener.onReportDisplayProblem() } mainActions.setClosingOnClickListener(shouldCloseMultiSelection) { id: Int -> @@ -142,6 +144,7 @@ abstract class MailActionsBottomSheetDialog : ActionsBottomSheetDialog() { fun onReportJunk() fun onPrint() fun onShare() + fun onSaveKDrive() fun onReportDisplayProblem() } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt index f48fffcc12..2cec14d683 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt @@ -33,6 +33,7 @@ import com.infomaniak.mail.MatomoMail.ACTION_POSTPONE_NAME import com.infomaniak.mail.MatomoMail.ACTION_PRINT_NAME import com.infomaniak.mail.MatomoMail.ACTION_REPLY_ALL_NAME import com.infomaniak.mail.MatomoMail.ACTION_REPLY_NAME +import com.infomaniak.mail.MatomoMail.ACTION_SAVE_KDRIVE_NAME import com.infomaniak.mail.MatomoMail.ACTION_SHARE_LINK_NAME import com.infomaniak.mail.MatomoMail.trackBottomSheetMessageActionsEvent import com.infomaniak.mail.MatomoMail.trackBottomSheetThreadActionsEvent @@ -168,6 +169,11 @@ class MessageActionsBottomSheetDialog : MailActionsBottomSheetDialog() { } } + override fun onSaveKDrive() { + trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) + + } + override fun onReportDisplayProblem() { descriptionDialog.show( title = getString(R.string.reportDisplayProblemTitle), diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt index d95f8bc6b8..ad56f52b81 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt @@ -35,6 +35,7 @@ import com.infomaniak.mail.MatomoMail.ACTION_POSTPONE_NAME import com.infomaniak.mail.MatomoMail.ACTION_PRINT_NAME import com.infomaniak.mail.MatomoMail.ACTION_REPLY_ALL_NAME import com.infomaniak.mail.MatomoMail.ACTION_REPLY_NAME +import com.infomaniak.mail.MatomoMail.ACTION_SAVE_KDRIVE_NAME import com.infomaniak.mail.MatomoMail.ACTION_SHARE_LINK_NAME import com.infomaniak.mail.MatomoMail.ACTION_SPAM_NAME import com.infomaniak.mail.MatomoMail.trackBottomSheetThreadActionsEvent @@ -195,6 +196,10 @@ class ThreadActionsBottomSheetDialog : MailActionsBottomSheetDialog() { } } + override fun onSaveKDrive() { + trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) + } + override fun onReportDisplayProblem() { descriptionDialog.show( title = getString(R.string.reportDisplayProblemTitle), diff --git a/app/src/main/res/layout/bottom_sheet_actions_menu.xml b/app/src/main/res/layout/bottom_sheet_actions_menu.xml index 305cab8a35..b6d885a46e 100644 --- a/app/src/main/res/layout/bottom_sheet_actions_menu.xml +++ b/app/src/main/res/layout/bottom_sheet_actions_menu.xml @@ -118,6 +118,15 @@ app:text="@string/shareEmail" app:visibleDivider="false" /> + + Date: Tue, 29 Oct 2024 17:26:54 +0100 Subject: [PATCH 03/29] feat: Save eml to kDrive --- app/build.gradle | 2 + app/src/main/AndroidManifest.xml | 2 +- .../com/infomaniak/mail/ui/MainViewModel.kt | 55 +++++++++++++++++-- .../MessageActionsBottomSheetDialog.kt | 2 +- .../actions/ThreadActionsBottomSheetDialog.kt | 1 + .../utils/extensions/AttachmentExtensions.kt | 19 +++++++ app/src/main/res/xml/exposed_files_path.xml | 9 +++ 7 files changed, 83 insertions(+), 7 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 646bfb1efb..baa21534a7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -47,6 +47,8 @@ android { buildConfigField 'String', 'GITHUB_REPO_URL', '"https://github.com/Infomaniak/android-kMail"' resValue 'string', 'ATTACHMENTS_AUTHORITY', 'com.infomaniak.mail.attachments' + resValue 'string', 'EML_AUTHORITY', 'com.infomaniak.mail.eml' + resValue 'string', 'ALL_AUTHORITY', 'com.infomaniak.mail.attachments;com.infomaniak.mail.eml' resourceConfigurations += ["en", "de", "es", "fr", "it"] } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 33c2836571..c88522725f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -208,7 +208,7 @@ + context?.let { + val uri = saveEmlToFile(context, byteArray, message.subject ?: "pas de nom test") + uri?.getIntentOrGoToPlayStore(it) + } + } + + } + + fun saveEmlToFile(context: Context, byteArray: ByteArray, fileName: String): Uri? { + val fileNameWithExtension = "$fileName.eml" + val fileDir = File(context.filesDir, "eml_export") + + if (!fileDir.exists()) { + fileDir.mkdirs() + } + + val file = File(fileDir, fileNameWithExtension) + + return try { + file.outputStream().use { it.write(byteArray) } + FileProvider.getUriForFile(context, context.getString(R.string.EML_AUTHORITY), file) + } catch (e: IOException) { + e.printStackTrace() + null + } + } + + companion object { private val TAG: String = MainViewModel::class.java.simpleName private val DEFAULT_SELECTED_FOLDER = FolderRole.INBOX private const val REFRESH_DELAY = 2_000L // We add this delay because `etop` isn't always big enough. private const val MAX_REFRESH_DELAY = 6_000L private const val EML_CONTENT_TYPE = "message/rfc822" + + private const val EML_EXPORT_DIR = "eml_export" } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt index 2cec14d683..8d731b2dd3 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt @@ -171,7 +171,7 @@ class MessageActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override fun onSaveKDrive() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) - + mainViewModel.saveOnKDrive(message.uid, context) } override fun onReportDisplayProblem() { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt index ad56f52b81..7279b904a5 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt @@ -197,6 +197,7 @@ class ThreadActionsBottomSheetDialog : MailActionsBottomSheetDialog() { } override fun onSaveKDrive() { + mainViewModel.saveOnKDrive(messageUidToReply, context) trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) } diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt index fe79b2227e..793b103f64 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt @@ -21,6 +21,7 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager +import android.net.Uri import android.os.Bundle import android.provider.MediaStore.Files.FileColumns import androidx.core.content.FileProvider @@ -81,6 +82,14 @@ object AttachmentExtensions { } } + private fun Uri.saveToDriveIntent(): Intent { + return Intent().apply { + component = ComponentName(DRIVE_PACKAGE, SAVE_EXTERNAL_ACTIVITY_CLASS) + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_STREAM, this@saveToDriveIntent) + } + } + private fun Attachment.openWithIntent(context: Context): Intent { val file = getUploadLocalFile() ?: getCacheFile(context) val uri = FileProvider.getUriForFile(context, context.getString(R.string.ATTACHMENTS_AUTHORITY), file) @@ -102,6 +111,15 @@ object AttachmentExtensions { } } + // TODO Keep same logic + fun Uri.getIntentOrGoToPlayStore(context: Context) { + if (canSaveOnKDrive(context)) { + saveToDriveIntent().let(context::startActivity) + } else { + context.goToPlayStore(DRIVE_PACKAGE) + } + } + fun Attachment.executeIntent( context: Context, intentType: AttachmentIntentType, @@ -114,6 +132,7 @@ object AttachmentExtensions { } } + fun Attachment.openAttachment( context: Context, navigateToDownloadProgressDialog: (Attachment, AttachmentIntentType) -> Unit, diff --git a/app/src/main/res/xml/exposed_files_path.xml b/app/src/main/res/xml/exposed_files_path.xml index 628e54431c..cedc5e5c45 100644 --- a/app/src/main/res/xml/exposed_files_path.xml +++ b/app/src/main/res/xml/exposed_files_path.xml @@ -22,4 +22,13 @@ + + + + + + + From febcefb23e824b032253ea412aa837dcf94ac382 Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Wed, 30 Oct 2024 09:20:28 +0100 Subject: [PATCH 04/29] refactor: Rename & Clean code --- app/build.gradle | 2 +- app/src/main/AndroidManifest.xml | 2 +- .../com/infomaniak/mail/ui/MainViewModel.kt | 31 +++++++++---------- .../actions/ThreadActionsBottomSheetDialog.kt | 2 +- .../utils/extensions/AttachmentExtensions.kt | 4 +-- app/src/main/res/xml/exposed_files_path.xml | 6 ---- 6 files changed, 18 insertions(+), 29 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index baa21534a7..3b86566684 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,7 +48,7 @@ android { resValue 'string', 'ATTACHMENTS_AUTHORITY', 'com.infomaniak.mail.attachments' resValue 'string', 'EML_AUTHORITY', 'com.infomaniak.mail.eml' - resValue 'string', 'ALL_AUTHORITY', 'com.infomaniak.mail.attachments;com.infomaniak.mail.eml' + resValue 'string', 'FILES_AUTHORITY', 'com.infomaniak.mail.attachments;com.infomaniak.mail.eml' resourceConfigurations += ["en", "de", "es", "fr", "it"] } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c88522725f..eaf4b09411 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -208,7 +208,7 @@ context?.let { - val uri = saveEmlToFile(context, byteArray, message.subject ?: "pas de nom test") - uri?.getIntentOrGoToPlayStore(it) + val emlFileName: String = message.subject ?: NO_SUBJECT_FILE + val uri = saveEmlToFile(context, byteArray, emlFileName) + uri?.openKDriveOrPlayStore(it) } } - } - fun saveEmlToFile(context: Context, byteArray: ByteArray, fileName: String): Uri? { + fun saveEmlToFile(context: Context, emlByteArray: ByteArray, fileName: String): Uri? { val fileNameWithExtension = "$fileName.eml" - val fileDir = File(context.filesDir, "eml_export") - - if (!fileDir.exists()) { - fileDir.mkdirs() - } + val fileDir = File(context.filesDir, EML_EXPORT_DIR) - val file = File(fileDir, fileNameWithExtension) + if (!fileDir.exists()) fileDir.mkdirs() return try { - file.outputStream().use { it.write(byteArray) } + val file = File(fileDir, fileNameWithExtension) + file.outputStream().use { it.write(emlByteArray) } FileProvider.getUriForFile(context, context.getString(R.string.EML_AUTHORITY), file) - } catch (e: IOException) { - e.printStackTrace() + } catch (_: IOException) { + snackbarManager.postValue(appContext.getString(RCore.string.anErrorHasOccurred)) null } } @@ -1248,8 +1244,9 @@ class MainViewModel @Inject constructor( private val DEFAULT_SELECTED_FOLDER = FolderRole.INBOX private const val REFRESH_DELAY = 2_000L // We add this delay because `etop` isn't always big enough. private const val MAX_REFRESH_DELAY = 6_000L - private const val EML_CONTENT_TYPE = "message/rfc822" + private const val EML_CONTENT_TYPE = "message/rfc822" private const val EML_EXPORT_DIR = "eml_export" + private const val NO_SUBJECT_FILE = "message" } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt index 7279b904a5..f33a00fcc9 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt @@ -197,8 +197,8 @@ class ThreadActionsBottomSheetDialog : MailActionsBottomSheetDialog() { } override fun onSaveKDrive() { - mainViewModel.saveOnKDrive(messageUidToReply, context) trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) + mainViewModel.saveOnKDrive(messageUidToReply, context) } override fun onReportDisplayProblem() { diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt index 793b103f64..ccb2198d7b 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt @@ -111,8 +111,7 @@ object AttachmentExtensions { } } - // TODO Keep same logic - fun Uri.getIntentOrGoToPlayStore(context: Context) { + fun Uri.openKDriveOrPlayStore(context: Context) { if (canSaveOnKDrive(context)) { saveToDriveIntent().let(context::startActivity) } else { @@ -132,7 +131,6 @@ object AttachmentExtensions { } } - fun Attachment.openAttachment( context: Context, navigateToDownloadProgressDialog: (Attachment, AttachmentIntentType) -> Unit, diff --git a/app/src/main/res/xml/exposed_files_path.xml b/app/src/main/res/xml/exposed_files_path.xml index cedc5e5c45..e771e44a14 100644 --- a/app/src/main/res/xml/exposed_files_path.xml +++ b/app/src/main/res/xml/exposed_files_path.xml @@ -25,10 +25,4 @@ - - - - - - From 463aea7036645d1e906791f27eb3997308893404 Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Thu, 31 Oct 2024 12:56:31 +0100 Subject: [PATCH 05/29] feat: Replace illegal file characters with a blank space --- app/build.gradle | 3 +++ .../java/com/infomaniak/mail/ui/MainViewModel.kt | 16 ++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 3b86566684..369bc446f0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -50,6 +50,9 @@ android { resValue 'string', 'EML_AUTHORITY', 'com.infomaniak.mail.eml' resValue 'string', 'FILES_AUTHORITY', 'com.infomaniak.mail.attachments;com.infomaniak.mail.eml' + resValue 'string', 'EXPOSED_EML_DIR', 'eml' + resValue 'string', 'EXPOSED_EML_PATH', 'eml_export' + resourceConfigurations += ["en", "de", "es", "fr", "it"] } 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 4f0669c952..48ec8c1fdf 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -78,7 +78,6 @@ import io.sentry.SentryLevel import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import java.io.File -import java.io.IOException import java.util.Date import java.util.UUID import javax.inject.Inject @@ -1223,21 +1222,23 @@ class MainViewModel @Inject constructor( } fun saveEmlToFile(context: Context, emlByteArray: ByteArray, fileName: String): Uri? { - val fileNameWithExtension = "$fileName.eml" - val fileDir = File(context.filesDir, EML_EXPORT_DIR) + val fileNameWithExtension = "${fileName.removeIllegalFileNameCharacter()}.eml" + val fileDir = File(context.filesDir, context.getString(R.string.EXPOSED_EML_PATH)) if (!fileDir.exists()) fileDir.mkdirs() - return try { + runCatching { val file = File(fileDir, fileNameWithExtension) file.outputStream().use { it.write(emlByteArray) } - FileProvider.getUriForFile(context, context.getString(R.string.EML_AUTHORITY), file) - } catch (_: IOException) { + return FileProvider.getUriForFile(context, context.getString(R.string.EML_AUTHORITY), file) + }.onFailure { exception -> + exception.printStackTrace() snackbarManager.postValue(appContext.getString(RCore.string.anErrorHasOccurred)) - null } + return null } + fun String.removeIllegalFileNameCharacter(): String = this.replace(DownloadManagerUtils.regexInvalidSystemChar, "") companion object { private val TAG: String = MainViewModel::class.java.simpleName @@ -1246,7 +1247,6 @@ class MainViewModel @Inject constructor( private const val MAX_REFRESH_DELAY = 6_000L private const val EML_CONTENT_TYPE = "message/rfc822" - private const val EML_EXPORT_DIR = "eml_export" private const val NO_SUBJECT_FILE = "message" } } From 1726c0ec39a34f433a93161d10edcf5d408416a2 Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Thu, 31 Oct 2024 13:53:48 +0100 Subject: [PATCH 06/29] feat: Allow to download a thread instead of only one message --- app/build.gradle | 1 - .../com/infomaniak/mail/ui/MainViewModel.kt | 27 ++++++++++--------- .../MessageActionsBottomSheetDialog.kt | 2 +- .../actions/ThreadActionsBottomSheetDialog.kt | 2 +- .../utils/extensions/AttachmentExtensions.kt | 8 +++--- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 369bc446f0..89f6305195 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -50,7 +50,6 @@ android { resValue 'string', 'EML_AUTHORITY', 'com.infomaniak.mail.eml' resValue 'string', 'FILES_AUTHORITY', 'com.infomaniak.mail.attachments;com.infomaniak.mail.eml' - resValue 'string', 'EXPOSED_EML_DIR', 'eml' resValue 'string', 'EXPOSED_EML_PATH', 'eml_export' resourceConfigurations += ["en", "de", "es", "fr", "it"] 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 48ec8c1fdf..5afc34e862 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -1200,25 +1200,28 @@ class MainViewModel @Inject constructor( } } - fun saveOnKDrive(messageUid: String, context: Context?) = viewModelScope.launch(ioCoroutineContext) { - val message = messageController.getMessage(messageUid) ?: return@launch + fun saveOnKDrive(threadUid: String, context: Context?) = viewModelScope.launch(ioCoroutineContext) { + val thread = threadController.getThread(threadUid) ?: return@launch val mailbox = currentMailbox.value ?: return@launch - val response = ApiRepository.getDownloadedAttachment(mailbox.uuid, message.folderId, message.shortUid) + val listUri: MutableList = mutableListOf() + thread.messages.forEach { message -> + val response = ApiRepository.getDownloadedAttachment(mailbox.uuid, message.folderId, message.shortUid) - if (!response.isSuccessful || response.body == null) { - snackbarManager.postValue(appContext.getString(RCore.string.anErrorHasOccurred)) + if (!response.isSuccessful || response.body == null) { + snackbarManager.postValue(appContext.getString(RCore.string.anErrorHasOccurred)) - return@launch - } + return@launch + } - response.body?.bytes()?.let { byteArray -> - context?.let { - val emlFileName: String = message.subject ?: NO_SUBJECT_FILE - val uri = saveEmlToFile(context, byteArray, emlFileName) - uri?.openKDriveOrPlayStore(it) + response.body?.bytes()?.let { byteArray -> + context?.let { + val emlFileName: String = message.subject ?: NO_SUBJECT_FILE + saveEmlToFile(context, byteArray, emlFileName)?.let { listUri.add(it) } + } } } + context?.let { ArrayList(listUri).openKDriveOrPlayStore(it) } } fun saveEmlToFile(context: Context, emlByteArray: ByteArray, fileName: String): Uri? { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt index 8d731b2dd3..a369696ff5 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt @@ -171,7 +171,7 @@ class MessageActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override fun onSaveKDrive() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) - mainViewModel.saveOnKDrive(message.uid, context) + mainViewModel.saveOnKDrive(threadUid, context) } override fun onReportDisplayProblem() { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt index f33a00fcc9..dc3a129d30 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt @@ -198,7 +198,7 @@ class ThreadActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override fun onSaveKDrive() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) - mainViewModel.saveOnKDrive(messageUidToReply, context) + mainViewModel.saveOnKDrive(threadUid, context) } override fun onReportDisplayProblem() { diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt index ccb2198d7b..d3a69e3193 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt @@ -82,11 +82,11 @@ object AttachmentExtensions { } } - private fun Uri.saveToDriveIntent(): Intent { + private fun ArrayList.saveToDriveIntent(): Intent { return Intent().apply { component = ComponentName(DRIVE_PACKAGE, SAVE_EXTERNAL_ACTIVITY_CLASS) - action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_STREAM, this@saveToDriveIntent) + action = Intent.ACTION_SEND_MULTIPLE + putParcelableArrayListExtra(Intent.EXTRA_STREAM, this@saveToDriveIntent) } } @@ -111,7 +111,7 @@ object AttachmentExtensions { } } - fun Uri.openKDriveOrPlayStore(context: Context) { + fun ArrayList.openKDriveOrPlayStore(context: Context) { if (canSaveOnKDrive(context)) { saveToDriveIntent().let(context::startActivity) } else { From 78a7d52e7df14edcc60071d393f67b8d5cac077c Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Thu, 31 Oct 2024 15:38:33 +0100 Subject: [PATCH 07/29] feat: Add option in 'MultiSelectBottomSheet' --- .../com/infomaniak/mail/ui/MainViewModel.kt | 35 +++++++++++-------- .../MessageActionsBottomSheetDialog.kt | 2 +- .../actions/MultiSelectBottomSheetDialog.kt | 6 ++++ .../actions/ThreadActionsBottomSheetDialog.kt | 2 +- .../res/layout/bottom_sheet_multi_select.xml | 8 +++++ 5 files changed, 36 insertions(+), 17 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 5afc34e862..11bb1267e8 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -1200,28 +1200,33 @@ class MainViewModel @Inject constructor( } } - fun saveOnKDrive(threadUid: String, context: Context?) = viewModelScope.launch(ioCoroutineContext) { - val thread = threadController.getThread(threadUid) ?: return@launch - val mailbox = currentMailbox.value ?: return@launch + fun saveOnKDrive(threadUid: List, context: Context?) { + viewModelScope.launch(ioCoroutineContext) { + val mailbox = currentMailbox.value ?: return@launch + val listUri: MutableList = mutableListOf() - val listUri: MutableList = mutableListOf() - thread.messages.forEach { message -> - val response = ApiRepository.getDownloadedAttachment(mailbox.uuid, message.folderId, message.shortUid) + threadUid.forEach { + val thread = threadController.getThread(it) ?: return@launch - if (!response.isSuccessful || response.body == null) { - snackbarManager.postValue(appContext.getString(RCore.string.anErrorHasOccurred)) + thread.messages.forEach { message -> + val response = ApiRepository.getDownloadedAttachment(mailbox.uuid, message.folderId, message.shortUid) - return@launch - } + if (!response.isSuccessful || response.body == null) { + snackbarManager.postValue(appContext.getString(RCore.string.anErrorHasOccurred)) - response.body?.bytes()?.let { byteArray -> - context?.let { - val emlFileName: String = message.subject ?: NO_SUBJECT_FILE - saveEmlToFile(context, byteArray, emlFileName)?.let { listUri.add(it) } + return@launch + } + + response.body?.bytes()?.let { byteArray -> + context?.let { + val emlFileName: String = message.subject ?: NO_SUBJECT_FILE + saveEmlToFile(context, byteArray, emlFileName)?.let { listUri.add(it) } + } + } } } + context?.let { ArrayList(listUri).openKDriveOrPlayStore(it) } } - context?.let { ArrayList(listUri).openKDriveOrPlayStore(it) } } fun saveEmlToFile(context: Context, emlByteArray: ByteArray, fileName: String): Uri? { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt index a369696ff5..8133a40171 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt @@ -171,7 +171,7 @@ class MessageActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override fun onSaveKDrive() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) - mainViewModel.saveOnKDrive(threadUid, context) + mainViewModel.saveOnKDrive(listOf(threadUid), context) } override fun onReportDisplayProblem() { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt index 27d3f265c7..33748d4c00 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt @@ -28,6 +28,7 @@ import com.infomaniak.mail.MatomoMail.ACTION_DELETE_NAME import com.infomaniak.mail.MatomoMail.ACTION_FAVORITE_NAME import com.infomaniak.mail.MatomoMail.ACTION_MARK_AS_SEEN_NAME import com.infomaniak.mail.MatomoMail.ACTION_MOVE_NAME +import com.infomaniak.mail.MatomoMail.ACTION_SAVE_KDRIVE_NAME import com.infomaniak.mail.MatomoMail.ACTION_SPAM_NAME import com.infomaniak.mail.MatomoMail.trackMultiSelectActionEvent import com.infomaniak.mail.R @@ -116,6 +117,11 @@ class MultiSelectBottomSheetDialog : ActionsBottomSheetDialog() { toggleThreadsFavoriteStatus(selectedThreadsUids, shouldFavorite) isMultiSelectOn = false } + binding.saveKDrive.setClosingOnClickListener(shouldCloseMultiSelection = true) { + trackMultiSelectActionEvent(ACTION_SAVE_KDRIVE_NAME, selectedThreadsCount, isFromBottomSheet = true) + saveOnKDrive(selectedThreadsUids, context) + isMultiSelectOn = false + } } private fun setStateDependentUi(shouldRead: Boolean, shouldFavorite: Boolean) { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt index dc3a129d30..f29fafb503 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt @@ -198,7 +198,7 @@ class ThreadActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override fun onSaveKDrive() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) - mainViewModel.saveOnKDrive(threadUid, context) + mainViewModel.saveOnKDrive(listOf(threadUid), context) } override fun onReportDisplayProblem() { diff --git a/app/src/main/res/layout/bottom_sheet_multi_select.xml b/app/src/main/res/layout/bottom_sheet_multi_select.xml index 2f5cca1586..30d5cdcd46 100644 --- a/app/src/main/res/layout/bottom_sheet_multi_select.xml +++ b/app/src/main/res/layout/bottom_sheet_multi_select.xml @@ -52,4 +52,12 @@ app:icon="@drawable/ic_star" app:text="@string/actionStar" /> + + From aae37ebb0db324b8980c21f2c69a0078912c0529 Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Fri, 1 Nov 2024 10:18:29 +0100 Subject: [PATCH 08/29] feat: Allow to add different threads or messages with same name --- .../com/infomaniak/mail/ui/MainViewModel.kt | 45 ++++++++++++++----- .../MessageActionsBottomSheetDialog.kt | 2 +- .../actions/MultiSelectBottomSheetDialog.kt | 2 +- .../actions/ThreadActionsBottomSheetDialog.kt | 2 +- 4 files changed, 37 insertions(+), 14 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 11bb1267e8..0395230a46 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -20,6 +20,7 @@ package com.infomaniak.mail.ui import android.app.Application import android.content.Context import android.net.Uri +import android.util.Log import androidx.core.content.FileProvider import androidx.lifecycle.* import com.infomaniak.lib.core.models.ApiResponse @@ -1200,13 +1201,16 @@ class MainViewModel @Inject constructor( } } - fun saveOnKDrive(threadUid: List, context: Context?) { + fun saveOnKDrive(threadUids: List, context: Context) { viewModelScope.launch(ioCoroutineContext) { val mailbox = currentMailbox.value ?: return@launch - val listUri: MutableList = mutableListOf() + val listUri = mutableListOf() + val listFileName = mutableSetOf().also { + it.addAll(getAllFileNameInExportEmlDir(context)) + } - threadUid.forEach { - val thread = threadController.getThread(it) ?: return@launch + threadUids.forEach { threadUid -> + val thread = threadController.getThread(threadUid) ?: return@launch thread.messages.forEach { message -> val response = ApiRepository.getDownloadedAttachment(mailbox.uuid, message.folderId, message.shortUid) @@ -1217,19 +1221,38 @@ class MainViewModel @Inject constructor( return@launch } - response.body?.bytes()?.let { byteArray -> - context?.let { - val emlFileName: String = message.subject ?: NO_SUBJECT_FILE - saveEmlToFile(context, byteArray, emlFileName)?.let { listUri.add(it) } - } + var messageSubject: String = message.subject?.removeIllegalFileNameCharacter() ?: NO_SUBJECT_FILE + createOriginalFileName(messageSubject, listFileName).let { fileName -> + listFileName.add(fileName) + saveEmlToFile(context, response.body!!.bytes(), fileName)?.let { listUri.add(it) } } } } - context?.let { ArrayList(listUri).openKDriveOrPlayStore(it) } + ArrayList(listUri).openKDriveOrPlayStore(context) + } + } + + // TODO Extract this code in core2 + private fun createOriginalFileName(originalFileName: String, listFileName: MutableSet): String { + var postfix = 1 + var fileName = originalFileName + + while (listFileName.contains(fileName)) { + fileName = "$originalFileName (${postfix++})" } + + return fileName + } + + private fun getAllFileNameInExportEmlDir(context: Context): List { + val fileDir = File(context.filesDir, context.getString(R.string.EXPOSED_EML_PATH)) + + if (!fileDir.exists()) fileDir.mkdirs() + + return fileDir.listFiles()?.map { it.name.removeSuffix(".eml") } ?: emptyList() } - fun saveEmlToFile(context: Context, emlByteArray: ByteArray, fileName: String): Uri? { + private fun saveEmlToFile(context: Context, emlByteArray: ByteArray, fileName: String): Uri? { val fileNameWithExtension = "${fileName.removeIllegalFileNameCharacter()}.eml" val fileDir = File(context.filesDir, context.getString(R.string.EXPOSED_EML_PATH)) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt index 8133a40171..849f948196 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt @@ -171,7 +171,7 @@ class MessageActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override fun onSaveKDrive() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) - mainViewModel.saveOnKDrive(listOf(threadUid), context) + context?.let { mainViewModel.saveOnKDrive(listOf(threadUid), it) } } override fun onReportDisplayProblem() { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt index 33748d4c00..f1d5f5fe92 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt @@ -119,7 +119,7 @@ class MultiSelectBottomSheetDialog : ActionsBottomSheetDialog() { } binding.saveKDrive.setClosingOnClickListener(shouldCloseMultiSelection = true) { trackMultiSelectActionEvent(ACTION_SAVE_KDRIVE_NAME, selectedThreadsCount, isFromBottomSheet = true) - saveOnKDrive(selectedThreadsUids, context) + context?.let { saveOnKDrive(selectedThreadsUids, it) } isMultiSelectOn = false } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt index f29fafb503..3753bf1cbc 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt @@ -198,7 +198,7 @@ class ThreadActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override fun onSaveKDrive() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) - mainViewModel.saveOnKDrive(listOf(threadUid), context) + context?.let { mainViewModel.saveOnKDrive(listOf(threadUid), it) } } override fun onReportDisplayProblem() { From 702848f4e238f86ef308fe6337633e309de7b8d2 Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Fri, 1 Nov 2024 10:35:06 +0100 Subject: [PATCH 09/29] feat: Save in cache instead of files --- app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt | 5 ++--- app/src/main/res/xml/exposed_files_path.xml | 2 +- 2 files changed, 3 insertions(+), 4 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 0395230a46..3d7fe0898d 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -20,7 +20,6 @@ package com.infomaniak.mail.ui import android.app.Application import android.content.Context import android.net.Uri -import android.util.Log import androidx.core.content.FileProvider import androidx.lifecycle.* import com.infomaniak.lib.core.models.ApiResponse @@ -1245,7 +1244,7 @@ class MainViewModel @Inject constructor( } private fun getAllFileNameInExportEmlDir(context: Context): List { - val fileDir = File(context.filesDir, context.getString(R.string.EXPOSED_EML_PATH)) + val fileDir = File(context.cacheDir, context.getString(R.string.EXPOSED_EML_PATH)) if (!fileDir.exists()) fileDir.mkdirs() @@ -1254,7 +1253,7 @@ class MainViewModel @Inject constructor( private fun saveEmlToFile(context: Context, emlByteArray: ByteArray, fileName: String): Uri? { val fileNameWithExtension = "${fileName.removeIllegalFileNameCharacter()}.eml" - val fileDir = File(context.filesDir, context.getString(R.string.EXPOSED_EML_PATH)) + val fileDir = File(context.cacheDir, context.getString(R.string.EXPOSED_EML_PATH)) if (!fileDir.exists()) fileDir.mkdirs() diff --git a/app/src/main/res/xml/exposed_files_path.xml b/app/src/main/res/xml/exposed_files_path.xml index e771e44a14..052c7f1f4b 100644 --- a/app/src/main/res/xml/exposed_files_path.xml +++ b/app/src/main/res/xml/exposed_files_path.xml @@ -22,7 +22,7 @@ - From 32905effb8d035a37f5b4b2bbb6f7a96328e6dbf Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Mon, 18 Nov 2024 09:06:09 +0100 Subject: [PATCH 10/29] refactor: Put all 'clickListener' from message bottom sheet together --- .../MessageActionsBottomSheetDialog.kt | 183 +++++++++--------- 1 file changed, 94 insertions(+), 89 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt index 849f948196..2778276df1 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt @@ -40,6 +40,7 @@ import com.infomaniak.mail.MatomoMail.trackBottomSheetThreadActionsEvent import com.infomaniak.mail.R import com.infomaniak.mail.data.models.Folder.FolderRole import com.infomaniak.mail.data.models.draft.Draft.DraftMode +import com.infomaniak.mail.data.models.message.Message import com.infomaniak.mail.ui.alertDialogs.DescriptionAlertDialog import com.infomaniak.mail.ui.main.move.MoveFragmentArgs import com.infomaniak.mail.ui.main.thread.PrintMailFragmentArgs @@ -80,109 +81,113 @@ class MessageActionsBottomSheetDialog : MailActionsBottomSheetDialog() { } } - initOnClickListener(object : OnActionClick { - //region Main actions - override fun onReply() { - trackBottomSheetMessageActionsEvent(ACTION_REPLY_NAME) - safeNavigateToNewMessageActivity( - draftMode = DraftMode.REPLY, - previousMessageUid = messageUid, - currentClassName = currentClassName, - shouldLoadDistantResources = navigationArgs.shouldLoadDistantResources, - ) - } + initActionClickListener(messageUid, message, threadUid) + } + } - override fun onReplyAll() { - trackBottomSheetMessageActionsEvent(ACTION_REPLY_ALL_NAME) - safeNavigateToNewMessageActivity( - draftMode = DraftMode.REPLY_ALL, - previousMessageUid = messageUid, - currentClassName = currentClassName, - shouldLoadDistantResources = navigationArgs.shouldLoadDistantResources, - ) - } + private fun initActionClickListener(messageUid: String, message: Message, threadUid: String) { + initOnClickListener(object : OnActionClick { + //region Main actions + override fun onReply() { + trackBottomSheetMessageActionsEvent(ACTION_REPLY_NAME) + safeNavigateToNewMessageActivity( + draftMode = DraftMode.REPLY, + previousMessageUid = messageUid, + currentClassName = currentClassName, + shouldLoadDistantResources = navigationArgs.shouldLoadDistantResources, + ) + } - override fun onForward() { - trackBottomSheetMessageActionsEvent(ACTION_FORWARD_NAME) - safeNavigateToNewMessageActivity( - draftMode = DraftMode.FORWARD, - previousMessageUid = messageUid, - currentClassName = currentClassName, - shouldLoadDistantResources = navigationArgs.shouldLoadDistantResources, - ) - } + override fun onReplyAll() { + trackBottomSheetMessageActionsEvent(ACTION_REPLY_ALL_NAME) + safeNavigateToNewMessageActivity( + draftMode = DraftMode.REPLY_ALL, + previousMessageUid = messageUid, + currentClassName = currentClassName, + shouldLoadDistantResources = navigationArgs.shouldLoadDistantResources, + ) + } - override fun onDelete() { - descriptionDialog.deleteWithConfirmationPopup(folderRole, count = 1) { - trackBottomSheetMessageActionsEvent(ACTION_DELETE_NAME) - mainViewModel.deleteMessage(threadUid, message) - } - } - //endregion + override fun onForward() { + trackBottomSheetMessageActionsEvent(ACTION_FORWARD_NAME) + safeNavigateToNewMessageActivity( + draftMode = DraftMode.FORWARD, + previousMessageUid = messageUid, + currentClassName = currentClassName, + shouldLoadDistantResources = navigationArgs.shouldLoadDistantResources, + ) + } - //region Actions - override fun onArchive() { - trackBottomSheetMessageActionsEvent(ACTION_ARCHIVE_NAME, message.folder.role == FolderRole.ARCHIVE) - mainViewModel.archiveMessage(threadUid, message) + override fun onDelete() { + descriptionDialog.deleteWithConfirmationPopup(folderRole, count = 1) { + trackBottomSheetMessageActionsEvent(ACTION_DELETE_NAME) + mainViewModel.deleteMessage(threadUid, message) } + } + //endregion - override fun onReadUnread() { - trackBottomSheetMessageActionsEvent(ACTION_MARK_AS_SEEN_NAME, message.isSeen) - mainViewModel.toggleMessageSeenStatus(threadUid, message) - twoPaneViewModel.closeThread() - } + //region Actions + override fun onArchive() { + trackBottomSheetMessageActionsEvent(ACTION_ARCHIVE_NAME, message.folder.role == FolderRole.ARCHIVE) + mainViewModel.archiveMessage(threadUid, message) + } - override fun onMove() { - trackBottomSheetMessageActionsEvent(ACTION_MOVE_NAME) - animatedNavigation( - resId = R.id.moveFragment, - args = MoveFragmentArgs(arrayOf(threadUid), messageUid).toBundle(), - currentClassName = currentClassName, - ) - } + override fun onReadUnread() { + trackBottomSheetMessageActionsEvent(ACTION_MARK_AS_SEEN_NAME, message.isSeen) + mainViewModel.toggleMessageSeenStatus(threadUid, message) + twoPaneViewModel.closeThread() + } - override fun onPostpone() { - trackBottomSheetMessageActionsEvent(ACTION_POSTPONE_NAME) - notYetImplemented() - } + override fun onMove() { + trackBottomSheetMessageActionsEvent(ACTION_MOVE_NAME) + animatedNavigation( + resId = R.id.moveFragment, + args = MoveFragmentArgs(arrayOf(threadUid), messageUid).toBundle(), + currentClassName = currentClassName, + ) + } - override fun onFavorite() { - trackBottomSheetMessageActionsEvent(ACTION_FAVORITE_NAME, message.isFavorite) - mainViewModel.toggleMessageFavoriteStatus(threadUid, message) - } + override fun onPostpone() { + trackBottomSheetMessageActionsEvent(ACTION_POSTPONE_NAME) + notYetImplemented() + } - override fun onReportJunk() = Unit + override fun onFavorite() { + trackBottomSheetMessageActionsEvent(ACTION_FAVORITE_NAME, message.isFavorite) + mainViewModel.toggleMessageFavoriteStatus(threadUid, message) + } - override fun onPrint() { - trackBottomSheetMessageActionsEvent(ACTION_PRINT_NAME) - safeNavigate( - resId = R.id.printMailFragment, - args = PrintMailFragmentArgs(messageUid).toBundle(), - currentClassName = MessageActionsBottomSheetDialog::class.java.name, - ) - } + override fun onReportJunk() = Unit - override fun onShare() { - activity?.apply { - trackBottomSheetThreadActionsEvent(ACTION_SHARE_LINK_NAME) - mainViewModel.shareThreadUrl(message.uid, ::shareString) - } - } + override fun onPrint() { + trackBottomSheetMessageActionsEvent(ACTION_PRINT_NAME) + safeNavigate( + resId = R.id.printMailFragment, + args = PrintMailFragmentArgs(messageUid).toBundle(), + currentClassName = MessageActionsBottomSheetDialog::class.java.name, + ) + } - override fun onSaveKDrive() { - trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) - context?.let { mainViewModel.saveOnKDrive(listOf(threadUid), it) } + override fun onShare() { + activity?.apply { + trackBottomSheetThreadActionsEvent(ACTION_SHARE_LINK_NAME) + mainViewModel.shareThreadUrl(message.uid, ::shareString) } + } - override fun onReportDisplayProblem() { - descriptionDialog.show( - title = getString(R.string.reportDisplayProblemTitle), - description = getString(R.string.reportDisplayProblemDescription), - onPositiveButtonClicked = { mainViewModel.reportDisplayProblem(message.uid) }, - ) - } - //endregion - }) - } + override fun onSaveKDrive() { + trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) + context?.let { mainViewModel.saveOnKDrive(listOf(threadUid), it) } + } + + override fun onReportDisplayProblem() { + descriptionDialog.show( + title = getString(R.string.reportDisplayProblemTitle), + description = getString(R.string.reportDisplayProblemDescription), + onPositiveButtonClicked = { mainViewModel.reportDisplayProblem(message.uid) }, + ) + } + //endregion + }) } } From 46730f2ce95fa4623742843a74f1a772fce64e14 Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Mon, 18 Nov 2024 09:06:55 +0100 Subject: [PATCH 11/29] refactor: Use a list instead of a set --- app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 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 3d7fe0898d..98cc29c314 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -1220,8 +1220,8 @@ class MainViewModel @Inject constructor( return@launch } - var messageSubject: String = message.subject?.removeIllegalFileNameCharacter() ?: NO_SUBJECT_FILE - createOriginalFileName(messageSubject, listFileName).let { fileName -> + val messageSubject: String = message.subject?.removeIllegalFileNameCharacter() ?: NO_SUBJECT_FILE + createOriginalFileName(messageSubject, listFileName.toList()).let { fileName -> listFileName.add(fileName) saveEmlToFile(context, response.body!!.bytes(), fileName)?.let { listUri.add(it) } } @@ -1232,7 +1232,7 @@ class MainViewModel @Inject constructor( } // TODO Extract this code in core2 - private fun createOriginalFileName(originalFileName: String, listFileName: MutableSet): String { + private fun createOriginalFileName(originalFileName: String, listFileName: List): String { var postfix = 1 var fileName = originalFileName From 6819b9febc6ae0d98d38e180a117385aad3dbc4f Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Thu, 21 Nov 2024 13:22:23 +0100 Subject: [PATCH 12/29] feat: Display progression while loading messages --- .idea/navEditor.xml | 28 +++- .../com/infomaniak/mail/ui/MainViewModel.kt | 76 ----------- .../mail/ui/main/folder/TwoPaneFragment.kt | 2 + .../actions/DownloadThreadsProgressDialog.kt | 88 +++++++++++++ .../actions/DownloadThreadsViewModel.kt | 124 ++++++++++++++++++ .../MessageActionsBottomSheetDialog.kt | 3 +- .../actions/MultiSelectBottomSheetDialog.kt | 7 +- .../actions/ThreadActionsBottomSheetDialog.kt | 2 +- .../utils/extensions/AttachmentExtensions.kt | 17 ++- .../utils/extensions/NavigationExtensions.kt | 9 ++ .../main/res/navigation/main_navigation.xml | 10 ++ 11 files changed, 278 insertions(+), 88 deletions(-) create mode 100644 app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsProgressDialog.kt create mode 100644 app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt diff --git a/.idea/navEditor.xml b/.idea/navEditor.xml index 533687d6a1..94f86c2952 100644 --- a/.idea/navEditor.xml +++ b/.idea/navEditor.xml @@ -303,8 +303,20 @@ + + + + + + + @@ -932,6 +944,18 @@ + + + + + + + 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 98cc29c314..02c4f3158b 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -18,9 +18,6 @@ package com.infomaniak.mail.ui import android.app.Application -import android.content.Context -import android.net.Uri -import androidx.core.content.FileProvider import androidx.lifecycle.* import com.infomaniak.lib.core.models.ApiResponse import com.infomaniak.lib.core.networking.NetworkAvailability @@ -66,7 +63,6 @@ import com.infomaniak.mail.utils.Utils.isPermanentDeleteFolder import com.infomaniak.mail.utils.Utils.runCatchingRealm import com.infomaniak.mail.utils.coroutineContext import com.infomaniak.mail.utils.extensions.* -import com.infomaniak.mail.utils.extensions.AttachmentExtensions.openKDriveOrPlayStore import com.infomaniak.mail.views.itemViews.AvatarMergedContactData import dagger.hilt.android.lifecycle.HiltViewModel import io.realm.kotlin.Realm @@ -77,7 +73,6 @@ import io.sentry.Sentry import io.sentry.SentryLevel import kotlinx.coroutines.* import kotlinx.coroutines.flow.* -import java.io.File import java.util.Date import java.util.UUID import javax.inject.Inject @@ -1200,76 +1195,6 @@ class MainViewModel @Inject constructor( } } - fun saveOnKDrive(threadUids: List, context: Context) { - viewModelScope.launch(ioCoroutineContext) { - val mailbox = currentMailbox.value ?: return@launch - val listUri = mutableListOf() - val listFileName = mutableSetOf().also { - it.addAll(getAllFileNameInExportEmlDir(context)) - } - - threadUids.forEach { threadUid -> - val thread = threadController.getThread(threadUid) ?: return@launch - - thread.messages.forEach { message -> - val response = ApiRepository.getDownloadedAttachment(mailbox.uuid, message.folderId, message.shortUid) - - if (!response.isSuccessful || response.body == null) { - snackbarManager.postValue(appContext.getString(RCore.string.anErrorHasOccurred)) - - return@launch - } - - val messageSubject: String = message.subject?.removeIllegalFileNameCharacter() ?: NO_SUBJECT_FILE - createOriginalFileName(messageSubject, listFileName.toList()).let { fileName -> - listFileName.add(fileName) - saveEmlToFile(context, response.body!!.bytes(), fileName)?.let { listUri.add(it) } - } - } - } - ArrayList(listUri).openKDriveOrPlayStore(context) - } - } - - // TODO Extract this code in core2 - private fun createOriginalFileName(originalFileName: String, listFileName: List): String { - var postfix = 1 - var fileName = originalFileName - - while (listFileName.contains(fileName)) { - fileName = "$originalFileName (${postfix++})" - } - - return fileName - } - - private fun getAllFileNameInExportEmlDir(context: Context): List { - val fileDir = File(context.cacheDir, context.getString(R.string.EXPOSED_EML_PATH)) - - if (!fileDir.exists()) fileDir.mkdirs() - - return fileDir.listFiles()?.map { it.name.removeSuffix(".eml") } ?: emptyList() - } - - private fun saveEmlToFile(context: Context, emlByteArray: ByteArray, fileName: String): Uri? { - val fileNameWithExtension = "${fileName.removeIllegalFileNameCharacter()}.eml" - val fileDir = File(context.cacheDir, context.getString(R.string.EXPOSED_EML_PATH)) - - if (!fileDir.exists()) fileDir.mkdirs() - - runCatching { - val file = File(fileDir, fileNameWithExtension) - file.outputStream().use { it.write(emlByteArray) } - return FileProvider.getUriForFile(context, context.getString(R.string.EML_AUTHORITY), file) - }.onFailure { exception -> - exception.printStackTrace() - snackbarManager.postValue(appContext.getString(RCore.string.anErrorHasOccurred)) - } - return null - } - - fun String.removeIllegalFileNameCharacter(): String = this.replace(DownloadManagerUtils.regexInvalidSystemChar, "") - companion object { private val TAG: String = MainViewModel::class.java.simpleName private val DEFAULT_SELECTED_FOLDER = FolderRole.INBOX @@ -1277,6 +1202,5 @@ class MainViewModel @Inject constructor( private const val MAX_REFRESH_DELAY = 6_000L private const val EML_CONTENT_TYPE = "message/rfc822" - private const val NO_SUBJECT_FILE = "message" } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folder/TwoPaneFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folder/TwoPaneFragment.kt index cb97c7ce18..48bf4d3edb 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folder/TwoPaneFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folder/TwoPaneFragment.kt @@ -41,6 +41,7 @@ import com.infomaniak.mail.ui.MainActivity import com.infomaniak.mail.ui.MainViewModel import com.infomaniak.mail.ui.main.search.SearchFragment import com.infomaniak.mail.ui.main.thread.ThreadFragment +import com.infomaniak.mail.ui.main.thread.actions.DownloadThreadsProgressDialog.Companion.DOWNLOAD_THREADS_RESULT import com.infomaniak.mail.utils.extensions.* import javax.inject.Inject @@ -120,6 +121,7 @@ abstract class TwoPaneFragment : Fragment() { private fun observeThreadNavigation() = with(twoPaneViewModel) { getBackNavigationResult(AttachmentExtensions.DOWNLOAD_ATTACHMENT_RESULT, ::startActivity) + getBackNavigationResult(DOWNLOAD_THREADS_RESULT, ::startActivity) newMessageArgs.observe(viewLifecycleOwner) { safeNavigateToNewMessageActivity(args = it.toBundle()) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsProgressDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsProgressDialog.kt new file mode 100644 index 0000000000..6efeaef269 --- /dev/null +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsProgressDialog.kt @@ -0,0 +1,88 @@ +/* + * Infomaniak Mail - Android + * Copyright (C) 2024 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.infomaniak.mail.ui.main.thread.actions + +import android.app.Dialog +import android.os.Bundle +import android.view.KeyEvent +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.findNavController +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.infomaniak.lib.core.R +import com.infomaniak.lib.core.utils.SnackbarUtils.showSnackbar +import com.infomaniak.lib.core.utils.setBackNavigationResult +import com.infomaniak.mail.databinding.DialogDownloadProgressBinding +import com.infomaniak.mail.ui.MainViewModel +import com.infomaniak.mail.utils.extensions.AttachmentExtensions.openKDriveOrPlayStore +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch + +@AndroidEntryPoint +class DownloadThreadsProgressDialog : DialogFragment() { + private val binding by lazy { DialogDownloadProgressBinding.inflate(layoutInflater) } + private val mainViewModel: MainViewModel by activityViewModels() + private val downloadThreadsViewModel: DownloadThreadsViewModel by viewModels() + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + isCancelable = false + return MaterialAlertDialogBuilder(requireContext()) + .setTitle("test") + .setView(binding.root) + .setOnKeyListener { _, keyCode, event -> + if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) { + findNavController().popBackStack() + true + } else false + } + .create() + } + + override fun onStart() { + downloadThreads() + super.onStart() + } + + private fun downloadThreads() { + downloadThreadsViewModel.downloadThreads(mainViewModel.currentMailbox.value).observe(this) { threadUris -> + if (threadUris == null) { + popBackStackWithError() + } else { + ArrayList(threadUris).openKDriveOrPlayStore(requireContext())?.let { openKDriveIntent -> + setBackNavigationResult(DOWNLOAD_THREADS_RESULT, openKDriveIntent) + } ?: run { findNavController().popBackStack() } + } + } + } + + private fun popBackStackWithError() { + lifecycleScope.launch { + mainViewModel.isNetworkAvailable.first { it != null }?.let { isNetworkAvailable -> + showSnackbar(title = if (isNetworkAvailable) R.string.anErrorHasOccurred else R.string.noConnection) + findNavController().popBackStack() + } + } + } + + companion object { + const val DOWNLOAD_THREADS_RESULT = "download_threads_result" + } +} diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt new file mode 100644 index 0000000000..8d3b6b03f2 --- /dev/null +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt @@ -0,0 +1,124 @@ +/* + * Infomaniak Mail - Android + * Copyright (C) 2024 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.infomaniak.mail.ui.main.thread.actions + +import android.app.Application +import android.content.Context +import android.net.Uri +import androidx.core.content.FileProvider +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.liveData +import androidx.lifecycle.viewModelScope +import com.infomaniak.lib.core.utils.DownloadManagerUtils +import com.infomaniak.mail.data.api.ApiRepository +import com.infomaniak.mail.data.cache.mailboxContent.ThreadController +import com.infomaniak.mail.data.models.mailbox.Mailbox +import com.infomaniak.mail.di.IoDispatcher +import com.infomaniak.mail.utils.coroutineContext +import com.infomaniak.mail.utils.extensions.appContext +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.CoroutineDispatcher +import java.io.File +import javax.inject.Inject + +@HiltViewModel +class DownloadThreadsViewModel @Inject constructor( + application: Application, + private val savedStateHandle: SavedStateHandle, + private val threadController: ThreadController, + @IoDispatcher private val ioDispatcher: CoroutineDispatcher, +) : AndroidViewModel(application) { + + private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher) + + private val messageLocalUids + inline get() = savedStateHandle.get>(DownloadThreadsProgressDialogArgs::threadUuids.name)!! + + fun downloadThreads(currentMailbox: Mailbox?) = liveData(ioCoroutineContext) { + val downloadedThreadUris: List? = runCatching { + val mailbox = currentMailbox ?: return@runCatching null + val listUri = mutableListOf() + val listFileName = mutableSetOf().also { it.addAll(getAllFileNameInExportEmlDir(appContext)) } + + messageLocalUids.forEach { threadUid -> + val thread = threadController.getThread(threadUid) ?: return@runCatching null + + thread.messages.forEach { message -> + val response = ApiRepository.getDownloadedAttachment(mailbox.uuid, message.folderId, message.shortUid) + + if (!response.isSuccessful || response.body == null) { + return@runCatching null + } + + val messageSubject: String = message.subject?.removeIllegalFileNameCharacter() ?: NO_SUBJECT_FILE + createOriginalFileName(messageSubject, listFileName.toList()).let { fileName -> + listFileName.add(fileName) + saveEmlToFile(appContext, response.body!!.bytes(), fileName)?.let { listUri.add(it) } + } + } + } + listUri + }.getOrNull() + + emit(downloadedThreadUris) + } + + // TODO Extract this code in core2 + private fun createOriginalFileName(originalFileName: String, listFileName: List): String { + var postfix = 1 + var fileName = originalFileName + + while (listFileName.contains(fileName)) { + fileName = "$originalFileName (${postfix++})" + } + + return fileName + } + + private fun getAllFileNameInExportEmlDir(context: Context): List { + val fileDir = File(context.cacheDir, context.getString(com.infomaniak.mail.R.string.EXPOSED_EML_PATH)) + + if (!fileDir.exists()) fileDir.mkdirs() + + return fileDir.listFiles()?.map { it.name.removeSuffix(".eml") } ?: emptyList() + } + + private fun saveEmlToFile(context: Context, emlByteArray: ByteArray, fileName: String): Uri? { + val fileNameWithExtension = "${fileName.removeIllegalFileNameCharacter()}.eml" + val fileDir = File(context.cacheDir, context.getString(com.infomaniak.mail.R.string.EXPOSED_EML_PATH)) + + if (!fileDir.exists()) fileDir.mkdirs() + + runCatching { + val file = File(fileDir, fileNameWithExtension) + file.outputStream().use { it.write(emlByteArray) } + return FileProvider.getUriForFile(context, context.getString(com.infomaniak.mail.R.string.EML_AUTHORITY), file) + }.onFailure { exception -> + exception.printStackTrace() + } + return null + } + + private fun String.removeIllegalFileNameCharacter(): String = this.replace(DownloadManagerUtils.regexInvalidSystemChar, "") + + companion object { + private const val EML_CONTENT_TYPE = "message/rfc822" + private const val NO_SUBJECT_FILE = "message" + } +} diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt index 2778276df1..77b763818e 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt @@ -18,6 +18,7 @@ package com.infomaniak.mail.ui.main.thread.actions import android.os.Bundle +import android.util.Log import android.view.View import androidx.core.view.isVisible import androidx.navigation.fragment.navArgs @@ -177,7 +178,7 @@ class MessageActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override fun onSaveKDrive() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) - context?.let { mainViewModel.saveOnKDrive(listOf(threadUid), it) } + navigateToDownloadThreadsProgressDialog(listOf(messageUid), MessageActionsBottomSheetDialog::class.java.name) } override fun onReportDisplayProblem() { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt index f1d5f5fe92..6bb619ac0a 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt @@ -23,6 +23,7 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.activityViewModels import com.infomaniak.lib.core.utils.safeBinding +import com.infomaniak.lib.core.utils.safeNavigate import com.infomaniak.mail.MatomoMail.ACTION_ARCHIVE_NAME import com.infomaniak.mail.MatomoMail.ACTION_DELETE_NAME import com.infomaniak.mail.MatomoMail.ACTION_FAVORITE_NAME @@ -119,7 +120,11 @@ class MultiSelectBottomSheetDialog : ActionsBottomSheetDialog() { } binding.saveKDrive.setClosingOnClickListener(shouldCloseMultiSelection = true) { trackMultiSelectActionEvent(ACTION_SAVE_KDRIVE_NAME, selectedThreadsCount, isFromBottomSheet = true) - context?.let { saveOnKDrive(selectedThreadsUids, it) } + safeNavigate( + resId = R.id.downloadThreadsProgressDialog, + args = DownloadThreadsProgressDialogArgs(threadUuids = selectedThreadsUids.toTypedArray()).toBundle(), + currentClassName = currentClassName, + ) isMultiSelectOn = false } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt index 3753bf1cbc..93fdcf8c86 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt @@ -198,7 +198,7 @@ class ThreadActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override fun onSaveKDrive() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) - context?.let { mainViewModel.saveOnKDrive(listOf(threadUid), it) } + navigateToDownloadThreadsProgressDialog(listOf(threadUid), ThreadActionsBottomSheetDialog::class.java.name) } override fun onReportDisplayProblem() { diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt index d3a69e3193..6ae3cd235b 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt @@ -59,6 +59,15 @@ object AttachmentExtensions { private const val DRIVE_PACKAGE = "com.infomaniak.drive" private const val SAVE_EXTERNAL_ACTIVITY_CLASS = "com.infomaniak.drive.ui.SaveExternalFilesActivity" + fun ArrayList.openKDriveOrPlayStore(context: Context): Intent? { + return if (canSaveOnKDrive(context)) { + saveToDriveIntent() + } else { + context.goToPlayStore(DRIVE_PACKAGE) + null + } + } + //region Intent private fun canSaveOnKDrive(context: Context) = runCatching { val packageInfo = context.packageManager.getPackageInfo(DRIVE_PACKAGE, PackageManager.GET_ACTIVITIES) @@ -111,13 +120,7 @@ object AttachmentExtensions { } } - fun ArrayList.openKDriveOrPlayStore(context: Context) { - if (canSaveOnKDrive(context)) { - saveToDriveIntent().let(context::startActivity) - } else { - context.goToPlayStore(DRIVE_PACKAGE) - } - } + fun Attachment.executeIntent( context: Context, diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt index 0c9a11e13f..da0b258cc6 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt @@ -36,6 +36,7 @@ import com.infomaniak.mail.ui.login.LoginActivity import com.infomaniak.mail.ui.login.LoginActivityArgs import com.infomaniak.mail.ui.login.NoMailboxActivity import com.infomaniak.mail.ui.main.thread.actions.AttachmentActionsBottomSheetDialog +import com.infomaniak.mail.ui.main.thread.actions.DownloadThreadsProgressDialogArgs import com.infomaniak.mail.ui.newMessage.NewMessageActivityArgs import com.infomaniak.mail.ui.noValidMailboxes.NoValidMailboxesActivity import com.infomaniak.mail.utils.AccountUtils @@ -90,6 +91,14 @@ fun Fragment.navigateToDownloadProgressDialog( ) } +fun Fragment.navigateToDownloadThreadsProgressDialog(threadUuids: List, currentClassName: String) { + safeNavigate( + resId = R.id.downloadThreadsProgressDialog, + args = DownloadThreadsProgressDialogArgs(threadUuids = threadUuids.toTypedArray()).toBundle(), + currentClassName = currentClassName, + ) +} + //region Launch Activities fun Context.getLoginActivityIntent(args: LoginActivityArgs? = null, shouldClearStack: Boolean = false): Intent { return Intent(this, LoginActivity::class.java).apply { diff --git a/app/src/main/res/navigation/main_navigation.xml b/app/src/main/res/navigation/main_navigation.xml index 89b4387fcd..7aa548c751 100644 --- a/app/src/main/res/navigation/main_navigation.xml +++ b/app/src/main/res/navigation/main_navigation.xml @@ -573,6 +573,16 @@ app:argType="com.infomaniak.mail.utils.extensions.AttachmentExtensions$AttachmentIntentType" /> + + + + Date: Thu, 21 Nov 2024 14:32:59 +0100 Subject: [PATCH 13/29] refactor: Send messages uids instead of thread uuids --- .../com/infomaniak/mail/ui/MainViewModel.kt | 9 ++++++ .../actions/DownloadThreadsViewModel.kt | 29 ++++++++----------- .../actions/MultiSelectBottomSheetDialog.kt | 9 +++--- .../actions/ThreadActionsBottomSheetDialog.kt | 5 +++- .../utils/extensions/NavigationExtensions.kt | 4 +-- .../main/res/navigation/main_navigation.xml | 2 +- 6 files changed, 32 insertions(+), 26 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 02c4f3158b..3c4e673459 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -1195,6 +1195,15 @@ class MainViewModel @Inject constructor( } } + fun getMessagesUidsFromThreadUids(selectedThreadsUuids: List): List { + val messageUids = mutableListOf() + selectedThreadsUuids.forEach { threadUuid -> + val thread = threadController.getThread(threadUuid) ?: return@forEach + messageUids.addAll(thread.messages.map { it.uid }) + } + return messageUids + } + companion object { private val TAG: String = MainViewModel::class.java.simpleName private val DEFAULT_SELECTED_FOLDER = FolderRole.INBOX diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt index 8d3b6b03f2..c3311166e6 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt @@ -27,7 +27,7 @@ import androidx.lifecycle.liveData import androidx.lifecycle.viewModelScope import com.infomaniak.lib.core.utils.DownloadManagerUtils import com.infomaniak.mail.data.api.ApiRepository -import com.infomaniak.mail.data.cache.mailboxContent.ThreadController +import com.infomaniak.mail.data.cache.mailboxContent.MessageController import com.infomaniak.mail.data.models.mailbox.Mailbox import com.infomaniak.mail.di.IoDispatcher import com.infomaniak.mail.utils.coroutineContext @@ -41,14 +41,14 @@ import javax.inject.Inject class DownloadThreadsViewModel @Inject constructor( application: Application, private val savedStateHandle: SavedStateHandle, - private val threadController: ThreadController, + private val messageController: MessageController, @IoDispatcher private val ioDispatcher: CoroutineDispatcher, ) : AndroidViewModel(application) { private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher) - private val messageLocalUids - inline get() = savedStateHandle.get>(DownloadThreadsProgressDialogArgs::threadUuids.name)!! + private val messageLocalUuids + inline get() = savedStateHandle.get>(DownloadThreadsProgressDialogArgs::messageUuids.name)!! fun downloadThreads(currentMailbox: Mailbox?) = liveData(ioCoroutineContext) { val downloadedThreadUris: List? = runCatching { @@ -56,21 +56,16 @@ class DownloadThreadsViewModel @Inject constructor( val listUri = mutableListOf() val listFileName = mutableSetOf().also { it.addAll(getAllFileNameInExportEmlDir(appContext)) } - messageLocalUids.forEach { threadUid -> - val thread = threadController.getThread(threadUid) ?: return@runCatching null + messageLocalUuids.forEach { messageUid -> + val message = messageController.getMessage(messageUid) ?: return@runCatching null + val response = ApiRepository.getDownloadedAttachment(mailbox.uuid, message.folderId, message.shortUid) - thread.messages.forEach { message -> - val response = ApiRepository.getDownloadedAttachment(mailbox.uuid, message.folderId, message.shortUid) + if (!response.isSuccessful || response.body == null) return@runCatching null - if (!response.isSuccessful || response.body == null) { - return@runCatching null - } - - val messageSubject: String = message.subject?.removeIllegalFileNameCharacter() ?: NO_SUBJECT_FILE - createOriginalFileName(messageSubject, listFileName.toList()).let { fileName -> - listFileName.add(fileName) - saveEmlToFile(appContext, response.body!!.bytes(), fileName)?.let { listUri.add(it) } - } + val messageSubject: String = message.subject?.removeIllegalFileNameCharacter() ?: NO_SUBJECT_FILE + createOriginalFileName(messageSubject, listFileName.toList()).let { fileName -> + listFileName.add(fileName) + saveEmlToFile(appContext, response.body!!.bytes(), fileName)?.let { listUri.add(it) } } } listUri diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt index 6bb619ac0a..f17f01c3d9 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt @@ -23,7 +23,6 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.activityViewModels import com.infomaniak.lib.core.utils.safeBinding -import com.infomaniak.lib.core.utils.safeNavigate import com.infomaniak.mail.MatomoMail.ACTION_ARCHIVE_NAME import com.infomaniak.mail.MatomoMail.ACTION_DELETE_NAME import com.infomaniak.mail.MatomoMail.ACTION_FAVORITE_NAME @@ -42,6 +41,7 @@ import com.infomaniak.mail.ui.main.folder.ThreadListMultiSelection import com.infomaniak.mail.ui.main.folder.ThreadListMultiSelection.Companion.getReadIconAndShortText import com.infomaniak.mail.utils.extensions.animatedNavigation import com.infomaniak.mail.utils.extensions.deleteWithConfirmationPopup +import com.infomaniak.mail.utils.extensions.navigateToDownloadThreadsProgressDialog import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -120,10 +120,9 @@ class MultiSelectBottomSheetDialog : ActionsBottomSheetDialog() { } binding.saveKDrive.setClosingOnClickListener(shouldCloseMultiSelection = true) { trackMultiSelectActionEvent(ACTION_SAVE_KDRIVE_NAME, selectedThreadsCount, isFromBottomSheet = true) - safeNavigate( - resId = R.id.downloadThreadsProgressDialog, - args = DownloadThreadsProgressDialogArgs(threadUuids = selectedThreadsUids.toTypedArray()).toBundle(), - currentClassName = currentClassName, + navigateToDownloadThreadsProgressDialog( + messageUuids = mainViewModel.getMessagesUidsFromThreadUids(selectedThreadsUids), + MultiSelectBottomSheetDialog::class.java.name ) isMultiSelectOn = false } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt index 93fdcf8c86..8e76edae77 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt @@ -198,7 +198,10 @@ class ThreadActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override fun onSaveKDrive() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) - navigateToDownloadThreadsProgressDialog(listOf(threadUid), ThreadActionsBottomSheetDialog::class.java.name) + navigateToDownloadThreadsProgressDialog( + messageUuids = thread.messages.map { it.uid }, + ThreadActionsBottomSheetDialog::class.java.name + ) } override fun onReportDisplayProblem() { diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt index da0b258cc6..0679da21b2 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt @@ -91,10 +91,10 @@ fun Fragment.navigateToDownloadProgressDialog( ) } -fun Fragment.navigateToDownloadThreadsProgressDialog(threadUuids: List, currentClassName: String) { +fun Fragment.navigateToDownloadThreadsProgressDialog(messageUuids: List, currentClassName: String) { safeNavigate( resId = R.id.downloadThreadsProgressDialog, - args = DownloadThreadsProgressDialogArgs(threadUuids = threadUuids.toTypedArray()).toBundle(), + args = DownloadThreadsProgressDialogArgs(messageUuids = messageUuids.toTypedArray()).toBundle(), currentClassName = currentClassName, ) } diff --git a/app/src/main/res/navigation/main_navigation.xml b/app/src/main/res/navigation/main_navigation.xml index 7aa548c751..bd65687710 100644 --- a/app/src/main/res/navigation/main_navigation.xml +++ b/app/src/main/res/navigation/main_navigation.xml @@ -579,7 +579,7 @@ android:label="DownloadThreadsProgressDialog" tools:layout="@layout/dialog_download_progress"> From 78c84ba94d142a58a5bf4ea05ab8ca6ebd12f2a4 Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Thu, 21 Nov 2024 15:05:30 +0100 Subject: [PATCH 14/29] refactor: Rename 'messageUuids' to 'messageUids' --- .../mail/ui/main/thread/actions/DownloadThreadsViewModel.kt | 6 +++--- .../ui/main/thread/actions/MultiSelectBottomSheetDialog.kt | 2 +- .../main/thread/actions/ThreadActionsBottomSheetDialog.kt | 2 +- .../mail/utils/extensions/NavigationExtensions.kt | 4 ++-- app/src/main/res/navigation/main_navigation.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt index c3311166e6..9465d345c4 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt @@ -47,8 +47,8 @@ class DownloadThreadsViewModel @Inject constructor( private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher) - private val messageLocalUuids - inline get() = savedStateHandle.get>(DownloadThreadsProgressDialogArgs::messageUuids.name)!! + private val messageLocalUids + inline get() = savedStateHandle.get>(DownloadThreadsProgressDialogArgs::messageUids.name)!! fun downloadThreads(currentMailbox: Mailbox?) = liveData(ioCoroutineContext) { val downloadedThreadUris: List? = runCatching { @@ -56,7 +56,7 @@ class DownloadThreadsViewModel @Inject constructor( val listUri = mutableListOf() val listFileName = mutableSetOf().also { it.addAll(getAllFileNameInExportEmlDir(appContext)) } - messageLocalUuids.forEach { messageUid -> + messageLocalUids.forEach { messageUid -> val message = messageController.getMessage(messageUid) ?: return@runCatching null val response = ApiRepository.getDownloadedAttachment(mailbox.uuid, message.folderId, message.shortUid) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt index f17f01c3d9..734381cf0b 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt @@ -121,7 +121,7 @@ class MultiSelectBottomSheetDialog : ActionsBottomSheetDialog() { binding.saveKDrive.setClosingOnClickListener(shouldCloseMultiSelection = true) { trackMultiSelectActionEvent(ACTION_SAVE_KDRIVE_NAME, selectedThreadsCount, isFromBottomSheet = true) navigateToDownloadThreadsProgressDialog( - messageUuids = mainViewModel.getMessagesUidsFromThreadUids(selectedThreadsUids), + messageUids = mainViewModel.getMessagesUidsFromThreadUids(selectedThreadsUids), MultiSelectBottomSheetDialog::class.java.name ) isMultiSelectOn = false diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt index 8e76edae77..eee5e4b55c 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt @@ -199,7 +199,7 @@ class ThreadActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override fun onSaveKDrive() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) navigateToDownloadThreadsProgressDialog( - messageUuids = thread.messages.map { it.uid }, + messageUids = thread.messages.map { it.uid }, ThreadActionsBottomSheetDialog::class.java.name ) } diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt index 0679da21b2..a46104b194 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt @@ -91,10 +91,10 @@ fun Fragment.navigateToDownloadProgressDialog( ) } -fun Fragment.navigateToDownloadThreadsProgressDialog(messageUuids: List, currentClassName: String) { +fun Fragment.navigateToDownloadThreadsProgressDialog(messageUids: List, currentClassName: String) { safeNavigate( resId = R.id.downloadThreadsProgressDialog, - args = DownloadThreadsProgressDialogArgs(messageUuids = messageUuids.toTypedArray()).toBundle(), + args = DownloadThreadsProgressDialogArgs(messageUids = messageUids.toTypedArray()).toBundle(), currentClassName = currentClassName, ) } diff --git a/app/src/main/res/navigation/main_navigation.xml b/app/src/main/res/navigation/main_navigation.xml index bd65687710..0023a457d3 100644 --- a/app/src/main/res/navigation/main_navigation.xml +++ b/app/src/main/res/navigation/main_navigation.xml @@ -579,7 +579,7 @@ android:label="DownloadThreadsProgressDialog" tools:layout="@layout/dialog_download_progress"> From f83ee029b322e47035f97c46cb711716f8f9c100 Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Thu, 21 Nov 2024 16:36:23 +0100 Subject: [PATCH 15/29] feat: Import downloading String --- app/src/main/res/values-de/strings.xml | 4 ++++ app/src/main/res/values-es/strings.xml | 4 ++++ app/src/main/res/values-fr/strings.xml | 5 +++++ app/src/main/res/values-it/strings.xml | 4 ++++ app/src/main/res/values/strings.xml | 4 ++++ 5 files changed, 21 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index de034f547c..4e8e461abc 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -235,6 +235,10 @@ Die Löschung Ihres Kontos ist endgültig. Sie werden nicht in der Lage sein, Ihr Konto zu reaktivieren. Um Anzeigeprobleme zu beheben, aktualisieren Sie bitte die Anwendung Android System Webview. Problem mit der Anzeige Ihrer E-Mails + + Herunterladen von %d-E-Mails + Herunterladen von %d-E-Mails + Entwürfe (Entwurf) Sie sind dabei, eine Nachricht ohne Betreff zu senden. Möchten Sie fortfahren? diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index a7c7e29a87..e518560b4e 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -235,6 +235,10 @@ La eliminación de su cuenta será definitiva. No podrá reactivar su cuenta. Para solucionar los problemas de visualización, actualice la aplicación Android System Webview. Problema de visualización de sus emails + + Descargando %d correos electrónicos + Descargando %d correos electrónicos + Borradores (Borrador) Está a punto de enviar un mensaje sin asunto. ¿Desea continuar? diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 0063450084..1a47950310 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -237,6 +237,11 @@ La suppression de votre compte sera définitive. Vous ne pourrez pas réactiver votre compte. Pour corriger les problèmes d’affichage, veuillez mettre à jour l’application Android System Webview. Problème d’affichage de vos e-mails + + Téléchargement de %d e-mails + Téléchargement de %d e-mails + Téléchargement de %d d’e-mails + Brouillons (Brouillon) Vous êtes sur le point d’envoyer un message sans objet. Voulez-vous continuer ? diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 8fcb19c6db..e2e1af565d 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -235,6 +235,10 @@ La cancellazione dell’account sarà definitiva. Non sarà possibile riattivare l’account. Per risolvere i problemi di visualizzazione, aggiornare l’applicazione Android System Webview. Problema di visualizzazione delle email + + Download di %d e-mail + Download di %d email + Bozze (Bozza) Stai per inviare un messaggio senza oggetto. Vuoi continuare? diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index abbf4e4431..0d9172c0a7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -241,6 +241,10 @@ The deletion of your account will be final. You will not be able to reactivate your account. To fix display problems, please update the Android System Webview application. Problem displaying your emails + + Downloading %d emails + Downloading %d emails + Drafts (Draft) You are about to send a message without a subject. Do you want to continue? From 7075cad035cf8280d4ada9da0e3873c0adcfed53 Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Fri, 22 Nov 2024 09:30:21 +0100 Subject: [PATCH 16/29] feat: Add dialog name --- .../com/infomaniak/mail/ui/MainViewModel.kt | 4 ++++ .../actions/DownloadThreadsProgressDialog.kt | 23 +++++++++++++++---- .../actions/DownloadThreadsViewModel.kt | 6 +---- .../MessageActionsBottomSheetDialog.kt | 7 ++++-- .../actions/MultiSelectBottomSheetDialog.kt | 3 ++- .../actions/ThreadActionsBottomSheetDialog.kt | 1 + .../utils/extensions/NavigationExtensions.kt | 11 +++++++-- .../main/res/navigation/main_navigation.xml | 4 ++++ 8 files changed, 45 insertions(+), 14 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 3c4e673459..ab0e3c312b 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -1204,6 +1204,10 @@ class MainViewModel @Inject constructor( return messageUids } + fun getSubject(threadUuid: String): String? { + return threadController.getThread(threadUuid)?.subject + } + companion object { private val TAG: String = MainViewModel::class.java.simpleName private val DEFAULT_SELECTED_FOLDER = FolderRole.INBOX diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsProgressDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsProgressDialog.kt index 6efeaef269..263de5b674 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsProgressDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsProgressDialog.kt @@ -20,32 +20,47 @@ package com.infomaniak.mail.ui.main.thread.actions import android.app.Dialog import android.os.Bundle import android.view.KeyEvent +import androidx.core.view.isVisible import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.infomaniak.lib.core.R import com.infomaniak.lib.core.utils.SnackbarUtils.showSnackbar import com.infomaniak.lib.core.utils.setBackNavigationResult +import com.infomaniak.mail.R import com.infomaniak.mail.databinding.DialogDownloadProgressBinding import com.infomaniak.mail.ui.MainViewModel import com.infomaniak.mail.utils.extensions.AttachmentExtensions.openKDriveOrPlayStore import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch +import com.infomaniak.lib.core.R as RCore @AndroidEntryPoint class DownloadThreadsProgressDialog : DialogFragment() { private val binding by lazy { DialogDownloadProgressBinding.inflate(layoutInflater) } private val mainViewModel: MainViewModel by activityViewModels() private val downloadThreadsViewModel: DownloadThreadsViewModel by viewModels() + private val navigationArgs: DownloadThreadsProgressDialogArgs by navArgs() override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val context = requireContext() + isCancelable = false - return MaterialAlertDialogBuilder(requireContext()) - .setTitle("test") + binding.icon.isVisible = false + + val textTitleDialog = if (navigationArgs.messageUids.size == 1) { + navigationArgs.nameFirstMessage + } else { + context.resources.getQuantityString(R.plurals.downloadingEmailsTitle, 1, navigationArgs.messageUids.size) + // TODO LOOK + } + + return MaterialAlertDialogBuilder(context) + .setTitle(textTitleDialog) .setView(binding.root) .setOnKeyListener { _, keyCode, event -> if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) { @@ -76,7 +91,7 @@ class DownloadThreadsProgressDialog : DialogFragment() { private fun popBackStackWithError() { lifecycleScope.launch { mainViewModel.isNetworkAvailable.first { it != null }?.let { isNetworkAvailable -> - showSnackbar(title = if (isNetworkAvailable) R.string.anErrorHasOccurred else R.string.noConnection) + showSnackbar(title = if (isNetworkAvailable) RCore.string.anErrorHasOccurred else RCore.string.noConnection) findNavController().popBackStack() } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt index 9465d345c4..c3e04423d3 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt @@ -79,18 +79,14 @@ class DownloadThreadsViewModel @Inject constructor( var postfix = 1 var fileName = originalFileName - while (listFileName.contains(fileName)) { - fileName = "$originalFileName (${postfix++})" - } + while (listFileName.contains(fileName)) fileName = "$originalFileName (${postfix++})" return fileName } private fun getAllFileNameInExportEmlDir(context: Context): List { val fileDir = File(context.cacheDir, context.getString(com.infomaniak.mail.R.string.EXPOSED_EML_PATH)) - if (!fileDir.exists()) fileDir.mkdirs() - return fileDir.listFiles()?.map { it.name.removeSuffix(".eml") } ?: emptyList() } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt index 77b763818e..ba522992c8 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt @@ -18,7 +18,6 @@ package com.infomaniak.mail.ui.main.thread.actions import android.os.Bundle -import android.util.Log import android.view.View import androidx.core.view.isVisible import androidx.navigation.fragment.navArgs @@ -178,7 +177,11 @@ class MessageActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override fun onSaveKDrive() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) - navigateToDownloadThreadsProgressDialog(listOf(messageUid), MessageActionsBottomSheetDialog::class.java.name) + navigateToDownloadThreadsProgressDialog( + listOf(messageUid), + mainViewModel.getSubject(threadUid), + MessageActionsBottomSheetDialog::class.java.name + ) } override fun onReportDisplayProblem() { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt index 734381cf0b..311531ad51 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt @@ -122,7 +122,8 @@ class MultiSelectBottomSheetDialog : ActionsBottomSheetDialog() { trackMultiSelectActionEvent(ACTION_SAVE_KDRIVE_NAME, selectedThreadsCount, isFromBottomSheet = true) navigateToDownloadThreadsProgressDialog( messageUids = mainViewModel.getMessagesUidsFromThreadUids(selectedThreadsUids), - MultiSelectBottomSheetDialog::class.java.name + nameFirstMessage = selectedThreads.firstOrNull()?.subject, + MultiSelectBottomSheetDialog::class.java.name, ) isMultiSelectOn = false } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt index eee5e4b55c..8be61b14d5 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt @@ -200,6 +200,7 @@ class ThreadActionsBottomSheetDialog : MailActionsBottomSheetDialog() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) navigateToDownloadThreadsProgressDialog( messageUids = thread.messages.map { it.uid }, + nameFirstMessage = thread.subject, ThreadActionsBottomSheetDialog::class.java.name ) } diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt index a46104b194..80b061a7e2 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt @@ -91,10 +91,17 @@ fun Fragment.navigateToDownloadProgressDialog( ) } -fun Fragment.navigateToDownloadThreadsProgressDialog(messageUids: List, currentClassName: String) { +fun Fragment.navigateToDownloadThreadsProgressDialog( + messageUids: List, + nameFirstMessage: String?, + currentClassName: String +) { safeNavigate( resId = R.id.downloadThreadsProgressDialog, - args = DownloadThreadsProgressDialogArgs(messageUids = messageUids.toTypedArray()).toBundle(), + args = DownloadThreadsProgressDialogArgs( + messageUids = messageUids.toTypedArray(), + nameFirstMessage = nameFirstMessage + ).toBundle(), currentClassName = currentClassName, ) } diff --git a/app/src/main/res/navigation/main_navigation.xml b/app/src/main/res/navigation/main_navigation.xml index 0023a457d3..e682da501d 100644 --- a/app/src/main/res/navigation/main_navigation.xml +++ b/app/src/main/res/navigation/main_navigation.xml @@ -581,6 +581,10 @@ + Date: Fri, 22 Nov 2024 11:00:05 +0100 Subject: [PATCH 17/29] feat: Centralize shared code in abstract class --- .../DownloadAttachmentProgressDialog.kt | 53 +---------- .../thread/actions/DownloadProgressDialog.kt | 81 +++++++++++++++++ .../actions/DownloadThreadsProgressDialog.kt | 87 +++++++------------ .../com/infomaniak/mail/utils/KDriveUtils.kt | 35 ++++++++ .../utils/extensions/AttachmentExtensions.kt | 27 +----- 5 files changed, 155 insertions(+), 128 deletions(-) create mode 100644 app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadProgressDialog.kt create mode 100644 app/src/main/java/com/infomaniak/mail/utils/KDriveUtils.kt diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadAttachmentProgressDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadAttachmentProgressDialog.kt index c63933bb92..f34d0744fa 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadAttachmentProgressDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadAttachmentProgressDialog.kt @@ -17,59 +17,23 @@ */ package com.infomaniak.mail.ui.main.thread.actions -import android.app.Dialog -import android.os.Bundle -import android.view.KeyEvent -import androidx.appcompat.content.res.AppCompatResources -import androidx.fragment.app.DialogFragment -import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels -import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.infomaniak.lib.core.R -import com.infomaniak.lib.core.utils.SnackbarUtils.showSnackbar import com.infomaniak.lib.core.utils.setBackNavigationResult -import com.infomaniak.mail.databinding.DialogDownloadProgressBinding -import com.infomaniak.mail.ui.MainViewModel import com.infomaniak.mail.utils.extensions.AttachmentExtensions import com.infomaniak.mail.utils.extensions.AttachmentExtensions.getIntentOrGoToPlayStore import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch @AndroidEntryPoint -class DownloadAttachmentProgressDialog : DialogFragment() { - - private val binding by lazy { DialogDownloadProgressBinding.inflate(layoutInflater) } +class DownloadAttachmentProgressDialog : DownloadProgressDialog() { private val navigationArgs: DownloadAttachmentProgressDialogArgs by navArgs() - private val mainViewModel: MainViewModel by activityViewModels() private val downloadAttachmentViewModel: DownloadAttachmentViewModel by viewModels() - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - isCancelable = false - val iconDrawable = AppCompatResources.getDrawable(requireContext(), navigationArgs.attachmentType.icon) - binding.icon.setImageDrawable(iconDrawable) - - return MaterialAlertDialogBuilder(requireContext()) - .setTitle(navigationArgs.attachmentName) - .setView(binding.root) - .setOnKeyListener { _, keyCode, event -> - if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) { - findNavController().popBackStack() - true - } else false - } - .create() - } - - override fun onStart() { - super.onStart() - downloadAttachment() - } + override val dialogTitle: String? by lazy { navigationArgs.attachmentName } + override val dialogIconDrawableRes: Int? by lazy { navigationArgs.attachmentType.icon } - private fun downloadAttachment() { + override fun download() { downloadAttachmentViewModel.downloadAttachment().observe(this) { cachedAttachment -> if (cachedAttachment == null) { popBackStackWithError() @@ -80,13 +44,4 @@ class DownloadAttachmentProgressDialog : DialogFragment() { } } } - - private fun popBackStackWithError() { - lifecycleScope.launch { - mainViewModel.isNetworkAvailable.first { it != null }?.let { isNetworkAvailable -> - showSnackbar(title = if (isNetworkAvailable) R.string.anErrorHasOccurred else R.string.noConnection) - findNavController().popBackStack() - } - } - } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadProgressDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadProgressDialog.kt new file mode 100644 index 0000000000..36880ea3a5 --- /dev/null +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadProgressDialog.kt @@ -0,0 +1,81 @@ +/* + * Infomaniak Mail - Android + * Copyright (C) 2024 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.infomaniak.mail.ui.main.thread.actions + +import android.app.Dialog +import android.os.Bundle +import android.view.KeyEvent +import androidx.annotation.DrawableRes +import androidx.appcompat.content.res.AppCompatResources +import androidx.core.view.isVisible +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.findNavController +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.infomaniak.lib.core.R +import com.infomaniak.lib.core.utils.SnackbarUtils.showSnackbar +import com.infomaniak.mail.databinding.DialogDownloadProgressBinding +import com.infomaniak.mail.ui.MainViewModel +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch + +@AndroidEntryPoint +abstract class DownloadProgressDialog : DialogFragment() { + protected val binding by lazy { DialogDownloadProgressBinding.inflate(layoutInflater) } + protected val mainViewModel: MainViewModel by activityViewModels() + abstract val dialogTitle: String? + @get:DrawableRes abstract val dialogIconDrawableRes: Int? + + override fun onStart() { + download() + super.onStart() + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val context = requireContext() + + dialogIconDrawableRes?.let { binding.icon.setImageDrawable(AppCompatResources.getDrawable(requireContext(), it)) } + binding.icon.isVisible = dialogIconDrawableRes == null + + isCancelable = false + + return MaterialAlertDialogBuilder(context) + .setTitle(dialogTitle) + .setView(binding.root) + .setOnKeyListener { _, keyCode, event -> + if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) { + findNavController().popBackStack() + true + } else false + } + .create() + } + + abstract fun download() + + protected fun popBackStackWithError() { + lifecycleScope.launch { + mainViewModel.isNetworkAvailable.first { it != null }?.let { isNetworkAvailable -> + showSnackbar(title = if (isNetworkAvailable) R.string.anErrorHasOccurred else R.string.noConnection) + findNavController().popBackStack() + } + } + } +} diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsProgressDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsProgressDialog.kt index 263de5b674..b13af290c2 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsProgressDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsProgressDialog.kt @@ -17,66 +17,29 @@ */ package com.infomaniak.mail.ui.main.thread.actions -import android.app.Dialog -import android.os.Bundle -import android.view.KeyEvent -import androidx.core.view.isVisible -import androidx.fragment.app.DialogFragment -import androidx.fragment.app.activityViewModels +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.net.Uri import androidx.fragment.app.viewModels -import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.infomaniak.lib.core.utils.SnackbarUtils.showSnackbar +import com.infomaniak.lib.core.utils.goToPlayStore import com.infomaniak.lib.core.utils.setBackNavigationResult import com.infomaniak.mail.R -import com.infomaniak.mail.databinding.DialogDownloadProgressBinding -import com.infomaniak.mail.ui.MainViewModel -import com.infomaniak.mail.utils.extensions.AttachmentExtensions.openKDriveOrPlayStore +import com.infomaniak.mail.utils.KDriveUtils.DRIVE_PACKAGE +import com.infomaniak.mail.utils.KDriveUtils.SAVE_EXTERNAL_ACTIVITY_CLASS +import com.infomaniak.mail.utils.KDriveUtils.canSaveOnKDrive import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch -import com.infomaniak.lib.core.R as RCore @AndroidEntryPoint -class DownloadThreadsProgressDialog : DialogFragment() { - private val binding by lazy { DialogDownloadProgressBinding.inflate(layoutInflater) } - private val mainViewModel: MainViewModel by activityViewModels() +class DownloadThreadsProgressDialog : DownloadProgressDialog() { private val downloadThreadsViewModel: DownloadThreadsViewModel by viewModels() private val navigationArgs: DownloadThreadsProgressDialogArgs by navArgs() + override val dialogTitle: String? by lazy { getDialogTitleFromArgs() } + override val dialogIconDrawableRes: Int? by lazy { null } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val context = requireContext() - - isCancelable = false - binding.icon.isVisible = false - - val textTitleDialog = if (navigationArgs.messageUids.size == 1) { - navigationArgs.nameFirstMessage - } else { - context.resources.getQuantityString(R.plurals.downloadingEmailsTitle, 1, navigationArgs.messageUids.size) - // TODO LOOK - } - - return MaterialAlertDialogBuilder(context) - .setTitle(textTitleDialog) - .setView(binding.root) - .setOnKeyListener { _, keyCode, event -> - if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) { - findNavController().popBackStack() - true - } else false - } - .create() - } - - override fun onStart() { - downloadThreads() - super.onStart() - } - - private fun downloadThreads() { + override fun download() { downloadThreadsViewModel.downloadThreads(mainViewModel.currentMailbox.value).observe(this) { threadUris -> if (threadUris == null) { popBackStackWithError() @@ -88,12 +51,26 @@ class DownloadThreadsProgressDialog : DialogFragment() { } } - private fun popBackStackWithError() { - lifecycleScope.launch { - mainViewModel.isNetworkAvailable.first { it != null }?.let { isNetworkAvailable -> - showSnackbar(title = if (isNetworkAvailable) RCore.string.anErrorHasOccurred else RCore.string.noConnection) - findNavController().popBackStack() - } + private fun getDialogTitleFromArgs() = if (navigationArgs.messageUids.size == 1) { + navigationArgs.nameFirstMessage + } else { + requireContext().resources.getQuantityString(R.plurals.downloadingEmailsTitle, 1, navigationArgs.messageUids.size) + } + + private fun ArrayList.openKDriveOrPlayStore(context: Context): Intent? { + return if (canSaveOnKDrive(context)) { + saveToDriveIntent() + } else { + context.goToPlayStore(DRIVE_PACKAGE) + null + } + } + + private fun ArrayList.saveToDriveIntent(): Intent { + return Intent().apply { + component = ComponentName(DRIVE_PACKAGE, SAVE_EXTERNAL_ACTIVITY_CLASS) + action = Intent.ACTION_SEND_MULTIPLE + putParcelableArrayListExtra(Intent.EXTRA_STREAM, this@saveToDriveIntent) } } diff --git a/app/src/main/java/com/infomaniak/mail/utils/KDriveUtils.kt b/app/src/main/java/com/infomaniak/mail/utils/KDriveUtils.kt new file mode 100644 index 0000000000..98029ef026 --- /dev/null +++ b/app/src/main/java/com/infomaniak/mail/utils/KDriveUtils.kt @@ -0,0 +1,35 @@ +/* + * Infomaniak Mail - Android + * Copyright (C) 2024 Infomaniak Network SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.infomaniak.mail.utils + +import android.content.Context +import android.content.pm.PackageManager +import io.sentry.Sentry + +object KDriveUtils { + const val DRIVE_PACKAGE = "com.infomaniak.drive" + const val SAVE_EXTERNAL_ACTIVITY_CLASS = "com.infomaniak.drive.ui.SaveExternalFilesActivity" + + fun canSaveOnKDrive(context: Context) = runCatching { + val packageInfo = context.packageManager.getPackageInfo(DRIVE_PACKAGE, PackageManager.GET_ACTIVITIES) + packageInfo.activities.any { it.name == SAVE_EXTERNAL_ACTIVITY_CLASS } + }.getOrElse { + Sentry.captureException(it) + false + } +} diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt index 6ae3cd235b..55cd6611d7 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt @@ -20,8 +20,6 @@ package com.infomaniak.mail.utils.extensions import android.content.ComponentName import android.content.Context import android.content.Intent -import android.content.pm.PackageManager -import android.net.Uri import android.os.Bundle import android.provider.MediaStore.Files.FileColumns import androidx.core.content.FileProvider @@ -39,6 +37,9 @@ import com.infomaniak.mail.data.models.mailbox.Mailbox import com.infomaniak.mail.ui.main.SnackbarManager import com.infomaniak.mail.ui.main.thread.actions.DownloadAttachmentProgressDialogArgs import com.infomaniak.mail.utils.AccountUtils +import com.infomaniak.mail.utils.KDriveUtils.DRIVE_PACKAGE +import com.infomaniak.mail.utils.KDriveUtils.SAVE_EXTERNAL_ACTIVITY_CLASS +import com.infomaniak.mail.utils.KDriveUtils.canSaveOnKDrive import com.infomaniak.mail.utils.SentryDebug import com.infomaniak.mail.utils.WorkerUtils.UploadMissingLocalFileException import com.infomaniak.mail.utils.extensions.AttachmentExtensions.AttachmentIntentType.OPEN_WITH @@ -56,18 +57,6 @@ object AttachmentExtensions { const val ATTACHMENT_TAG = "attachmentUpload" const val DOWNLOAD_ATTACHMENT_RESULT = "download_attachment_result" - private const val DRIVE_PACKAGE = "com.infomaniak.drive" - private const val SAVE_EXTERNAL_ACTIVITY_CLASS = "com.infomaniak.drive.ui.SaveExternalFilesActivity" - - fun ArrayList.openKDriveOrPlayStore(context: Context): Intent? { - return if (canSaveOnKDrive(context)) { - saveToDriveIntent() - } else { - context.goToPlayStore(DRIVE_PACKAGE) - null - } - } - //region Intent private fun canSaveOnKDrive(context: Context) = runCatching { val packageInfo = context.packageManager.getPackageInfo(DRIVE_PACKAGE, PackageManager.GET_ACTIVITIES) @@ -91,14 +80,6 @@ object AttachmentExtensions { } } - private fun ArrayList.saveToDriveIntent(): Intent { - return Intent().apply { - component = ComponentName(DRIVE_PACKAGE, SAVE_EXTERNAL_ACTIVITY_CLASS) - action = Intent.ACTION_SEND_MULTIPLE - putParcelableArrayListExtra(Intent.EXTRA_STREAM, this@saveToDriveIntent) - } - } - private fun Attachment.openWithIntent(context: Context): Intent { val file = getUploadLocalFile() ?: getCacheFile(context) val uri = FileProvider.getUriForFile(context, context.getString(R.string.ATTACHMENTS_AUTHORITY), file) @@ -120,8 +101,6 @@ object AttachmentExtensions { } } - - fun Attachment.executeIntent( context: Context, intentType: AttachmentIntentType, From 32d64e63835c97acb4cabdb62b02ae12fc52fac7 Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Fri, 22 Nov 2024 12:44:27 +0100 Subject: [PATCH 18/29] refactor: Rename for better clarity --- .../java/com/infomaniak/mail/data/api/ApiRepository.kt | 2 +- .../main/java/com/infomaniak/mail/ui/MainViewModel.kt | 3 +-- .../infomaniak/mail/ui/main/folder/TwoPaneFragment.kt | 4 ++-- ...ressDialog.kt => DownloadMessagesProgressDialog.kt} | 10 +++++----- ...hreadsViewModel.kt => DownloadMessagesViewModel.kt} | 6 +++--- .../thread/actions/MessageActionsBottomSheetDialog.kt | 2 +- .../thread/actions/MultiSelectBottomSheetDialog.kt | 4 ++-- .../thread/actions/ThreadActionsBottomSheetDialog.kt | 2 +- .../mail/utils/extensions/NavigationExtensions.kt | 8 ++++---- app/src/main/res/navigation/main_navigation.xml | 6 +++--- 10 files changed, 23 insertions(+), 24 deletions(-) rename app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/{DownloadThreadsProgressDialog.kt => DownloadMessagesProgressDialog.kt} (88%) rename app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/{DownloadThreadsViewModel.kt => DownloadMessagesViewModel.kt} (95%) diff --git a/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt b/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt index e7ca860488..046d6330a3 100644 --- a/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt +++ b/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt @@ -435,7 +435,7 @@ object ApiRepository : ApiRepositoryCore() { return callApi(url = ApiRoutes.shareLink(mailboxUuid, folderId, mailId), method = POST) } - fun getDownloadedAttachment(mailboxUuid: String, folderId: String, shortUid: Int): Response { + fun getDownloadedMessage(mailboxUuid: String, folderId: String, shortUid: Int): Response { val request = Request.Builder().url(ApiRoutes.downloadMessage(mailboxUuid, folderId, shortUid)) .headers(HttpUtils.getHeaders(null)) .get() 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 ab0e3c312b..1c9cebcfa0 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -923,7 +923,7 @@ class MainViewModel @Inject constructor( val message = messageController.getMessage(messageUid) ?: return@launch val mailbox = currentMailbox.value ?: return@launch - val response = ApiRepository.getDownloadedAttachment(mailbox.uuid, message.folderId, message.shortUid) + val response = ApiRepository.getDownloadedMessage(mailbox.uuid, message.folderId, message.shortUid) if (!response.isSuccessful || response.body == null) { reportDisplayProblemTrigger.postValue(Unit) @@ -1213,7 +1213,6 @@ class MainViewModel @Inject constructor( private val DEFAULT_SELECTED_FOLDER = FolderRole.INBOX private const val REFRESH_DELAY = 2_000L // We add this delay because `etop` isn't always big enough. private const val MAX_REFRESH_DELAY = 6_000L - private const val EML_CONTENT_TYPE = "message/rfc822" } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folder/TwoPaneFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folder/TwoPaneFragment.kt index 48bf4d3edb..2f46055afc 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folder/TwoPaneFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folder/TwoPaneFragment.kt @@ -41,7 +41,7 @@ import com.infomaniak.mail.ui.MainActivity import com.infomaniak.mail.ui.MainViewModel import com.infomaniak.mail.ui.main.search.SearchFragment import com.infomaniak.mail.ui.main.thread.ThreadFragment -import com.infomaniak.mail.ui.main.thread.actions.DownloadThreadsProgressDialog.Companion.DOWNLOAD_THREADS_RESULT +import com.infomaniak.mail.ui.main.thread.actions.DownloadMessagesProgressDialog import com.infomaniak.mail.utils.extensions.* import javax.inject.Inject @@ -121,7 +121,7 @@ abstract class TwoPaneFragment : Fragment() { private fun observeThreadNavigation() = with(twoPaneViewModel) { getBackNavigationResult(AttachmentExtensions.DOWNLOAD_ATTACHMENT_RESULT, ::startActivity) - getBackNavigationResult(DOWNLOAD_THREADS_RESULT, ::startActivity) + getBackNavigationResult(DownloadMessagesProgressDialog.DOWNLOAD_MESSAGES_RESULT, ::startActivity) newMessageArgs.observe(viewLifecycleOwner) { safeNavigateToNewMessageActivity(args = it.toBundle()) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsProgressDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesProgressDialog.kt similarity index 88% rename from app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsProgressDialog.kt rename to app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesProgressDialog.kt index b13af290c2..30f0ad574f 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsProgressDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesProgressDialog.kt @@ -33,9 +33,9 @@ import com.infomaniak.mail.utils.KDriveUtils.canSaveOnKDrive import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint -class DownloadThreadsProgressDialog : DownloadProgressDialog() { - private val downloadThreadsViewModel: DownloadThreadsViewModel by viewModels() - private val navigationArgs: DownloadThreadsProgressDialogArgs by navArgs() +class DownloadMessagesProgressDialog : DownloadProgressDialog() { + private val downloadThreadsViewModel: DownloadMessagesViewModel by viewModels() + private val navigationArgs: DownloadMessagesProgressDialogArgs by navArgs() override val dialogTitle: String? by lazy { getDialogTitleFromArgs() } override val dialogIconDrawableRes: Int? by lazy { null } @@ -45,7 +45,7 @@ class DownloadThreadsProgressDialog : DownloadProgressDialog() { popBackStackWithError() } else { ArrayList(threadUris).openKDriveOrPlayStore(requireContext())?.let { openKDriveIntent -> - setBackNavigationResult(DOWNLOAD_THREADS_RESULT, openKDriveIntent) + setBackNavigationResult(DOWNLOAD_MESSAGES_RESULT, openKDriveIntent) } ?: run { findNavController().popBackStack() } } } @@ -75,6 +75,6 @@ class DownloadThreadsProgressDialog : DownloadProgressDialog() { } companion object { - const val DOWNLOAD_THREADS_RESULT = "download_threads_result" + const val DOWNLOAD_MESSAGES_RESULT = "download_messages_result" } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt similarity index 95% rename from app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt rename to app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt index c3e04423d3..b87e6c8a96 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadThreadsViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt @@ -38,7 +38,7 @@ import java.io.File import javax.inject.Inject @HiltViewModel -class DownloadThreadsViewModel @Inject constructor( +class DownloadMessagesViewModel @Inject constructor( application: Application, private val savedStateHandle: SavedStateHandle, private val messageController: MessageController, @@ -48,7 +48,7 @@ class DownloadThreadsViewModel @Inject constructor( private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher) private val messageLocalUids - inline get() = savedStateHandle.get>(DownloadThreadsProgressDialogArgs::messageUids.name)!! + inline get() = savedStateHandle.get>(DownloadMessagesProgressDialogArgs::messageUids.name)!! fun downloadThreads(currentMailbox: Mailbox?) = liveData(ioCoroutineContext) { val downloadedThreadUris: List? = runCatching { @@ -58,7 +58,7 @@ class DownloadThreadsViewModel @Inject constructor( messageLocalUids.forEach { messageUid -> val message = messageController.getMessage(messageUid) ?: return@runCatching null - val response = ApiRepository.getDownloadedAttachment(mailbox.uuid, message.folderId, message.shortUid) + val response = ApiRepository.getDownloadedMessage(mailbox.uuid, message.folderId, message.shortUid) if (!response.isSuccessful || response.body == null) return@runCatching null diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt index ba522992c8..ba3eb5a302 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt @@ -177,7 +177,7 @@ class MessageActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override fun onSaveKDrive() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) - navigateToDownloadThreadsProgressDialog( + navigateToDownloadMessagesProgressDialog( listOf(messageUid), mainViewModel.getSubject(threadUid), MessageActionsBottomSheetDialog::class.java.name diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt index 311531ad51..5c2eb916ab 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt @@ -41,7 +41,7 @@ import com.infomaniak.mail.ui.main.folder.ThreadListMultiSelection import com.infomaniak.mail.ui.main.folder.ThreadListMultiSelection.Companion.getReadIconAndShortText import com.infomaniak.mail.utils.extensions.animatedNavigation import com.infomaniak.mail.utils.extensions.deleteWithConfirmationPopup -import com.infomaniak.mail.utils.extensions.navigateToDownloadThreadsProgressDialog +import com.infomaniak.mail.utils.extensions.navigateToDownloadMessagesProgressDialog import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -120,7 +120,7 @@ class MultiSelectBottomSheetDialog : ActionsBottomSheetDialog() { } binding.saveKDrive.setClosingOnClickListener(shouldCloseMultiSelection = true) { trackMultiSelectActionEvent(ACTION_SAVE_KDRIVE_NAME, selectedThreadsCount, isFromBottomSheet = true) - navigateToDownloadThreadsProgressDialog( + navigateToDownloadMessagesProgressDialog( messageUids = mainViewModel.getMessagesUidsFromThreadUids(selectedThreadsUids), nameFirstMessage = selectedThreads.firstOrNull()?.subject, MultiSelectBottomSheetDialog::class.java.name, diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt index 8be61b14d5..1f2bd418d5 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt @@ -198,7 +198,7 @@ class ThreadActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override fun onSaveKDrive() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) - navigateToDownloadThreadsProgressDialog( + navigateToDownloadMessagesProgressDialog( messageUids = thread.messages.map { it.uid }, nameFirstMessage = thread.subject, ThreadActionsBottomSheetDialog::class.java.name diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt index 80b061a7e2..95659dd45b 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt @@ -36,7 +36,7 @@ import com.infomaniak.mail.ui.login.LoginActivity import com.infomaniak.mail.ui.login.LoginActivityArgs import com.infomaniak.mail.ui.login.NoMailboxActivity import com.infomaniak.mail.ui.main.thread.actions.AttachmentActionsBottomSheetDialog -import com.infomaniak.mail.ui.main.thread.actions.DownloadThreadsProgressDialogArgs +import com.infomaniak.mail.ui.main.thread.actions.DownloadMessagesProgressDialogArgs import com.infomaniak.mail.ui.newMessage.NewMessageActivityArgs import com.infomaniak.mail.ui.noValidMailboxes.NoValidMailboxesActivity import com.infomaniak.mail.utils.AccountUtils @@ -91,14 +91,14 @@ fun Fragment.navigateToDownloadProgressDialog( ) } -fun Fragment.navigateToDownloadThreadsProgressDialog( +fun Fragment.navigateToDownloadMessagesProgressDialog( messageUids: List, nameFirstMessage: String?, currentClassName: String ) { safeNavigate( - resId = R.id.downloadThreadsProgressDialog, - args = DownloadThreadsProgressDialogArgs( + resId = R.id.downloadMessagesProgressDialog, + args = DownloadMessagesProgressDialogArgs( messageUids = messageUids.toTypedArray(), nameFirstMessage = nameFirstMessage ).toBundle(), diff --git a/app/src/main/res/navigation/main_navigation.xml b/app/src/main/res/navigation/main_navigation.xml index e682da501d..a8a76ef0cf 100644 --- a/app/src/main/res/navigation/main_navigation.xml +++ b/app/src/main/res/navigation/main_navigation.xml @@ -574,9 +574,9 @@ Date: Fri, 22 Nov 2024 15:43:43 +0100 Subject: [PATCH 19/29] refactor: Apply suggestion from code review --- .../infomaniak/mail/data/api/ApiRepository.kt | 2 +- .../com/infomaniak/mail/ui/MainViewModel.kt | 9 ++++--- .../DownloadAttachmentProgressDialog.kt | 14 ++++++++-- .../actions/DownloadMessagesProgressDialog.kt | 15 ++++++----- .../actions/DownloadMessagesViewModel.kt | 26 ++++++++++++------- .../thread/actions/DownloadProgressDialog.kt | 14 +++------- .../MessageActionsBottomSheetDialog.kt | 6 ++--- .../actions/MultiSelectBottomSheetDialog.kt | 20 ++++++++++---- .../actions/ThreadActionsBottomSheetDialog.kt | 2 +- .../{KDriveUtils.kt => SaveOnKDriveUtils.kt} | 2 +- .../utils/extensions/AttachmentExtensions.kt | 6 ++--- .../utils/extensions/NavigationExtensions.kt | 4 +-- .../res/layout/dialog_download_progress.xml | 1 + .../main/res/navigation/main_navigation.xml | 3 +-- 14 files changed, 73 insertions(+), 51 deletions(-) rename app/src/main/java/com/infomaniak/mail/utils/{KDriveUtils.kt => SaveOnKDriveUtils.kt} (98%) diff --git a/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt b/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt index 046d6330a3..2afd20b031 100644 --- a/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt +++ b/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt @@ -437,7 +437,7 @@ object ApiRepository : ApiRepositoryCore() { fun getDownloadedMessage(mailboxUuid: String, folderId: String, shortUid: Int): Response { val request = Request.Builder().url(ApiRoutes.downloadMessage(mailboxUuid, folderId, shortUid)) - .headers(HttpUtils.getHeaders(null)) + .headers(HttpUtils.getHeaders()) .get() .build() 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 1c9cebcfa0..283dd33a43 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -1195,17 +1195,18 @@ class MainViewModel @Inject constructor( } } - fun getMessagesUidsFromThreadUids(selectedThreadsUuids: List): List { + fun getMessagesUidsFromThreadUids(selectedThreadsUids: List): List { val messageUids = mutableListOf() - selectedThreadsUuids.forEach { threadUuid -> + selectedThreadsUids.forEach { threadUuid -> val thread = threadController.getThread(threadUuid) ?: return@forEach messageUids.addAll(thread.messages.map { it.uid }) } + return messageUids } - fun getSubject(threadUuid: String): String? { - return threadController.getThread(threadUuid)?.subject + fun getSubject(threadUid: String): String { + return threadController.getThread(threadUid)?.subject ?: "" } companion object { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadAttachmentProgressDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadAttachmentProgressDialog.kt index f34d0744fa..1c0e40f487 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadAttachmentProgressDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadAttachmentProgressDialog.kt @@ -17,6 +17,12 @@ */ package com.infomaniak.mail.ui.main.thread.actions +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.content.res.AppCompatResources +import androidx.core.view.isVisible import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs @@ -25,13 +31,17 @@ import com.infomaniak.mail.utils.extensions.AttachmentExtensions import com.infomaniak.mail.utils.extensions.AttachmentExtensions.getIntentOrGoToPlayStore import dagger.hilt.android.AndroidEntryPoint -@AndroidEntryPoint class DownloadAttachmentProgressDialog : DownloadProgressDialog() { private val navigationArgs: DownloadAttachmentProgressDialogArgs by navArgs() private val downloadAttachmentViewModel: DownloadAttachmentViewModel by viewModels() override val dialogTitle: String? by lazy { navigationArgs.attachmentName } - override val dialogIconDrawableRes: Int? by lazy { navigationArgs.attachmentType.icon } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + binding.icon.isVisible = true + binding.icon.setImageDrawable(AppCompatResources.getDrawable(requireContext(), navigationArgs.attachmentType.icon)) + return super.onCreateView(inflater, container, savedInstanceState) + } override fun download() { downloadAttachmentViewModel.downloadAttachment().observe(this) { cachedAttachment -> diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesProgressDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesProgressDialog.kt index 30f0ad574f..de5068dd36 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesProgressDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesProgressDialog.kt @@ -27,17 +27,14 @@ import androidx.navigation.fragment.navArgs import com.infomaniak.lib.core.utils.goToPlayStore import com.infomaniak.lib.core.utils.setBackNavigationResult import com.infomaniak.mail.R -import com.infomaniak.mail.utils.KDriveUtils.DRIVE_PACKAGE -import com.infomaniak.mail.utils.KDriveUtils.SAVE_EXTERNAL_ACTIVITY_CLASS -import com.infomaniak.mail.utils.KDriveUtils.canSaveOnKDrive -import dagger.hilt.android.AndroidEntryPoint +import com.infomaniak.mail.utils.SaveOnKDriveUtils.DRIVE_PACKAGE +import com.infomaniak.mail.utils.SaveOnKDriveUtils.SAVE_EXTERNAL_ACTIVITY_CLASS +import com.infomaniak.mail.utils.SaveOnKDriveUtils.canSaveOnKDrive -@AndroidEntryPoint class DownloadMessagesProgressDialog : DownloadProgressDialog() { private val downloadThreadsViewModel: DownloadMessagesViewModel by viewModels() private val navigationArgs: DownloadMessagesProgressDialogArgs by navArgs() override val dialogTitle: String? by lazy { getDialogTitleFromArgs() } - override val dialogIconDrawableRes: Int? by lazy { null } override fun download() { downloadThreadsViewModel.downloadThreads(mainViewModel.currentMailbox.value).observe(this) { threadUris -> @@ -54,7 +51,11 @@ class DownloadMessagesProgressDialog : DownloadProgressDialog() { private fun getDialogTitleFromArgs() = if (navigationArgs.messageUids.size == 1) { navigationArgs.nameFirstMessage } else { - requireContext().resources.getQuantityString(R.plurals.downloadingEmailsTitle, 1, navigationArgs.messageUids.size) + requireContext().resources.getQuantityString( + R.plurals.downloadingEmailsTitle, + navigationArgs.messageUids.size, + navigationArgs.messageUids.size + ) } private fun ArrayList.openKDriveOrPlayStore(context: Context): Intent? { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt index b87e6c8a96..9152a93e45 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt @@ -26,6 +26,7 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.liveData import androidx.lifecycle.viewModelScope import com.infomaniak.lib.core.utils.DownloadManagerUtils +import com.infomaniak.mail.R import com.infomaniak.mail.data.api.ApiRepository import com.infomaniak.mail.data.cache.mailboxContent.MessageController import com.infomaniak.mail.data.models.mailbox.Mailbox @@ -47,23 +48,28 @@ class DownloadMessagesViewModel @Inject constructor( private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher) - private val messageLocalUids - inline get() = savedStateHandle.get>(DownloadMessagesProgressDialogArgs::messageUids.name)!! + private val messageLocalUids: Array + inline get() = savedStateHandle[DownloadMessagesProgressDialogArgs::messageUids.name]!! fun downloadThreads(currentMailbox: Mailbox?) = liveData(ioCoroutineContext) { val downloadedThreadUris: List? = runCatching { val mailbox = currentMailbox ?: return@runCatching null val listUri = mutableListOf() - val listFileName = mutableSetOf().also { it.addAll(getAllFileNameInExportEmlDir(appContext)) } + val listFileName = getAllFileNameInExportEmlDir(appContext).toMutableSet() messageLocalUids.forEach { messageUid -> val message = messageController.getMessage(messageUid) ?: return@runCatching null - val response = ApiRepository.getDownloadedMessage(mailbox.uuid, message.folderId, message.shortUid) + val response = ApiRepository.getDownloadedMessage( + mailboxUuid = mailbox.uuid, + folderId = message.folderId, + shortUid = message.shortUid, + contentType = EML_CONTENT_TYPE, + ) if (!response.isSuccessful || response.body == null) return@runCatching null val messageSubject: String = message.subject?.removeIllegalFileNameCharacter() ?: NO_SUBJECT_FILE - createOriginalFileName(messageSubject, listFileName.toList()).let { fileName -> + createUniqueFileName(messageSubject, listFileName.toList()).let { fileName -> listFileName.add(fileName) saveEmlToFile(appContext, response.body!!.bytes(), fileName)?.let { listUri.add(it) } } @@ -75,7 +81,7 @@ class DownloadMessagesViewModel @Inject constructor( } // TODO Extract this code in core2 - private fun createOriginalFileName(originalFileName: String, listFileName: List): String { + private fun createUniqueFileName(originalFileName: String, listFileName: List): String { var postfix = 1 var fileName = originalFileName @@ -85,21 +91,21 @@ class DownloadMessagesViewModel @Inject constructor( } private fun getAllFileNameInExportEmlDir(context: Context): List { - val fileDir = File(context.cacheDir, context.getString(com.infomaniak.mail.R.string.EXPOSED_EML_PATH)) + val fileDir = File(context.cacheDir, context.getString(R.string.EXPOSED_EML_PATH)) if (!fileDir.exists()) fileDir.mkdirs() return fileDir.listFiles()?.map { it.name.removeSuffix(".eml") } ?: emptyList() } private fun saveEmlToFile(context: Context, emlByteArray: ByteArray, fileName: String): Uri? { val fileNameWithExtension = "${fileName.removeIllegalFileNameCharacter()}.eml" - val fileDir = File(context.cacheDir, context.getString(com.infomaniak.mail.R.string.EXPOSED_EML_PATH)) + val fileDir = File(context.cacheDir, context.getString(R.string.EXPOSED_EML_PATH)) if (!fileDir.exists()) fileDir.mkdirs() runCatching { val file = File(fileDir, fileNameWithExtension) file.outputStream().use { it.write(emlByteArray) } - return FileProvider.getUriForFile(context, context.getString(com.infomaniak.mail.R.string.EML_AUTHORITY), file) + return FileProvider.getUriForFile(context, context.getString(R.string.EML_AUTHORITY), file) }.onFailure { exception -> exception.printStackTrace() } @@ -109,7 +115,7 @@ class DownloadMessagesViewModel @Inject constructor( private fun String.removeIllegalFileNameCharacter(): String = this.replace(DownloadManagerUtils.regexInvalidSystemChar, "") companion object { - private const val EML_CONTENT_TYPE = "message/rfc822" private const val NO_SUBJECT_FILE = "message" + private const val EML_CONTENT_TYPE = "message/rfc822" } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadProgressDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadProgressDialog.kt index 36880ea3a5..7334080963 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadProgressDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadProgressDialog.kt @@ -20,9 +20,6 @@ package com.infomaniak.mail.ui.main.thread.actions import android.app.Dialog import android.os.Bundle import android.view.KeyEvent -import androidx.annotation.DrawableRes -import androidx.appcompat.content.res.AppCompatResources -import androidx.core.view.isVisible import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope @@ -38,10 +35,13 @@ import kotlinx.coroutines.launch @AndroidEntryPoint abstract class DownloadProgressDialog : DialogFragment() { + protected val binding by lazy { DialogDownloadProgressBinding.inflate(layoutInflater) } protected val mainViewModel: MainViewModel by activityViewModels() + abstract val dialogTitle: String? - @get:DrawableRes abstract val dialogIconDrawableRes: Int? + + protected abstract fun download() override fun onStart() { download() @@ -50,10 +50,6 @@ abstract class DownloadProgressDialog : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val context = requireContext() - - dialogIconDrawableRes?.let { binding.icon.setImageDrawable(AppCompatResources.getDrawable(requireContext(), it)) } - binding.icon.isVisible = dialogIconDrawableRes == null - isCancelable = false return MaterialAlertDialogBuilder(context) @@ -68,8 +64,6 @@ abstract class DownloadProgressDialog : DialogFragment() { .create() } - abstract fun download() - protected fun popBackStackWithError() { lifecycleScope.launch { mainViewModel.isNetworkAvailable.first { it != null }?.let { isNetworkAvailable -> diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt index ba3eb5a302..8525235575 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt @@ -178,9 +178,9 @@ class MessageActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override fun onSaveKDrive() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) navigateToDownloadMessagesProgressDialog( - listOf(messageUid), - mainViewModel.getSubject(threadUid), - MessageActionsBottomSheetDialog::class.java.name + messageUids = listOf(messageUid), + nameFirstMessage = mainViewModel.getSubject(threadUid), + currentClassName = MessageActionsBottomSheetDialog::class.java.name, ) } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt index 5c2eb916ab..089c687d64 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt @@ -22,6 +22,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.activityViewModels +import com.infomaniak.lib.core.utils.SentryLog import com.infomaniak.lib.core.utils.safeBinding import com.infomaniak.mail.MatomoMail.ACTION_ARCHIVE_NAME import com.infomaniak.mail.MatomoMail.ACTION_DELETE_NAME @@ -118,13 +119,18 @@ class MultiSelectBottomSheetDialog : ActionsBottomSheetDialog() { toggleThreadsFavoriteStatus(selectedThreadsUids, shouldFavorite) isMultiSelectOn = false } + binding.saveKDrive.setClosingOnClickListener(shouldCloseMultiSelection = true) { trackMultiSelectActionEvent(ACTION_SAVE_KDRIVE_NAME, selectedThreadsCount, isFromBottomSheet = true) - navigateToDownloadMessagesProgressDialog( - messageUids = mainViewModel.getMessagesUidsFromThreadUids(selectedThreadsUids), - nameFirstMessage = selectedThreads.firstOrNull()?.subject, - MultiSelectBottomSheetDialog::class.java.name, - ) + runCatching { + navigateToDownloadMessagesProgressDialog( + messageUids = mainViewModel.getMessagesUidsFromThreadUids(selectedThreadsUids), + nameFirstMessage = mainViewModel.getSubject(selectedThreadsUids.first()), + currentClassName = MultiSelectBottomSheetDialog::class.java.name, + ) + }.onFailure { + SentryLog.e(TAG, "SelectedThreadUids is empty, it should not happened") + } isMultiSelectOn = false } } @@ -154,4 +160,8 @@ class MultiSelectBottomSheetDialog : ActionsBottomSheetDialog() { private fun getSpamIconAndText(isFromSpam: Boolean): Pair { return if (isFromSpam) R.drawable.ic_non_spam to R.string.actionNonSpam else R.drawable.ic_spam to R.string.actionSpam } + + companion object { + private val TAG = this::class.java.simpleName + } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt index 1f2bd418d5..b94c8e9657 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt @@ -200,7 +200,7 @@ class ThreadActionsBottomSheetDialog : MailActionsBottomSheetDialog() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) navigateToDownloadMessagesProgressDialog( messageUids = thread.messages.map { it.uid }, - nameFirstMessage = thread.subject, + nameFirstMessage = thread.subject ?: "", ThreadActionsBottomSheetDialog::class.java.name ) } diff --git a/app/src/main/java/com/infomaniak/mail/utils/KDriveUtils.kt b/app/src/main/java/com/infomaniak/mail/utils/SaveOnKDriveUtils.kt similarity index 98% rename from app/src/main/java/com/infomaniak/mail/utils/KDriveUtils.kt rename to app/src/main/java/com/infomaniak/mail/utils/SaveOnKDriveUtils.kt index 98029ef026..095b09e1cb 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/KDriveUtils.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/SaveOnKDriveUtils.kt @@ -21,7 +21,7 @@ import android.content.Context import android.content.pm.PackageManager import io.sentry.Sentry -object KDriveUtils { +object SaveOnKDriveUtils { const val DRIVE_PACKAGE = "com.infomaniak.drive" const val SAVE_EXTERNAL_ACTIVITY_CLASS = "com.infomaniak.drive.ui.SaveExternalFilesActivity" diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt index 55cd6611d7..67344b77e9 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt @@ -37,9 +37,9 @@ import com.infomaniak.mail.data.models.mailbox.Mailbox import com.infomaniak.mail.ui.main.SnackbarManager import com.infomaniak.mail.ui.main.thread.actions.DownloadAttachmentProgressDialogArgs import com.infomaniak.mail.utils.AccountUtils -import com.infomaniak.mail.utils.KDriveUtils.DRIVE_PACKAGE -import com.infomaniak.mail.utils.KDriveUtils.SAVE_EXTERNAL_ACTIVITY_CLASS -import com.infomaniak.mail.utils.KDriveUtils.canSaveOnKDrive +import com.infomaniak.mail.utils.SaveOnKDriveUtils.DRIVE_PACKAGE +import com.infomaniak.mail.utils.SaveOnKDriveUtils.SAVE_EXTERNAL_ACTIVITY_CLASS +import com.infomaniak.mail.utils.SaveOnKDriveUtils.canSaveOnKDrive import com.infomaniak.mail.utils.SentryDebug import com.infomaniak.mail.utils.WorkerUtils.UploadMissingLocalFileException import com.infomaniak.mail.utils.extensions.AttachmentExtensions.AttachmentIntentType.OPEN_WITH diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt index 95659dd45b..6f84e84da9 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt @@ -93,8 +93,8 @@ fun Fragment.navigateToDownloadProgressDialog( fun Fragment.navigateToDownloadMessagesProgressDialog( messageUids: List, - nameFirstMessage: String?, - currentClassName: String + nameFirstMessage: String, + currentClassName: String, ) { safeNavigate( resId = R.id.downloadMessagesProgressDialog, diff --git a/app/src/main/res/layout/dialog_download_progress.xml b/app/src/main/res/layout/dialog_download_progress.xml index a6e6ebcc20..d496f61e0e 100644 --- a/app/src/main/res/layout/dialog_download_progress.xml +++ b/app/src/main/res/layout/dialog_download_progress.xml @@ -29,6 +29,7 @@ android:layout_marginStart="@dimen/marginStandardMedium" android:importantForAccessibility="no" android:src="@drawable/ic_file_unknown" + android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> diff --git a/app/src/main/res/navigation/main_navigation.xml b/app/src/main/res/navigation/main_navigation.xml index a8a76ef0cf..83445dd994 100644 --- a/app/src/main/res/navigation/main_navigation.xml +++ b/app/src/main/res/navigation/main_navigation.xml @@ -583,8 +583,7 @@ app:argType="string[]" /> + app:argType="string" /> Date: Mon, 16 Dec 2024 08:21:18 +0100 Subject: [PATCH 20/29] refactor: Put EML_CONTENT_TYPE for all --- .../main/java/com/infomaniak/mail/data/api/ApiRepository.kt | 4 +++- .../mail/ui/main/thread/actions/DownloadMessagesViewModel.kt | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt b/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt index 2afd20b031..d0c7315a65 100644 --- a/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt +++ b/app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt @@ -436,8 +436,10 @@ object ApiRepository : ApiRepositoryCore() { } fun getDownloadedMessage(mailboxUuid: String, folderId: String, shortUid: Int): Response { + val emlContentType = "message/rfc822" + val request = Request.Builder().url(ApiRoutes.downloadMessage(mailboxUuid, folderId, shortUid)) - .headers(HttpUtils.getHeaders()) + .headers(HttpUtils.getHeaders(emlContentType)) .get() .build() diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt index 9152a93e45..387d69a204 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt @@ -63,7 +63,6 @@ class DownloadMessagesViewModel @Inject constructor( mailboxUuid = mailbox.uuid, folderId = message.folderId, shortUid = message.shortUid, - contentType = EML_CONTENT_TYPE, ) if (!response.isSuccessful || response.body == null) return@runCatching null @@ -116,6 +115,5 @@ class DownloadMessagesViewModel @Inject constructor( companion object { private const val NO_SUBJECT_FILE = "message" - private const val EML_CONTENT_TYPE = "message/rfc822" } } From 130eb51498dd84b92f7502fbdf805258f2f33aca Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Mon, 16 Dec 2024 09:21:05 +0100 Subject: [PATCH 21/29] refactor: Apply suggestion from code review --- .../mail/ui/main/thread/actions/DownloadProgressDialog.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadProgressDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadProgressDialog.kt index 7334080963..176b7d6bcf 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadProgressDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadProgressDialog.kt @@ -49,10 +49,9 @@ abstract class DownloadProgressDialog : DialogFragment() { } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val context = requireContext() isCancelable = false - return MaterialAlertDialogBuilder(context) + return MaterialAlertDialogBuilder(requireContext()) .setTitle(dialogTitle) .setView(binding.root) .setOnKeyListener { _, keyCode, event -> From c0482bf97d8ca419ef394ff47b67d1e5d3df7250 Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Mon, 16 Dec 2024 15:26:49 +0100 Subject: [PATCH 22/29] refactor: Remove 'EXPOSED_EML_PATH' from 'build.gradle' --- app/build.gradle | 2 -- .../mail/ui/main/thread/actions/DownloadMessagesViewModel.kt | 5 +++-- .../main/java/com/infomaniak/mail/utils/LocalStorageUtils.kt | 3 +++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 89f6305195..3b86566684 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -50,8 +50,6 @@ android { resValue 'string', 'EML_AUTHORITY', 'com.infomaniak.mail.eml' resValue 'string', 'FILES_AUTHORITY', 'com.infomaniak.mail.attachments;com.infomaniak.mail.eml' - resValue 'string', 'EXPOSED_EML_PATH', 'eml_export' - resourceConfigurations += ["en", "de", "es", "fr", "it"] } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt index 387d69a204..f1a09e3d72 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt @@ -31,6 +31,7 @@ import com.infomaniak.mail.data.api.ApiRepository import com.infomaniak.mail.data.cache.mailboxContent.MessageController import com.infomaniak.mail.data.models.mailbox.Mailbox import com.infomaniak.mail.di.IoDispatcher +import com.infomaniak.mail.utils.LocalStorageUtils.getEmlCacheDir import com.infomaniak.mail.utils.coroutineContext import com.infomaniak.mail.utils.extensions.appContext import dagger.hilt.android.lifecycle.HiltViewModel @@ -90,14 +91,14 @@ class DownloadMessagesViewModel @Inject constructor( } private fun getAllFileNameInExportEmlDir(context: Context): List { - val fileDir = File(context.cacheDir, context.getString(R.string.EXPOSED_EML_PATH)) + val fileDir = getEmlCacheDir(context) if (!fileDir.exists()) fileDir.mkdirs() return fileDir.listFiles()?.map { it.name.removeSuffix(".eml") } ?: emptyList() } private fun saveEmlToFile(context: Context, emlByteArray: ByteArray, fileName: String): Uri? { val fileNameWithExtension = "${fileName.removeIllegalFileNameCharacter()}.eml" - val fileDir = File(context.cacheDir, context.getString(R.string.EXPOSED_EML_PATH)) + val fileDir = getEmlCacheDir(context) if (!fileDir.exists()) fileDir.mkdirs() diff --git a/app/src/main/java/com/infomaniak/mail/utils/LocalStorageUtils.kt b/app/src/main/java/com/infomaniak/mail/utils/LocalStorageUtils.kt index d682a8d95f..d47a927040 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/LocalStorageUtils.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/LocalStorageUtils.kt @@ -36,11 +36,14 @@ object LocalStorageUtils { private const val ATTACHMENTS_UPLOAD_DIR = "attachments_upload" private const val HIDDEN_FILE_NAME = "HIDDEN_FILE_NAME" private const val NAME_TOO_LONG_EXCEPTION = "ENAMETOOLONG" + private const val EML_CACHE_DIR = "eml_export" private inline val Context.attachmentsCacheRootDir get() = File(cacheDir, ATTACHMENTS_CACHE_DIR) private inline val Context.attachmentsUploadRootDir get() = File(filesDir, ATTACHMENTS_UPLOAD_DIR) //region Cache + fun getEmlCacheDir(context: Context): File = File(context.cacheDir, EML_CACHE_DIR) + fun getAttachmentsCacheDir( context: Context, attachmentPath: String, From 6e1217a5b478e593d21b782155f6882b0fe8e766 Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Tue, 21 Jan 2025 08:46:16 +0100 Subject: [PATCH 23/29] refactor: Optimise and remove 'while' loop --- .../com/infomaniak/mail/ui/MainViewModel.kt | 4 +- .../actions/DownloadMessagesViewModel.kt | 46 +++++++++++++------ .../MessageActionsBottomSheetDialog.kt | 2 +- .../actions/ThreadActionsBottomSheetDialog.kt | 2 +- .../utils/extensions/NavigationExtensions.kt | 2 +- 5 files changed, 36 insertions(+), 20 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 283dd33a43..7eadb6d3b9 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -1195,8 +1195,8 @@ class MainViewModel @Inject constructor( } } - fun getMessagesUidsFromThreadUids(selectedThreadsUids: List): List { - val messageUids = mutableListOf() + fun getMessagesUidsFromThreadUids(selectedThreadsUids: List): Set { + val messageUids = mutableSetOf() selectedThreadsUids.forEach { threadUuid -> val thread = threadController.getThread(threadUuid) ?: return@forEach messageUids.addAll(thread.messages.map { it.uid }) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt index f1a09e3d72..8f30e71e39 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt @@ -56,7 +56,7 @@ class DownloadMessagesViewModel @Inject constructor( val downloadedThreadUris: List? = runCatching { val mailbox = currentMailbox ?: return@runCatching null val listUri = mutableListOf() - val listFileName = getAllFileNameInExportEmlDir(appContext).toMutableSet() + val listFileName = getAllFileNameInExportEmlDir(appContext) messageLocalUids.forEach { messageUid -> val message = messageController.getMessage(messageUid) ?: return@runCatching null @@ -69,10 +69,16 @@ class DownloadMessagesViewModel @Inject constructor( if (!response.isSuccessful || response.body == null) return@runCatching null val messageSubject: String = message.subject?.removeIllegalFileNameCharacter() ?: NO_SUBJECT_FILE - createUniqueFileName(messageSubject, listFileName.toList()).let { fileName -> - listFileName.add(fileName) - saveEmlToFile(appContext, response.body!!.bytes(), fileName)?.let { listUri.add(it) } + + val fileName = if (listFileName[messageSubject] == null) { + listFileName[messageSubject] = 0 + messageSubject + } else { + listFileName[messageSubject] = listFileName[messageSubject]!! + 1 + "$messageSubject (${listFileName[messageSubject]!! + 1})" } + + saveEmlToFile(appContext, response.body!!.bytes(), fileName)?.let { listUri.add(it) } } listUri }.getOrNull() @@ -80,20 +86,30 @@ class DownloadMessagesViewModel @Inject constructor( emit(downloadedThreadUris) } - // TODO Extract this code in core2 - private fun createUniqueFileName(originalFileName: String, listFileName: List): String { - var postfix = 1 - var fileName = originalFileName + private fun getAllFileNameInExportEmlDir(context: Context): HashMap { - while (listFileName.contains(fileName)) fileName = "$originalFileName (${postfix++})" - - return fileName - } - - private fun getAllFileNameInExportEmlDir(context: Context): List { + val fileNameMap = HashMap() val fileDir = getEmlCacheDir(context) if (!fileDir.exists()) fileDir.mkdirs() - return fileDir.listFiles()?.map { it.name.removeSuffix(".eml") } ?: emptyList() + + fileDir.listFiles()?.forEach { file -> + val nameWithoutExtension = file.name.removeSuffix(".eml") + + val regex = Regex("\\((\\d+)\\)$") + val match = regex.find(nameWithoutExtension) + val numberInParentheses = match?.groupValues?.last() + + var fileNumber = numberInParentheses?.toInt() ?: 0 + val fileName = nameWithoutExtension.replace(regex, "").trim() + + fileNameMap[fileName]?.let { + fileNumber = if (it > fileNumber) it else fileNumber + } + + fileNameMap[fileName] = fileNumber + } + + return fileNameMap } private fun saveEmlToFile(context: Context, emlByteArray: ByteArray, fileName: String): Uri? { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt index 8525235575..e4fb2ff02e 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt @@ -178,7 +178,7 @@ class MessageActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override fun onSaveKDrive() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) navigateToDownloadMessagesProgressDialog( - messageUids = listOf(messageUid), + messageUids = setOf(messageUid), nameFirstMessage = mainViewModel.getSubject(threadUid), currentClassName = MessageActionsBottomSheetDialog::class.java.name, ) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt index b94c8e9657..ef75b459ee 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt @@ -199,7 +199,7 @@ class ThreadActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override fun onSaveKDrive() { trackBottomSheetThreadActionsEvent(ACTION_SAVE_KDRIVE_NAME) navigateToDownloadMessagesProgressDialog( - messageUids = thread.messages.map { it.uid }, + messageUids = thread.messages.map { it.uid }.toSet(), nameFirstMessage = thread.subject ?: "", ThreadActionsBottomSheetDialog::class.java.name ) diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt index 6f84e84da9..1ad902d488 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/NavigationExtensions.kt @@ -92,7 +92,7 @@ fun Fragment.navigateToDownloadProgressDialog( } fun Fragment.navigateToDownloadMessagesProgressDialog( - messageUids: List, + messageUids: Set, nameFirstMessage: String, currentClassName: String, ) { From aeb7f29aaee9ad1a1a38aa43f0035e3c8b1316dc Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Tue, 21 Jan 2025 14:10:28 +0100 Subject: [PATCH 24/29] refactor: Replace ArrayList by List for maintainability --- .../main/thread/actions/DownloadMessagesProgressDialog.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesProgressDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesProgressDialog.kt index de5068dd36..e822c4cb4e 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesProgressDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesProgressDialog.kt @@ -41,7 +41,7 @@ class DownloadMessagesProgressDialog : DownloadProgressDialog() { if (threadUris == null) { popBackStackWithError() } else { - ArrayList(threadUris).openKDriveOrPlayStore(requireContext())?.let { openKDriveIntent -> + threadUris.openKDriveOrPlayStore(requireContext())?.let { openKDriveIntent -> setBackNavigationResult(DOWNLOAD_MESSAGES_RESULT, openKDriveIntent) } ?: run { findNavController().popBackStack() } } @@ -58,7 +58,7 @@ class DownloadMessagesProgressDialog : DownloadProgressDialog() { ) } - private fun ArrayList.openKDriveOrPlayStore(context: Context): Intent? { + private fun List.openKDriveOrPlayStore(context: Context): Intent? { return if (canSaveOnKDrive(context)) { saveToDriveIntent() } else { @@ -67,11 +67,11 @@ class DownloadMessagesProgressDialog : DownloadProgressDialog() { } } - private fun ArrayList.saveToDriveIntent(): Intent { + private fun List.saveToDriveIntent(): Intent { return Intent().apply { component = ComponentName(DRIVE_PACKAGE, SAVE_EXTERNAL_ACTIVITY_CLASS) action = Intent.ACTION_SEND_MULTIPLE - putParcelableArrayListExtra(Intent.EXTRA_STREAM, this@saveToDriveIntent) + putParcelableArrayListExtra(Intent.EXTRA_STREAM, ArrayList(this@saveToDriveIntent)) } } From bdc9dc7e8117ec3dcd997bf17bf4dc806a6038bc Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Tue, 21 Jan 2025 16:04:40 +0100 Subject: [PATCH 25/29] feat: Delete all files in cache dir after share them to kDrive --- .../mail/ui/main/folder/TwoPaneFragment.kt | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folder/TwoPaneFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folder/TwoPaneFragment.kt index 2f46055afc..2df92f55f7 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folder/TwoPaneFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folder/TwoPaneFragment.kt @@ -17,9 +17,11 @@ */ package com.infomaniak.mail.ui.main.folder +import android.app.Activity import android.content.res.Configuration import android.os.Bundle import android.view.View +import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult import androidx.annotation.ColorRes import androidx.core.content.res.ResourcesCompat import androidx.core.view.isGone @@ -42,7 +44,9 @@ import com.infomaniak.mail.ui.MainViewModel import com.infomaniak.mail.ui.main.search.SearchFragment import com.infomaniak.mail.ui.main.thread.ThreadFragment import com.infomaniak.mail.ui.main.thread.actions.DownloadMessagesProgressDialog +import com.infomaniak.mail.utils.LocalStorageUtils.getEmlCacheDir import com.infomaniak.mail.utils.extensions.* +import java.io.File import javax.inject.Inject abstract class TwoPaneFragment : Fragment() { @@ -119,9 +123,21 @@ abstract class TwoPaneFragment : Fragment() { } } + private val resultActivityResultLauncher = registerForActivityResult(StartActivityForResult()) { result -> + if (result.resultCode == Activity.RESULT_OK) { + val fileDir: File = getEmlCacheDir(requireContext()) + + if (!fileDir.exists()) fileDir.mkdirs() + + if (!fileDir.deleteRecursively()) { + // TODO: Manage error + } + } + } + private fun observeThreadNavigation() = with(twoPaneViewModel) { getBackNavigationResult(AttachmentExtensions.DOWNLOAD_ATTACHMENT_RESULT, ::startActivity) - getBackNavigationResult(DownloadMessagesProgressDialog.DOWNLOAD_MESSAGES_RESULT, ::startActivity) + getBackNavigationResult(DownloadMessagesProgressDialog.DOWNLOAD_MESSAGES_RESULT, resultActivityResultLauncher::launch) newMessageArgs.observe(viewLifecycleOwner) { safeNavigateToNewMessageActivity(args = it.toBundle()) From 23fb0424831bc2f1d18069368ae56504a45c306c Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Tue, 21 Jan 2025 16:05:16 +0100 Subject: [PATCH 26/29] refactor: Allow to manage error --- .../main/thread/actions/DownloadMessagesViewModel.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt index 8f30e71e39..604b4d68a5 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt @@ -78,7 +78,9 @@ class DownloadMessagesViewModel @Inject constructor( "$messageSubject (${listFileName[messageSubject]!! + 1})" } - saveEmlToFile(appContext, response.body!!.bytes(), fileName)?.let { listUri.add(it) } + saveEmlToFile(appContext, response.body!!.bytes(), fileName)?.let(listUri::add) ?: { + // TODO: Manage error case + } } listUri }.getOrNull() @@ -118,14 +120,11 @@ class DownloadMessagesViewModel @Inject constructor( if (!fileDir.exists()) fileDir.mkdirs() - runCatching { + return runCatching { val file = File(fileDir, fileNameWithExtension) file.outputStream().use { it.write(emlByteArray) } return FileProvider.getUriForFile(context, context.getString(R.string.EML_AUTHORITY), file) - }.onFailure { exception -> - exception.printStackTrace() - } - return null + }.getOrNull() } private fun String.removeIllegalFileNameCharacter(): String = this.replace(DownloadManagerUtils.regexInvalidSystemChar, "") From eae057976849cff18378e18d1398038d895a86d6 Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Tue, 21 Jan 2025 16:11:48 +0100 Subject: [PATCH 27/29] feat: Limit file name length --- .../mail/ui/main/thread/actions/DownloadMessagesViewModel.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt index 604b4d68a5..d7b9d4fc81 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt @@ -69,6 +69,7 @@ class DownloadMessagesViewModel @Inject constructor( if (!response.isSuccessful || response.body == null) return@runCatching null val messageSubject: String = message.subject?.removeIllegalFileNameCharacter() ?: NO_SUBJECT_FILE + .take(MAX_FILE_NAME_LENGTH) val fileName = if (listFileName[messageSubject] == null) { listFileName[messageSubject] = 0 @@ -131,5 +132,6 @@ class DownloadMessagesViewModel @Inject constructor( companion object { private const val NO_SUBJECT_FILE = "message" + private const val MAX_FILE_NAME_LENGTH = 256 } } From fec88186c61bd17752d9624238ce588395e188e9 Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Tue, 21 Jan 2025 16:21:25 +0100 Subject: [PATCH 28/29] refactor: No need to check for existing files because they are deleted before --- .../actions/DownloadMessagesViewModel.kt | 28 +------------------ 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt index d7b9d4fc81..e0e7c4e4d0 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt @@ -56,7 +56,7 @@ class DownloadMessagesViewModel @Inject constructor( val downloadedThreadUris: List? = runCatching { val mailbox = currentMailbox ?: return@runCatching null val listUri = mutableListOf() - val listFileName = getAllFileNameInExportEmlDir(appContext) + val listFileName = hashMapOf() messageLocalUids.forEach { messageUid -> val message = messageController.getMessage(messageUid) ?: return@runCatching null @@ -89,32 +89,6 @@ class DownloadMessagesViewModel @Inject constructor( emit(downloadedThreadUris) } - private fun getAllFileNameInExportEmlDir(context: Context): HashMap { - - val fileNameMap = HashMap() - val fileDir = getEmlCacheDir(context) - if (!fileDir.exists()) fileDir.mkdirs() - - fileDir.listFiles()?.forEach { file -> - val nameWithoutExtension = file.name.removeSuffix(".eml") - - val regex = Regex("\\((\\d+)\\)$") - val match = regex.find(nameWithoutExtension) - val numberInParentheses = match?.groupValues?.last() - - var fileNumber = numberInParentheses?.toInt() ?: 0 - val fileName = nameWithoutExtension.replace(regex, "").trim() - - fileNameMap[fileName]?.let { - fileNumber = if (it > fileNumber) it else fileNumber - } - - fileNameMap[fileName] = fileNumber - } - - return fileNameMap - } - private fun saveEmlToFile(context: Context, emlByteArray: ByteArray, fileName: String): Uri? { val fileNameWithExtension = "${fileName.removeIllegalFileNameCharacter()}.eml" val fileDir = getEmlCacheDir(context) From 0c465dfc8aa2e2b91496f1390459edfbefbbe09e Mon Sep 17 00:00:00 2001 From: Nicolas Bourdin Date: Wed, 22 Jan 2025 15:52:18 +0100 Subject: [PATCH 29/29] feat: Optimize message downloads with parallel execution --- .../actions/DownloadMessagesViewModel.kt | 57 +++++++++++-------- .../utils/extensions/AttachmentExtensions.kt | 1 + 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt index e0e7c4e4d0..238e2069b4 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt @@ -35,7 +35,7 @@ import com.infomaniak.mail.utils.LocalStorageUtils.getEmlCacheDir import com.infomaniak.mail.utils.coroutineContext import com.infomaniak.mail.utils.extensions.appContext import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.* import java.io.File import javax.inject.Inject @@ -58,31 +58,42 @@ class DownloadMessagesViewModel @Inject constructor( val listUri = mutableListOf() val listFileName = hashMapOf() - messageLocalUids.forEach { messageUid -> - val message = messageController.getMessage(messageUid) ?: return@runCatching null - val response = ApiRepository.getDownloadedMessage( - mailboxUuid = mailbox.uuid, - folderId = message.folderId, - shortUid = message.shortUid, - ) - - if (!response.isSuccessful || response.body == null) return@runCatching null - - val messageSubject: String = message.subject?.removeIllegalFileNameCharacter() ?: NO_SUBJECT_FILE - .take(MAX_FILE_NAME_LENGTH) - - val fileName = if (listFileName[messageSubject] == null) { - listFileName[messageSubject] = 0 - messageSubject - } else { - listFileName[messageSubject] = listFileName[messageSubject]!! + 1 - "$messageSubject (${listFileName[messageSubject]!! + 1})" + coroutineScope { + val deferredResponses: List> = messageLocalUids.map { messageUid -> + async { + val message = messageController.getMessage(messageUid) ?: return@async null + val response = ApiRepository.getDownloadedMessage( + mailboxUuid = mailbox.uuid, + folderId = message.folderId, + shortUid = message.shortUid, + ) + + val messageSubject: String = message.subject?.removeIllegalFileNameCharacter() + ?: NO_SUBJECT_FILE + val truncatedSubject = messageSubject.take(MAX_FILE_NAME_LENGTH) + + val fileName = if (listFileName[truncatedSubject] == null) { + listFileName[truncatedSubject] = 0 + truncatedSubject + } else { + listFileName[truncatedSubject] = listFileName[truncatedSubject]!! + 1 + "$truncatedSubject (${listFileName[truncatedSubject]!! + 1})" + } + + if (!response.isSuccessful || response.body == null) return@async null + + saveEmlToFile(appContext, response.body!!.bytes(), fileName).also { + if (it == null) { + // TODO: Manage error case + } + } + } } - saveEmlToFile(appContext, response.body!!.bytes(), fileName)?.let(listUri::add) ?: { - // TODO: Manage error case - } + val uris = deferredResponses.awaitAll() + listUri.addAll(uris.filterNotNull()) } + listUri }.getOrNull() diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt index 67344b77e9..a85d348b6e 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/AttachmentExtensions.kt @@ -20,6 +20,7 @@ package com.infomaniak.mail.utils.extensions import android.content.ComponentName import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import android.os.Bundle import android.provider.MediaStore.Files.FileColumns import androidx.core.content.FileProvider