I have a DiffUtil.Callback that compares 2 lists of model StorageModelUi.
sealed class StorageModelUi {
class ServerItem(val server: Server): StorageModelUi()
class StorageContent(val used: Int, val large: Int, val server: Server, val attachmentWithMessageList: List<AttachmentWithMessage>): StorageModelUi()
}
data class AttachmentWithMessage(
@Embedded
val attachment: Attachment,
@Relation(entity = Message::class, parentColumn = "attachments_message_id", entityColumn = "messages_message_id")
val message: Message
)
Each StorageModelUi item can be either a Server (which is like a Header to the RecyclerView) or a StorageContent (which is 2 TextViews and a RecyclerView below them). So i create a list of StorageModelUi with the function
fun Map<Server, List<AttachmentWithMessage>>.groupToStorageModelUi(): List<StorageModelUi> {
val final = mutableListOf<StorageModelUi>()
for ((server, list) in this) {
final.add(StorageModelUi.ServerItem(server))
final.add(
StorageModelUi.StorageContent(
used = list.sumOf { s -> s.attachment.size },
large = list.filter { s -> s.attachment.size > 5 * 1000 * 1024 }.sumOf { s -> s.attachment.size },
server = server,
attachmentWithMessageList = list
)
)
}
return final
}
The recyclerView gets updated when an attachment is being downloaded, so the only thing that changes the UI is the percent, which i take it from the attachmentWithMessage.message.body
field. For that reason i have created a DiffUtil to trace that change and return a payload to the BindViewHolder.
class StorageDiffUtilCallback(
private val oldList: List<StorageModelUi>,
private val newList: List<StorageModelUi>
): DiffUtil.Callback() {
override fun getOldListSize(): Int =
oldList.size
override fun getNewListSize(): Int =
newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldList[oldItemPosition]
val newItem = newList[newItemPosition]
return if (oldItem is StorageModelUi.StorageContent && newItem is StorageModelUi.StorageContent)
oldItem.server.serverId == newItem.server.serverId
else if (oldItem is StorageModelUi.ServerItem && newItem is StorageModelUi.ServerItem)
oldItem.server.serverId == newItem.server.serverId
else false
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldList[oldItemPosition]
val newItem = newList[newItemPosition]
return if (oldItem is StorageModelUi.StorageContent && newItem is StorageModelUi.StorageContent) {
oldItem.attachmentWithMessageList.map { it.message } == newItem.attachmentWithMessageList.map { it.message }
} else if (oldItem is StorageModelUi.ServerItem && newItem is StorageModelUi.ServerItem)
oldItem.server.serverId == newItem.server.serverId
else false
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val oldItem = oldList[oldItemPosition]
val newItem = newList[newItemPosition]
println("## $$ $oldItem")
println("## $$ $newItem")
return when {
oldItem is StorageModelUi.StorageContent && newItem is StorageModelUi.StorageContent -> {
println("## $$ oldItem is StorageModelUi.StorageContent")
if (oldItem.large != newItem.large) {
println("## $$ large")
Bundle().apply {
putString("key", "large")
}
} else if (oldItem.used != newItem.used) {
println("## $$ used")
Bundle().apply {
putString("key", "used")
}
} else if (oldItem.attachmentWithMessageList.map { it.message.body.value } != newItem.attachmentWithMessageList.map { it.message.body.value }) {
println("## $$ IT SHOULD HAVE BEEN IN HERE BUT IT DOES NOT...!!!!!!!")
println("## $$ attachmentWithMessageList payLoad")
Bundle().apply {
putString("key", "percent")
}
}
else {
println("## $$ First else")
super.getChangePayload(oldItemPosition, newItemPosition)
}
}
else -> {
println("## $$ super else")
super.getChangePayload(oldItemPosition, newItemPosition)
}
}
}
The list of the RecyclerView is updated with this
storageViewModel.storageModelUi.observe(viewLifecycleOwner) {
it?.let {
viewAdapter.updateStorage(it)
}
}
// StorageRecycler Adapter
var storage = mutableListOf<StorageModelUi>()
fun updateStorage(newStorage: List<StorageModelUi>) {
val diffCallback = StorageDiffUtilCallback(this.storage, newStorage)
val diffResult = DiffUtil.calculateDiff(diffCallback)
this.storage.clear()
this.storage.addAll(newStorage)
diffResult.dispatchUpdatesTo(this)
}
// Inside BindViewHolder of the StorageRecycler
fun bind(storage: StorageModelUi.StorageContent, storageCallback: RecyclerCallback.StorageCallback) {
bindTexts(storage)
val viewManager = LinearLayoutManager(itemView.context)
viewManager.initialPrefetchItemCount = storage.attachmentWithMessageList.size
viewAdapter = DownloadRecyclerAdapter(storageCallback)
downloadRecycler.apply {
setHasFixedSize(true)
layoutManager = viewManager
adapter = viewAdapter
}
bindAdapter(storage)
}
fun bindTexts(storage: StorageModelUi.StorageContent) {
used.text = FileUtils.getFileSize(storage.used.toLong())
large.text = FileUtils.getFileSize(storage.large.toLong())
}
fun bindAdapter(storage: StorageModelUi.StorageContent) {
viewAdapter.server = storage.server
viewAdapter.updateItem( storage.attachmentWithMessageList.toMutableList())
}
// Nested RecyclerAdapter (that shows the Download attachments
var downloadItems = mutableListOf<AttachmentWithMessage>()
var server: Server? = null
fun updateItem(newDownloadItem: List<AttachmentWithMessage>) {
val diffCallback = DownloadRecyclerDiffUtilCallback(this.downloadItems, newDownloadItem)
val diffResult = DiffUtil.calculateDiff(diffCallback)
this.downloadItems.clear()
this.downloadItems.addAll(newDownloadItem)
diffResult.dispatchUpdatesTo(this)
}
However, i have noticed that although the "areContentsTheSame" returns false, when it goes to the getChangePayload, it never goes inside the 3rd IF statement. So after debugging it seems that the oldItem == newItem returns true, and as i see in the Logcat, the oldItem.message == newItem.message. How can this be? I mean why the old and new items are the same??
Problem solved. First of all we should declare the list inside the Adapter as listOf<> and not as mutableListOf(). That means that the .clear() and .addAll() should change. After that in the DiffUtil.Callback i should check the fields that i only need