Merge pull request #531 from vector-im/feature/fix_crash_530

Fix / EmojiCompat not initialized
This commit is contained in:
Valere 2019-08-29 17:46:51 +02:00 committed by GitHub
commit 28e82cb8ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 96 additions and 30 deletions

View File

@ -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
}
}
}

View File

@ -19,13 +19,10 @@ package im.vector.riotx
import android.app.Application import android.app.Application
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color
import android.os.Handler import android.os.Handler
import android.os.HandlerThread import android.os.HandlerThread
import androidx.core.provider.FontRequest import androidx.core.provider.FontRequest
import androidx.core.provider.FontsContractCompat import androidx.core.provider.FontsContractCompat
import androidx.emoji.text.EmojiCompat
import androidx.emoji.text.FontRequestEmojiCompatConfig
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.OnLifecycleEvent
@ -69,6 +66,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
@Inject lateinit var authenticator: Authenticator @Inject lateinit var authenticator: Authenticator
@Inject lateinit var vectorConfiguration: VectorConfiguration @Inject lateinit var vectorConfiguration: VectorConfiguration
@Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider @Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider
@Inject lateinit var emojiCompatWrapper: EmojiCompatWrapper
@Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler @Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
@Inject lateinit var activeSessionHolder: ActiveSessionHolder @Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager @Inject lateinit var notificationDrawerManager: NotificationDrawerManager
@ -109,21 +107,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
FontsContractCompat.requestFont(this, fontRequest, emojiCompatFontProvider, getFontThreadHandler()) FontsContractCompat.requestFont(this, fontRequest, emojiCompatFontProvider, getFontThreadHandler())
vectorConfiguration.initConfiguration() vectorConfiguration.initConfiguration()


//Use emoji compat for the benefit of emoji spans emojiCompatWrapper.init(fontRequest)
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")
}
})


