Use DiffUtil at conversations screen

This commit is contained in:
Naveen
2022-11-13 00:38:11 +05:30
parent 3092253022
commit 9bdc8b6f7e
3 changed files with 68 additions and 38 deletions

View File

@@ -299,7 +299,7 @@ class MainActivity : SimpleActivity() {
val currAdapter = conversations_list.adapter val currAdapter = conversations_list.adapter
if (currAdapter == null) { if (currAdapter == null) {
hideKeyboard() hideKeyboard()
ConversationsAdapter(this, sortedConversations, conversations_list) { ConversationsAdapter(this, conversations_list) {
Intent(this, ThreadActivity::class.java).apply { Intent(this, ThreadActivity::class.java).apply {
val conversation = it as Conversation val conversation = it as Conversation
putExtra(THREAD_ID, conversation.threadId) putExtra(THREAD_ID, conversation.threadId)
@@ -308,6 +308,7 @@ class MainActivity : SimpleActivity() {
} }
}.apply { }.apply {
conversations_list.adapter = this conversations_list.adapter = this
updateConversations(sortedConversations)
} }
if (areSystemAnimationsEnabled) { if (areSystemAnimationsEnabled) {
@@ -315,13 +316,14 @@ class MainActivity : SimpleActivity() {
} }
} else { } else {
try { try {
(currAdapter as ConversationsAdapter).updateConversations(sortedConversations) (currAdapter as ConversationsAdapter).updateConversations(sortedConversations) {
if (currAdapter.conversations.isEmpty()) { if (currAdapter.currentList.isEmpty()) {
conversations_fastscroller.beGone() conversations_fastscroller.beGone()
no_conversations_placeholder.text = getString(R.string.no_conversations_found) no_conversations_placeholder.text = getString(R.string.no_conversations_found)
no_conversations_placeholder.beVisible() no_conversations_placeholder.beVisible()
no_conversations_placeholder_2.beVisible() no_conversations_placeholder_2.beVisible()
} }
}
} catch (ignored: Exception) { } catch (ignored: Exception) {
} }
} }

View File

@@ -2,15 +2,18 @@ package com.simplemobiletools.smsmessenger.adapters
import android.content.Intent import android.content.Intent
import android.graphics.Typeface import android.graphics.Typeface
import android.os.Parcelable
import android.text.TextUtils import android.text.TextUtils
import android.util.TypedValue import android.util.TypedValue
import android.view.Menu import android.view.Menu
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller import com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter import com.simplemobiletools.commons.adapters.MyRecyclerViewListAdapter
import com.simplemobiletools.commons.dialogs.ConfirmationDialog import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.KEY_PHONE import com.simplemobiletools.commons.helpers.KEY_PHONE
@@ -26,14 +29,23 @@ import com.simplemobiletools.smsmessenger.models.Conversation
import kotlinx.android.synthetic.main.item_conversation.view.* import kotlinx.android.synthetic.main.item_conversation.view.*
class ConversationsAdapter( class ConversationsAdapter(
activity: SimpleActivity, var conversations: ArrayList<Conversation>, recyclerView: MyRecyclerView, itemClick: (Any) -> Unit activity: SimpleActivity, recyclerView: MyRecyclerView, itemClick: (Any) -> Unit
) : MyRecyclerViewAdapter(activity, recyclerView, itemClick), RecyclerViewFastScroller.OnPopupTextUpdate { ) : MyRecyclerViewListAdapter<Conversation>(activity, recyclerView, ConversationDiffCallback(), itemClick), RecyclerViewFastScroller.OnPopupTextUpdate {
private var fontSize = activity.getTextSize() private var fontSize = activity.getTextSize()
private var drafts = HashMap<Long, String?>() private var drafts = HashMap<Long, String?>()
private var recyclerViewState: Parcelable? = null
init { init {
setupDragListener(true) setupDragListener(true)
fetchDrafts(drafts) fetchDrafts(drafts)
setHasStableIds(true)
registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onChanged() = restoreRecyclerViewState()
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) = restoreRecyclerViewState()
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) = restoreRecyclerViewState()
})
} }
override fun getActionMenuId() = R.menu.cab_conversations override fun getActionMenuId() = R.menu.cab_conversations
@@ -71,13 +83,13 @@ class ConversationsAdapter(
} }
} }
override fun getSelectableItemCount() = conversations.size override fun getSelectableItemCount() = itemCount
override fun getIsItemSelectable(position: Int) = true override fun getIsItemSelectable(position: Int) = true
override fun getItemSelectionKey(position: Int) = conversations.getOrNull(position)?.hashCode() override fun getItemSelectionKey(position: Int) = currentList.getOrNull(position)?.hashCode()
override fun getItemKeyPosition(key: Int) = conversations.indexOfFirst { it.hashCode() == key } override fun getItemKeyPosition(key: Int) = currentList.indexOfFirst { it.hashCode() == key }
override fun onActionModeCreated() {} override fun onActionModeCreated() {}
@@ -86,14 +98,14 @@ class ConversationsAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_conversation, parent) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_conversation, parent)
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val conversation = conversations[position] val conversation = getItem(position)
holder.bindView(conversation, true, true) { itemView, layoutPosition -> holder.bindView(conversation, allowSingleClick = true, allowLongClick = true) { itemView, _ ->
setupView(itemView, conversation) setupView(itemView, conversation)
} }
bindViewHolder(holder) bindViewHolder(holder)
} }
override fun getItemCount() = conversations.size override fun getItemId(position: Int) = getItem(position).threadId
override fun onViewRecycled(holder: ViewHolder) { override fun onViewRecycled(holder: ViewHolder) {
super.onViewRecycled(holder) super.onViewRecycled(holder)
@@ -118,8 +130,7 @@ class ConversationsAdapter(
} }
val numbersToBlock = getSelectedItems() val numbersToBlock = getSelectedItems()
val positions = getSelectedItemPositions() val newList = currentList.toMutableList().apply { removeAll(numbersToBlock) }
conversations.removeAll(numbersToBlock)
ensureBackgroundThread { ensureBackgroundThread {
numbersToBlock.map { it.phoneNumber }.forEach { number -> numbersToBlock.map { it.phoneNumber }.forEach { number ->
@@ -127,7 +138,7 @@ class ConversationsAdapter(
} }
activity.runOnUiThread { activity.runOnUiThread {
removeSelectedItems(positions) submitList(newList)
finishActMode() finishActMode()
} }
} }
@@ -165,25 +176,25 @@ class ConversationsAdapter(
return return
} }
val conversationsToRemove = conversations.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation> val conversationsToRemove = currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
val positions = getSelectedItemPositions()
conversationsToRemove.forEach { conversationsToRemove.forEach {
activity.deleteConversation(it.threadId) activity.deleteConversation(it.threadId)
activity.notificationManager.cancel(it.hashCode()) activity.notificationManager.cancel(it.hashCode())
} }
try { val newList = try {
conversations.removeAll(conversationsToRemove.toSet()) currentList.toMutableList().apply { removeAll(conversationsToRemove) }
} catch (ignored: Exception) { } catch (ignored: Exception) {
currentList.toMutableList()
} }
activity.runOnUiThread { activity.runOnUiThread {
if (conversationsToRemove.isEmpty()) { if (newList.none { selectedKeys.contains(it.hashCode()) }) {
refreshMessages() refreshMessages()
finishActMode() finishActMode()
} else { } else {
removeSelectedItems(positions) submitList(newList)
if (conversations.isEmpty()) { if (newList.isEmpty()) {
refreshMessages() refreshMessages()
} }
} }
@@ -195,7 +206,7 @@ class ConversationsAdapter(
return return
} }
val conversationsMarkedAsRead = conversations.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation> val conversationsMarkedAsRead = currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
ensureBackgroundThread { ensureBackgroundThread {
conversationsMarkedAsRead.filter { conversation -> !conversation.read }.forEach { conversationsMarkedAsRead.filter { conversation -> !conversation.read }.forEach {
activity.markThreadMessagesRead(it.threadId) activity.markThreadMessagesRead(it.threadId)
@@ -213,7 +224,7 @@ class ConversationsAdapter(
return return
} }
val conversationsMarkedAsUnread = conversations.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation> val conversationsMarkedAsUnread = currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
ensureBackgroundThread { ensureBackgroundThread {
conversationsMarkedAsUnread.filter { conversation -> conversation.read }.forEach { conversationsMarkedAsUnread.filter { conversation -> conversation.read }.forEach {
activity.markThreadMessagesUnread(it.threadId) activity.markThreadMessagesUnread(it.threadId)
@@ -236,7 +247,7 @@ class ConversationsAdapter(
} }
} }
private fun getSelectedItems() = conversations.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation> private fun getSelectedItems() = currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
private fun pinConversation(pin: Boolean) { private fun pinConversation(pin: Boolean) {
val conversations = getSelectedItems() val conversations = getSelectedItems()
@@ -275,14 +286,9 @@ class ConversationsAdapter(
notifyDataSetChanged() notifyDataSetChanged()
} }
fun updateConversations(newConversations: ArrayList<Conversation>) { fun updateConversations(newConversations: ArrayList<Conversation>, commitCallback: (() -> Unit)? = null) {
val latestConversations = newConversations.clone() as ArrayList<Conversation> saveRecyclerViewState()
val oldHashCode = conversations.hashCode() submitList(newConversations.toList(), commitCallback)
val newHashCode = latestConversations.hashCode()
if (newHashCode != oldHashCode) {
conversations = latestConversations
notifyDataSetChanged()
}
} }
fun updateDrafts() { fun updateDrafts() {
@@ -346,5 +352,23 @@ class ConversationsAdapter(
} }
} }
override fun onChange(position: Int) = conversations.getOrNull(position)?.title ?: "" override fun onChange(position: Int) = currentList.getOrNull(position)?.title ?: ""
private fun saveRecyclerViewState() {
recyclerViewState = recyclerView.layoutManager?.onSaveInstanceState()
}
private fun restoreRecyclerViewState() {
recyclerView.layoutManager?.onRestoreInstanceState(recyclerViewState)
}
private class ConversationDiffCallback : DiffUtil.ItemCallback<Conversation>() {
override fun areItemsTheSame(oldItem: Conversation, newItem: Conversation): Boolean {
return oldItem.areItemsTheSame(newItem)
}
override fun areContentsTheSame(oldItem: Conversation, newItem: Conversation): Boolean {
return oldItem.areContentsTheSame(newItem)
}
}
} }

View File

@@ -18,6 +18,10 @@ data class Conversation(
@ColumnInfo(name = "is_scheduled") var isScheduled: Boolean = false @ColumnInfo(name = "is_scheduled") var isScheduled: Boolean = false
) { ) {
fun areItemsTheSame(other: Conversation): Boolean {
return threadId == other.threadId
}
fun areContentsTheSame(other: Conversation): Boolean { fun areContentsTheSame(other: Conversation): Boolean {
return snippet == other.snippet && return snippet == other.snippet &&
date == other.date && date == other.date &&