Remove `PlaceHolderEmoji`.

Test: NA
Change-Id: If75afb9ddea118462875dfedf4834eca38cb0a60
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerBodyAdapter.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerBodyAdapter.kt
index 4f688fd..66f3bef 100644
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerBodyAdapter.kt
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerBodyAdapter.kt
@@ -81,8 +81,6 @@
                         }
                     })
             }
-
-            ItemType.PLACEHOLDER_EMOJI -> object : ViewHolder(View(context)) {}
         }
 
     override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
@@ -99,8 +97,6 @@
             ItemType.EMOJI -> {
                 (viewHolder as EmojiViewHolder).bindEmoji((item as EmojiViewData).emoji)
             }
-
-            ItemType.PLACEHOLDER_EMOJI -> {}
         }
     }
 
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerItems.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerItems.kt
index 33ea012..7ac2aa6 100644
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerItems.kt
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerItems.kt
@@ -24,8 +24,7 @@
  * [titleItem] comes first.
  * [contentItems] comes after [titleItem].
  * [emptyPlaceholderItem] will be served after [titleItem] only if [contentItems] is empty.
- * [forceContentSize], if provided, will truncate [contentItems] to certain size or pad with
- * [PlaceholderEmoji]s.
+ * [maxContentItemCount], if provided, will truncate [contentItems] to certain size.
  *
  * [categoryIconId] is the corresponding category icon in emoji picker header.
  */
@@ -33,23 +32,24 @@
     @DrawableRes internal val categoryIconId: Int,
     internal val titleItem: CategoryTitle,
     private val contentItems: List<EmojiViewData>,
-    private val forceContentSize: Int? = null,
+    private val maxContentItemCount: Int? = null,
     private val emptyPlaceholderItem: PlaceholderText? = null
 ) {
 
     val size: Int
-        get() = 1 /* title */ +
-            (forceContentSize ?: maxOf(
-                contentItems.size,
-                if (emptyPlaceholderItem != null) 1 else 0
-            ))
+        get() = 1 /* title */ + when {
+            contentItems.isEmpty() -> if (emptyPlaceholderItem != null) 1 else 0
+            maxContentItemCount != null && contentItems.size > maxContentItemCount ->
+                maxContentItemCount
+            else -> contentItems.size
+        }
 
     operator fun get(index: Int): ItemViewData {
         if (index == 0) return titleItem
         val contentIndex = index - 1
         if (contentIndex < contentItems.size) return contentItems[contentIndex]
         if (contentIndex == 0 && emptyPlaceholderItem != null) return emptyPlaceholderItem
-        return PlaceholderEmoji
+        throw IndexOutOfBoundsException()
     }
 
     fun getAll(): List<ItemViewData> = IntRange(0, size - 1).map { get(it) }
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerView.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerView.kt
index ca5569f..918a7d6 100644
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerView.kt
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerView.kt
@@ -88,6 +88,7 @@
     private val scope = CoroutineScope(EmptyCoroutineContext)
 
     private var recentEmojiProvider: RecentEmojiProvider = DefaultRecentEmojiProvider(context)
+    private var recentNeedsRefreshing: Boolean = true
     private val recentItems: MutableList<EmojiViewData> = mutableListOf()
     private lateinit var recentItemGroup: ItemGroup
 
@@ -150,10 +151,8 @@
             emojiPickerItemsProvider = { emojiPickerItems },
             onEmojiPickedListener = { emojiViewItem ->
                 onEmojiPickedListener?.accept(emojiViewItem)
-
-                scope.launch {
-                    recentEmojiProvider.recordSelection(emojiViewItem.emoji)
-                }
+                recentEmojiProvider.recordSelection(emojiViewItem.emoji)
+                recentNeedsRefreshing = true
             }
         )
     }
@@ -163,7 +162,7 @@
             R.drawable.quantum_gm_ic_access_time_filled_vd_theme_24,
             CategoryTitle(context.getString(R.string.emoji_category_recent)),
             recentItems,
-            forceContentSize = DEFAULT_MAX_RECENT_ITEM_ROWS * emojiGridColumns,
+            maxContentItemCount = DEFAULT_MAX_RECENT_ITEM_ROWS * emojiGridColumns,
             emptyPlaceholderItem = PlaceholderText(
                 context.getString(R.string.emoji_empty_recent_category)
             )
