Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 4e25aebd authored by Rafael Tonholo's avatar Rafael Tonholo
Browse files

feat: add support for swipe action with no archive folder

parent b5aad02a
Loading
Loading
Loading
Loading
+27 −8
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ import net.jcip.annotations.GuardedBy
import net.thunderbird.core.android.account.AccountManager
import net.thunderbird.core.android.account.Expunge
import net.thunderbird.core.android.account.LegacyAccount
import net.thunderbird.core.android.account.LegacyAccountWrapper
import net.thunderbird.core.android.account.SortType
import net.thunderbird.core.android.network.ConnectivityManager
import net.thunderbird.core.logging.legacy.Log
@@ -124,6 +125,7 @@ class MessageListFragment :
    private lateinit var adapter: MessageListAdapter

    private lateinit var accountUuids: Array<String>
    private lateinit var accounts: List<LegacyAccountWrapper>
    private var account: LegacyAccount? = null
    private var currentFolder: FolderInfoHolder? = null
    private var remoteSearchFuture: Future<*>? = null
@@ -248,6 +250,7 @@ class MessageListFragment :
            account = null
            accountUuids = searchAccounts.map { it.uuid }.toTypedArray()
        }
        accounts = searchAccounts.map(LegacyAccountWrapper::from)

        isSingleFolderMode = false
        if (isSingleAccountMode && localSearch.folderIds.size == 1) {
@@ -410,13 +413,13 @@ class MessageListFragment :

        val itemTouchHelper = ItemTouchHelper(
            MessageListSwipeCallback(
                requireContext(),
                context = requireContext(),
                resourceProvider = SwipeResourceProvider(requireContext()),
                swipeActionSupportProvider,
                swipeRightAction = K9.swipeRightAction,
                swipeLeftAction = K9.swipeLeftAction,
                adapter,
                swipeListener,
                swipeActionSupportProvider = swipeActionSupportProvider,
                swipeActions = K9.swipeLeftAction to K9.swipeRightAction,
                adapter = adapter,
                listener = swipeListener,
                accounts = accounts,
            ),
        )
        itemTouchHelper.attachToRecyclerView(recyclerView)
@@ -862,6 +865,14 @@ class MessageListFragment :
                ConfirmationDialogFragment.newInstance(dialogId, title, message, confirmText, cancelText)
            }

            R.id.dialog_setup_archive_folder -> {
                val title = "Email can not be archived"
                val message = "Configure archive folder now"
                val confirmText = "Set archive folder"
                val cancelText = "Skip for now"
                ConfirmationDialogFragment.newInstance(dialogId, title, message, confirmText, cancelText)
            }

            else -> {
                throw RuntimeException("Called showDialog(int) with unknown dialog id.")
            }
