MessageReactionsAdapter.kt

package chat.rocket.android.chatroom.adapter

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.chatroom.uimodel.ReactionUiModel
import chat.rocket.android.dagger.DaggerLocalComponent
import chat.rocket.android.emoji.Emoji
import chat.rocket.android.emoji.EmojiKeyboardListener
import chat.rocket.android.emoji.EmojiPickerPopup
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.infrastructure.LocalRepository
import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.item_reaction.view.*
import java.util.concurrent.CopyOnWriteArrayList
import javax.inject.Inject

class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    private val reactions = CopyOnWriteArrayList<ReactionUiModel>()
    var listener: EmojiReactionListener? = null

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        val view: View
        return when (viewType) {
            ADD_REACTION_VIEW_TYPE -> {
                view = inflater.inflate(R.layout.item_add_reaction, parent, false)
                AddReactionViewHolder(view, listener)
            }
            else -> {
                view = inflater.inflate(R.layout.item_reaction, parent, false)
                ReactionViewHolder(view, listener)
            }
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        if (holder is ReactionViewHolder) {
            holder.bind(reactions[position])
        } else {
            holder as AddReactionViewHolder
            holder.bind(reactions.first().messageId)
        }
    }

    override fun getItemCount() = if (reactions.isEmpty()) 0 else reactions.size + 1

    override fun getItemViewType(position: Int): Int {
        if (position == reactions.size) {
            return ADD_REACTION_VIEW_TYPE
        }
        return REACTION_VIEW_TYPE
    }

    fun addReactions(reactions: List<ReactionUiModel>) {
        this.reactions.clear()
        this.reactions.addAllAbsent(reactions)
        notifyItemRangeInserted(0, reactions.size)
    }

    fun clear() {
        val oldSize = reactions.size
        reactions.clear()
        notifyItemRangeRemoved(0, oldSize)
    }

    fun contains(reactionShortname: String) =
        reactions.firstOrNull { it.shortname == reactionShortname } != null

    class ReactionViewHolder(
        view: View,
        private val listener: EmojiReactionListener?
    ) : RecyclerView.ViewHolder(view), View.OnClickListener, View.OnLongClickListener {

        @Inject
        lateinit var localRepository: LocalRepository
        @Volatile
        lateinit var reaction: ReactionUiModel
        @Volatile
        var clickHandled = false

        init {
            DaggerLocalComponent.builder()
                .context(itemView.context)
                .build()
                .inject(this)
        }

        fun bind(reaction: ReactionUiModel) {
            clickHandled = false
            this.reaction = reaction
            with(itemView) {
                if (reaction.url.isNullOrEmpty()) {
                    // The view at index 0 corresponds to the one to display unicode text emoji.
                    view_flipper_reaction.displayedChild = 0
                    text_emoji.text = reaction.unicode
                } else {
                    // The view at index 1 corresponds to the one to display custom emojis which are images.
                    view_flipper_reaction.displayedChild = 1
                    val glideRequest = if (reaction.url!!.endsWith("gif", true)) {
                        Glide.with(context).asGif()
                    } else {
                        Glide.with(context).asBitmap()
                    }

                    glideRequest.load(reaction.url).into(image_emoji)
                }

                val myself = localRepository.get(LocalRepository.CURRENT_USERNAME_KEY)
                if (reaction.usernames.contains(myself)) {
                    val context = itemView.context
                    text_count.setTextColor(ContextCompat.getColor(context, R.color.colorAccent))
                }

                text_count.text = reaction.count.toString()

                view_flipper_reaction.setOnClickListener(this@ReactionViewHolder)
                text_count.setOnClickListener(this@ReactionViewHolder)
                view_flipper_reaction.setOnLongClickListener(this@ReactionViewHolder)
                text_count.setOnLongClickListener(this@ReactionViewHolder)
            }
        }

        override fun onClick(v: View) {
            synchronized(this) {
                if (!clickHandled) {
                    clickHandled = true
                    listener?.onReactionTouched(reaction.messageId, reaction.shortname)
                }
            }
        }

        override fun onLongClick(v: View?): Boolean {
            listener?.onReactionLongClicked(reaction.shortname, reaction.isCustom, reaction.url, reaction.usernames)
            return true
        }
    }

    class AddReactionViewHolder(
        view: View,
        private val listener: EmojiReactionListener?
    ) : RecyclerView.ViewHolder(view) {

        fun bind(messageId: String) {
            itemView as ImageView
            itemView.setOnClickListener {
                val emojiPickerPopup = EmojiPickerPopup(itemView.context)
                emojiPickerPopup.listener = object : EmojiKeyboardListener {
                    override fun onEmojiAdded(emoji: Emoji) {
                        listener?.onReactionAdded(messageId, emoji)
                    }
                }
                emojiPickerPopup.show()
            }
        }
    }

    companion object {
        private const val REACTION_VIEW_TYPE = 0
        private const val ADD_REACTION_VIEW_TYPE = 1
    }
}