Skip to content

Commit f5f6345

Browse files
committed
Open calendar event by factorizing code with attachments opening
1 parent a133234 commit f5f6345

File tree

7 files changed

+110
-41
lines changed

7 files changed

+110
-41
lines changed

app/src/main/java/com/infomaniak/mail/data/models/Attachment.kt

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import java.io.File
3838
@Serializable
3939
class Attachment : EmbeddedRealmObject {
4040

41+
//region Remote data
4142
var uuid: String = ""
4243
@SerialName("mime_type")
4344
var mimeType: String = ""
@@ -52,12 +53,15 @@ class Attachment : EmbeddedRealmObject {
5253
var resource: String? = null
5354
@SerialName("drive_url")
5455
var driveUrl: String? = null
56+
//endregion
5557

5658
//region Local data (Transient)
5759
@Transient
5860
var uploadLocalUri: String? = null
5961
//endregion
6062

63+
val isCalendarEvent: Boolean get() = mimeType == "application/ics"
64+
6165
val disposition: AttachmentDisposition?
6266
get() = enumValueOfOrNull<AttachmentDisposition>(_disposition)
6367

app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt

+23-4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package com.infomaniak.mail.ui.main.thread
2020
import android.annotation.SuppressLint
2121
import android.content.Context
2222
import android.net.Uri
23+
import android.os.Bundle
2324
import android.text.format.Formatter
2425
import android.view.*
2526
import android.webkit.WebView
@@ -46,6 +47,7 @@ import com.infomaniak.mail.data.models.message.Message.*
4647
import com.infomaniak.mail.databinding.ItemMessageBinding
4748
import com.infomaniak.mail.ui.main.thread.ThreadAdapter.*
4849
import com.infomaniak.mail.utils.*
50+
import com.infomaniak.mail.utils.AttachmentIntentUtils.createDownloadDialogNavArgs
4951
import com.infomaniak.mail.utils.SharedUtils.Companion.createHtmlForPlainText
5052
import com.infomaniak.mail.utils.UiUtils.getPrettyNameAndEmail
5153
import com.infomaniak.mail.utils.Utils
@@ -72,6 +74,8 @@ class ThreadAdapter(
7274
onAllExpandedMessagesLoaded: () -> Unit,
7375
navigateToNewMessageActivity: (Uri) -> Unit,
7476
navigateToAttendeeBottomSheet: (List<Attendee>) -> Unit,
77+
navigateToDownloadProgressDialog: (Int, Bundle) -> Unit,
78+
showSnackBar: (String) -> Unit,
7579
promptLink: (String, ContextMenuType) -> Unit,
7680
) : ListAdapter<Message, ThreadViewHolder>(MessageDiffCallback()) {
7781

@@ -114,6 +118,8 @@ class ThreadAdapter(
114118
onAllExpandedMessagesLoaded,
115119
navigateToNewMessageActivity,
116120
navigateToAttendeeBottomSheet,
121+
navigateToDownloadProgressDialog,
122+
showSnackBar,
117123
promptLink,
118124
)
119125
}
@@ -174,18 +180,29 @@ class ThreadAdapter(
174180
}
175181

176182
private fun ThreadViewHolder.bindCalendarEvent(message: Message) {
183+
val attachment = message.attachments.singleOrNull(Attachment::isCalendarEvent) ?: return
177184
val calendarEvent = message.latestCalendarEventResponse?.calendarEvent
185+
178186
binding.calendarEvent.apply {
179187
isVisible = calendarEvent != null
180188

181189
calendarEvent?.let {
182190
val hasBeenDeleted = message.latestCalendarEventResponse!!.userStoredEventDeleted
183-
loadCalendarEvent(it, hasBeenDeleted)
191+
loadCalendarEvent(it, hasBeenDeleted, attachment)
184192
}
185193

186-
initCallback { attendees ->
187-
threadAdapterCallbacks?.navigateToAttendeeBottomSheet?.invoke(attendees)
188-
}
194+
initCallback(
195+
navigateToAttendeesBottomSheet = { attendees ->
196+
threadAdapterCallbacks?.navigateToAttendeeBottomSheet?.invoke(attendees)
197+
},
198+
navigateToDownloadProgressDialog = {
199+
threadAdapterCallbacks?.navigateToDownloadProgressDialog?.invoke(
200+
R.id.downloadAttachmentProgressDialog,
201+
attachment.createDownloadDialogNavArgs(AttachmentIntentUtils.AttachmentIntentType.OPEN_WITH),
202+
)
203+
},
204+
showSnackBar = { threadAdapterCallbacks?.showSnackBar?.invoke(it) }
205+
)
189206
}
190207
}
191208

@@ -669,6 +686,8 @@ class ThreadAdapter(
669686
var onAllExpandedMessagesLoaded: () -> Unit,
670687
var navigateToNewMessageActivity: (Uri) -> Unit,
671688
var navigateToAttendeeBottomSheet: (List<Attendee>) -> Unit,
689+
var navigateToDownloadProgressDialog: (Int, Bundle) -> Unit,
690+
var showSnackBar: (String) -> Unit,
672691
var promptLink: (String, ContextMenuType) -> Unit,
673692
)
674693

app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadFragment.kt

+2
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,8 @@ class ThreadFragment : Fragment() {
239239
)
240240
},
241241
navigateToNewMessageActivity = { twoPaneViewModel.navigateToNewMessage(mailToUri = it) },
242+
navigateToDownloadProgressDialog = { resId, args -> safeNavigate(resId, args) },
243+
showSnackBar = { message -> mainViewModel.snackBarManager.setValue(message) },
242244
promptLink = { data, type ->
243245
// When adding a phone number to contacts, Google decodes this value in case it's url-encoded. But I could not
244246
// reproduce this issue when manually creating a url-encoded href. If this is triggered, fix it by also

app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadViewModel.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import com.infomaniak.mail.data.cache.mailboxContent.RefreshController
2828
import com.infomaniak.mail.data.cache.mailboxContent.RefreshController.RefreshMode
2929
import com.infomaniak.mail.data.cache.mailboxContent.ThreadController
3030
import com.infomaniak.mail.data.cache.mailboxInfo.MailboxController
31+
import com.infomaniak.mail.data.models.Attachment
3132
import com.infomaniak.mail.data.models.mailbox.Mailbox
3233
import com.infomaniak.mail.data.models.message.Message
3334
import com.infomaniak.mail.data.models.thread.Thread
@@ -199,7 +200,7 @@ class ThreadViewModel @Inject constructor(
199200
val alreadyTreated = !treatedMessagesForCalendarEvent.add(message.uid)
200201
if (alreadyTreated) return@forEach
201202

202-
val icsAttachments = message.attachments.filter { it.mimeType == "application/ics" }
203+
val icsAttachments = message.attachments.filter(Attachment::isCalendarEvent)
203204
if (icsAttachments.count() != 1) return@forEach
204205

205206
val icsAttachment = icsAttachments.single()

app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/AttachmentActionsBottomSheetDialog.kt

+23-33
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* Infomaniak Mail - Android
3-
* Copyright (C) 2023 Infomaniak Network SA
3+
* Copyright (C) 2023-2024 Infomaniak Network SA
44
*
55
* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -25,7 +25,6 @@ import androidx.fragment.app.activityViewModels
2525
import androidx.fragment.app.viewModels
2626
import androidx.navigation.fragment.findNavController
2727
import com.infomaniak.lib.core.utils.context
28-
import com.infomaniak.lib.core.utils.hasSupportedApplications
2928
import com.infomaniak.lib.core.utils.safeBinding
3029
import com.infomaniak.lib.core.utils.safeNavigate
3130
import com.infomaniak.mail.MatomoMail.trackAttachmentActionsEvent
@@ -34,14 +33,13 @@ import com.infomaniak.mail.data.models.Attachment
3433
import com.infomaniak.mail.databinding.BottomSheetAttachmentActionsBinding
3534
import com.infomaniak.mail.ui.MainViewModel
3635
import com.infomaniak.mail.utils.AttachmentIntentUtils.AttachmentIntentType
37-
import com.infomaniak.mail.utils.AttachmentIntentUtils.AttachmentIntentType.OPEN_WITH
3836
import com.infomaniak.mail.utils.AttachmentIntentUtils.AttachmentIntentType.SAVE_TO_DRIVE
39-
import com.infomaniak.mail.utils.AttachmentIntentUtils.getIntentOrGoToPlaystore
40-
import com.infomaniak.mail.utils.AttachmentIntentUtils.openWithIntent
37+
import com.infomaniak.mail.utils.AttachmentIntentUtils.createDownloadDialogNavArgs
38+
import com.infomaniak.mail.utils.AttachmentIntentUtils.executeIntent
39+
import com.infomaniak.mail.utils.AttachmentIntentUtils.openAttachment
4140
import com.infomaniak.mail.utils.PermissionUtils
4241
import dagger.hilt.android.AndroidEntryPoint
4342
import javax.inject.Inject
44-
import com.infomaniak.lib.core.R as RCore
4543

4644
@AndroidEntryPoint
4745
class AttachmentActionsBottomSheetDialog : ActionsBottomSheetDialog() {
@@ -71,45 +69,37 @@ class AttachmentActionsBottomSheetDialog : ActionsBottomSheetDialog() {
7169
}
7270

7371
private fun setupListeners(attachment: Attachment) = with(binding) {
72+
73+
fun navigateToDownloadProgressDialog(attachmentIntentType: AttachmentIntentType) {
74+
safeNavigate(
75+
resId = R.id.downloadAttachmentProgressDialog,
76+
args = attachment.createDownloadDialogNavArgs(attachmentIntentType),
77+
currentClassName = AttachmentActionsBottomSheetDialog::class.java.name,
78+
)
79+
}
80+
7481
openWithItem.setClosingOnClickListener {
7582
trackAttachmentActionsEvent("open")
76-
if (attachment.openWithIntent(context).hasSupportedApplications(context)) {
77-
attachment.executeIntent(OPEN_WITH)
78-
} else {
79-
mainViewModel.snackBarManager.setValue(getString(RCore.string.errorNoSupportingAppFound))
80-
}
83+
attachment.openAttachment(
84+
context,
85+
navigateToDownloadProgressDialog = { navigateToDownloadProgressDialog(it) },
86+
showSnackBar = { snackBarErrorMessage -> mainViewModel.snackBarManager.setValue(snackBarErrorMessage) }
87+
)
8188
}
8289
kDriveItem.setClosingOnClickListener {
8390
trackAttachmentActionsEvent("saveToKDrive")
84-
attachment.executeIntent(SAVE_TO_DRIVE)
91+
attachment.executeIntent(
92+
context,
93+
SAVE_TO_DRIVE,
94+
navigateToDownloadProgressDialog = { navigateToDownloadProgressDialog(it) },
95+
)
8596
}
8697
deviceItem.setOnClickListener {
8798
trackAttachmentActionsEvent("download")
8899
scheduleDownloadManager(attachment.downloadUrl, attachment.name)
89100
}
90101
}
91102

92-
private fun Attachment.executeIntent(intentType: AttachmentIntentType) {
93-
if (hasUsableCache(requireContext()) || isInlineCachedFile(requireContext())) {
94-
getIntentOrGoToPlaystore(requireContext(), intentType)?.let(::startActivity)
95-
} else {
96-
navigateToDownloadProgressDialog(intentType)
97-
}
98-
}
99-
100-
private fun Attachment.navigateToDownloadProgressDialog(intentType: AttachmentIntentType) {
101-
safeNavigate(
102-
resId = R.id.downloadAttachmentProgressDialog,
103-
args = DownloadAttachmentProgressDialogArgs(
104-
attachmentResource = resource!!,
105-
attachmentName = name,
106-
attachmentType = getFileTypeFromMimeType(),
107-
intentType = intentType,
108-
).toBundle(),
109-
currentClassName = AttachmentActionsBottomSheetDialog::class.java.name,
110-
)
111-
}
112-
113103
private fun scheduleDownloadManager(downloadUrl: String, filename: String) {
114104
if (permissionUtils.hasDownloadManagerPermission) {
115105
scheduleDownloadAndPopBack(downloadUrl, filename)

app/src/main/java/com/infomaniak/mail/ui/main/thread/calendar/CalendarEventBannerView.kt

+19-2
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,12 @@ import androidx.core.view.isVisible
2727
import com.google.android.material.button.MaterialButton
2828
import com.infomaniak.lib.core.utils.*
2929
import com.infomaniak.mail.R
30+
import com.infomaniak.mail.data.models.Attachment
3031
import com.infomaniak.mail.data.models.calendar.Attendee
3132
import com.infomaniak.mail.data.models.calendar.CalendarEvent
3233
import com.infomaniak.mail.databinding.ViewCalendarEventBannerBinding
34+
import com.infomaniak.mail.utils.AttachmentIntentUtils
35+
import com.infomaniak.mail.utils.AttachmentIntentUtils.openAttachment
3336
import com.infomaniak.mail.utils.UiUtils.getPrettyNameAndEmail
3437
import com.infomaniak.mail.utils.toDate
3538
import io.realm.kotlin.types.RealmList
@@ -48,6 +51,8 @@ class CalendarEventBannerView @JvmOverloads constructor(
4851
private val allDayLong by lazy { context.getString(R.string.calendarAllDayLong) }
4952

5053
private var navigateToAttendeesBottomSheet: ((List<Attendee>) -> Unit)? = null
54+
private var navigateToDownloadProgressDialog: (Attachment.(AttachmentIntentUtils.AttachmentIntentType) -> Unit)? = null
55+
private var showSnackBar: ((String) -> Unit)? = null
5156

5257
init {
5358
with(binding) {
@@ -61,7 +66,7 @@ class CalendarEventBannerView @JvmOverloads constructor(
6166
}
6267
}
6368

64-
fun loadCalendarEvent(calendarEvent: CalendarEvent, hasBeenDeleted: Boolean) = with(binding) {
69+
fun loadCalendarEvent(calendarEvent: CalendarEvent, hasBeenDeleted: Boolean, attachment: Attachment) = with(binding) {
6570
val startDate = calendarEvent.start.toDate()
6671
val endDate = calendarEvent.end.toDate()
6772

@@ -74,6 +79,12 @@ class CalendarEventBannerView @JvmOverloads constructor(
7479
}
7580

7681
setAttendees(calendarEvent.attendees)
82+
83+
addToCalendarButton.setOnClickListener {
84+
val navigationCallback = navigateToDownloadProgressDialog ?: return@setOnClickListener
85+
val snackBarCallback = showSnackBar ?: return@setOnClickListener
86+
attachment.openAttachment(context, navigationCallback, snackBarCallback)
87+
}
7788
}
7889

7990
private fun setWarnings(startDate: Date, hasBeenDeleted: Boolean) = with(binding) {
@@ -122,8 +133,14 @@ class CalendarEventBannerView @JvmOverloads constructor(
122133
manyAvatarsView.setAttendees(attendees)
123134
}
124135

125-
fun initCallback(navigateToAttendeesBottomSheet: (List<Attendee>) -> Unit) {
136+
fun initCallback(
137+
navigateToAttendeesBottomSheet: (List<Attendee>) -> Unit,
138+
navigateToDownloadProgressDialog: Attachment.(AttachmentIntentUtils.AttachmentIntentType) -> Unit,
139+
showSnackBar: (String) -> Unit,
140+
) {
126141
this.navigateToAttendeesBottomSheet = navigateToAttendeesBottomSheet
142+
this.navigateToDownloadProgressDialog = navigateToDownloadProgressDialog
143+
this.showSnackBar = showSnackBar
127144
}
128145

129146
private fun MaterialButton.handleChoiceButtonBehavior() {

app/src/main/java/com/infomaniak/mail/utils/AttachmentIntentUtils.kt

+37-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* Infomaniak Mail - Android
3-
* Copyright (C) 2023 Infomaniak Network SA
3+
* Copyright (C) 2023-2024 Infomaniak Network SA
44
*
55
* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -21,10 +21,13 @@ import android.content.ComponentName
2121
import android.content.Context
2222
import android.content.Intent
2323
import android.content.pm.PackageManager
24+
import android.os.Bundle
2425
import androidx.core.content.FileProvider
2526
import com.infomaniak.lib.core.utils.goToPlayStore
27+
import com.infomaniak.lib.core.utils.hasSupportedApplications
2628
import com.infomaniak.mail.R
2729
import com.infomaniak.mail.data.models.Attachment
30+
import com.infomaniak.mail.ui.main.thread.actions.DownloadAttachmentProgressDialogArgs
2831
import com.infomaniak.mail.utils.AttachmentIntentUtils.AttachmentIntentType.OPEN_WITH
2932
import com.infomaniak.mail.utils.AttachmentIntentUtils.AttachmentIntentType.SAVE_TO_DRIVE
3033
import io.sentry.Sentry
@@ -73,6 +76,39 @@ object AttachmentIntentUtils {
7376
}
7477
}
7578

79+
fun Attachment.executeIntent(
80+
context: Context,
81+
intentType: AttachmentIntentType,
82+
navigateToDownloadProgressDialog: Attachment.(intentType: AttachmentIntentType) -> Unit,
83+
) {
84+
if (hasUsableCache(context) || isInlineCachedFile(context)) {
85+
getIntentOrGoToPlaystore(context, intentType)?.let(context::startActivity)
86+
} else {
87+
navigateToDownloadProgressDialog(intentType)
88+
}
89+
}
90+
91+
fun Attachment.openAttachment(
92+
context: Context,
93+
navigateToDownloadProgressDialog: Attachment.(intentType: AttachmentIntentType) -> Unit,
94+
showSnackBar: (String) -> Unit,
95+
) {
96+
if (openWithIntent(context).hasSupportedApplications(context)) {
97+
executeIntent(context, OPEN_WITH, navigateToDownloadProgressDialog)
98+
} else {
99+
showSnackBar(context.getString(com.infomaniak.lib.core.R.string.errorNoSupportingAppFound))
100+
}
101+
}
102+
103+
fun Attachment.createDownloadDialogNavArgs(intentType: AttachmentIntentType): Bundle {
104+
return DownloadAttachmentProgressDialogArgs(
105+
attachmentResource = resource!!,
106+
attachmentName = name,
107+
attachmentType = getFileTypeFromMimeType(),
108+
intentType = intentType,
109+
).toBundle()
110+
}
111+
76112
enum class AttachmentIntentType {
77113
OPEN_WITH, SAVE_TO_DRIVE
78114
}

0 commit comments

Comments
 (0)