@@ -1195,6 +1206,14 @@ class MessageListFragment :
        onArchive(listOf(message))
    }

    private fun onArchive(item: MessageListItem) {
        if (!item.accountWrapper.hasArchiveFolder()) {
            showDialog(R.id.dialog_setup_archive_folder)
            return
        }
        onArchive(item.messageReference)
    }

    private fun onArchive(messages: List<MessageReference>) {
        if (!checkCopyOrMovePossible(messages, FolderOperation.MOVE)) return

@@ -1738,7 +1757,7 @@ class MessageListFragment :
                }

                SwipeAction.Archive -> {
                    onArchive(item.messageReference)
                    onArchive(item)
                }

                SwipeAction.Delete -> {
@@ -1776,7 +1795,7 @@ class MessageListFragment :
            SwipeAction.ToggleRead -> !isOutbox
            SwipeAction.ToggleStar -> !isOutbox
            SwipeAction.Archive -> {
                !isOutbox && item.account.hasArchiveFolder() && item.folderId != item.account.archiveFolderId
                !isOutbox && item.folderId != item.account.archiveFolderId
            }

            SwipeAction.Delete -> true
+2 −0
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ package com.fsck.k9.ui.messagelist
import app.k9mail.legacy.message.controller.MessageReference
import com.fsck.k9.mail.Address
import net.thunderbird.core.android.account.LegacyAccount
import net.thunderbird.core.android.account.LegacyAccountWrapper

data class MessageListItem(
    val account: LegacyAccount,
@@ -25,6 +26,7 @@ data class MessageListItem(
    val databaseId: Long,
    val threadRoot: Long,
) {
    val accountWrapper: LegacyAccountWrapper = LegacyAccountWrapper.from(account)
    val messageReference: MessageReference
        get() = MessageReference(account.uuid, folderId, messageUid)
}
+42 −24
Original line number Diff line number Diff line
@@ -18,17 +18,20 @@ import com.fsck.k9.ui.R
import com.google.android.material.color.ColorRoles
import com.google.android.material.textview.MaterialTextView
import kotlin.math.abs
import net.thunderbird.core.android.account.LegacyAccountWrapper

@SuppressLint("InflateParams")
class MessageListSwipeCallback(
    context: Context,
    resourceProvider: SwipeResourceProvider,
    private val swipeActionSupportProvider: SwipeActionSupportProvider,
    private val swipeRightAction: SwipeAction,
    private val swipeLeftAction: SwipeAction,
    swipeActions: Pair<SwipeAction, SwipeAction>,
    private val adapter: MessageListAdapter,
    private val listener: MessageListSwipeListener,
    private val accounts: List<LegacyAccountWrapper>,
) : ItemTouchHelper.Callback() {
    private val swipeLeftAction: SwipeAction = swipeActions.first
    private val swipeRightAction: SwipeAction = swipeActions.second
    private val swipePadding = context.resources.getDimension(R.dimen.messageListSwipeIconPadding).toInt()
    private val swipeThreshold = context.resources.getDimension(R.dimen.messageListSwipeThreshold)
    private val backgroundColorPaint = Paint()
@@ -39,11 +42,12 @@ class MessageListSwipeCallback(
    private val swipeLeftLayout: View
    private val swipeLeftIcon: ImageView
    private val swipeLeftText: MaterialTextView
    private val swipeRightConfig: SwipeActionConfig?
    private val swipeLeftConfig: SwipeActionConfig?
    private val swipeRightConfig: Map<LegacyAccountWrapper, SwipeActionConfig>
    private val swipeLeftConfig: Map<LegacyAccountWrapper, SwipeActionConfig>

    private var maxSwipeRightDistance: Int = -1
    private var maxSwipeLeftDistance: Int = -1
    private var activeSwipingMessageListItem: MessageListItem? = null

    init {
        val layoutInflater = LayoutInflater.from(context)
@@ -91,6 +95,7 @@ class MessageListSwipeCallback(

    override fun onSwipeStarted(viewHolder: ViewHolder, direction: Int) {
        val item = viewHolder.messageListItem ?: return
        activeSwipingMessageListItem = item

        // Mark view to prevent MessageListItemAnimator from interfering with swipe animations
        viewHolder.markAsSwiped(true)
@@ -130,6 +135,7 @@ class MessageListSwipeCallback(
        val item = viewHolder.messageListItem ?: return

        listener.onSwipeEnded(item)
        activeSwipingMessageListItem = null
    }

    override fun clearView(recyclerView: RecyclerView, viewHolder: ViewHolder) {
@@ -157,12 +163,12 @@ class MessageListSwipeCallback(

        if (dX != 0F) {
            canvas.withTranslation(x = view.left.toFloat(), y = view.top.toFloat()) {
                if (isCurrentlyActive || !success) {
                val holder = viewHolder as MessageViewHolder
                val item = adapter.getItemById(holder.uniqueId) ?: return@withTranslation
                if (isCurrentlyActive || !success) {
                    drawLayout(dX, viewWidth, viewHeight, item)
                } else {
                    drawBackground(dX, viewWidth, viewHeight)
                    drawBackground(dX, viewWidth, viewHeight, item)
                }
            }
        }
@@ -170,13 +176,13 @@ class MessageListSwipeCallback(
        super.onChildDraw(canvas, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive, success)
    }

    private fun Canvas.drawBackground(dX: Float, width: Int, height: Int) {
    private fun Canvas.drawBackground(dX: Float, width: Int, height: Int, item: MessageListItem) {
        val swipeActionConfig = if (dX > 0) swipeRightConfig else swipeLeftConfig
        if (swipeActionConfig == null) {
            error("drawBackground() called despite swipeActionConfig == null")
        if (swipeActionConfig[item.accountWrapper] == null) {
            error("drawBackground() called despite swipeActionConfig[item.accountWrapper] == null")
        }

        backgroundColorPaint.color = swipeActionConfig.backgroundColor
        backgroundColorPaint.color = swipeActionConfig.getValue(item.accountWrapper).backgroundColor
        drawRect(
            0F,
            0F,
@@ -189,8 +195,9 @@ class MessageListSwipeCallback(
    private fun Canvas.drawLayout(dX: Float, width: Int, height: Int, item: MessageListItem) {
        val swipeRight = dX > 0
        val swipeThresholdReached = abs(dX) > swipeThreshold
        val account = item.accountWrapper

        val swipeActionConfig = if (swipeRight) swipeRightConfig else swipeLeftConfig
        val swipeActionConfig = if (swipeRight) swipeRightConfig[account] else swipeLeftConfig[account]
        if (swipeActionConfig == null) {
            error("drawLayout() called despite swipeActionConfig == null")
        }
@@ -278,11 +285,20 @@ class MessageListSwipeCallback(
    }

    override fun shouldAnimateOut(direction: Int): Boolean {
        return when (direction) {
            ItemTouchHelper.RIGHT -> swipeRightAction.removesItem
            ItemTouchHelper.LEFT -> swipeLeftAction.removesItem
        val swipeAction = when (direction) {
            ItemTouchHelper.RIGHT -> swipeRightAction
            ItemTouchHelper.LEFT -> swipeLeftAction
            else -> error("Unsupported direction")
        }

        return when (swipeAction) {
            SwipeAction.Archive ->
                activeSwipingMessageListItem
                    ?.accountWrapper
                    ?.hasArchiveFolder() == true

            else -> swipeAction.removesItem
        }
    }

    override fun getAnimationDuration(
@@ -298,19 +314,21 @@ class MessageListSwipeCallback(
    private fun setupSwipeAction(
        swipeAction: SwipeAction,
        resourceProvider: SwipeResourceProvider,
    ): SwipeActionConfig? {
    ): Map<LegacyAccountWrapper, SwipeActionConfig> {
        return if (swipeAction == SwipeAction.None) {
            null
            mapOf()
        } else {
            accounts.associateWith { account ->
                SwipeActionConfig(
                colorRoles = resourceProvider.getActionColorRoles(swipeAction),
                    colorRoles = resourceProvider.getActionColorRoles(swipeAction, account),
                    icon = resourceProvider.getActionIcon(swipeAction),
                    iconToggled = resourceProvider.getActionIconToggled(swipeAction),
                actionName = resourceProvider.getActionName(swipeAction),
                    actionName = resourceProvider.getActionName(swipeAction, account),
                    actionNameToggled = resourceProvider.getActionNameToggled(swipeAction),
                )
            }
        }
    }

    private fun isToggled(swipeAction: SwipeAction, item: MessageListItem): Boolean {
        return when (swipeAction) {
+13 −6
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ import com.fsck.k9.SwipeAction
import com.fsck.k9.ui.R
import com.google.android.material.color.ColorRoles
import com.google.android.material.color.MaterialColors
import net.thunderbird.core.android.account.LegacyAccountWrapper

class SwipeResourceProvider(private val context: Context) {

@@ -40,20 +41,23 @@ class SwipeResourceProvider(private val context: Context) {
        }
    }

    fun getActionColorRoles(action: SwipeAction): ColorRoles {
        val harmonizedColor = MaterialColors.harmonizeWithPrimary(context, getActionColor(action))
    fun getActionColorRoles(action: SwipeAction, account: LegacyAccountWrapper): ColorRoles {
        val harmonizedColor = MaterialColors.harmonizeWithPrimary(context, getActionColor(action, account))
        return MaterialColors.getColorRoles(context, harmonizedColor)
    }

    @ColorInt
    private fun getActionColor(action: SwipeAction): Int {
    private fun getActionColor(action: SwipeAction, account: LegacyAccountWrapper): Int {
        return context.resolveColorAttribute(
            when (action) {
                SwipeAction.None -> error("action == SwipeAction.None")
                SwipeAction.ToggleSelection -> R.attr.messageListSwipeSelectColor
                SwipeAction.ToggleRead -> R.attr.messageListSwipeToggleReadColor
                SwipeAction.ToggleStar -> R.attr.messageListSwipeToggleStarColor
                SwipeAction.Archive -> R.attr.messageListSwipeArchiveColor
                SwipeAction.Archive if account.hasArchiveFolder() ->
                    R.attr.messageListSwipeArchiveColor

                SwipeAction.Archive -> com.google.android.material.R.attr.colorSurfaceContainerLowest
                SwipeAction.Delete -> R.attr.messageListSwipeDeleteColor
                SwipeAction.Spam -> R.attr.messageListSwipeSpamColor
                SwipeAction.Move -> R.attr.messageListSwipeMoveColor
@@ -61,14 +65,17 @@ class SwipeResourceProvider(private val context: Context) {
        )
    }

    fun getActionName(action: SwipeAction): String {
    fun getActionName(action: SwipeAction, account: LegacyAccountWrapper): String {
        return context.loadString(
            when (action) {
                SwipeAction.None -> error("action == SwipeAction.None")
                SwipeAction.ToggleSelection -> R.string.swipe_action_select
                SwipeAction.ToggleRead -> R.string.swipe_action_mark_as_read
                SwipeAction.ToggleStar -> R.string.swipe_action_add_star
                SwipeAction.Archive -> R.string.swipe_action_archive
                SwipeAction.Archive if account.hasArchiveFolder() ->
                    R.string.swipe_action_archive

                SwipeAction.Archive -> R.string.swipe_action_archive_folder_not_set
                SwipeAction.Delete -> R.string.swipe_action_delete
                SwipeAction.Spam -> R.string.swipe_action_spam
                SwipeAction.Move -> R.string.swipe_action_move
+1 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
    <item type="id" name="dialog_confirm_delete" />
    <item type="id" name="dialog_confirm_spam" />
    <item type="id" name="dialog_attachment_progress" />
    <item type="id" name="dialog_setup_archive_folder" />

    <item type="id" name="settings_export_list_general_item" />
    <item type="id" name="settings_export_list_account_item" />
Loading