EmojiPickerBodyAdapter.kt

/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.emoji2.emojipicker

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams
import android.widget.TextView
import androidx.annotation.LayoutRes
import androidx.annotation.UiThread
import androidx.core.view.ViewCompat
import androidx.emoji2.emojipicker.Extensions.toItemType
import androidx.recyclerview.widget.RecyclerView.Adapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder

/** RecyclerView adapter for emoji body.  */
internal class EmojiPickerBodyAdapter(
    private val context: Context,
    private val emojiGridColumns: Int,
    private val emojiGridRows: Float?,
    private val stickyVariantProvider: StickyVariantProvider,
    private val emojiPickerItemsProvider: () -> EmojiPickerItems,
    private val onEmojiPickedListener: EmojiPickerBodyAdapter.(EmojiViewItem) -> Unit,
) : Adapter<ViewHolder>() {
    private val layoutInflater: LayoutInflater = LayoutInflater.from(context)
    private var emojiCellWidth: Int? = null
    private var emojiCellHeight: Int? = null

    @UiThread
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        emojiCellWidth = emojiCellWidth ?: (getParentWidth(parent) / emojiGridColumns)
        emojiCellHeight =
            emojiCellHeight ?: emojiGridRows?.let { getEmojiCellTotalHeight(parent) / it }
                ?.toInt() ?: emojiCellWidth

        return when (viewType.toItemType()) {
            ItemType.CATEGORY_TITLE -> createSimpleHolder(R.layout.category_text_view, parent)
            ItemType.PLACEHOLDER_TEXT -> createSimpleHolder(
                R.layout.empty_category_text_view, parent
            ) {
                minimumHeight = emojiCellHeight!!
            }

            ItemType.EMOJI -> {
                EmojiViewHolder(context,
                    parent,
                    emojiCellWidth!!,
                    emojiCellHeight!!,
                    layoutInflater,
                    stickyVariantProvider,
                    onEmojiPickedListener = { emojiViewItem ->
                        onEmojiPickedListener(emojiViewItem)
                    },
                    onEmojiPickedFromPopupListener = { emoji ->
                        val baseEmoji = BundledEmojiListLoader.getEmojiVariantsLookup()[emoji]!![0]
                        emojiPickerItemsProvider().forEachIndexed { index, itemViewData ->
                            if (itemViewData is EmojiViewData &&
                                BundledEmojiListLoader.getEmojiVariantsLookup()
                                    [itemViewData.emoji]?.get(0) == baseEmoji &&
                                itemViewData.updateToSticky
                            ) {
                                itemViewData.emoji = emoji
                                notifyItemChanged(index)
                            }
                        }
                    })
            }
        }
    }

    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
        val item = emojiPickerItemsProvider().getBodyItem(position)
        when (getItemViewType(position).toItemType()) {
            ItemType.CATEGORY_TITLE -> ViewCompat.requireViewById<TextView>(
                viewHolder.itemView, R.id.category_name
            ).text = (item as CategoryTitle).title

            ItemType.PLACEHOLDER_TEXT -> ViewCompat.requireViewById<TextView>(
                viewHolder.itemView, R.id.emoji_picker_empty_category_view
            ).text = (item as PlaceholderText).text

            ItemType.EMOJI -> {
                (viewHolder as EmojiViewHolder).bindEmoji((item as EmojiViewData).emoji)
            }
        }
    }

    override fun getItemId(position: Int): Long =
        emojiPickerItemsProvider().getBodyItem(position).hashCode().toLong()

    override fun getItemCount(): Int {
        return emojiPickerItemsProvider().size
    }

    override fun getItemViewType(position: Int): Int {
        return emojiPickerItemsProvider().getBodyItem(position).viewType
    }

    private fun getParentWidth(parent: ViewGroup): Int {
        return parent.measuredWidth - parent.paddingLeft - parent.paddingRight
    }

    private fun getEmojiCellTotalHeight(parent: ViewGroup) =
        parent.measuredHeight - context.resources.getDimensionPixelSize(
            R.dimen.emoji_picker_category_name_height
        ) * 2 - context.resources.getDimensionPixelSize(
            R.dimen.emoji_picker_category_name_padding_top
        )

    private fun createSimpleHolder(
        @LayoutRes layoutId: Int,
        parent: ViewGroup,
        init: (View.() -> Unit)? = null,
    ) = object : ViewHolder(layoutInflater.inflate(
        layoutId, parent, /* attachToRoot = */ false
    ).also {
        it.layoutParams = LayoutParams(
            LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT
        )
        init?.invoke(it)
    }) {}
}