Improve EmojiChooserFragment: improve filtering result: sort

This commit is contained in:
Benoit Marty 2019-12-10 00:42:24 +01:00
parent f00f34b244
commit 3c18fd5335
6 changed files with 78 additions and 98 deletions

View file

@ -95,20 +95,18 @@ class EmojiReactionPickerActivity : VectorBaseActivity(),
viewModel.eventId = intent.getStringExtra(EXTRA_EVENT_ID)
emojiDataSource.rawData?.categories?.let { categories ->
for (category in categories) {
val s = category.emojis[0]
tabLayout.newTab()
.also { tab ->
tab.text = emojiDataSource.rawData!!.emojis[s]!!.emoji
tab.contentDescription = category.name
}
.also { tab ->
tabLayout.addTab(tab)
}
}
tabLayout.addOnTabSelectedListener(tabLayoutSelectionListener)
emojiDataSource.rawData.categories.forEach { category ->
val s = category.emojis[0]
tabLayout.newTab()
.also { tab ->
tab.text = emojiDataSource.rawData.emojis[s]!!.emoji
tab.contentDescription = category.name
}
.also { tab ->
tabLayout.addTab(tab)
}
}
tabLayout.addOnTabSelectedListener(tabLayoutSelectionListener)
viewModel.currentSection.observe(this, Observer { section ->
section?.let {

View file

@ -40,12 +40,11 @@ import kotlin.math.abs
/**
*
* TODO: Configure Span using available width and emoji size
* TODO: Search
* TODO: Performances
* TODO: Scroll to section - Find a way to snap section to the top
*/
class EmojiRecyclerAdapter @Inject constructor(
private val dataSource: EmojiDataSource?
private val dataSource: EmojiDataSource
) :
RecyclerView.Adapter<EmojiRecyclerAdapter.ViewHolder>() {
@ -70,13 +69,12 @@ class EmojiRecyclerAdapter @Inject constructor(
private val itemClickListener = View.OnClickListener { view ->
mRecyclerView?.getChildLayoutPosition(view)?.let { itemPosition ->
if (itemPosition != RecyclerView.NO_POSITION) {
val categories = dataSource?.rawData?.categories ?: return@OnClickListener
val sectionNumber = getSectionForAbsoluteIndex(itemPosition)
if (!isSection(itemPosition)) {
val sectionMojis = categories[sectionNumber].emojis
val sectionMojis = dataSource.rawData.categories[sectionNumber].emojis
val sectionOffset = getSectionOffset(sectionNumber)
val emoji = sectionMojis[itemPosition - sectionOffset]
val item = dataSource.rawData!!.emojis.getValue(emoji).emoji
val item = dataSource.rawData.emojis.getValue(emoji).emoji
reactionClickListener?.onReactionSelected(item)
}
}
@ -117,7 +115,7 @@ class EmojiRecyclerAdapter @Inject constructor(
}
fun scrollToSection(section: Int) {
if (section < 0 || section >= dataSource?.rawData?.categories?.size ?: 0) {
if (section < 0 || section >= dataSource.rawData.categories.size) {
// ignore
return
}
@ -149,14 +147,12 @@ class EmojiRecyclerAdapter @Inject constructor(
}
private fun isSection(position: Int): Boolean {
dataSource?.rawData?.categories?.let { categories ->
var sectionOffset = 1
var lastItemInSection: Int
for (category in categories) {
lastItemInSection = sectionOffset + category.emojis.size - 1
if (position == sectionOffset - 1) return true
sectionOffset = lastItemInSection + 2
}
var sectionOffset = 1
var lastItemInSection: Int
dataSource.rawData.categories.forEach { category ->
lastItemInSection = sectionOffset + category.emojis.size - 1
if (position == sectionOffset - 1) return true
sectionOffset = lastItemInSection + 2
}
return false
}
@ -165,13 +161,11 @@ class EmojiRecyclerAdapter @Inject constructor(
var sectionOffset = 1
var lastItemInSection: Int
var index = 0
dataSource?.rawData?.categories?.let {
for (category in it) {
lastItemInSection = sectionOffset + category.emojis.size - 1
if (position <= lastItemInSection) return index
sectionOffset = lastItemInSection + 2
index++
}
dataSource.rawData.categories.forEach { category ->
lastItemInSection = sectionOffset + category.emojis.size - 1
if (position <= lastItemInSection) return index
sectionOffset = lastItemInSection + 2
index++
}
return index
}
@ -180,36 +174,32 @@ class EmojiRecyclerAdapter @Inject constructor(
// Todo cache this for fast access
var sectionOffset = 1
var lastItemInSection: Int
dataSource?.rawData?.categories?.let {
for ((index, category) in it.withIndex()) {
lastItemInSection = sectionOffset + category.emojis.size - 1
if (section == index) return sectionOffset
sectionOffset = lastItemInSection + 2
}
dataSource.rawData.categories.forEachIndexed { index, category ->
lastItemInSection = sectionOffset + category.emojis.size - 1
if (section == index) return sectionOffset
sectionOffset = lastItemInSection + 2
}
return sectionOffset
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
beginTraceSession("MyAdapter.onBindViewHolder")
dataSource?.rawData?.categories?.let { categories ->
val sectionNumber = getSectionForAbsoluteIndex(position)
if (isSection(position)) {
holder.bind(categories[sectionNumber].name)
} else {
val sectionMojis = categories[sectionNumber].emojis
val sectionOffset = getSectionOffset(sectionNumber)
val emoji = sectionMojis[position - sectionOffset]
val item = dataSource.rawData!!.emojis[emoji]!!.emoji
(holder as EmojiViewHolder).data = item
if (scrollState != ScrollState.SETTLING || !isFastScroll) {
val sectionNumber = getSectionForAbsoluteIndex(position)
if (isSection(position)) {
holder.bind(dataSource.rawData.categories[sectionNumber].name)
} else {
val sectionMojis = dataSource.rawData.categories[sectionNumber].emojis
val sectionOffset = getSectionOffset(sectionNumber)
val emoji = sectionMojis[position - sectionOffset]
val item = dataSource.rawData.emojis[emoji]!!.emoji
(holder as EmojiViewHolder).data = item
if (scrollState != ScrollState.SETTLING || !isFastScroll) {
// Log.i("PERF","Bind with draw at position:$position")
holder.bind(item)
} else {
holder.bind(item)
} else {
// Log.i("PERF","Bind without draw at position:$position")
toUpdateWhenNotBusy.add(item to holder)
holder.bind(null)
}
toUpdateWhenNotBusy.add(item to holder)
holder.bind(null)
}
}
endTraceSession()
@ -230,15 +220,8 @@ class EmojiRecyclerAdapter @Inject constructor(
super.onViewRecycled(holder)
}
override fun getItemCount(): Int {
return dataSource?.rawData?.categories?.let {
var count = /*number of sections*/ it.size
for (ad in it) {
count += ad.emojis.size
}
count
} ?: 0
}
override fun getItemCount() = dataSource.rawData.categories
.sumBy { emojiCategory -> 1 /* Section */ + emojiCategory.emojis.size }
abstract class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
abstract fun bind(s: String?)

View file

@ -46,7 +46,7 @@ abstract class EmojiSearchResultItem : EpoxyModelWithHolder<EmojiSearchResultIte
holder.emojiText.text = emojiItem.emoji
holder.emojiText.typeface = emojiTypeFace ?: Typeface.DEFAULT
holder.emojiNameText.text = emojiItem.name
holder.emojiKeywordText.setTextOrHide(emojiItem.keywords?.joinToString())
holder.emojiKeywordText.setTextOrHide(emojiItem.keywords.joinToString())
holder.view.setOnClickListener {
onClickListener?.onReactionSelected(emojiItem.emoji)
}

View file

@ -15,7 +15,10 @@
*/
package im.vector.riotx.features.reactions
import com.airbnb.mvrx.*
import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.riotx.core.platform.VectorViewModel
@ -52,21 +55,26 @@ class EmojiSearchResultViewModel @AssistedInject constructor(
}
private fun updateQuery(action: EmojiSearchAction.UpdateQuery) {
val words = action.queryString.split("\\s".toRegex())
setState {
copy(
query = action.queryString,
results = dataSource.rawData?.emojis?.toList()
?.map { it.second }
?.filter {
it.name.contains(action.queryString, true)
|| action.queryString
.split("\\s".toRegex())
.fold(true, { prev, q ->
prev
&& (it.keywords?.any { it.contains(q, true) }
?: false)
})
} ?: emptyList()
// First add emojis with name matching query, sorted by name
// Then emojis with keyword matching any of the word in the query, sorted by name
results = dataSource.rawData.emojis
.values
.filter { emojiItem ->
emojiItem.name.contains(action.queryString, true)
}
.sortedBy { it.name }
+ dataSource.rawData.emojis
.values
.filter { emojiItem ->
words.fold(true, { prev, word ->
prev && emojiItem.keywords.any { keyword -> keyword.contains(word, true) }
})
}
.sortedBy { it.name }
)
}
}

View file

@ -15,31 +15,22 @@
*/
package im.vector.riotx.features.reactions.data
import android.content.Context
import android.content.res.Resources
import com.squareup.moshi.Moshi
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenScope
import timber.log.Timber
import javax.inject.Inject
import kotlin.system.measureTimeMillis
@ScreenScope
class EmojiDataSource @Inject constructor(
context: Context
resources: Resources
) {
var rawData: EmojiData? = null
init {
measureTimeMillis {
context.resources.openRawResource(R.raw.emoji_picker_datasource).use { input ->
val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(EmojiData::class.java)
val inputAsString = input.bufferedReader().use { it.readText() }
this.rawData = jsonAdapter.fromJson(inputAsString)
val rawData = resources.openRawResource(R.raw.emoji_picker_datasource)
.use { input ->
Moshi.Builder()
.build()
.adapter(EmojiData::class.java)
.fromJson(input.bufferedReader().use { it.readText() })
}
}.also {
Timber.e("Emoji: $it millis")
}
}
?: EmojiData(emptyList(), emptyMap(), emptyMap())
}

View file

@ -40,7 +40,7 @@ import com.squareup.moshi.JsonClass
data class EmojiItem(
@Json(name = "a") val name: String,
@Json(name = "b") val unicode: String,
@Json(name = "j") val keywords: List<String>?
@Json(name = "j") val keywords: List<String> = emptyList()
) {
// Cannot be private...
var cache: String? = null