package org.thoughtcrime.securesms.home import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListUpdateCallback import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.NO_ID import network.loki.messenger.R import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.dependencies.ConfigFactory import org.thoughtcrime.securesms.mms.GlideRequests class HomeAdapter( private val context: Context, private val configFactory: ConfigFactory, private val listener: ConversationClickListener ) : RecyclerView.Adapter(), ListUpdateCallback { companion object { private const val HEADER = 0 private const val ITEM = 1 } var header: View? = null private var _data: List = emptyList() var data: List get() = _data.toList() set(newData) { val previousData = _data.toList() val diff = HomeDiffUtil(previousData, newData, context, configFactory) val diffResult = DiffUtil.calculateDiff(diff) _data = newData diffResult.dispatchUpdatesTo(this as ListUpdateCallback) } fun hasHeaderView(): Boolean = header != null var requestCount = 0 private val headerCount: Int get() = if (header == null) 0 else 1 override fun onInserted(position: Int, count: Int) { notifyItemRangeInserted(position + headerCount, count) } override fun onRemoved(position: Int, count: Int) { notifyItemRangeRemoved(position + headerCount, count) } override fun onMoved(fromPosition: Int, toPosition: Int) { notifyItemMoved(fromPosition + headerCount, toPosition + headerCount) } override fun onChanged(position: Int, count: Int, payload: Any?) { notifyItemRangeChanged(position + headerCount, count, payload) } override fun getItemId(position: Int): Long { if (hasHeaderView() && position == 0) return NO_ID val offsetPosition = if (hasHeaderView()) position-1 else position return _data[offsetPosition].threadId } lateinit var glide: GlideRequests var typingThreadIDs = setOf() set(value) { if (field == value) { return } field = value // TODO: replace this with a diffed update or a partial change set with payloads notifyDataSetChanged() } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = when (viewType) { HEADER -> { HeaderFooterViewHolder(header!!) } ITEM -> { val conversationView = LayoutInflater.from(parent.context).inflate(R.layout.view_conversation, parent, false) as ConversationView val viewHolder = ConversationViewHolder(conversationView) viewHolder.view.setOnClickListener { viewHolder.view.thread?.let { listener.onConversationClick(it) } } viewHolder.view.setOnLongClickListener { viewHolder.view.thread?.let { listener.onLongConversationClick(it) } true } viewHolder } else -> throw Exception("viewType $viewType isn't valid") } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { if (holder is ConversationViewHolder) { val offset = if (hasHeaderView()) position - 1 else position val thread = data[offset] val isTyping = typingThreadIDs.contains(thread.threadId) holder.view.bind(thread, isTyping, glide) } } override fun onViewRecycled(holder: RecyclerView.ViewHolder) { if (holder is ConversationViewHolder) { holder.view.recycle() } else { super.onViewRecycled(holder) } } override fun getItemViewType(position: Int): Int = if (hasHeaderView() && position == 0) HEADER else ITEM override fun getItemCount(): Int = data.size + if (hasHeaderView()) 1 else 0 class ConversationViewHolder(val view: ConversationView) : RecyclerView.ViewHolder(view) class HeaderFooterViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) }