Fix possible emoji-autocompletion crash

Thread: main, Exception: java.lang.ClassCastException: im.vector.app.features.autocomplete.AutocompleteHeaderItem$Holder cannot be cast to im.vector.app.features.autocomplete.emoji.AutocompleteEmojiItem$Holder
at im.vector.app.features.autocomplete.emoji.AutocompleteEmojiItem_.handlePreBind(AutocompleteEmojiItem_.java:1)
at com.airbnb.epoxy.BaseEpoxyAdapter.onBindViewHolder(BaseEpoxyAdapter.java:22)
at com.airbnb.epoxy.BaseEpoxyAdapter.onBindViewHolder(BaseEpoxyAdapter.java:3)
at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:43)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:59)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:974)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:54)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:54)
at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:400)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:67)
at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:135)
at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:8)

Change-Id: I4d4919c35babea2606a06b3e99b5c3b3ce08e95d
This commit is contained in:
SpiritCroc 2023-05-02 12:18:06 +02:00
parent eeb4b8dc0d
commit 0faa712f23
5 changed files with 67 additions and 13 deletions

View File

@ -67,7 +67,7 @@ class AutocompleteEmojiController @Inject constructor(
}
private fun buildHeaderItem(header: AutocompleteEmojiDataItem.Header) {
autocompleteHeaderItem {
autocompleteEmojiHeaderItem {
id("h/${header.id}")
title(header.title)
}
@ -110,9 +110,7 @@ class AutocompleteEmojiController @Inject constructor(
}
companion object {
// Count of standard emoji matches - WARN: do not set to 8 or less, or epoxy/recycler will sometimes crash with some class casts,
// e.g. when repeatedly typing `:turt`, then deleting back to `:`?!?
// What makes 8 magic? Probably that no-search proposals also returns 8 results? But wtf?
// Count of standard emoji matches
const val STANDARD_EMOJI_MAX = 9
// Count of emojis for the current room's image pack
const val CUSTOM_THIS_ROOM_MAX = 8

View File

@ -0,0 +1,44 @@
/*
* Copyright 2019 New Vector Ltd
* Copyright 2023 SpiritCroc
*
* 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 im.vector.app.features.autocomplete.emoji
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyModel
// We were getting ClassCastExceptions for the holder of AutocompleteHeaderItem, which sometimes seemed to still be an AutocompleteEmojiItem...
// https://github.com/SchildiChat/SchildiChat-android-rageshakes/issues/1040
// So let's build some HeaderItem using the same holder
@EpoxyModelClass
abstract class AutocompleteEmojiHeaderItem : VectorEpoxyModel<AutocompleteEmojiItem.Holder>(R.layout.item_autocomplete_emoji) {
@EpoxyAttribute var title: String? = null
override fun bind(holder: AutocompleteEmojiItem.Holder) {
super.bind(holder)
holder.titleView.isVisible = true
holder.emojiText.isVisible = false
holder.emoteImage.isVisible = false
holder.emojiKeywordText.isVisible = false
holder.emojiNameText.isVisible = false
holder.titleView.text = title
}
}

View File

@ -51,6 +51,8 @@ abstract class AutocompleteEmojiItem : VectorEpoxyModel<AutocompleteEmojiItem.Ho
override fun bind(holder: Holder) {
super.bind(holder)
holder.titleView.isVisible = false
holder.emojiNameText.isVisible = true
if (emoteUrl?.isNotEmpty().orFalse()) {
holder.emojiText.isVisible = false
holder.emoteImage.isVisible = true
@ -75,5 +77,6 @@ abstract class AutocompleteEmojiItem : VectorEpoxyModel<AutocompleteEmojiItem.Ho
val emoteImage by bind<ImageView>(R.id.itemAutocompleteEmote)
val emojiNameText by bind<TextView>(R.id.itemAutocompleteEmojiName)
val emojiKeywordText by bind<TextView>(R.id.itemAutocompleteEmojiSubname)
val titleView by bind<TextView>(R.id.headerItemAutocompleteTitle)
}
}

View File

@ -27,7 +27,7 @@ import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.onClick
import im.vector.app.features.themes.ThemeUtils
@EpoxyModelClass // Re-using item_autocomplete_emoji layout for now because I'm lazy - may want to change that if it causes troubles
@EpoxyModelClass // Re-using item_autocomplete_emoji to avoid class-cast exceptions like https://github.com/SchildiChat/SchildiChat-android-rageshakes/issues/1040
abstract class AutocompleteExpandItem : VectorEpoxyModel<AutocompleteEmojiItem.Holder>(R.layout.item_autocomplete_emoji) {
@EpoxyAttribute
@ -38,8 +38,10 @@ abstract class AutocompleteExpandItem : VectorEpoxyModel<AutocompleteEmojiItem.H
override fun bind(holder: AutocompleteEmojiItem.Holder) {
super.bind(holder)
holder.titleView.isVisible = false
holder.emojiText.isVisible = false
holder.emoteImage.isVisible = true
holder.emojiNameText.isVisible = true
holder.emoteImage.setImageResource(R.drawable.ic_expand_more)
holder.emoteImage.imageTintList = ColorStateList.valueOf(ThemeUtils.getColor(holder.emoteImage.context, R.attr.vctr_content_secondary))
holder.emojiText.typeface = Typeface.DEFAULT
@ -54,12 +56,4 @@ abstract class AutocompleteExpandItem : VectorEpoxyModel<AutocompleteEmojiItem.H
holder.view.onClick(onClickListener)
}
/*
class Holder : VectorEpoxyHolder() {
val emojiText by bind<TextView>(R.id.itemAutocompleteEmoji)
val emoteImage by bind<ImageView>(R.id.itemAutocompleteEmote)
val emojiNameText by bind<TextView>(R.id.itemAutocompleteEmojiName)
val emojiKeywordText by bind<TextView>(R.id.itemAutocompleteEmojiSubname)
}
*/
}

View File

@ -9,6 +9,21 @@
android:padding="8dp"
tools:viewBindingIgnore="true">
<!-- this one is only for AutocompleteEmojiHeaderItem...
see also https://github.com/SchildiChat/SchildiChat-android-rageshakes/issues/1040
(ClassCastException when having different holder types in autocompletions) -->
<TextView
android:id="@+id/headerItemAutocompleteTitle"
style="@style/Widget.Vector.TextView.Subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:maxLines="1"
android:textColor="?vctr_content_secondary"
android:ellipsize="end"
android:visibility="gone"
tools:text="@string/custom_emotes_this_room" />
<TextView
android:id="@+id/itemAutocompleteEmoji"
style="@style/Widget.Vector.TextView.Title"