diff --git a/vector/src/main/java/im/vector/riotx/VectorApplication.kt b/vector/src/main/java/im/vector/riotx/VectorApplication.kt index f3043fbec8..b6b0d16360 100644 --- a/vector/src/main/java/im/vector/riotx/VectorApplication.kt +++ b/vector/src/main/java/im/vector/riotx/VectorApplication.kt @@ -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) diff --git a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt index 1a7a07bee2..442c5f6f96 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt @@ -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 } diff --git a/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt b/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt index 9b5552a73b..3b16c3ea22 100644 --- a/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt +++ b/vector/src/main/java/im/vector/riotx/core/utils/Emoji.kt @@ -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? = null @@ -77,6 +78,7 @@ fun isSingleEmoji(string: String): Boolean { } return knownEmojiSet?.contains(string) ?: false } + */ /** * Test if a string contains emojis. diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt index c49bdcbf92..bbedeff552 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserFragment.kt @@ -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() } } diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserViewModel.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserViewModel.kt index 014250d860..9a0317f454 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiChooserViewModel.kt @@ -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 = MutableLiveData() - val navigateEvent: MutableLiveData> = MutableLiveData() var selectedReaction: String? = null var eventId: String? = null val currentSection: MutableLiveData = MutableLiveData() + val moveToSection: MutableLiveData = 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 { diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt index 02f564ba72..7c2a9349e0 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiReactionPickerActivity.kt @@ -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() { diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiRecyclerAdapter.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiRecyclerAdapter.kt index c7df7fbe7b..a955189b17 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiRecyclerAdapter.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiRecyclerAdapter.kt @@ -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() { + 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") diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt index b3e70bcdf6..fd0a6d70ff 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/EmojiSearchResultItem.kt @@ -43,12 +43,12 @@ abstract class EmojiSearchResultItem : EpoxyModelWithHolder = emptyList() ) : MvRxState -class EmojiSearchResultViewModel(val dataSource: EmojiDataSource, initialState: EmojiSearchResultViewState) +class EmojiSearchResultViewModel @AssistedInject constructor( + @Assisted initialState: EmojiSearchResultViewState, + private val dataSource: EmojiDataSource) : VectorViewModel(initialState) { + @AssistedInject.Factory + interface Factory { + fun create(initialState: EmojiSearchResultViewState): EmojiSearchResultViewModel + } + + companion object : MvRxViewModelFactory { + + 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 { - - 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) - } - } } diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt index 5564182f4e..c9d683a2b9 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/data/EmojiDataSource.kt @@ -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") } } }