Merge pull request #6647 from thundernest/message_details_folder_name

Display folder name in message details screen
This commit is contained in:
cketti 2023-02-10 10:55:25 +01:00 committed by GitHub
commit ae374bd5f4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 122 additions and 4 deletions

View file

@ -0,0 +1,34 @@
package com.fsck.k9.ui.messagedetails
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.DrawableRes
import com.fsck.k9.ui.R
import com.mikepenz.fastadapter.FastAdapter
import com.mikepenz.fastadapter.items.AbstractItem
internal class FolderNameItem(
val displayName: String,
@DrawableRes val iconResourceId: Int
) : AbstractItem<FolderNameItem.ViewHolder>() {
override val type: Int = R.id.message_details_folder_name
override val layoutRes = R.layout.message_details_folder_name_item
override fun getViewHolder(v: View) = ViewHolder(v)
class ViewHolder(view: View) : FastAdapter.ViewHolder<FolderNameItem>(view) {
private val folderIcon: ImageView = view.findViewById(R.id.folder_icon)
private val folderName = view.findViewById<TextView>(R.id.folder_name)
override fun bindView(item: FolderNameItem, payloads: List<Any>) {
folderName.text = item.displayName
folderIcon.setImageResource(item.iconResourceId)
}
override fun unbindView(item: FolderNameItem) {
folderName.text = null
folderIcon.setImageDrawable(null)
}
}
}

View file

@ -8,11 +8,13 @@ val messageDetailsUiModule = module {
MessageDetailsViewModel( MessageDetailsViewModel(
resources = get(), resources = get(),
messageRepository = get(), messageRepository = get(),
folderRepository = get(),
contactSettingsProvider = get(), contactSettingsProvider = get(),
contacts = get(), contacts = get(),
clipboardManager = get(), clipboardManager = get(),
accountManager = get(), accountManager = get(),
participantFormatter = get() participantFormatter = get(),
folderNameFormatter = get()
) )
} }
factory { ContactSettingsProvider() } factory { ContactSettingsProvider() }

View file

