diff --git a/.idea/navEditor.xml b/.idea/navEditor.xml index d4ab77659a..df795dcae0 100644 --- a/.idea/navEditor.xml +++ b/.idea/navEditor.xml @@ -112,7 +112,7 @@ @@ -124,8 +124,8 @@ @@ -136,7 +136,7 @@ @@ -148,7 +148,7 @@ @@ -160,7 +160,7 @@ @@ -172,7 +172,7 @@ @@ -193,7 +193,7 @@ @@ -205,7 +205,7 @@ @@ -217,7 +217,7 @@ @@ -229,7 +229,7 @@ @@ -241,7 +241,7 @@ @@ -253,7 +253,7 @@ @@ -265,7 +265,7 @@ @@ -291,7 +291,7 @@ @@ -303,8 +303,20 @@ + + + + + + + @@ -315,7 +327,7 @@ @@ -327,7 +339,7 @@ @@ -339,7 +351,7 @@ @@ -363,7 +375,7 @@ @@ -375,7 +387,7 @@ @@ -387,7 +399,7 @@ @@ -399,7 +411,7 @@ @@ -430,7 +442,7 @@ @@ -454,7 +466,7 @@ @@ -466,7 +478,7 @@ @@ -488,7 +500,7 @@ @@ -523,7 +535,7 @@ @@ -535,7 +547,7 @@ @@ -547,7 +559,7 @@ @@ -571,7 +583,7 @@ @@ -583,7 +595,7 @@ @@ -595,7 +607,7 @@ @@ -616,7 +628,7 @@ @@ -642,7 +654,7 @@ @@ -718,7 +730,7 @@ @@ -730,7 +742,7 @@ @@ -742,7 +754,7 @@ @@ -754,7 +766,7 @@ @@ -775,7 +787,7 @@ @@ -787,7 +799,7 @@ @@ -799,7 +811,7 @@ @@ -811,7 +823,7 @@ @@ -823,7 +835,7 @@ @@ -879,7 +891,7 @@ @@ -891,7 +903,7 @@ @@ -915,40 +927,6 @@ - - - - - - - @@ -959,7 +937,7 @@ @@ -971,7 +949,7 @@ @@ -983,7 +961,7 @@ @@ -1002,19 +980,7 @@ - - - - - - - - + - - - - - - - - - - - - - - diff --git a/app/build.gradle b/app/build.gradle index 118210149d..3e594c74f9 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', '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 5e9fe00ae8..7cb0209dbf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -204,7 +204,7 @@ { - return ApiController.callApi( - url = MyKSuiteApiRoutes.myKSuiteData(), - method = ApiController.ApiMethod.GET, - okHttpClient = okHttpClient, - useKotlinxSerialization = true, - ) + return callApi(url = MyKSuiteApiRoutes.myKSuiteData(), method = GET, okHttpClient = okHttpClient) } /** 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 775240b2b2..eac639afde 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 androidx.lifecycle.* import com.infomaniak.lib.core.models.ApiResponse -import com.infomaniak.lib.core.networking.HttpUtils import com.infomaniak.lib.core.networking.NetworkAvailability import com.infomaniak.lib.core.utils.ApiErrorCode.Companion.translateError import com.infomaniak.lib.core.utils.DownloadManagerUtils @@ -30,7 +29,6 @@ import com.infomaniak.mail.MatomoMail.trackMultiSelectionEvent import com.infomaniak.mail.R import com.infomaniak.mail.data.LocalSettings import com.infomaniak.mail.data.api.ApiRepository -import com.infomaniak.mail.data.api.ApiRoutes import com.infomaniak.mail.data.cache.RealmDatabase import com.infomaniak.mail.data.cache.mailboxContent.FolderController import com.infomaniak.mail.data.cache.mailboxContent.MessageController @@ -60,6 +58,7 @@ import com.infomaniak.mail.utils.ContactUtils.getPhoneContacts import com.infomaniak.mail.utils.ContactUtils.mergeApiContactsIntoPhoneContacts import com.infomaniak.mail.utils.NotificationUtils.Companion.cancelNotification import com.infomaniak.mail.utils.SharedUtils.Companion.updateSignatures +import com.infomaniak.mail.utils.Utils.EML_CONTENT_TYPE import com.infomaniak.mail.utils.Utils.isPermanentDeleteFolder import com.infomaniak.mail.utils.Utils.runCatchingRealm import com.infomaniak.mail.utils.extensions.* @@ -75,7 +74,6 @@ import io.sentry.Sentry import io.sentry.SentryLevel import kotlinx.coroutines.* import kotlinx.coroutines.flow.* -import okhttp3.Request import java.util.Date import java.util.UUID import javax.inject.Inject @@ -1019,21 +1017,11 @@ 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 apiResponse = ApiRepository.getDownloadedMessage(mailbox.uuid, message.folderId, message.shortUid) - if (!response.isSuccessful || response.body == null) { + if (apiResponse.body == null || !apiResponse.isSuccessful) { reportDisplayProblemTrigger.postValue(Unit) snackbarManager.postValue(appContext.getString(RCore.string.anErrorHasOccurred)) @@ -1041,7 +1029,7 @@ class MainViewModel @Inject constructor( } val filename = UUID.randomUUID().toString() - val emlAttachment = Attachment(response.body?.bytes(), filename, EML_CONTENT_TYPE) + val emlAttachment = Attachment(apiResponse.body?.bytes(), filename, EML_CONTENT_TYPE) Sentry.captureMessage("Message display problem reported", SentryLevel.ERROR) { scope -> scope.addAttachment(emlAttachment) } @@ -1191,7 +1179,7 @@ class MainViewModel @Inject constructor( } fun hasOtherExpeditors(threadUid: String) = liveData(ioCoroutineContext) { - val hasOtherExpeditors = threadController.getThread(threadUid)?.messages?.flatMap { it.from }?.any { !it.isMe() } ?: false + val hasOtherExpeditors = threadController.getThread(threadUid)?.messages?.flatMap { it.from }?.any { !it.isMe() } == true emit(hasOtherExpeditors) } @@ -1312,6 +1300,5 @@ 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 cb97c7ce18..5c8ce397c5 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 @@ -20,6 +20,7 @@ package com.infomaniak.mail.ui.main.folder 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 @@ -41,6 +42,8 @@ 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.DownloadMessagesProgressDialog +import com.infomaniak.mail.utils.LocalStorageUtils.clearEmlCacheDir import com.infomaniak.mail.utils.extensions.* import javax.inject.Inject @@ -118,8 +121,13 @@ abstract class TwoPaneFragment : Fragment() { } } + private val resultActivityResultLauncher = registerForActivityResult(StartActivityForResult()) { _ -> + clearEmlCacheDir(requireContext()) + } + private fun observeThreadNavigation() = with(twoPaneViewModel) { getBackNavigationResult(AttachmentExtensions.DOWNLOAD_ATTACHMENT_RESULT, ::startActivity) + getBackNavigationResult(DownloadMessagesProgressDialog.DOWNLOAD_MESSAGES_RESULT, resultActivityResultLauncher::launch) newMessageArgs.observe(viewLifecycleOwner) { safeNavigateToNewMessageActivity(args = it.toBundle()) 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..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,59 +17,33 @@ */ package com.infomaniak.mail.ui.main.thread.actions -import android.app.Dialog import android.os.Bundle -import android.view.KeyEvent +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup import androidx.appcompat.content.res.AppCompatResources -import androidx.fragment.app.DialogFragment -import androidx.fragment.app.activityViewModels +import androidx.core.view.isVisible 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 val dialogTitle: String? by lazy { navigationArgs.attachmentName } - override fun onStart() { - super.onStart() - downloadAttachment() + 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) } - private fun downloadAttachment() { + override fun download() { downloadAttachmentViewModel.downloadAttachment().observe(this) { cachedAttachment -> if (cachedAttachment == null) { popBackStackWithError() @@ -80,13 +54,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/DownloadMessagesProgressDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesProgressDialog.kt new file mode 100644 index 0000000000..b2bc98850e --- /dev/null +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesProgressDialog.kt @@ -0,0 +1,79 @@ +/* + * 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.content.ComponentName +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import com.infomaniak.lib.core.utils.goToPlayStore +import com.infomaniak.lib.core.utils.setBackNavigationResult +import com.infomaniak.mail.utils.LocalStorageUtils.clearEmlCacheDir +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 + +class DownloadMessagesProgressDialog : DownloadProgressDialog() { + private val downloadThreadsViewModel: DownloadMessagesViewModel by viewModels() + + override val dialogTitle: String? by lazy { downloadThreadsViewModel.getDialogName() } + + override fun onCreate(savedInstanceState: Bundle?) { + observeDownload() + super.onCreate(savedInstanceState) + } + + override fun download() { + downloadThreadsViewModel.downloadMessages(mainViewModel.currentMailbox.value) + } + + private fun observeDownload() { + downloadThreadsViewModel.downloadMessagesLiveData.observe(this) { messageUris -> + messageUris?.openKDriveOrPlayStore(requireContext())?.let { openKDriveIntent -> + setBackNavigationResult(DOWNLOAD_MESSAGES_RESULT, openKDriveIntent) + } ?: run { + clearEmlCacheDir(requireContext()) + if (messageUris == null) popBackStackWithError() else findNavController().popBackStack() + } + } + } + + private fun List.openKDriveOrPlayStore(context: Context): Intent? { + return if (canSaveOnKDrive(context)) { + saveToDriveIntent() + } else { + context.goToPlayStore(DRIVE_PACKAGE) + null + } + } + + 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, ArrayList(this@saveToDriveIntent)) + } + } + + companion object { + const val DOWNLOAD_MESSAGES_RESULT = "download_messages_result" + } +} 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 new file mode 100644 index 0000000000..0afeb0dcb2 --- /dev/null +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadMessagesViewModel.kt @@ -0,0 +1,150 @@ +/* + * 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.MutableLiveData +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.viewModelScope +import com.infomaniak.lib.core.utils.DownloadManagerUtils +import com.infomaniak.lib.core.utils.SentryLog +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 +import com.infomaniak.mail.data.models.message.Message +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 +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.launch +import java.io.File +import javax.inject.Inject +import kotlin.collections.set + +@HiltViewModel +class DownloadMessagesViewModel @Inject constructor( + application: Application, + private val savedStateHandle: SavedStateHandle, + private val messageController: MessageController, + @IoDispatcher private val ioDispatcher: CoroutineDispatcher, +) : AndroidViewModel(application) { + + private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher) + + private val messageLocalUids: Array? + inline get() = savedStateHandle[DownloadMessagesProgressDialogArgs::messageUids.name] + + val downloadMessagesLiveData = MutableLiveData?>() + + private fun getAllMessages(): Set { + val messages = mutableSetOf() + messageLocalUids?.mapNotNull(messageController::getMessage)?.let(messages::addAll) + return messages + } + + private fun createUniqueFileName(listFileName: HashMap, truncatedSubject: String): String { + val fileName = if (listFileName[truncatedSubject] == null) { + listFileName[truncatedSubject] = 0 + truncatedSubject + } else { + listFileName[truncatedSubject] = listFileName[truncatedSubject]!! + 1 + "$truncatedSubject (${listFileName[truncatedSubject]!! + 1})" + } + return fileName + } + + private fun saveEmlToFile(context: Context, emlByteArray: ByteArray, fileName: String): Uri { + val fileNameWithExtension = "${fileName.removeIllegalFileNameCharacter()}.eml" + val fileDir = getEmlCacheDir(context) + + if (!fileDir.exists()) fileDir.mkdirs() + + val file = File(fileDir, fileNameWithExtension) + file.outputStream().use { it.write(emlByteArray) } + return FileProvider.getUriForFile(context, context.getString(R.string.EML_AUTHORITY), file) + } + + private fun getFirstMessageSubject(): String? = getAllMessages().firstOrNull()?.subject + + private fun numberOfMessagesToDownloads(): Int = messageLocalUids?.size ?: 0 + + fun downloadMessages(currentMailbox: Mailbox?) = viewModelScope.launch(ioCoroutineContext) { + val mailbox = currentMailbox ?: return@launch + + val downloadedThreadUris = runCatching { + val listFileName = HashMap() + + val deferredResponses = getAllMessages().map { message -> + async { + val apiResponse = ApiRepository.getDownloadedMessage( + mailboxUuid = mailbox.uuid, + folderId = message.folderId, + shortUid = message.shortUid, + ) + + if (apiResponse.body == null || !apiResponse.isSuccessful) { + throw ByteArrayNetworkException(apiResponse.body.toString(), apiResponse.code) + } + + val messageSubject = message.subject ?: NO_SUBJECT_FILE + val truncatedSubject = messageSubject.take(MAX_FILE_NAME_LENGTH) + val fileName = createUniqueFileName(listFileName, truncatedSubject) + + saveEmlToFile(appContext, apiResponse.body?.bytes()!!, fileName) + } + } + + deferredResponses.awaitAll() + }.getOrElse { + if (it is ByteArrayNetworkException) SentryLog.e(TAG, "Error while sharing messages to kDrive:", it) + null + } + + downloadMessagesLiveData.postValue(downloadedThreadUris) + } + + fun getDialogName(): String { + val numberOfMessagesToDownload = numberOfMessagesToDownloads() + + return if (numberOfMessagesToDownload == 1) { + getFirstMessageSubject() ?: appContext.getString(R.string.noSubjectTitle) + } else { + appContext.resources.getString(R.string.downloadingEmailsTitle, numberOfMessagesToDownload) + } + } + + private fun String.removeIllegalFileNameCharacter(): String = replace(DownloadManagerUtils.regexInvalidSystemChar, "") + + companion object { + private const val NO_SUBJECT_FILE = "message" + private const val MAX_FILE_NAME_LENGTH = 256 + private val TAG = DownloadMessagesViewModel::class.simpleName.toString() + + private data class ByteArrayNetworkException(val responseBody: String, val responseCode: Int) : + Exception("Failed to get EML $responseCode: $responseBody") + } +} 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..dfd25ee7a2 --- /dev/null +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadProgressDialog.kt @@ -0,0 +1,67 @@ +/* + * 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 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? + + protected abstract fun download() + + override fun onStart() { + download() + super.onStart() + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + isCancelable = false + + return MaterialAlertDialogBuilder(requireContext()) + .setTitle(dialogTitle) + .setView(binding.root) + .create() + } + + 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/MailActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt index b7570be3ff..cb8a096f79 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 onSaveToKDrive() = 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.onSaveToKDrive() } 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 onSaveToKDrive() 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 0cccbc3418..7ca780a365 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,12 +33,14 @@ 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_TO_KDRIVE_NAME import com.infomaniak.mail.MatomoMail.ACTION_SHARE_LINK_NAME import com.infomaniak.mail.MatomoMail.trackBottomSheetMessageActionsEvent 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 @@ -79,104 +81,116 @@ 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) - } - } + override fun onPrint() { + trackBottomSheetMessageActionsEvent(ACTION_PRINT_NAME) + safeNavigate( + resId = R.id.printMailFragment, + args = PrintMailFragmentArgs(messageUid).toBundle(), + currentClassName = MessageActionsBottomSheetDialog::class.java.name, + ) + } - override fun onReportDisplayProblem() { - descriptionDialog.show( - title = getString(R.string.reportDisplayProblemTitle), - description = getString(R.string.reportDisplayProblemDescription), - onPositiveButtonClicked = { mainViewModel.reportDisplayProblem(message.uid) }, - ) + override fun onShare() { + activity?.apply { + trackBottomSheetThreadActionsEvent(ACTION_SHARE_LINK_NAME) + mainViewModel.shareThreadUrl(message.uid) } - //endregion - }) - } + } + + override fun onSaveToKDrive() { + trackBottomSheetThreadActionsEvent(ACTION_SAVE_TO_KDRIVE_NAME) + navigateToDownloadMessagesProgressDialog( + messageUids = listOf(messageUid), + currentClassName = MessageActionsBottomSheetDialog::class.java.name, + ) + } + + override fun onReportDisplayProblem() { + descriptionDialog.show( + title = getString(R.string.reportDisplayProblemTitle), + description = getString(R.string.reportDisplayProblemDescription), + onPositiveButtonClicked = { mainViewModel.reportDisplayProblem(message.uid) }, + ) + } + //endregion + }) } } 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 516d89cc30..dc2fb41a6d 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_TO_KDRIVE_NAME import com.infomaniak.mail.MatomoMail.ACTION_SPAM_NAME import com.infomaniak.mail.MatomoMail.trackMultiSelectActionEvent import com.infomaniak.mail.R @@ -40,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.navigateToDownloadMessagesProgressDialog import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -118,6 +120,15 @@ class MultiSelectBottomSheetDialog : ActionsBottomSheetDialog() { toggleThreadsFavoriteStatus(threadsUids, shouldFavorite) isMultiSelectOn = false } + + binding.saveKDrive.setClosingOnClickListener(shouldCloseMultiSelection = true) { + trackMultiSelectActionEvent(ACTION_SAVE_TO_KDRIVE_NAME, threadsCount, isFromBottomSheet = true) + navigateToDownloadMessagesProgressDialog( + messageUids = threads.flatMap { it.messages }.map { it.uid }, + currentClassName = MultiSelectBottomSheetDialog::class.java.name, + ) + 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 bf2b0b6005..00b276a679 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_TO_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,14 @@ class ThreadActionsBottomSheetDialog : MailActionsBottomSheetDialog() { } } + override fun onSaveToKDrive() { + trackBottomSheetThreadActionsEvent(ACTION_SAVE_TO_KDRIVE_NAME) + navigateToDownloadMessagesProgressDialog( + messageUids = thread.messages.map { it.uid }, + currentClassName = ThreadActionsBottomSheetDialog::class.java.name, + ) + } + override fun onReportDisplayProblem() { descriptionDialog.show( title = getString(R.string.reportDisplayProblemTitle), 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..74f1ce7648 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/LocalStorageUtils.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/LocalStorageUtils.kt @@ -34,6 +34,7 @@ object LocalStorageUtils { private const val ATTACHMENTS_CACHE_DIR = "attachments_cache" private const val ATTACHMENTS_UPLOAD_DIR = "attachments_upload" + private const val EML_CACHE_DIR = "eml_export" private const val HIDDEN_FILE_NAME = "HIDDEN_FILE_NAME" private const val NAME_TOO_LONG_EXCEPTION = "ENAMETOOLONG" @@ -41,6 +42,12 @@ object LocalStorageUtils { 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 clearEmlCacheDir(context: Context) { + getEmlCacheDir(context).deleteRecursively() + } + fun getAttachmentsCacheDir( context: Context, attachmentPath: String, diff --git a/app/src/main/java/com/infomaniak/mail/utils/SaveOnKDriveUtils.kt b/app/src/main/java/com/infomaniak/mail/utils/SaveOnKDriveUtils.kt new file mode 100644 index 0000000000..095b09e1cb --- /dev/null +++ b/app/src/main/java/com/infomaniak/mail/utils/SaveOnKDriveUtils.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 SaveOnKDriveUtils { + 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/Utils.kt b/app/src/main/java/com/infomaniak/mail/utils/Utils.kt index 6409ec18ee..6b9b37abf8 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/Utils.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/Utils.kt @@ -38,6 +38,7 @@ object Utils { val UTF_8: String = StandardCharsets.UTF_8.name() const val TEXT_HTML = "text/html" const val TEXT_PLAIN = "text/plain" + const val EML_CONTENT_TYPE = "message/rfc822" /** The MIME type for data whose type is otherwise unknown. */ const val MIMETYPE_UNKNOWN = "application/octet-stream" 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 4bbd19efbc..e60536a951 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 @@ -38,6 +38,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.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 @@ -55,18 +58,7 @@ 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" - //region Intent - private 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 - } - private fun Attachment.saveToDriveIntent(context: Context): Intent { val fileFromCache = getCacheFile(context) val lastModifiedDate = fileFromCache.lastModified() 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..508f083b74 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.DownloadMessagesProgressDialogArgs 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.navigateToDownloadMessagesProgressDialog(messageUids: List? = null, currentClassName: String) { + safeNavigate( + resId = R.id.downloadMessagesProgressDialog, + args = DownloadMessagesProgressDialogArgs(messageUids = messageUids?.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/layout/bottom_sheet_actions_menu.xml b/app/src/main/res/layout/bottom_sheet_actions_menu.xml index c0451e5cb7..a15675cf4e 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:title="@string/shareEmail" app:visibleDivider="false" /> + + + + 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 5278fc0212..9cde3c4da4 100644 --- a/app/src/main/res/navigation/main_navigation.xml +++ b/app/src/main/res/navigation/main_navigation.xml @@ -590,6 +590,17 @@ app:argType="com.infomaniak.mail.utils.extensions.AttachmentExtensions$AttachmentIntentType" /> + + + + Benötigen Sie mehr Speicherplatz und Funktionen? Um Anzeigeprobleme zu beheben, aktualisieren Sie bitte die Anwendung Android System Webview. Problem mit der Anzeige Ihrer E-Mails + Herunterladen von %d-E-Mails Entwürfe (Entwurf) Diese Nachricht wird in Ihre Entwürfe verschoben, damit Sie sie senden können, wann immer Sie möchten. diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index d505e4ebb4..c2d179b8d1 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -248,6 +248,7 @@ ¿Necesita más almacenamiento y funciones? 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 Borradores (Borrador) Este mensaje se moverá a tus borradores para que puedas enviarlo cuando lo desees. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 6c621ffb68..9da10a11b4 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -250,6 +250,7 @@ Besoin de plus de stockage et de fonctionnalités ? 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 Brouillons (Brouillon) Ce message sera déplacé dans vos brouillons pour être envoyé quand vous le souhaitez. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index ebcb4bfdc1..4d8f6f3ac7 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -248,6 +248,7 @@ Hai bisogno di più spazio di archiviazione e funzionalità? Per risolvere i problemi di visualizzazione, aggiornare l’applicazione Android System Webview. Problema di visualizzazione delle email + Download di %d e-mail Bozze (Bozza) Questo messaggio verrà spostato nelle tue bozze per essere inviato quando vuoi. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f054021784..59e4f25ed3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -254,6 +254,7 @@ Need more storage and features? To fix display problems, please update the Android System Webview application. Problem displaying your emails + Downloading %d emails Drafts (Draft) This message will be moved to your drafts to be sent whenever you want. diff --git a/app/src/main/res/xml/exposed_files_path.xml b/app/src/main/res/xml/exposed_files_path.xml index 628e54431c..052c7f1f4b 100644 --- a/app/src/main/res/xml/exposed_files_path.xml +++ b/app/src/main/res/xml/exposed_files_path.xml @@ -22,4 +22,7 @@ +