Skip to content

Commit

Permalink
Merge branch 'master' into app-lock-on-screen-off
Browse files Browse the repository at this point in the history
  • Loading branch information
LouisCAD committed Dec 18, 2024
2 parents e051eee + 51d5bd8 commit 534a532
Show file tree
Hide file tree
Showing 22 changed files with 172 additions and 140 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ android {
applicationId 'com.infomaniak.mail'
minSdk 25
targetSdk 34
versionCode 1_06_008_01
versionName '1.6.8'
versionCode 1_06_009_01
versionName '1.6.9'
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

setProperty "archivesBaseName", "infomaniak-mail-$versionName ($versionCode)"
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@
android:theme="@style/AppThemeLauncher">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.APP_EMAIL" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>

<meta-data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ object FlatteningSubBodiesSerializer : JsonTransformingSerializer<RealmList<SubB
outputList: MutableList<JsonElement> = mutableListOf(),
): List<JsonElement> {

val remoteSubBody = inputList.removeFirst() as? JsonObject
val remoteSubBody = inputList.removeAt(0) as? JsonObject
val remoteBody = remoteSubBody?.get("body") as? JsonObject
val remoteSubBodies = remoteBody?.get("subBody") as? JsonArray

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ class FolderController @Inject constructor(
suspend fun updateFolderAndChildren(id: String, realm: Realm, onUpdate: (Folder) -> Unit) {

tailrec fun updateChildrenRecursively(inputList: MutableList<Folder>) {
val folder = inputList.removeFirst()
val folder = inputList.removeAt(0)
onUpdate(folder)
inputList.addAll(folder.children)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ class RefreshController @Inject constructor(
"3_folderId" to folder.id,
"5_deleted" to activities.deletedShortUids.map { it },
"6_updated" to activities.updatedMessages.map { it.shortUid },
"7_updated" to activities.addedShortUids.map { it },
"7_added" to activities.addedShortUids.map { it },
),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,8 @@ class Message : RealmObject {
// TODO: Remove this `runCatching / getOrElse` when the issue is fixed
inline val folder
get() = runCatching {
threads.single { it.folder.id == folderId || it.folder.id == FolderController.SEARCH_FOLDER_ID }.folder
(threads.singleOrNull { it.folder.id == folderId }
?: threads.single { it.folder.id == FolderController.SEARCH_FOLDER_ID }).folder
}.getOrElse { exception ->

val reason = when {
Expand All @@ -180,6 +181,7 @@ class Message : RealmObject {
scope.setExtra("threadsCount", "${threads.count()}")
scope.setExtra("threadsFolder", "${threads.map { "role:[${it.folder.role?.name}] (id:[${it.folder.id}])" }}")
scope.setExtra("messageUid", uid)
scope.setExtra("folderId", folderId)
scope.setExtra("email", AccountUtils.currentMailboxEmail.toString())
scope.setExtra("exception", exception.message.toString())
}
Expand Down Expand Up @@ -322,7 +324,7 @@ class Message : RealmObject {
fun shouldBeExpanded(index: Int, lastIndex: Int) = !isDraft && (!isSeen || index == lastIndex)

fun toThread() = Thread().apply {
uid = this@Message.uid // TODO: Check if we can use random UUID instead ?
uid = this@Message.uid
folderId = this@Message.folderId
messagesIds += this@Message.messageIds
messages += this@Message
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import com.infomaniak.mail.ui.MainActivity
import com.infomaniak.mail.ui.alertDialogs.DescriptionAlertDialog
import com.infomaniak.mail.ui.alertDialogs.TitleAlertDialog
import com.infomaniak.mail.ui.main.SnackbarManager
import com.infomaniak.mail.ui.main.folder.ThreadListViewModel.ContentDisplayMode
import com.infomaniak.mail.ui.main.settings.appearance.swipe.SwipeActionsSettingsFragment
import com.infomaniak.mail.ui.main.thread.ThreadFragment
import com.infomaniak.mail.ui.newMessage.NewMessageActivityArgs
Expand All @@ -80,8 +81,6 @@ import com.infomaniak.mail.utils.Utils.isPermanentDeleteFolder
import com.infomaniak.mail.utils.Utils.runCatchingRealm
import com.infomaniak.mail.utils.extensions.*
import dagger.hilt.android.AndroidEntryPoint
import io.sentry.Sentry
import io.sentry.SentryLevel
import kotlinx.coroutines.launch
import java.util.Date
import javax.inject.Inject
Expand Down Expand Up @@ -164,6 +163,7 @@ class ThreadListFragment : TwoPaneFragment(), SwipeRefreshLayout.OnRefreshListen
observeUpdateInstall()
observeWebViewOutdated()
observeLoadMoreTriggers()
observeContentDisplayMode()
}.getOrDefault(Unit)

@ColorRes
Expand Down Expand Up @@ -706,46 +706,60 @@ class ThreadListFragment : TwoPaneFragment(), SwipeRefreshLayout.OnRefreshListen

private fun updateThreadsVisibility() = with(threadListViewModel) {

fun displayThreadsView(
areThereThreads: Boolean,
isFilterEnabled: Boolean,
isBooting: Boolean,
isWaitingFirstThreads: Boolean,
) {
val isNetworkConnected = mainViewModel.hasNetwork

with(binding) {
emptyStateView.isGone = true
threadsList.isVisible = true
}
// The folder's cursor is null, meaning it's the 1st time we are opening this folder.
val isCursorNull = currentFolderCursor == null

if (!areThereThreads && !isFilterEnabled && !isBooting && !isWaitingFirstThreads) {
val currentFolder = mainViewModel.currentFolder.value
Sentry.captureMessage(
"Should display threads is true but there are no threads to display",
SentryLevel.WARNING,
) { scope ->
scope.setExtra("cursor", "$currentFolderCursor")
scope.setExtra("folderRole", currentFolder?.role?.name.toString())
scope.setExtra("folderThreadsCount", "${currentFolder?.threads?.count()}")
}
}
}
// We have a cursor, but don't have any info about threads yet, meaning the app is still booting and loading things.
val isBooting = !isCursorNull && currentThreadsCount == null

val areThereThreads = (currentThreadsCount ?: 0) > 0
// We know that there is existing threads in this folder, so if we wait long enough, they'll be there.
val areThereThreadsSoon = mainViewModel.currentFolderLive.value?.oldMessagesUidsToFetch?.isNotEmpty() == true

// If there is network connectivity, but we either don't have a cursor yet
// or don't have threads yet (but we know that they are coming), it means
// we are opening this folder for the 1st time and we know we'll have a result at the end.
val isWaitingFirstThreads = (isCursorNull || areThereThreadsSoon) && isNetworkConnected

// There is at least 1 thread available to be displayed right now.
val areThereThreadsNow = (currentThreadsCount ?: 0) > 0

// If we filtered on something, it means we have threads, so we want to display the Threads display mode.
val isFilterEnabled = mainViewModel.currentFilter.value != ThreadFilter.ALL
val isCursorNull = currentFolderCursor == null
val isNetworkConnected = mainViewModel.hasNetwork
val isBooting = currentThreadsCount == null && !isCursorNull && isNetworkConnected
val isWaitingFirstThreads = isCursorNull && isNetworkConnected
val shouldDisplayThreadsView = isBooting || isWaitingFirstThreads || areThereThreads || isFilterEnabled

when {
shouldDisplayThreadsView -> displayThreadsView(areThereThreads, isFilterEnabled, isBooting, isWaitingFirstThreads)
isCursorNull || !isNetworkConnected -> setEmptyState(EmptyState.NETWORK)
isCurrentFolderRole(FolderRole.INBOX) -> setEmptyState(EmptyState.INBOX)
isCurrentFolderRole(FolderRole.TRASH) -> setEmptyState(EmptyState.TRASH)
else -> setEmptyState(EmptyState.FOLDER)
// If any of these conditions is true, it means Threads are on their way or the
// app is still loading things, so either way we want to display the Threads mode.
val shouldDisplayThreadsView = isBooting || isWaitingFirstThreads || areThereThreadsNow || isFilterEnabled

contentDisplayMode.value = when {
shouldDisplayThreadsView -> ContentDisplayMode.Threads
!isNetworkConnected -> ContentDisplayMode.NoNetwork
else -> ContentDisplayMode.EmptyFolder
}
}

private fun observeContentDisplayMode() {

fun folderEmptyState() = when {
isCurrentFolderRole(FolderRole.INBOX) -> EmptyState.INBOX
isCurrentFolderRole(FolderRole.TRASH) -> EmptyState.TRASH
else -> EmptyState.FOLDER
}

threadListViewModel.contentDisplayMode
.observe(viewLifecycleOwner) {
when (it) {
ContentDisplayMode.Threads, null -> displayThreadsView()
ContentDisplayMode.NoNetwork -> setEmptyState(EmptyState.NETWORK)
ContentDisplayMode.EmptyFolder -> setEmptyState(folderEmptyState())
}
}
}

private fun displayThreadsView() = with(binding) {
emptyStateView.isGone = true
threadsList.isVisible = true
}

private fun setEmptyState(emptyState: EmptyState): Unit = with(binding) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class ThreadListViewModel @Inject constructor(
val updatedAtTrigger = MutableLiveData<Unit>()
val isWebViewOutdated = MutableLiveData(false)

val contentDisplayMode = MutableLiveData(ContentDisplayMode.Threads)

var currentFolderCursor: String? = null
var currentThreadsCount: Int? = null

Expand Down Expand Up @@ -84,6 +86,12 @@ class ThreadListViewModel @Inject constructor(
isWebViewOutdated.value = canShowWebViewOutdated && hasOutdatedMajorVersion
}

enum class ContentDisplayMode {
Threads,
NoNetwork,
EmptyFolder,
}

companion object {
private const val WEBVIEW_OFFICIAL_PACKAGE_NAME = "com.google.android.webview"
private const val WEBVIEW_OFFICIAL_MIN_VERSION = 124
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class RecentSearchAdapter(

while (count() > MAX_HISTORY_COUNT) {
notifyItemRemoved(lastIndex)
removeLast()
removeAt(lastIndex)
}

return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ import androidx.recyclerview.widget.RecyclerView.Adapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.infomaniak.mail.R
import com.infomaniak.mail.data.models.Attachable
import com.infomaniak.mail.data.models.Attachment
import com.infomaniak.mail.data.models.Attachment.AttachmentDisposition
import com.infomaniak.mail.databinding.ItemAttachmentBinding
import com.infomaniak.mail.ui.main.thread.AttachmentAdapter.AttachmentViewHolder
import com.infomaniak.mail.utils.Utils.runCatchingRealm
Expand Down Expand Up @@ -78,13 +76,9 @@ class AttachmentAdapter(

override fun getItemCount(): Int = runCatchingRealm { attachments.count() }.getOrDefault(0)

fun setAttachments(newList: List<Attachable>) = runCatchingRealm {
fun submitList(newList: List<Attachable>) = runCatchingRealm {
attachments.clear()
attachments.addAll(newList)
}

fun submitList(newList: List<Attachment>) {
setAttachments(newList.filterNot { it.disposition == AttachmentDisposition.INLINE })
notifyDataSetChanged()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ class ThreadAdapter(
bindHeader(message)
bindAlerts(message.uid)
bindCalendarEvent(message)
bindAttachment(message)
bindAttachments(message)
bindContent(message)

onExpandOrCollapseMessage(message, shouldTrack = false)
Expand Down Expand Up @@ -462,7 +462,7 @@ class ThreadAdapter(
private fun ItemMessageBinding.areOneOrMoreAlertsVisible() = alerts.children.any { it.isVisible }

@SuppressLint("SetTextI18n")
private fun MessageViewHolder.bindAttachment(message: Message) = with(binding) {
private fun MessageViewHolder.bindAttachments(message: Message) = with(binding) {

if (!message.hasAttachable) {
attachmentLayout.root.isVisible = false
Expand All @@ -482,7 +482,7 @@ class ThreadAdapter(
)
}

attachmentAdapter.setAttachments(attachments)
attachmentAdapter.submitList(attachments)

attachmentLayout.attachmentsSizeText.text = totalAttachmentsSize
attachmentLayout.attachmentsInfo.setOnClickListener { threadAdapterCallbacks?.onDownloadAllClicked?.invoke(message) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import com.infomaniak.mail.R
import com.infomaniak.mail.data.LocalSettings
import com.infomaniak.mail.data.LocalSettings.ExternalContent
import com.infomaniak.mail.data.models.Attachment
import com.infomaniak.mail.data.models.Attachment.AttachmentDisposition
import com.infomaniak.mail.data.models.draft.Draft
import com.infomaniak.mail.data.models.draft.Draft.DraftAction
import com.infomaniak.mail.data.models.draft.Draft.DraftMode
Expand Down Expand Up @@ -601,7 +602,9 @@ class NewMessageFragment : Fragment() {

// When removing an Attachment, both counts will be the same, because the Adapter is already notified.
// We don't want to notify it again, because it will cancel the nice animation.
if (attachments.count() != attachmentAdapter.itemCount) attachmentAdapter.submitList(attachments)
if (attachments.count() != attachmentAdapter.itemCount) {
attachmentAdapter.submitList(attachments.filterNot { it.disposition == AttachmentDisposition.INLINE })
}

if (attachments.isEmpty()) TransitionManager.beginDelayedTransition(binding.root)
binding.attachmentsRecyclerView.isVisible = attachments.isNotEmpty()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,6 @@ class NewMessageViewModel @Inject constructor(
fun arrivedFromExistingDraft() = arrivedFromExistingDraft
fun draftLocalUuid() = draftLocalUuid
fun draftMode() = draftMode
fun recipient() = recipient
fun shouldLoadDistantResources() = shouldLoadDistantResources

fun initDraftAndViewModel(intent: Intent): LiveData<Draft?> = liveData(ioCoroutineContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,7 @@ class RecipientFieldView @JvmOverloads constructor(
onContactClicked = { addRecipient(it.email, it.name) },
onAddUnrecognizedContact = {
val input = textInput.text.toString()
if (input.isEmail()) {
addRecipient(email = input, name = input)
} else {
snackbarManager.setValue(context.getString(R.string.addUnknownRecipientInvalidEmail))
}
addRecipient(email = input, name = input)
},
snackbarManager = snackbarManager,
)
Expand Down Expand Up @@ -312,6 +308,11 @@ class RecipientFieldView @JvmOverloads constructor(

private fun addRecipient(email: String, name: String) {

if (!email.isEmail()) {
snackbarManager.setValue(context.getString(R.string.addUnknownRecipientInvalidEmail))
return
}

if (contactChipAdapter.itemCount > MAX_ALLOWED_RECIPIENT) {
snackbarManager.setValue(context.getString(R.string.tooManyRecipients))
return
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/com/infomaniak/mail/utils/ErrorCode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ object ErrorCode {
//region Mailbox
const val MAILBOX_LOCKED = "mailbox_locked"
const val ACCESS_DENIED = "access_denied"
const val NOT_AUTHORIZED = "not_authorized"
const val ERROR_WHILE_LINKING_MAILBOX = "error_while_linking_mailbox"
const val INVALID_MAILBOX_PASSWORD = "invalid_mailbox_password"
//endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ class FetchMessagesManager @Inject constructor(
private fun shouldLogToSentry(throwable: Throwable?): Boolean {
return when (throwable) {
is CancellationException, is NetworkException -> false
is ApiErrorException -> throwable.errorCode != ErrorCode.ACCESS_DENIED
is ApiErrorException -> {
throwable.errorCode != ErrorCode.ACCESS_DENIED && throwable.errorCode != ErrorCode.NOT_AUTHORIZED
}
else -> true
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ fun List<Folder>.flattenFolderChildrenAndRemoveMessages(dismissHiddenChildren: B
outputList: MutableList<Folder> = mutableListOf(),
): List<Folder> {

val folder = inputList.removeFirst()
val folder = inputList.removeAt(0)

val children = if (folder.isManaged()) {
outputList.add(folder.copyFromRealm(depth = 1u))
Expand Down
3 changes: 1 addition & 2 deletions app/src/main/res/layout/fragment_thread_list.xml
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,7 @@
android:clipToPadding="false"
android:paddingBottom="@dimen/recyclerViewPaddingBottom"
app:behind_swiped_item_icon_margin="@dimen/marginStandard"
tools:listitem="@layout/cardview_thread_item"
tools:visibility="visible" />
tools:listitem="@layout/cardview_thread_item" />

</LinearLayout>

Expand Down
Loading

0 comments on commit 534a532

Please sign in to comment.