@@ -11,7 +11,6 @@ import android.view.ViewGroup
1111import androidx.compose.ui.platform.ComposeView
1212import androidx.recyclerview.widget.DiffUtil
1313import androidx.recyclerview.widget.RecyclerView
14- import androidx.recyclerview.widget.RecyclerView.NO_POSITION
1514import app.k9mail.feature.launcher.FeatureLauncherActivity
1615import app.k9mail.feature.launcher.FeatureLauncherTarget
1716import app.k9mail.legacy.message.controller.MessageReference
@@ -31,6 +30,7 @@ import net.thunderbird.core.ui.theme.api.FeatureThemeProvider
3130import net.thunderbird.feature.notification.api.ui.action.NotificationAction
3231
3332private const val FOOTER_ID = 1L
33+ private const val IN_APP_NOTIFICATION_BANNER_INLINE_LIST_ID = - 1L
3434
3535private const val TYPE_MESSAGE = 0
3636private const val TYPE_FOOTER = 1
@@ -51,14 +51,15 @@ class MessageListAdapter internal constructor(
5151
5252 val colors: MessageViewHolderColors = MessageViewHolderColors .resolveColors(theme)
5353
54- var messages : List <MessageListItem > = emptyList()
54+ var viewItems : List <MessageListViewItem > = emptyList()
5555 @SuppressLint(" NotifyDataSetChanged" )
5656 set(value) {
5757 val oldMessageList = field
5858
5959 field = value
60- accountUuids = value.map { it.account.uuid }.toSet()
61- messagesMap = value.associateBy { it.uniqueId }
60+ val messages = value.filterMessageListItem()
61+ accountUuids = messages.map { it.account.uuid }.toSet()
62+ messagesMap = messages.associateBy { it.uniqueId }
6263
6364 if (selected.isNotEmpty()) {
6465 val uniqueIds = messagesMap.keys
@@ -71,6 +72,7 @@ class MessageListAdapter internal constructor(
7172 diffResult.dispatchUpdatesTo(this )
7273 }
7374
75+ private val messages get() = viewItems.filterMessageListItem()
7476 private var messagesMap = emptyMap<Long , MessageListItem >()
7577 private var accountUuids = emptySet<String >()
7678
@@ -122,34 +124,6 @@ class MessageListAdapter internal constructor(
122124 var selectedCount: Int = 0
123125 private set
124126
125- var footerText: String? = null
126- set(value) {
127- if (field == value) return
128-
129- val hadFooterText = field != null
130- val previousFooterPosition = footerPosition
131- field = value
132-
133- if (hadFooterText) {
134- if (value == null ) {
135- notifyItemRemoved(previousFooterPosition)
136- } else {
137- notifyItemChanged(footerPosition)
138- }
139- } else {
140- notifyItemInserted(footerPosition)
141- }
142- }
143-
144- private val hasFooter: Boolean
145- get() = footerText != null
146-
147- private val lastMessagePosition: Int
148- get() = messages.lastIndex
149-
150- private val footerPosition: Int
151- get() = if (hasFooter) lastMessagePosition + 1 else NO_POSITION
152-
153127 private val messageClickedListener = OnClickListener { view: View ->
154128 val messageListItem = getItemFromView(view) ? : return @OnClickListener
155129 listItemListener.onMessageClicked(messageListItem)
@@ -185,48 +159,48 @@ class MessageListAdapter internal constructor(
185159 setHasStableIds(true )
186160 }
187161
188- override fun getItemCount (): Int = messages .size + if (hasFooter) 1 else 0
162+ override fun getItemCount (): Int = viewItems .size
189163
190164 override fun getItemId (position : Int ): Long {
191- return if (position <= lastMessagePosition) {
192- messages[position].uniqueId
193- } else {
194- FOOTER_ID
195- }
165+ return viewItems[position].viewId
196166 }
197167
198168 override fun getItemViewType (position : Int ): Int {
199- return when {
200- position == 0 && isInAppNotificationEnabled -> TYPE_IN_APP_NOTIFICATION_BANNER_INLINE_LIST
201- position <= lastMessagePosition -> TYPE_MESSAGE
202- else -> TYPE_FOOTER
203- }
169+ return viewItems[position].viewType
204170 }
205171
206- private fun getItem (position : Int ): MessageListItem = messages [position]
172+ private fun getItem (position : Int ): MessageListItem = (viewItems [position] as MessageListViewItem . Message ).item
207173
208174 fun getItemById (uniqueId : Long ): MessageListItem ? {
209175 return messagesMap[uniqueId]
210176 }
211177
212178 fun getItem (messageReference : MessageReference ): MessageListItem ? {
213- return messages.firstOrNull {
214- it.account.uuid == messageReference.accountUuid &&
215- it.folderId == messageReference.folderId &&
216- it.messageUid == messageReference.uid
217- }
179+ return viewItems
180+ .filterMessageListItem()
181+ .firstOrNull {
182+ it.account.uuid == messageReference.accountUuid &&
183+ it.folderId == messageReference.folderId &&
184+ it.messageUid == messageReference.uid
185+ }
218186 }
219187
220188 fun getPosition (messageListItem : MessageListItem ): Int? {
221- return messages.indexOf(messageListItem).takeIf { it != - 1 }
189+ return viewItems
190+ .map { (it as ? MessageListViewItem .Message )?.item }
191+ .indexOf(messageListItem).takeIf { it != - 1 }
222192 }
223193
224194 private fun getPosition (messageReference : MessageReference ? ): Int? {
225195 if (messageReference == null ) return null
226196
227- return messages.indexOfFirst {
228- messageReference.equals(it.account.uuid, it.folderId, it.messageUid)
229- }.takeIf { it != - 1 }
197+ return viewItems
198+ .map { (it as ? MessageListViewItem .Message )?.item }
199+ .indexOfFirst {
200+ it != null &&
201+ messageReference.equals(it.account.uuid, it.folderId, it.messageUid)
202+ }
203+ .takeIf { it != - 1 }
230204 }
231205
232206 override fun onCreateViewHolder (parent : ViewGroup , viewType : Int ): MessageListViewHolder {
@@ -241,6 +215,7 @@ class MessageListAdapter internal constructor(
241215 }
242216
243217 TYPE_FOOTER -> FooterViewHolder .create(layoutInflater, parent, footerClickListener)
218+
244219 TYPE_IN_APP_NOTIFICATION_BANNER_INLINE_LIST if isInAppNotificationEnabled ->
245220 BannerInlineListInAppNotificationViewHolder (
246221 view = ComposeView (context = parent.context),
@@ -325,7 +300,8 @@ class MessageListAdapter internal constructor(
325300
326301 TYPE_FOOTER -> {
327302 val footerViewHolder = holder as FooterViewHolder
328- footerViewHolder.bind(footerText)
303+ val footer = viewItems[position] as MessageListViewItem .Footer
304+ footerViewHolder.bind(footer.text)
329305 }
330306
331307 else -> {
@@ -386,18 +362,17 @@ class MessageListAdapter internal constructor(
386362 }
387363
388364 private fun calculateSelectionCount (): Int {
389- if (selected.isEmpty()) {
390- return 0
391- }
392-
393- if (! appearance.showingThreadedList) {
394- return selected.size
365+ return when {
366+ selected.isEmpty() -> 0
367+ ! appearance.showingThreadedList -> selected.size
368+ else ->
369+ viewItems
370+ .asSequence()
371+ .filterIsInstance<MessageListViewItem .Message >()
372+ .map { it.item }
373+ .filter { it.uniqueId in selected }
374+ .sumOf { it.threadCount.coerceAtLeast(1 ) }
395375 }
396-
397- return messages
398- .asSequence()
399- .filter { it.uniqueId in selected }
400- .sumOf { it.threadCount.coerceAtLeast(1 ) }
401376 }
402377
403378 private fun getItemFromView (view : View ): MessageListItem ? {
@@ -409,18 +384,33 @@ class MessageListAdapter internal constructor(
409384 return getItemById(messageViewHolder.uniqueId)
410385 }
411386 }
387+
388+ private fun List<MessageListViewItem>.filterMessageListItem (): List <MessageListItem > =
389+ filterIsInstance<MessageListViewItem .Message >()
390+ .map { it.item }
412391}
413392
414393private class MessageListDiffCallback (
415- private val oldMessageList : List <MessageListItem >,
416- private val newMessageList : List <MessageListItem >,
394+ private val oldMessageList : List <MessageListViewItem >,
395+ private val newMessageList : List <MessageListViewItem >,
417396) : DiffUtil.Callback() {
418397 override fun getOldListSize (): Int = oldMessageList.size
419398
420399 override fun getNewListSize (): Int = newMessageList.size
421400
422401 override fun areItemsTheSame (oldItemPosition : Int , newItemPosition : Int ): Boolean {
423- return oldMessageList[oldItemPosition].uniqueId == newMessageList[newItemPosition].uniqueId
402+ val oldItem = oldMessageList[oldItemPosition]
403+ val newItem = newMessageList[newItemPosition]
404+ return when (oldItem) {
405+ is MessageListViewItem .InAppNotificationBannerList
406+ if newItem is MessageListViewItem .InAppNotificationBannerList -> true
407+
408+ is MessageListViewItem .Message
409+ if newItem is MessageListViewItem .Message -> oldItem.item.uniqueId == newItem.item.uniqueId
410+
411+ is MessageListViewItem .Footer if newItem is MessageListViewItem .Footer -> true
412+ else -> false
413+ }
424414 }
425415
426416 override fun areContentsTheSame (oldItemPosition : Int , newItemPosition : Int ): Boolean {
@@ -434,3 +424,23 @@ interface MessageListItemActionListener {
434424 fun onToggleMessageFlag (item : MessageListItem )
435425 fun onFooterClicked ()
436426}
427+
428+ sealed interface MessageListViewItem {
429+ val viewId: Long
430+ val viewType: Int
431+
432+ data object InAppNotificationBannerList : MessageListViewItem {
433+ override val viewId: Long = IN_APP_NOTIFICATION_BANNER_INLINE_LIST_ID
434+ override val viewType: Int = TYPE_IN_APP_NOTIFICATION_BANNER_INLINE_LIST
435+ }
436+
437+ data class Message (val item : MessageListItem ) : MessageListViewItem {
438+ override val viewId: Long get() = item.uniqueId
439+ override val viewType: Int = TYPE_MESSAGE
440+ }
441+
442+ data class Footer (val text : String ) : MessageListViewItem {
443+ override val viewId: Long = FOOTER_ID
444+ override val viewType: Int = TYPE_FOOTER
445+ }
446+ }
0 commit comments