NotificationUtils.createNotificationChannels(applicationContext) NotificationUtils.createNotificationChannels(applicationContext)
if (authenticator.hasAuthenticatedSessions() && !activeSessionHolder.hasActiveSession()) { if (authenticator.hasAuthenticatedSessions() && !activeSessionHolder.hasActiveSession()) {

View File

@ -58,6 +58,7 @@ import im.vector.riotx.features.rageshake.BugReportActivity
import im.vector.riotx.features.rageshake.BugReporter import im.vector.riotx.features.rageshake.BugReporter
import im.vector.riotx.features.rageshake.RageShake import im.vector.riotx.features.rageshake.RageShake
import im.vector.riotx.features.reactions.EmojiReactionPickerActivity 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.PublicRoomsFragment
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActivity import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActivity
@ -181,6 +182,8 @@ interface ScreenComponent {


fun inject(displayReadReceiptsBottomSheet: DisplayReadReceiptsBottomSheet) fun inject(displayReadReceiptsBottomSheet: DisplayReadReceiptsBottomSheet)


fun inject(reactionButton: ReactionButton)

@Component.Factory @Component.Factory
interface Factory { interface Factory {
fun create(vectorComponent: VectorComponent, fun create(vectorComponent: VectorComponent,

View File

@ -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.auth.Authenticator
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.riotx.EmojiCompatFontProvider import im.vector.riotx.EmojiCompatFontProvider
import im.vector.riotx.EmojiCompatWrapper
import im.vector.riotx.VectorApplication import im.vector.riotx.VectorApplication
import im.vector.riotx.core.pushers.PushersManager import im.vector.riotx.core.pushers.PushersManager
import im.vector.riotx.features.configuration.VectorConfiguration import im.vector.riotx.features.configuration.VectorConfiguration
@ -70,6 +71,8 @@ interface VectorComponent {


fun emojiCompatFontProvider(): EmojiCompatFontProvider fun emojiCompatFontProvider(): EmojiCompatFontProvider


fun emojiCompatWrapper() : EmojiCompatWrapper

fun eventHtmlRenderer(): EventHtmlRenderer fun eventHtmlRenderer(): EventHtmlRenderer


fun navigator(): Navigator fun navigator(): Navigator

View File

@ -46,9 +46,7 @@ class ViewReactionBottomSheet : VectorBaseBottomSheetDialogFragment() {
@BindView(R.id.bottom_sheet_display_reactions_list) @BindView(R.id.bottom_sheet_display_reactions_list)
lateinit var epoxyRecyclerView: EpoxyRecyclerView lateinit var epoxyRecyclerView: EpoxyRecyclerView


private val epoxyController by lazy { @Inject lateinit var epoxyController: ViewReactionsEpoxyController
ViewReactionsEpoxyController(requireContext())
}


override fun injectWith(screenComponent: ScreenComponent) { override fun injectWith(screenComponent: ScreenComponent) {
screenComponent.inject(this) screenComponent.inject(this)

View File

@ -17,21 +17,23 @@
package im.vector.riotx.features.home.room.detail.timeline.action package im.vector.riotx.features.home.room.detail.timeline.action


import android.content.Context 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.epoxy.TypedEpoxyController
import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Incomplete import com.airbnb.mvrx.Incomplete
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import im.vector.riotx.EmojiCompatWrapper
import im.vector.riotx.R 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.genericFooterItem
import im.vector.riotx.core.ui.list.genericLoaderItem import im.vector.riotx.core.ui.list.genericLoaderItem
import javax.inject.Inject


/** /**
* Epoxy controller for reaction event list * 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<DisplayReactionsViewState>() { : TypedEpoxyController<DisplayReactionsViewState>() {


override fun buildModels(state: DisplayReactionsViewState) { override fun buildModels(state: DisplayReactionsViewState) {
@ -44,7 +46,7 @@ class ViewReactionsEpoxyController(private val context: Context)
is Fail -> { is Fail -> {
genericFooterItem { genericFooterItem {
id("failure") id("failure")
text(context.getString(R.string.unknown_error)) text(stringProvider.getString(R.string.unknown_error))
} }
} }
is Success -> { is Success -> {
@ -52,7 +54,7 @@ class ViewReactionsEpoxyController(private val context: Context)
reactionInfoSimpleItem { reactionInfoSimpleItem {
id(it.eventId) id(it.eventId)
timeStamp(it.timestamp) timeStamp(it.timestamp)
reactionKey(EmojiCompat.get().process(it.reactionKey)) reactionKey(emojiCompatWrapper.safeEmojiSpanify(it.reactionKey))
authorDisplayName(it.authorName ?: it.authorId) authorDisplayName(it.authorName ?: it.authorId)
} }
} }

View File

@ -21,7 +21,6 @@ import android.animation.AnimatorSet
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.content.Context import android.content.Context
import android.content.res.TypedArray import android.content.res.TypedArray
import android.graphics.Typeface
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
@ -35,9 +34,11 @@ import android.widget.TextView
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.annotation.ColorRes import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.emoji.text.EmojiCompat import im.vector.riotx.EmojiCompatWrapper
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.di.HasScreenInjector
import im.vector.riotx.core.utils.TextUtils import im.vector.riotx.core.utils.TextUtils
import javax.inject.Inject


/** /**
* An animated reaction button. * An animated reaction button.
@ -47,6 +48,12 @@ class ReactionButton @JvmOverloads constructor(context: Context, attrs: Attribut
defStyleAttr: Int = 0) defStyleAttr: Int = 0)
: FrameLayout(context, attrs, defStyleAttr), View.OnClickListener, View.OnLongClickListener { : FrameLayout(context, attrs, defStyleAttr), View.OnClickListener, View.OnLongClickListener {


init {
if (context is HasScreenInjector) {
context.injector().inject(this)
}
}

companion object { companion object {
private val DECCELERATE_INTERPOLATOR = DecelerateInterpolator() private val DECCELERATE_INTERPOLATOR = DecelerateInterpolator()
private val ACCELERATE_DECELERATE_INTERPOLATOR = AccelerateDecelerateInterpolator() 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 emojiView: TextView? = null
private var countTextView: TextView? = null private var countTextView: TextView? = null


@ -78,7 +87,7 @@ class ReactionButton @JvmOverloads constructor(context: Context, attrs: Attribut
set(value) { set(value) {
field = value field = value
//maybe cache this for performances? //maybe cache this for performances?
val emojiSpanned = EmojiCompat.get().process(value) val emojiSpanned = emojiCompatWrapper.safeEmojiSpanify(value)
emojiView?.text = emojiSpanned emojiView?.text = emojiSpanned
} }