@ -24,6 +24,7 @@ import com.fsck.k9.controller.MessageReference
import com.fsck.k9.mail.Address import com.fsck.k9.mail.Address
import com.fsck.k9.mailstore.CryptoResultAnnotation import com.fsck.k9.mailstore.CryptoResultAnnotation
import com.fsck.k9.ui.R import com.fsck.k9.ui.R
import com.fsck.k9.ui.folders.FolderIconProvider
import com.fsck.k9.ui.observe import com.fsck.k9.ui.observe
import com.fsck.k9.ui.withArguments import com.fsck.k9.ui.withArguments
import com.mikepenz.fastadapter.FastAdapter import com.mikepenz.fastadapter.FastAdapter
@ -32,12 +33,14 @@ import com.mikepenz.fastadapter.adapters.ItemAdapter
import com.mikepenz.fastadapter.listeners.ClickEventHook import com.mikepenz.fastadapter.listeners.ClickEventHook
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
class MessageDetailsFragment : ToolbarBottomSheetDialogFragment() { class MessageDetailsFragment : ToolbarBottomSheetDialogFragment() {
private val viewModel: MessageDetailsViewModel by viewModel() private val viewModel: MessageDetailsViewModel by viewModel()
private val addToContactsLauncher: AddToContactsLauncher by inject() private val addToContactsLauncher: AddToContactsLauncher by inject()
private val showContactLauncher: ShowContactLauncher by inject() private val showContactLauncher: ShowContactLauncher by inject()
private val contactPictureLoader: ContactPictureLoader by inject() private val contactPictureLoader: ContactPictureLoader by inject()
private val folderIconProvider: FolderIconProvider by inject { parametersOf(requireContext().theme) }
private lateinit var messageReference: MessageReference private lateinit var messageReference: MessageReference
@ -138,6 +141,10 @@ class MessageDetailsFragment : ToolbarBottomSheetDialogFragment() {
addParticipants(details.to, R.string.message_details_to_section_title, showContactPicture) addParticipants(details.to, R.string.message_details_to_section_title, showContactPicture)
addParticipants(details.cc, R.string.message_details_cc_section_title, showContactPicture) addParticipants(details.cc, R.string.message_details_cc_section_title, showContactPicture)
addParticipants(details.bcc, R.string.message_details_bcc_section_title, showContactPicture) addParticipants(details.bcc, R.string.message_details_bcc_section_title, showContactPicture)
if (details.folder != null) {
addFolderName(details.folder)
}
} }
val adapter = FastAdapter.with(itemAdapter).apply { val adapter = FastAdapter.with(itemAdapter).apply {
@ -166,6 +173,14 @@ class MessageDetailsFragment : ToolbarBottomSheetDialogFragment() {
} }
} }
private fun ItemAdapter<GenericItem>.addFolderName(folder: FolderInfoUi) {
val folderNameItem = FolderNameItem(
displayName = folder.displayName,
iconResourceId = folderIconProvider.getFolderIcon(folder.type)
)
add(folderNameItem)
}
private val cryptoStatusClickEventHook = object : ClickEventHook<CryptoStatusItem>() { private val cryptoStatusClickEventHook = object : ClickEventHook<CryptoStatusItem>() {
override fun onBind(viewHolder: RecyclerView.ViewHolder): View? { override fun onBind(viewHolder: RecyclerView.ViewHolder): View? {
return if (viewHolder is CryptoStatusItem.ViewHolder) { return if (viewHolder is CryptoStatusItem.ViewHolder) {

View file

@ -2,6 +2,7 @@ package com.fsck.k9.ui.messagedetails
import android.net.Uri import android.net.Uri
import com.fsck.k9.mail.Address import com.fsck.k9.mail.Address
import com.fsck.k9.mailstore.FolderType
import com.fsck.k9.view.MessageCryptoDisplayStatus import com.fsck.k9.view.MessageCryptoDisplayStatus
data class MessageDetailsUi( data class MessageDetailsUi(
@ -12,7 +13,8 @@ data class MessageDetailsUi(
val replyTo: List<Participant>, val replyTo: List<Participant>,
val to: List<Participant>, val to: List<Participant>,
val cc: List<Participant>, val cc: List<Participant>,
val bcc: List<Participant> val bcc: List<Participant>,
val folder: FolderInfoUi?
) )
data class CryptoDetails( data class CryptoDetails(
@ -31,3 +33,8 @@ data class Participant(
val address: Address val address: Address
get() = Address(emailAddress, displayName?.toString()) get() = Address(emailAddress, displayName?.toString())
} }
data class FolderInfoUi(
val displayName: String,
val type: FolderType
)

View file

@ -10,10 +10,13 @@ import com.fsck.k9.helper.ClipboardManager
import com.fsck.k9.helper.Contacts import com.fsck.k9.helper.Contacts
import com.fsck.k9.mail.Address import com.fsck.k9.mail.Address
import com.fsck.k9.mailstore.CryptoResultAnnotation import com.fsck.k9.mailstore.CryptoResultAnnotation
import com.fsck.k9.mailstore.Folder
import com.fsck.k9.mailstore.FolderRepository
import com.fsck.k9.mailstore.MessageDate import com.fsck.k9.mailstore.MessageDate
import com.fsck.k9.mailstore.MessageRepository import com.fsck.k9.mailstore.MessageRepository
import com.fsck.k9.preferences.AccountManager import com.fsck.k9.preferences.AccountManager
import com.fsck.k9.ui.R import com.fsck.k9.ui.R
import com.fsck.k9.ui.folders.FolderNameFormatter
import com.fsck.k9.view.MessageCryptoDisplayStatus import com.fsck.k9.view.MessageCryptoDisplayStatus
import java.text.DateFormat import java.text.DateFormat
import java.util.Locale import java.util.Locale
@ -27,11 +30,13 @@ import kotlinx.coroutines.launch
internal class MessageDetailsViewModel( internal class MessageDetailsViewModel(
private val resources: Resources, private val resources: Resources,
private val messageRepository: MessageRepository, private val messageRepository: MessageRepository,
private val folderRepository: FolderRepository,
private val contactSettingsProvider: ContactSettingsProvider, private val contactSettingsProvider: ContactSettingsProvider,
private val contacts: Contacts, private val contacts: Contacts,
private val clipboardManager: ClipboardManager, private val clipboardManager: ClipboardManager,
private val accountManager: AccountManager, private val accountManager: AccountManager,
private val participantFormatter: MessageDetailsParticipantFormatter private val participantFormatter: MessageDetailsParticipantFormatter,
private val folderNameFormatter: FolderNameFormatter
) : ViewModel() { ) : ViewModel() {
private val dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM, Locale.getDefault()) private val dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM, Locale.getDefault())
private val uiState = MutableStateFlow<MessageDetailsState>(MessageDetailsState.Loading) private val uiState = MutableStateFlow<MessageDetailsState>(MessageDetailsState.Loading)
@ -46,6 +51,8 @@ internal class MessageDetailsViewModel(
val account = accountManager.getAccount(messageReference.accountUuid) ?: error("Account not found") val account = accountManager.getAccount(messageReference.accountUuid) ?: error("Account not found")
val messageDetails = messageRepository.getMessageDetails(messageReference) val messageDetails = messageRepository.getMessageDetails(messageReference)
val folder = folderRepository.getFolder(account, folderId = messageReference.folderId)
val senderList = messageDetails.sender?.let { listOf(it) } ?: emptyList() val senderList = messageDetails.sender?.let { listOf(it) } ?: emptyList()
val messageDetailsUi = MessageDetailsUi( val messageDetailsUi = MessageDetailsUi(
date = buildDisplayDate(messageDetails.date), date = buildDisplayDate(messageDetails.date),
@ -55,7 +62,8 @@ internal class MessageDetailsViewModel(
replyTo = messageDetails.replyTo.toParticipants(account), replyTo = messageDetails.replyTo.toParticipants(account),
to = messageDetails.to.toParticipants(account), to = messageDetails.to.toParticipants(account),
cc = messageDetails.cc.toParticipants(account), cc = messageDetails.cc.toParticipants(account),
bcc = messageDetails.bcc.toParticipants(account) bcc = messageDetails.bcc.toParticipants(account),
folder = folder?.toFolderInfo()
) )
MessageDetailsState.DataLoaded( MessageDetailsState.DataLoaded(
@ -100,6 +108,13 @@ internal class MessageDetailsViewModel(
} }
} }
private fun Folder.toFolderInfo(): FolderInfoUi {
return FolderInfoUi(
displayName = folderNameFormatter.displayName(this),
type = this.type
)
}
fun onCryptoStatusClicked() { fun onCryptoStatusClicked() {
val cryptoResult = cryptoResult ?: return val cryptoResult = cryptoResult ?: return
val cryptoStatus = MessageCryptoDisplayStatus.fromResultAnnotation(cryptoResult) val cryptoStatus = MessageCryptoDisplayStatus.fromResultAnnotation(cryptoResult)

View file

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/keyline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="72dp" />
<ImageView
android:id="@+id/folder_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:contentDescription="@null"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/keyline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
tools:srcCompat="@drawable/ic_folder" />
<TextView
android:id="@+id/folder_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:textAppearance="?attr/textAppearanceBody2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/keyline"
app:layout_constraintTop_toTopOf="parent"
tools:text="Regular folder" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -4,5 +4,6 @@
<item type="id" name="message_details_crypto_status"/> <item type="id" name="message_details_crypto_status"/>
<item type="id" name="message_details_section_header"/> <item type="id" name="message_details_section_header"/>
<item type="id" name="message_details_participant"/> <item type="id" name="message_details_participant"/>
<item type="id" name="message_details_folder_name"/>
<item type="id" name="message_details_divider"/> <item type="id" name="message_details_divider"/>
</resources> </resources>