Improve EmojiChooserFragment: DI
This commit is contained in:
parent
63e0b15f3d
commit
f00f34b244
|
@ -43,7 +43,6 @@ import im.vector.riotx.core.di.HasVectorInjector
|
|||
import im.vector.riotx.core.di.VectorComponent
|
||||
import im.vector.riotx.core.extensions.configureAndStart
|
||||
import im.vector.riotx.core.rx.setupRxPlugin
|
||||
import im.vector.riotx.core.utils.initKnownEmojiHashSet
|
||||
import im.vector.riotx.features.configuration.VectorConfiguration
|
||||
import im.vector.riotx.features.lifecycle.VectorActivityLifecycleCallbacks
|
||||
import im.vector.riotx.features.notifications.NotificationDrawerManager
|
||||
|
@ -137,7 +136,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
|
|||
})
|
||||
ProcessLifecycleOwner.get().lifecycle.addObserver(appStateHandler)
|
||||
// This should be done as early as possible
|
||||
initKnownEmojiHashSet(appContext)
|
||||
// initKnownEmojiHashSet(appContext)
|
||||
}
|
||||
|
||||
override fun providesMatrixConfiguration() = MatrixConfiguration(BuildConfig.FLAVOR_DESCRIPTION)
|
||||
|
|
|
@ -38,6 +38,7 @@ import im.vector.riotx.features.home.room.detail.RoomDetailFragment
|
|||
import im.vector.riotx.features.home.room.list.RoomListFragment
|
||||
import im.vector.riotx.features.login.*
|
||||
import im.vector.riotx.features.login.terms.LoginTermsFragment
|
||||
import im.vector.riotx.features.reactions.EmojiChooserFragment
|
||||
import im.vector.riotx.features.reactions.EmojiSearchResultFragment
|
||||
import im.vector.riotx.features.roomdirectory.PublicRoomsFragment
|
||||
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment
|
||||
|
@ -255,4 +256,9 @@ interface FragmentModule {
|
|||
@IntoMap
|
||||
@FragmentKey(BreadcrumbsFragment::class)
|
||||
fun bindBreadcrumbsFragment(fragment: BreadcrumbsFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(EmojiChooserFragment::class)
|
||||
fun bindEmojiChooserFragment(fragment: EmojiChooserFragment): Fragment
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ private val emojisPattern = Pattern.compile("((?:[\uD83C\uDF00-\uD83D\uDDFF]" +
|
|||
"|\uD83C\uDCCF\uFE0F?" +
|
||||
"|[\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA]\uFE0F?))")
|
||||
|
||||
/*
|
||||
// A hashset from all supported emoji
|
||||
private var knownEmojiSet: HashSet<String>? = null
|
||||
|
||||
|
@ -77,6 +78,7 @@ fun isSingleEmoji(string: String): Boolean {
|
|||
}
|
||||
return knownEmojiSet?.contains(string) ?: false
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test if a string contains emojis.
|
||||
|
|
|
@ -17,13 +17,18 @@ package im.vector.riotx.features.reactions
|
|||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.lifecycle.observe
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.extensions.cleanup
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
class EmojiChooserFragment @Inject constructor() : VectorBaseFragment() {
|
||||
class EmojiChooserFragment @Inject constructor(
|
||||
private val emojiRecyclerAdapter: EmojiRecyclerAdapter
|
||||
) : VectorBaseFragment(),
|
||||
EmojiRecyclerAdapter.InteractionListener,
|
||||
ReactionClickListener {
|
||||
|
||||
override fun getLayoutResId() = R.layout.emoji_chooser_fragment
|
||||
|
||||
|
@ -32,15 +37,32 @@ class EmojiChooserFragment @Inject constructor() : VectorBaseFragment() {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
viewModel = activityViewModelProvider.get(EmojiChooserViewModel::class.java)
|
||||
viewModel.initWithContext(context!!)
|
||||
|
||||
emojiRecyclerAdapter.reactionClickListener = this
|
||||
emojiRecyclerAdapter.interactionListener = this
|
||||
|
||||
(view as? RecyclerView)?.let {
|
||||
it.adapter = viewModel.adapter
|
||||
it.adapter = emojiRecyclerAdapter
|
||||
it.adapter?.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
viewModel.moveToSection.observe(viewLifecycleOwner) { section ->
|
||||
emojiRecyclerAdapter.scrollToSection(section)
|
||||
}
|
||||
}
|
||||
|
||||
override fun firstVisibleSectionChange(section: Int) {
|
||||
viewModel.setCurrentSection(section)
|
||||
}
|
||||
|
||||
override fun onReactionSelected(reaction: String) {
|
||||
viewModel.onReactionSelected(reaction)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
(view as? RecyclerView)?.cleanup()
|
||||
emojiRecyclerAdapter.reactionClickListener = null
|
||||
emojiRecyclerAdapter.interactionListener = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,46 +15,33 @@
|
|||
*/
|
||||
package im.vector.riotx.features.reactions
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import im.vector.riotx.core.utils.LiveEvent
|
||||
import im.vector.riotx.features.reactions.data.EmojiDataSource
|
||||
import javax.inject.Inject
|
||||
|
||||
class EmojiChooserViewModel @Inject constructor() : ViewModel() {
|
||||
|
||||
// TODO Move the adapter out of the ViewModel
|
||||
var adapter: EmojiRecyclerAdapter? = null
|
||||
val emojiSourceLiveData: MutableLiveData<EmojiDataSource> = MutableLiveData()
|
||||
|
||||
val navigateEvent: MutableLiveData<LiveEvent<String>> = MutableLiveData()
|
||||
var selectedReaction: String? = null
|
||||
var eventId: String? = null
|
||||
|
||||
val currentSection: MutableLiveData<Int> = MutableLiveData()
|
||||
val moveToSection: MutableLiveData<Int> = MutableLiveData()
|
||||
|
||||
var reactionClickListener = object : ReactionClickListener {
|
||||
override fun onReactionSelected(reaction: String) {
|
||||
selectedReaction = reaction
|
||||
navigateEvent.value = LiveEvent(NAVIGATE_FINISH)
|
||||
}
|
||||
fun onReactionSelected(reaction: String) {
|
||||
selectedReaction = reaction
|
||||
navigateEvent.value = LiveEvent(NAVIGATE_FINISH)
|
||||
}
|
||||
|
||||
fun initWithContext(context: Context) {
|
||||
// TODO load async
|
||||
val emojiDataSource = EmojiDataSource(context)
|
||||
emojiSourceLiveData.value = emojiDataSource
|
||||
adapter = EmojiRecyclerAdapter(emojiDataSource, reactionClickListener)
|
||||
adapter?.interactionListener = object : EmojiRecyclerAdapter.InteractionListener {
|
||||
override fun firstVisibleSectionChange(section: Int) {
|
||||
currentSection.value = section
|
||||
}
|
||||
}
|
||||
// Called by the Fragment, when the List is scrolled
|
||||
fun setCurrentSection(section: Int) {
|
||||
currentSection.value = section
|
||||
}
|
||||
|
||||
fun scrollToSection(sectionIndex: Int) {
|
||||
adapter?.scrollToSection(sectionIndex)
|
||||
// Called by the Activity, when a tab item is clicked
|
||||
fun scrollToSection(section: Int) {
|
||||
moveToSection.value = section
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -35,6 +35,7 @@ import im.vector.riotx.R
|
|||
import im.vector.riotx.core.di.ScreenComponent
|
||||
import im.vector.riotx.core.extensions.observeEvent
|
||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||
import im.vector.riotx.features.reactions.data.EmojiDataSource
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.android.synthetic.main.activity_emoji_reaction_picker.*
|
||||
import timber.log.Timber
|
||||
|
@ -44,7 +45,6 @@ import javax.inject.Inject
|
|||
/**
|
||||
*
|
||||
* TODO: Loading indicator while getting emoji data source?
|
||||
* TODO: migrate to MvRx
|
||||
* TODO: Finish Refactor to vector base activity
|
||||
*/
|
||||
class EmojiReactionPickerActivity : VectorBaseActivity(),
|
||||
|
@ -60,7 +60,9 @@ class EmojiReactionPickerActivity : VectorBaseActivity(),
|
|||
|
||||
override fun getTitleRes() = R.string.title_activity_emoji_reaction_picker
|
||||
|
||||
@Inject lateinit var emojiSearchResultViewModelFactory: EmojiSearchResultViewModel.Factory
|
||||
@Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider
|
||||
@Inject lateinit var emojiDataSource: EmojiDataSource
|
||||
|
||||
private val searchResultViewModel: EmojiSearchResultViewModel by viewModel()
|
||||
|
||||
|
@ -93,22 +95,20 @@ class EmojiReactionPickerActivity : VectorBaseActivity(),
|
|||
|
||||
viewModel.eventId = intent.getStringExtra(EXTRA_EVENT_ID)
|
||||
|
||||
viewModel.emojiSourceLiveData.observe(this, Observer {
|
||||
it.rawData?.categories?.let { categories ->
|
||||
for (category in categories) {
|
||||
val s = category.emojis[0]
|
||||
tabLayout.newTab()
|
||||
.also { tab ->
|
||||
tab.text = it.rawData!!.emojis[s]!!.emojiString()
|
||||
tab.contentDescription = category.name
|
||||
}
|
||||
.also { tab ->
|
||||
tabLayout.addTab(tab)
|
||||
}
|
||||
}
|
||||
tabLayout.addOnTabSelectedListener(tabLayoutSelectionListener)
|
||||
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)
|
||||
}
|
||||
|
||||
viewModel.currentSection.observe(this, Observer { section ->
|
||||
section?.let {
|
||||
|
@ -136,7 +136,6 @@ class EmojiReactionPickerActivity : VectorBaseActivity(),
|
|||
|
||||
override fun compatibilityFontUpdate(typeface: Typeface?) {
|
||||
EmojiDrawView.configureTextPaint(this, typeface)
|
||||
searchResultViewModel.dataSource
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
|
|
@ -34,6 +34,7 @@ import im.vector.riotx.features.reactions.data.EmojiDataSource
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.abs
|
||||
|
||||
/**
|
||||
|
@ -43,10 +44,12 @@ import kotlin.math.abs
|
|||
* TODO: Performances
|
||||
* TODO: Scroll to section - Find a way to snap section to the top
|
||||
*/
|
||||
class EmojiRecyclerAdapter(private val dataSource: EmojiDataSource? = null,
|
||||
private var reactionClickListener: ReactionClickListener?) :
|
||||
class EmojiRecyclerAdapter @Inject constructor(
|
||||
private val dataSource: EmojiDataSource?
|
||||
) :
|
||||
RecyclerView.Adapter<EmojiRecyclerAdapter.ViewHolder>() {
|
||||
|
||||
var reactionClickListener: ReactionClickListener? = null
|
||||
var interactionListener: InteractionListener? = null
|
||||
private var mRecyclerView: RecyclerView? = null
|
||||
|
||||
|
@ -73,7 +76,7 @@ class EmojiRecyclerAdapter(private val dataSource: EmojiDataSource? = null,
|
|||
val sectionMojis = categories[sectionNumber].emojis
|
||||
val sectionOffset = getSectionOffset(sectionNumber)
|
||||
val emoji = sectionMojis[itemPosition - sectionOffset]
|
||||
val item = dataSource.rawData!!.emojis.getValue(emoji).emojiString()
|
||||
val item = dataSource.rawData!!.emojis.getValue(emoji).emoji
|
||||
reactionClickListener?.onReactionSelected(item)
|
||||
}
|
||||
}
|
||||
|
@ -197,7 +200,7 @@ class EmojiRecyclerAdapter(private val dataSource: EmojiDataSource? = null,
|
|||
val sectionMojis = categories[sectionNumber].emojis
|
||||
val sectionOffset = getSectionOffset(sectionNumber)
|
||||
val emoji = sectionMojis[position - sectionOffset]
|
||||
val item = dataSource.rawData!!.emojis[emoji]!!.emojiString()
|
||||
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")
|
||||
|
|
|
@ -43,12 +43,12 @@ abstract class EmojiSearchResultItem : EpoxyModelWithHolder<EmojiSearchResultIte
|
|||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
// TODO use query string to highlight the matched query in name and keywords?
|
||||
holder.emojiText.text = emojiItem.emojiString()
|
||||
holder.emojiText.text = emojiItem.emoji
|
||||
holder.emojiText.typeface = emojiTypeFace ?: Typeface.DEFAULT
|
||||
holder.emojiNameText.text = emojiItem.name
|
||||
holder.emojiKeywordText.setTextOrHide(emojiItem.keywords?.joinToString())
|
||||
holder.view.setOnClickListener {
|
||||
onClickListener?.onReactionSelected(emojiItem.emojiString())
|
||||
onClickListener?.onReactionSelected(emojiItem.emoji)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
*/
|
||||
package im.vector.riotx.features.reactions
|
||||
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.airbnb.mvrx.*
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
import im.vector.riotx.features.reactions.data.EmojiDataSource
|
||||
import im.vector.riotx.features.reactions.data.EmojiItem
|
||||
|
@ -27,9 +27,24 @@ data class EmojiSearchResultViewState(
|
|||
val results: List<EmojiItem> = emptyList()
|
||||
) : MvRxState
|
||||
|
||||
class EmojiSearchResultViewModel(val dataSource: EmojiDataSource, initialState: EmojiSearchResultViewState)
|
||||
class EmojiSearchResultViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: EmojiSearchResultViewState,
|
||||
private val dataSource: EmojiDataSource)
|
||||
: VectorViewModel<EmojiSearchResultViewState, EmojiSearchAction>(initialState) {
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: EmojiSearchResultViewState): EmojiSearchResultViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<EmojiSearchResultViewModel, EmojiSearchResultViewState> {
|
||||
|
||||
override fun create(viewModelContext: ViewModelContext, state: EmojiSearchResultViewState): EmojiSearchResultViewModel? {
|
||||
val activity: EmojiReactionPickerActivity = (viewModelContext as ActivityViewModelContext).activity()
|
||||
return activity.emojiSearchResultViewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
|
||||
override fun handle(action: EmojiSearchAction) {
|
||||
when (action) {
|
||||
is EmojiSearchAction.UpdateQuery -> updateQuery(action)
|
||||
|
@ -55,12 +70,4 @@ class EmojiSearchResultViewModel(val dataSource: EmojiDataSource, initialState:
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<EmojiSearchResultViewModel, EmojiSearchResultViewState> {
|
||||
|
||||
override fun create(viewModelContext: ViewModelContext, state: EmojiSearchResultViewState): EmojiSearchResultViewModel? {
|
||||
// TODO get the data source from activity? share it with other fragment
|
||||
return EmojiSearchResultViewModel(EmojiDataSource(viewModelContext.activity), state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,17 +18,28 @@ package im.vector.riotx.features.reactions.data
|
|||
import android.content.Context
|
||||
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
|
||||
|
||||
class EmojiDataSource(val context: Context) {
|
||||
@ScreenScope
|
||||
class EmojiDataSource @Inject constructor(
|
||||
context: Context
|
||||
) {
|
||||
|
||||
var rawData: EmojiData? = null
|
||||
|
||||
init {
|
||||
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)
|
||||
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)
|
||||
}
|
||||
}.also {
|
||||
Timber.e("Emoji: $it millis")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue