0

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??

3
  • how do you update the list? Commented May 30, 2022 at 11:49
  • I have edited the question and added some code that shows the update process. Commented May 30, 2022 at 12:08
  • still impossible to tell because you don't show how storageModelUi is being updated. In any case I strongly believe that even though you provide a new list, the new list doesn't contain new elements. Meaning that they are the same as the old list Commented May 30, 2022 at 12:32

1 Answer 1

0

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.