@@ -194,7 +193,6 @@
             spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
                 override fun getSpanSize(position: Int): Int {
                     return when (emojiPickerItems.getBodyItem(position).itemType) {
-                        ItemType.PLACEHOLDER_EMOJI -> 0
                         ItemType.CATEGORY_TITLE, ItemType.PLACEHOLDER_TEXT -> emojiGridColumns
                         else -> 1
                     }
@@ -243,11 +241,14 @@
                 addOnScrollListener(object : RecyclerView.OnScrollListener() {
                     override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                         super.onScrolled(recyclerView, dx, dy)
-                        val position =
-                            bodyLayoutManager.findFirstCompletelyVisibleItemPosition()
                         headerAdapter.selectedGroupIndex =
-                            emojiPickerItems.groupIndexByItemPosition(position)
-                        if (position !in emojiPickerItems.groupRange(recentItemGroup)) {
+                            emojiPickerItems.groupIndexByItemPosition(
+                                bodyLayoutManager.findFirstCompletelyVisibleItemPosition()
+                            )
+                        if (recentNeedsRefreshing &&
+                            bodyLayoutManager.findFirstVisibleItemPosition() !in
+                            emojiPickerItems.groupRange(recentItemGroup)
+                        ) {
                             scope.launch {
                                 refreshRecent()
                             }
@@ -268,18 +269,37 @@
     }
 
     internal suspend fun refreshRecent() {
+        if (!recentNeedsRefreshing) {
+            return
+        }
+        val oldGroupSize = if (::recentItemGroup.isInitialized) recentItemGroup.size else 0
         val recent = recentEmojiProvider.getRecentEmojiList()
-        recentItems.clear()
-        recentItems.addAll(recent.map {
-            EmojiViewData(
-                it,
-                updateToSticky = false,
-            )
-        })
-        if (isLaidOut) {
-            val range = emojiPickerItems.groupRange(recentItemGroup)
-            withContext(Dispatchers.Main) {
-                bodyAdapter.notifyItemRangeChanged(range.first, range.last + 1)
+        withContext(Dispatchers.Main) {
+            recentItems.clear()
+            recentItems.addAll(recent.map {
+                EmojiViewData(
+                    it,
+                    updateToSticky = false,
+                )
+            })
+            if (::emojiPickerItems.isInitialized) {
+                val range = emojiPickerItems.groupRange(recentItemGroup)
+                if (recentItemGroup.size > oldGroupSize) {
+                    bodyAdapter.notifyItemRangeInserted(
+                        range.first + oldGroupSize,
+                        recentItemGroup.size - oldGroupSize
+                    )
+                } else if (recentItemGroup.size < oldGroupSize) {
+                    bodyAdapter.notifyItemRangeRemoved(
+                        range.first + recentItemGroup.size,
+                        oldGroupSize - recentItemGroup.size
+                    )
+                }
+                bodyAdapter.notifyItemRangeChanged(
+                    range.first,
+                    minOf(oldGroupSize, recentItemGroup.size)
+                )
+                recentNeedsRefreshing = false
             }
         }
     }
@@ -295,6 +315,7 @@
     fun setRecentEmojiProvider(recentEmojiProvider: RecentEmojiProvider) {
         this.recentEmojiProvider = recentEmojiProvider
         scope.launch {
+            recentNeedsRefreshing = true
             refreshRecent()
         }
     }
@@ -406,4 +427,4 @@
     override fun removeViewsInLayout(start: Int, count: Int) {
         throw UnsupportedOperationException(EmojiPickerConstants.REMOVE_VIEW_EXCEPTION_MESSAGE)
     }
-}
+}
\ No newline at end of file
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/ItemViewData.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/ItemViewData.kt
index d71b573..73a1549 100644
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/ItemViewData.kt
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/ItemViewData.kt
@@ -20,7 +20,6 @@
     CATEGORY_TITLE,
     PLACEHOLDER_TEXT,
     EMOJI,
-    PLACEHOLDER_EMOJI,
 }
 
 /**
@@ -50,8 +49,6 @@
     val dataIndex: Int = 0
 ) : ItemViewData(ItemType.EMOJI)
 
-internal object PlaceholderEmoji : ItemViewData(ItemType.PLACEHOLDER_EMOJI)
-
 internal object Extensions {
     internal fun Int.toItemType() = ItemType.values()[this]
 }