Add React With Any Search and update UX.

This commit is contained in:
Cody Henthorne
2021-06-24 15:14:34 -04:00
parent da2ee33dff
commit 2a1e5e4471
52 changed files with 1014 additions and 608 deletions

View File

@@ -7,6 +7,7 @@ import androidx.appcompat.widget.AppCompatImageView
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.util.MappingModel
import org.thoughtcrime.securesms.util.MappingViewHolder
import java.util.function.Consumer
interface KeyboardPageCategoryIconMappingModel<T : KeyboardPageCategoryIconMappingModel<T>> : MappingModel<T> {
val key: String
@@ -15,14 +16,14 @@ interface KeyboardPageCategoryIconMappingModel<T : KeyboardPageCategoryIconMappi
fun getIcon(context: Context): Drawable
}
class KeyboardPageCategoryIconViewHolder<T : KeyboardPageCategoryIconMappingModel<T>>(itemView: View, private val onPageSelected: (String) -> Unit) : MappingViewHolder<T>(itemView) {
class KeyboardPageCategoryIconViewHolder<T : KeyboardPageCategoryIconMappingModel<T>>(itemView: View, private val onPageSelected: Consumer<String>) : MappingViewHolder<T>(itemView) {
private val iconView: AppCompatImageView = itemView.findViewById(R.id.category_icon)
private val iconSelected: View = itemView.findViewById(R.id.category_icon_selected)
override fun bind(model: T) {
itemView.setOnClickListener {
onPageSelected(model.key)
onPageSelected.accept(model.key)
}
iconView.setImageDrawable(model.getIcon(context))

View File

@@ -3,8 +3,9 @@ package org.thoughtcrime.securesms.keyboard.emoji
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.keyboard.KeyboardPageCategoryIconViewHolder
import org.thoughtcrime.securesms.util.MappingAdapter
import java.util.function.Consumer
class EmojiKeyboardPageCategoriesAdapter(private val onPageSelected: (String) -> Unit) : MappingAdapter() {
class EmojiKeyboardPageCategoriesAdapter(private val onPageSelected: Consumer<String>) : MappingAdapter() {
init {
registerFactory(EmojiKeyboardPageCategoryMappingModel.RecentsMappingModel::class.java, LayoutFactory({ v -> KeyboardPageCategoryIconViewHolder<EmojiKeyboardPageCategoryMappingModel.RecentsMappingModel>(v, onPageSelected) }, R.layout.keyboard_pager_category_icon))
registerFactory(EmojiKeyboardPageCategoryMappingModel.EmojiCategoryMappingModel::class.java, LayoutFactory({ v -> KeyboardPageCategoryIconViewHolder<EmojiKeyboardPageCategoryMappingModel.EmojiCategoryMappingModel>(v, onPageSelected) }, R.layout.keyboard_pager_category_icon))

View File

@@ -4,6 +4,7 @@ import android.content.Context
import android.graphics.drawable.Drawable
import androidx.annotation.AttrRes
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel
import org.thoughtcrime.securesms.emoji.EmojiCategory
import org.thoughtcrime.securesms.keyboard.KeyboardPageCategoryIconMappingModel
import org.thoughtcrime.securesms.util.ThemeUtil
@@ -22,14 +23,10 @@ sealed class EmojiKeyboardPageCategoryMappingModel(
return newItem.key == key
}
class RecentsMappingModel(selected: Boolean) : EmojiKeyboardPageCategoryMappingModel(KEY, R.attr.emoji_category_recent, selected) {
class RecentsMappingModel(selected: Boolean) : EmojiKeyboardPageCategoryMappingModel(RecentEmojiPageModel.KEY, R.attr.emoji_category_recent, selected) {
override fun areContentsTheSame(newItem: EmojiKeyboardPageCategoryMappingModel): Boolean {
return newItem is RecentsMappingModel && super.areContentsTheSame(newItem)
}
companion object {
const val KEY = "Recents"
}
}
class EmojiCategoryMappingModel(private val emojiCategory: EmojiCategory, selected: Boolean) : EmojiKeyboardPageCategoryMappingModel(emojiCategory.key, emojiCategory.icon, selected) {

View File

@@ -20,7 +20,7 @@ class EmojiKeyboardPageViewModel : ViewModel() {
val categories: LiveData<MappingModelList> = Transformations.map(internalSelectedKey) { selected ->
MappingModelList().apply {
add(EmojiKeyboardPageCategoryMappingModel.RecentsMappingModel(selected == EmojiKeyboardPageCategoryMappingModel.RecentsMappingModel.KEY))
add(EmojiKeyboardPageCategoryMappingModel.RecentsMappingModel(selected == RecentEmojiPageModel.KEY))
EmojiCategory.values().forEach {
add(EmojiKeyboardPageCategoryMappingModel.EmojiCategoryMappingModel(it, it.key == selected))
@@ -41,7 +41,7 @@ class EmojiKeyboardPageViewModel : ViewModel() {
}
private fun getPageForCategory(mappingModel: EmojiKeyboardPageCategoryMappingModel): EmojiPageMappingModel {
val page = if (mappingModel.key == EmojiKeyboardPageCategoryMappingModel.RecentsMappingModel.KEY) {
val page = if (mappingModel.key == RecentEmojiPageModel.KEY) {
RecentEmojiPageModel(ApplicationDependencies.getApplication(), EmojiKeyboardProvider.RECENT_STORAGE_KEY)
} else {
EmojiSource.latest.displayPages.first { it.iconAttr == mappingModel.iconId }
@@ -57,7 +57,7 @@ class EmojiKeyboardPageViewModel : ViewModel() {
companion object {
fun getStartingTab(): String {
return if (RecentEmojiPageModel.hasRecents(ApplicationDependencies.getApplication(), EmojiKeyboardProvider.RECENT_STORAGE_KEY)) {
EmojiKeyboardPageCategoryMappingModel.RecentsMappingModel.KEY
RecentEmojiPageModel.KEY
} else {
EmojiCategory.PEOPLE.key
}

View File

@@ -71,9 +71,7 @@ class KeyboardPageSearchView @JvmOverloads constructor(
}
}
clearButton.setOnClickListener {
input.text.clear()
}
clearButton.setOnClickListener { clearQuery() }
context.obtainStyledAttributes(attrs, R.styleable.KeyboardPageSearchView, 0, 0).use { typedArray ->
val showAlways: Boolean = typedArray.getBoolean(R.styleable.KeyboardPageSearchView_show_always, false)
@@ -97,6 +95,7 @@ class KeyboardPageSearchView @JvmOverloads constructor(
val iconTint = typedArray.getColorStateList(R.styleable.KeyboardPageSearchView_search_icon_tint) ?: ContextCompat.getColorStateList(context, R.color.signal_icon_tint_primary)
ImageViewCompat.setImageTintList(navButton, iconTint)
ImageViewCompat.setImageTintList(clearButton, iconTint)
input.setHintTextColor(iconTint)
val clickOnly: Boolean = typedArray.getBoolean(R.styleable.KeyboardPageSearchView_click_only, false)
if (clickOnly) {
@@ -109,10 +108,14 @@ class KeyboardPageSearchView @JvmOverloads constructor(
fun showRequested(): Boolean = state == State.SHOW_REQUESTED
fun enableBackNavigation() {
navButton.setImageResource(R.drawable.ic_arrow_left_24)
navButton.setOnClickListener {
callbacks?.onNavigationClicked()
fun enableBackNavigation(enable: Boolean = true) {
navButton.setImageResource(if (enable) R.drawable.ic_arrow_left_24 else R.drawable.ic_search_24)
if (enable) {
navButton.setImageResource(R.drawable.ic_arrow_left_24)
navButton.setOnClickListener { callbacks?.onNavigationClicked() }
} else {
navButton.setImageResource(R.drawable.ic_search_24)
navButton.setOnClickListener(null)
}
}
@@ -168,6 +171,15 @@ class KeyboardPageSearchView @JvmOverloads constructor(
enableBackNavigation()
}
override fun clearFocus() {
super.clearFocus()
clearChildFocus(input)
}
fun clearQuery() {
input.text.clear()
}
interface Callbacks {
fun onFocusLost() = Unit
fun onFocusGained() = Unit

View File

@@ -10,6 +10,7 @@ import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.EmojiSearchDatabase
import org.thoughtcrime.securesms.emoji.EmojiSource
import java.util.function.Consumer
private const val MINIMUM_QUERY_THRESHOLD = 1
private const val EMOJI_SEARCH_LIMIT = 20
@@ -18,18 +19,18 @@ class EmojiSearchRepository(private val context: Context) {
private val emojiSearchDatabase: EmojiSearchDatabase = DatabaseFactory.getEmojiSearchDatabase(context)
fun submitQuery(query: String, consumer: (EmojiPageModel) -> Unit) {
if (query.length < MINIMUM_QUERY_THRESHOLD) {
consumer(RecentEmojiPageModel(context, EmojiKeyboardProvider.RECENT_STORAGE_KEY))
fun submitQuery(query: String, includeRecents: Boolean, limit: Int = EMOJI_SEARCH_LIMIT, consumer: Consumer<EmojiPageModel>) {
if (query.length < MINIMUM_QUERY_THRESHOLD && includeRecents) {
consumer.accept(RecentEmojiPageModel(context, EmojiKeyboardProvider.RECENT_STORAGE_KEY))
} else {
SignalExecutors.SERIAL.execute {
val emoji: List<String> = emojiSearchDatabase.query(query, EMOJI_SEARCH_LIMIT)
val emoji: List<String> = emojiSearchDatabase.query(query, limit)
val displayEmoji: List<Emoji> = emoji
.mapNotNull { canonical -> EmojiSource.latest.canonicalToVariations[canonical] }
.map { Emoji(it) }
consumer(EmojiSearchResultsPageModel(emoji, displayEmoji))
consumer.accept(EmojiSearchResultsPageModel(emoji, displayEmoji))
}
}
}
@@ -38,6 +39,8 @@ class EmojiSearchRepository(private val context: Context) {
private val emoji: List<String>,
private val displayEmoji: List<Emoji>
) : EmojiPageModel {
override fun getKey(): String = ""
override fun getIconAttr(): Int = -1
override fun getEmoji(): List<String> = emoji

View File

@@ -17,7 +17,7 @@ class EmojiSearchViewModel(private val repository: EmojiSearchRepository) : View
}
fun onQueryChanged(query: String) {
repository.submitQuery(query, internalPageModel::postValue)
repository.submitQuery(query = query, includeRecents = true, consumer = internalPageModel::postValue)
}
class Factory(private val repository: EmojiSearchRepository) : ViewModelProvider.Factory {