mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 20:06:51 +03:00
Merge pull request #4748 from vector-im/feature/adm/late-emoji-initialisation
Unable to render messages
This commit is contained in:
commit
e155eefb7b
8 changed files with 49 additions and 17 deletions
1
changelog.d/4733.bugfix
Normal file
1
changelog.d/4733.bugfix
Normal file
|
@ -0,0 +1 @@
|
|||
Fixes unable to render messages by allowing them to render whilst the emoji library is initialising
|
|
@ -24,6 +24,7 @@ import android.text.Spanned
|
|||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.StrikethroughSpan
|
||||
import android.text.style.UnderlineSpan
|
||||
import androidx.emoji2.text.EmojiCompat
|
||||
import im.vector.app.InstrumentedTest
|
||||
import org.amshove.kluent.shouldBeEqualTo
|
||||
import org.amshove.kluent.shouldBeTrue
|
||||
|
@ -32,12 +33,18 @@ import org.junit.Test
|
|||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.junit.runners.MethodSorters
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@RunWith(JUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
class SpanUtilsTest : InstrumentedTest {
|
||||
|
||||
private val spanUtils = SpanUtils()
|
||||
private val spanUtils = SpanUtils {
|
||||
val emojiCompat = EmojiCompat.get()
|
||||
emojiCompat.waitForInit()
|
||||
emojiCompat.process(it) ?: it
|
||||
}
|
||||
|
||||
private fun SpanUtils.canUseTextFuture(message: CharSequence): Boolean {
|
||||
return getBindingOptions(message).canUseTextFuture
|
||||
|
@ -122,4 +129,17 @@ class SpanUtilsTest : InstrumentedTest {
|
|||
}
|
||||
|
||||
private fun trueIfAlwaysAllowed() = Build.VERSION.SDK_INT < Build.VERSION_CODES.P
|
||||
|
||||
private fun EmojiCompat.waitForInit() {
|
||||
val latch = CountDownLatch(1)
|
||||
registerInitCallback(object : EmojiCompat.InitCallback() {
|
||||
override fun onInitialized() = latch.countDown()
|
||||
override fun onFailed(throwable: Throwable?) {
|
||||
latch.countDown()
|
||||
throw RuntimeException(throwable)
|
||||
}
|
||||
})
|
||||
EmojiCompat.init(context())
|
||||
latch.await(30, TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,12 @@ import timber.log.Timber
|
|||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
fun interface EmojiSpanify {
|
||||
fun spanify(sequence: CharSequence): CharSequence
|
||||
}
|
||||
|
||||
@Singleton
|
||||
class EmojiCompatWrapper @Inject constructor(private val context: Context) {
|
||||
class EmojiCompatWrapper @Inject constructor(private val context: Context) : EmojiSpanify {
|
||||
|
||||
private var initialized = false
|
||||
|
||||
|
@ -49,7 +53,7 @@ class EmojiCompatWrapper @Inject constructor(private val context: Context) {
|
|||
})
|
||||
}
|
||||
|
||||
fun safeEmojiSpanify(sequence: CharSequence): CharSequence {
|
||||
override fun spanify(sequence: CharSequence): CharSequence {
|
||||
if (initialized) {
|
||||
try {
|
||||
return EmojiCompat.get().process(sequence) ?: sequence
|
||||
|
|
|
@ -26,6 +26,8 @@ import dagger.Module
|
|||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import im.vector.app.EmojiCompatWrapper
|
||||
import im.vector.app.EmojiSpanify
|
||||
import im.vector.app.core.dispatchers.CoroutineDispatchers
|
||||
import im.vector.app.core.error.DefaultErrorFormatter
|
||||
import im.vector.app.core.error.ErrorFormatter
|
||||
|
@ -76,6 +78,9 @@ abstract class VectorBindModule {
|
|||
|
||||
@Binds
|
||||
abstract fun bindDefaultClock(clock: DefaultClock): Clock
|
||||
|
||||
@Binds
|
||||
abstract fun bindEmojiSpanify(emojiCompatWrapper: EmojiCompatWrapper): EmojiSpanify
|
||||
}
|
||||
|
||||
@InstallIn(SingletonComponent::class)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
package im.vector.app.features.home.room.detail.timeline.format
|
||||
|
||||
import dagger.Lazy
|
||||
import im.vector.app.EmojiCompatWrapper
|
||||
import im.vector.app.EmojiSpanify
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.ColorProvider
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
|
@ -39,7 +39,7 @@ import javax.inject.Inject
|
|||
class DisplayableEventFormatter @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val colorProvider: ColorProvider,
|
||||
private val emojiCompatWrapper: EmojiCompatWrapper,
|
||||
private val emojiSpanify: EmojiSpanify,
|
||||
private val noticeEventFormatter: NoticeEventFormatter,
|
||||
private val htmlRenderer: Lazy<EventHtmlRenderer>
|
||||
) {
|
||||
|
@ -100,7 +100,7 @@ class DisplayableEventFormatter @Inject constructor(
|
|||
}
|
||||
EventType.REACTION -> {
|
||||
timelineEvent.root.getClearContent().toModel<ReactionContent>()?.relatesTo?.let {
|
||||
val emojiSpanned = emojiCompatWrapper.safeEmojiSpanify(stringProvider.getString(R.string.sent_a_reaction, it.key))
|
||||
val emojiSpanned = emojiSpanify.spanify(stringProvider.getString(R.string.sent_a_reaction, it.key))
|
||||
simpleFormat(senderName, emojiSpanned, appendAuthor)
|
||||
} ?: span { }
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import com.airbnb.epoxy.TypedEpoxyController
|
|||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Incomplete
|
||||
import com.airbnb.mvrx.Success
|
||||
import im.vector.app.EmojiCompatWrapper
|
||||
import im.vector.app.EmojiSpanify
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.ui.list.genericFooterItem
|
||||
|
@ -32,8 +32,8 @@ import javax.inject.Inject
|
|||
*/
|
||||
class ViewReactionsEpoxyController @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val emojiCompatWrapper: EmojiCompatWrapper) :
|
||||
TypedEpoxyController<DisplayReactionsViewState>() {
|
||||
private val emojiSpanify: EmojiSpanify) :
|
||||
TypedEpoxyController<DisplayReactionsViewState>() {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
|
@ -56,7 +56,7 @@ class ViewReactionsEpoxyController @Inject constructor(
|
|||
reactionInfoSimpleItem {
|
||||
id(reactionInfo.eventId)
|
||||
timeStamp(reactionInfo.timestamp)
|
||||
reactionKey(host.emojiCompatWrapper.safeEmojiSpanify(reactionInfo.reactionKey))
|
||||
reactionKey(host.emojiSpanify.spanify(reactionInfo.reactionKey))
|
||||
authorDisplayName(reactionInfo.authorName ?: reactionInfo.authorId)
|
||||
userClicked { host.listener?.didSelectUser(reactionInfo.authorId) }
|
||||
}
|
||||
|
|
|
@ -21,13 +21,15 @@ import android.text.Spanned
|
|||
import android.text.style.MetricAffectingSpan
|
||||
import android.text.style.StrikethroughSpan
|
||||
import android.text.style.UnderlineSpan
|
||||
import androidx.emoji2.text.EmojiCompat
|
||||
import im.vector.app.EmojiSpanify
|
||||
import im.vector.app.features.home.room.detail.timeline.item.BindingOptions
|
||||
import javax.inject.Inject
|
||||
|
||||
class SpanUtils @Inject constructor() {
|
||||
class SpanUtils @Inject constructor(
|
||||
private val emojiSpanify: EmojiSpanify
|
||||
) {
|
||||
fun getBindingOptions(charSequence: CharSequence): BindingOptions {
|
||||
val emojiCharSequence = EmojiCompat.get().process(charSequence)
|
||||
val emojiCharSequence = emojiSpanify.spanify(charSequence)
|
||||
|
||||
if (emojiCharSequence !is Spanned) {
|
||||
return BindingOptions()
|
||||
|
|
|
@ -24,7 +24,7 @@ import android.widget.LinearLayout
|
|||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.withStyledAttributes
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import im.vector.app.EmojiCompatWrapper
|
||||
import im.vector.app.EmojiSpanify
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.utils.DimensionConverter
|
||||
import im.vector.app.core.utils.TextUtils
|
||||
|
@ -39,9 +39,9 @@ import javax.inject.Inject
|
|||
class ReactionButton @JvmOverloads constructor(context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0) :
|
||||
LinearLayout(context, attrs, defStyleAttr), View.OnClickListener, View.OnLongClickListener {
|
||||
LinearLayout(context, attrs, defStyleAttr), View.OnClickListener, View.OnLongClickListener {
|
||||
|
||||
@Inject lateinit var emojiCompatWrapper: EmojiCompatWrapper
|
||||
@Inject lateinit var emojiSpanify: EmojiSpanify
|
||||
|
||||
private val views: ReactionButtonBinding
|
||||
|
||||
|
@ -57,7 +57,7 @@ class ReactionButton @JvmOverloads constructor(context: Context,
|
|||
set(value) {
|
||||
field = value
|
||||
// maybe cache this for performances?
|
||||
val emojiSpanned = emojiCompatWrapper.safeEmojiSpanify(value)
|
||||
val emojiSpanned = emojiSpanify.spanify(value)
|
||||
views.reactionText.text = emojiSpanned
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue