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

Unverified Commit 59e06582 authored by cketti's avatar cketti Committed by GitHub
Browse files

Merge pull request #6482 from thundernest/reply_actions_in_message_view

Message View Redesign: Reply actions in message view
parents 6be40510 53aeb422
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
package com.fsck.k9.message

import com.fsck.k9.Account
import com.fsck.k9.helper.ReplyToParser
import com.fsck.k9.mail.Message

/**
 * Figures out which reply actions are available to the user.
 */
class ReplyActionStrategy(private val replyRoParser: ReplyToParser) {
    fun getReplyActions(account: Account, message: Message): ReplyActions {
        val recipientsToReplyTo = replyRoParser.getRecipientsToReplyTo(message, account)
        val recipientsToReplyAllTo = replyRoParser.getRecipientsToReplyAllTo(message, account)

        val replyToAllCount = recipientsToReplyAllTo.to.size + recipientsToReplyAllTo.cc.size
        return if (replyToAllCount <= 1) {
            if (recipientsToReplyTo.to.isEmpty()) {
                ReplyActions(defaultAction = null)
            } else {
                ReplyActions(defaultAction = ReplyAction.REPLY)
            }
        } else {
            ReplyActions(defaultAction = ReplyAction.REPLY_ALL, additionalActions = listOf(ReplyAction.REPLY))
        }
    }
}

data class ReplyActions(
    val defaultAction: ReplyAction?,
    val additionalActions: List<ReplyAction> = emptyList()
)

enum class ReplyAction {
    REPLY,
    REPLY_ALL
}
+111 −0
Original line number Diff line number Diff line
package com.fsck.k9.message

import com.fsck.k9.Account
import com.fsck.k9.Identity
import com.fsck.k9.helper.ReplyToParser
import com.fsck.k9.mail.buildMessage
import com.google.common.truth.Truth.assertThat
import org.junit.Test

private const val IDENTITY_EMAIL_ADDRESS = "myself@domain.example"

class ReplyActionStrategyTest {
    private val account = createAccount()
    private val replyActionStrategy = ReplyActionStrategy(ReplyToParser())

    @Test
    fun `message sent to only our identity`() {
        val message = buildMessage {
            header("From", "sender@domain.example")
            header("To", IDENTITY_EMAIL_ADDRESS)
        }

        val replyActions = replyActionStrategy.getReplyActions(account, message)

        assertThat(replyActions.defaultAction).isEqualTo(ReplyAction.REPLY)
        assertThat(replyActions.additionalActions).isEmpty()
    }

    @Test
    fun `message sent to our identity and others`() {
        val message = buildMessage {
            header("From", "sender@domain.example")
            header("To", "$IDENTITY_EMAIL_ADDRESS, other@domain.example")
        }

        val replyActions = replyActionStrategy.getReplyActions(account, message)

        assertThat(replyActions.defaultAction).isEqualTo(ReplyAction.REPLY_ALL)
        assertThat(replyActions.additionalActions).containsExactly(ReplyAction.REPLY)
    }

    @Test
    fun `message sent to our identity and others (CC)`() {
        val message = buildMessage {
            header("From", "sender@domain.example")
            header("Cc", "$IDENTITY_EMAIL_ADDRESS, other@domain.example")
        }

        val replyActions = replyActionStrategy.getReplyActions(account, message)

        assertThat(replyActions.defaultAction).isEqualTo(ReplyAction.REPLY_ALL)
        assertThat(replyActions.additionalActions).containsExactly(ReplyAction.REPLY)
    }

    @Test
    fun `message sent to our identity and others (To+CC)`() {
        val message = buildMessage {
            header("From", "sender@domain.example")
            header("To", IDENTITY_EMAIL_ADDRESS)
            header("Cc", "other@domain.example")
        }

        val replyActions = replyActionStrategy.getReplyActions(account, message)

        assertThat(replyActions.defaultAction).isEqualTo(ReplyAction.REPLY_ALL)
        assertThat(replyActions.additionalActions).containsExactly(ReplyAction.REPLY)
    }

    @Test
    fun `message sent to our identity and others (CC+To)`() {
        val message = buildMessage {
            header("From", "sender@domain.example")
            header("To", "other@domain.example")
            header("Cc", IDENTITY_EMAIL_ADDRESS)
        }

        val replyActions = replyActionStrategy.getReplyActions(account, message)

        assertThat(replyActions.defaultAction).isEqualTo(ReplyAction.REPLY_ALL)
        assertThat(replyActions.additionalActions).containsExactly(ReplyAction.REPLY)
    }

    @Test
    fun `message where neither sender nor recipient addresses belong to account`() {
        val message = buildMessage {
            header("From", "sender@domain.example")
            header("To", "recipient@domain.example")
        }

        val replyActions = replyActionStrategy.getReplyActions(account, message)

        assertThat(replyActions.defaultAction).isEqualTo(ReplyAction.REPLY_ALL)
        assertThat(replyActions.additionalActions).containsExactly(ReplyAction.REPLY)
    }

    @Test
    fun `message without any sender or recipient headers`() {
        val message = buildMessage {}

        val replyActions = replyActionStrategy.getReplyActions(account, message)

        assertThat(replyActions.defaultAction).isNull()
        assertThat(replyActions.additionalActions).isEmpty()
    }

    private fun createAccount(): Account {
        return Account("00000000-0000-4000-0000-000000000000").apply {
            identities += Identity(name = "Myself", email = IDENTITY_EMAIL_ADDRESS)
        }
    }
}
+5 −0
Original line number Diff line number Diff line
package com.fsck.k9.ui.messageview

fun interface MessageHeaderOnMenuItemClickListener {
    fun onMenuItemClick(itemId: Int)
}
+2 −2
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.PopupMenu.OnMenuItemClickListener;

import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -226,7 +226,7 @@ public class MessageTopView extends LinearLayout {
        mHeaderContainer.setOnFlagListener(listener);
    }

    public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
    public void setOnMenuItemClickListener(MessageHeaderOnMenuItemClickListener listener) {
        mHeaderContainer.setOnMenuItemClickListener(listener);
    }

+2 −6
Original line number Diff line number Diff line
@@ -158,9 +158,7 @@ class MessageViewFragment :
            onToggleFlagged()
        }

        messageTopView.setOnMenuItemClickListener { item ->
            onReplyMenuItemClicked(item.itemId)
        }
        messageTopView.setOnMenuItemClickListener(::onReplyMenuItemClicked)

        messageTopView.setOnDownloadButtonClickListener {
            onDownloadButtonClicked()
@@ -381,7 +379,7 @@ class MessageViewFragment :
        messageTopView.setSubject(displaySubject)
    }

    private fun onReplyMenuItemClicked(itemId: Int): Boolean {
    private fun onReplyMenuItemClicked(itemId: Int) {
        when (itemId) {
            R.id.reply -> onReply()
            R.id.reply_all -> onReplyAll()
@@ -390,8 +388,6 @@ class MessageViewFragment :
            R.id.share -> onSendAlternate()
            else -> error("Missing handler for reply menu item $itemId")
        }

        return true
    }

    private fun onDownloadButtonClicked() {
Loading