diff --git a/vector/src/main/java/im/vector/riotredesign/VectorApplication.kt b/vector/src/main/java/im/vector/riotredesign/VectorApplication.kt index 4fc217a0..0327f652 100644 --- a/vector/src/main/java/im/vector/riotredesign/VectorApplication.kt +++ b/vector/src/main/java/im/vector/riotredesign/VectorApplication.kt @@ -54,6 +54,7 @@ import im.vector.riotredesign.push.fcm.FcmHelper import timber.log.Timber import java.text.SimpleDateFormat import java.util.* +import im.vector.riotredesign.core.utils.initKnownEmojiHashSet import javax.inject.Inject class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.Provider, androidx.work.Configuration.Provider { @@ -124,6 +125,8 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration. FcmHelper.onEnterBackground(appContext, activeSessionHolder) } }) + //This should be done as early as possible + initKnownEmojiHashSet(appContext) } override fun providesMatrixConfiguration() = MatrixConfiguration(BuildConfig.FLAVOR_DESCRIPTION) diff --git a/vector/src/main/java/im/vector/riotredesign/core/utils/Emoji.kt b/vector/src/main/java/im/vector/riotredesign/core/utils/Emoji.kt index 3595ad87..ac4a7536 100644 --- a/vector/src/main/java/im/vector/riotredesign/core/utils/Emoji.kt +++ b/vector/src/main/java/im/vector/riotredesign/core/utils/Emoji.kt @@ -1,5 +1,12 @@ package im.vector.riotredesign.core.utils +import android.content.Context +import com.squareup.moshi.Moshi +import im.vector.riotredesign.R +import im.vector.riotredesign.features.reactions.EmojiDataSource +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import timber.log.Timber import java.util.regex.Pattern private val emojisPattern = Pattern.compile("((?:[\uD83C\uDF00-\uD83D\uDDFF]" + @@ -26,6 +33,34 @@ 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 + +fun initKnownEmojiHashSet(context: Context, done: (() -> Unit)? = null) { + GlobalScope.launch { + context.resources.openRawResource(R.raw.emoji_picker_datasource).use { input -> + val moshi = Moshi.Builder().build() + val jsonAdapter = moshi.adapter(EmojiDataSource.EmojiData::class.java) + val inputAsString = input.bufferedReader().use { it.readText() } + val source = jsonAdapter.fromJson(inputAsString) + knownEmojiSet = HashSet() + source?.emojis?.values?.forEach { + knownEmojiSet?.add(it.emojiString()) + } + done?.invoke() + } + } +} + +fun isSingleEmoji(string: String): Boolean { + if (knownEmojiSet == null) { + Timber.e("Known Emoji Hashset not initialized") + //use fallback regexp + return containsOnlyEmojis(string) + } + return knownEmojiSet?.contains(string) ?: false +} + /** * Test if a string contains emojis. * It seems that the regex [emoji_regex]+ does not work. @@ -66,4 +101,3 @@ fun containsOnlyEmojis(str: String?): Boolean { return res } - diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt index 9aa14e6a..30cc1d1e 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/MessageMenuViewModel.kt @@ -36,6 +36,9 @@ import im.vector.riotredesign.core.resources.StringProvider import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData import org.json.JSONObject +import im.vector.riotredesign.core.utils.isSingleEmoji + + data class SimpleAction(val uid: String, val titleRes: Int, val iconResId: Int?, val data: Any? = null) data class MessageMenuState( @@ -92,7 +95,7 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M val event = session.getRoom(state.roomId)?.getTimeLineEvent(state.eventId) ?: return state val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel() - ?: event.root.content.toModel() + ?: event.root.content.toModel() val type = messageContent?.type val actions = if (!event.sendState.isSent()) { @@ -143,8 +146,8 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M if (messageContent is MessageImageContent) { this.add( SimpleAction(ACTION_SHARE, - R.string.share, R.drawable.ic_share, - session.contentUrlResolver().resolveFullSize(messageContent.url)) + R.string.share, R.drawable.ic_share, + session.contentUrlResolver().resolveFullSize(messageContent.url)) ) } //TODO @@ -212,31 +215,31 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M } } - private fun canRedact(event: TimelineEvent, myUserId: String): Boolean { - //Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment - if (event.root.getClearType() != EventType.MESSAGE) return false - //TODO if user is admin or moderator - return event.root.senderId == myUserId - } + private fun canRedact(event: TimelineEvent, myUserId: String): Boolean { + //Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment + if (event.root.getClearType() != EventType.MESSAGE) return false + //TODO if user is admin or moderator + return event.root.senderId == myUserId + } private fun canViewReactions(event: TimelineEvent): Boolean { //Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment if (event.root.getClearType() != EventType.MESSAGE) return false //TODO if user is admin or moderator - return event.annotations?.reactionsSummary?.isNotEmpty() ?: false + return event.annotations?.reactionsSummary?.any { isSingleEmoji(it.key) } ?: false } - private fun canEdit(event: TimelineEvent, myUserId: String): Boolean { - //Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment - if (event.root.getClearType() != EventType.MESSAGE) return false - //TODO if user is admin or moderator - val messageContent = event.root.content.toModel() - return event.root.senderId == myUserId && ( - messageContent?.type == MessageType.MSGTYPE_TEXT - || messageContent?.type == MessageType.MSGTYPE_EMOTE - ) - } + private fun canEdit(event: TimelineEvent, myUserId: String): Boolean { + //Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment + if (event.root.getClearType() != EventType.MESSAGE) return false + //TODO if user is admin or moderator + val messageContent = event.root.content.toModel() + return event.root.senderId == myUserId && ( + messageContent?.type == MessageType.MSGTYPE_TEXT + || messageContent?.type == MessageType.MSGTYPE_EMOTE + ) + } private fun canCopy(type: String?): Boolean { diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionViewModel.kt index cdcebd55..54991524 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/ViewReactionViewModel.kt @@ -8,6 +8,7 @@ import im.vector.matrix.android.api.session.room.model.ReactionAggregatedSummary import im.vector.matrix.rx.RxRoom import im.vector.riotredesign.core.extensions.localDateTime import im.vector.riotredesign.core.platform.VectorViewModel +import im.vector.riotredesign.core.utils.isSingleEmoji import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter import io.reactivex.Observable import io.reactivex.Single @@ -69,7 +70,9 @@ class ViewReactionViewModel @AssistedInject constructor(@Assisted .flatMapSingle { summaries -> Observable .fromIterable(summaries) - .flatMapIterable {it.reactionsSummary} + .flatMapIterable { it.reactionsSummary + .filter { reactionAggregatedSummary -> isSingleEmoji(reactionAggregatedSummary.key) } + } .toReactionInfoList() } .execute { diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/util/MessageInformationDataFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/util/MessageInformationDataFactory.kt index 508ea5af..0f8a81c9 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/util/MessageInformationDataFactory.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/util/MessageInformationDataFactory.kt @@ -20,6 +20,7 @@ import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.riotredesign.core.extensions.localDateTime import im.vector.riotredesign.core.resources.ColorProvider +import im.vector.riotredesign.core.utils.isSingleEmoji import im.vector.riotredesign.features.home.getColorFromUserId import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData @@ -68,9 +69,11 @@ class MessageInformationDataFactory @Inject constructor(private val timelineDate avatarUrl = avatarUrl, memberName = formattedMemberName, showInformation = showInformation, - orderedReactionList = event.annotations?.reactionsSummary?.map { - ReactionInfoData(it.key, it.count, it.addedByMe, it.localEchoEvents.isEmpty()) - }, + orderedReactionList = event.annotations?.reactionsSummary + ?.filter { isSingleEmoji(it.key) } + ?.map { + ReactionInfoData(it.key, it.count, it.addedByMe, it.localEchoEvents.isEmpty()) + }, hasBeenEdited = hasBeenEdited ) }