diff --git a/vector/src/main/java/im/vector/riotx/EmojiCompatWrapper.kt b/vector/src/main/java/im/vector/riotx/EmojiCompatWrapper.kt new file mode 100644 index 00000000..b1832840 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/EmojiCompatWrapper.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.riotx + +import android.content.Context +import androidx.appcompat.app.AppCompatActivity +import androidx.core.provider.FontRequest +import androidx.emoji.text.EmojiCompat +import androidx.emoji.text.FontRequestEmojiCompatConfig +import timber.log.Timber +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class EmojiCompatWrapper @Inject constructor(private val context: Context) { + + private var initialized = false + + fun init(fontRequest: FontRequest) { + + //Use emoji compat for the benefit of emoji spans + val config = FontRequestEmojiCompatConfig(context, fontRequest) + // we want to replace all emojis with selected font + .setReplaceAll(true) + //Debug options +// .setEmojiSpanIndicatorEnabled(true) +// .setEmojiSpanIndicatorColor(Color.GREEN) + EmojiCompat.init(config) + .registerInitCallback(object : EmojiCompat.InitCallback() { + override fun onInitialized() { + Timber.v("Emoji compat onInitialized success ") + initialized = true + } + + override fun onFailed(throwable: Throwable?) { + Timber.e(throwable, "Failed to init EmojiCompat") + } + }) + } + + fun safeEmojiSpanify(sequence: CharSequence): CharSequence { + if (initialized) { + try { + return EmojiCompat.get().process(sequence) + } catch (throwable: Throwable) { + //Defensive coding against error (should not happend as it is initialized) + Timber.e(throwable, "Failed to init EmojiCompat") + return sequence + } + } else { + return sequence + } + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotx/VectorApplication.kt b/vector/src/main/java/im/vector/riotx/VectorApplication.kt index 4e09d790..b62c44f5 100644 --- a/vector/src/main/java/im/vector/riotx/VectorApplication.kt +++ b/vector/src/main/java/im/vector/riotx/VectorApplication.kt @@ -19,13 +19,10 @@ package im.vector.riotx import android.app.Application import android.content.Context import android.content.res.Configuration -import android.graphics.Color import android.os.Handler import android.os.HandlerThread import androidx.core.provider.FontRequest import androidx.core.provider.FontsContractCompat -import androidx.emoji.text.EmojiCompat -import androidx.emoji.text.FontRequestEmojiCompatConfig import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent @@ -69,6 +66,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration. @Inject lateinit var authenticator: Authenticator @Inject lateinit var vectorConfiguration: VectorConfiguration @Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider + @Inject lateinit var emojiCompatWrapper: EmojiCompatWrapper @Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler @Inject lateinit var activeSessionHolder: ActiveSessionHolder @Inject lateinit var notificationDrawerManager: NotificationDrawerManager @@ -109,21 +107,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration. FontsContractCompat.requestFont(this, fontRequest, emojiCompatFontProvider, getFontThreadHandler()) vectorConfiguration.initConfiguration() - //Use emoji compat for the benefit of emoji spans - val config = FontRequestEmojiCompatConfig(this, fontRequest) - .setReplaceAll(true) // we want to replace all emojis with selected font -// .setEmojiSpanIndicatorEnabled(true) -// .setEmojiSpanIndicatorColor(Color.GREEN) - EmojiCompat.init(config) - .registerInitCallback(object : EmojiCompat.InitCallback() { - override fun onInitialized() { - Timber.v("Emoji compat onInitialized success ") - } - - override fun onFailed(throwable: Throwable?) { - Timber.e(throwable,"Failed to init EmojiCompat") - } - }) + emojiCompatWrapper.init(fontRequest) NotificationUtils.createNotificationChannels(applicationContext) if (authenticator.hasAuthenticatedSessions() && !activeSessionHolder.hasActiveSession()) { diff --git a/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt b/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt index ffde2bc2..6bfbddba 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt @@ -58,6 +58,7 @@ import im.vector.riotx.features.rageshake.BugReportActivity import im.vector.riotx.features.rageshake.BugReporter import im.vector.riotx.features.rageshake.RageShake import im.vector.riotx.features.reactions.EmojiReactionPickerActivity +import im.vector.riotx.features.reactions.widget.ReactionButton import im.vector.riotx.features.roomdirectory.PublicRoomsFragment import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActivity @@ -181,6 +182,8 @@ interface ScreenComponent { fun inject(displayReadReceiptsBottomSheet: DisplayReadReceiptsBottomSheet) + fun inject(reactionButton: ReactionButton) + @Component.Factory interface Factory { fun create(vectorComponent: VectorComponent, diff --git a/vector/src/main/java/im/vector/riotx/core/di/VectorComponent.kt b/vector/src/main/java/im/vector/riotx/core/di/VectorComponent.kt index d1b87f0b..7cbbc306 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/VectorComponent.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/VectorComponent.kt @@ -24,6 +24,7 @@ import im.vector.matrix.android.api.Matrix import im.vector.matrix.android.api.auth.Authenticator import im.vector.matrix.android.api.session.Session import im.vector.riotx.EmojiCompatFontProvider +import im.vector.riotx.EmojiCompatWrapper import im.vector.riotx.VectorApplication import im.vector.riotx.core.pushers.PushersManager import im.vector.riotx.features.configuration.VectorConfiguration @@ -70,6 +71,8 @@ interface VectorComponent { fun emojiCompatFontProvider(): EmojiCompatFontProvider + fun emojiCompatWrapper() : EmojiCompatWrapper + fun eventHtmlRenderer(): EventHtmlRenderer fun navigator(): Navigator diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewReactionBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewReactionBottomSheet.kt index 9d21c89e..83d61782 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewReactionBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewReactionBottomSheet.kt @@ -46,9 +46,7 @@ class ViewReactionBottomSheet : VectorBaseBottomSheetDialogFragment() { @BindView(R.id.bottom_sheet_display_reactions_list) lateinit var epoxyRecyclerView: EpoxyRecyclerView - private val epoxyController by lazy { - ViewReactionsEpoxyController(requireContext()) - } + @Inject lateinit var epoxyController: ViewReactionsEpoxyController override fun injectWith(screenComponent: ScreenComponent) { screenComponent.inject(this) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewReactionsEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewReactionsEpoxyController.kt index 33e1d4df..904a2395 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewReactionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewReactionsEpoxyController.kt @@ -17,21 +17,23 @@ package im.vector.riotx.features.home.room.detail.timeline.action import android.content.Context -import android.graphics.Typeface -import android.text.format.DateUtils -import androidx.emoji.text.EmojiCompat import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Incomplete import com.airbnb.mvrx.Success +import im.vector.riotx.EmojiCompatWrapper import im.vector.riotx.R +import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.ui.list.genericFooterItem import im.vector.riotx.core.ui.list.genericLoaderItem +import javax.inject.Inject /** * Epoxy controller for reaction event list */ -class ViewReactionsEpoxyController(private val context: Context) +class ViewReactionsEpoxyController @Inject constructor( + private val stringProvider: StringProvider, + private val emojiCompatWrapper: EmojiCompatWrapper ) : TypedEpoxyController() { override fun buildModels(state: DisplayReactionsViewState) { @@ -44,7 +46,7 @@ class ViewReactionsEpoxyController(private val context: Context) is Fail -> { genericFooterItem { id("failure") - text(context.getString(R.string.unknown_error)) + text(stringProvider.getString(R.string.unknown_error)) } } is Success -> { @@ -52,7 +54,7 @@ class ViewReactionsEpoxyController(private val context: Context) reactionInfoSimpleItem { id(it.eventId) timeStamp(it.timestamp) - reactionKey(EmojiCompat.get().process(it.reactionKey)) + reactionKey(emojiCompatWrapper.safeEmojiSpanify(it.reactionKey)) authorDisplayName(it.authorName ?: it.authorId) } } diff --git a/vector/src/main/java/im/vector/riotx/features/reactions/widget/ReactionButton.kt b/vector/src/main/java/im/vector/riotx/features/reactions/widget/ReactionButton.kt index 0644e962..483165ab 100644 --- a/vector/src/main/java/im/vector/riotx/features/reactions/widget/ReactionButton.kt +++ b/vector/src/main/java/im/vector/riotx/features/reactions/widget/ReactionButton.kt @@ -21,7 +21,6 @@ import android.animation.AnimatorSet import android.animation.ObjectAnimator import android.content.Context import android.content.res.TypedArray -import android.graphics.Typeface import android.graphics.drawable.Drawable import android.util.AttributeSet import android.view.LayoutInflater @@ -35,9 +34,11 @@ import android.widget.TextView import androidx.annotation.ColorInt import androidx.annotation.ColorRes import androidx.core.content.ContextCompat -import androidx.emoji.text.EmojiCompat +import im.vector.riotx.EmojiCompatWrapper import im.vector.riotx.R +import im.vector.riotx.core.di.HasScreenInjector import im.vector.riotx.core.utils.TextUtils +import javax.inject.Inject /** * An animated reaction button. @@ -47,6 +48,12 @@ class ReactionButton @JvmOverloads constructor(context: Context, attrs: Attribut defStyleAttr: Int = 0) : FrameLayout(context, attrs, defStyleAttr), View.OnClickListener, View.OnLongClickListener { + init { + if (context is HasScreenInjector) { + context.injector().inject(this) + } + } + companion object { private val DECCELERATE_INTERPOLATOR = DecelerateInterpolator() private val ACCELERATE_DECELERATE_INTERPOLATOR = AccelerateDecelerateInterpolator() @@ -54,6 +61,8 @@ class ReactionButton @JvmOverloads constructor(context: Context, attrs: Attribut } + @Inject lateinit var emojiCompatWrapper: EmojiCompatWrapper + private var emojiView: TextView? = null private var countTextView: TextView? = null @@ -78,7 +87,7 @@ class ReactionButton @JvmOverloads constructor(context: Context, attrs: Attribut set(value) { field = value //maybe cache this for performances? - val emojiSpanned = EmojiCompat.get().process(value) + val emojiSpanned = emojiCompatWrapper.safeEmojiSpanify(value) emojiView?.text = emojiSpanned }