Use Font emoji compat for quickReactions and pills

This commit is contained in:
Valere 2019-06-04 17:16:02 +02:00
parent 53c91dc0c2
commit d2f648edec
9 changed files with 149 additions and 59 deletions

View File

@ -0,0 +1,48 @@
package im.vector.riotredesign

import android.graphics.Typeface
import androidx.core.provider.FontsContractCompat
import timber.log.Timber


class EmojiCompatFontProvider : FontsContractCompat.FontRequestCallback() {

var typeface: Typeface? = null
set(value) {
if (value != field) {
field = value
listeners.forEach {
try {
it.compatibilityFontUpdate(value)
} catch (t: Throwable) {
Timber.e(t)
}
}
}
}

private val listeners = ArrayList<FontProviderListener>()

override fun onTypefaceRetrieved(typeface: Typeface) {
this.typeface = typeface
}

override fun onTypefaceRequestFailed(reason: Int) {
Timber.e("Failed to load Emoji Compatible font, reason:$reason")
}

fun addListener(listener: FontProviderListener) {
if (!listeners.contains(listener)) {
listeners.add(listener)
}
}

fun removeListener(listener: FontProviderListener) {
listeners.remove(listener)
}


interface FontProviderListener {
fun compatibilityFontUpdate(typeface: Typeface?)
}
}

View File

@ -18,6 +18,10 @@ package im.vector.riotredesign

import android.app.Application
import android.content.Context
import android.os.Handler
import android.os.HandlerThread
import androidx.core.provider.FontRequest
import androidx.core.provider.FontsContractCompat
import android.content.res.Configuration
import androidx.multidex.MultiDex
import com.airbnb.epoxy.EpoxyAsyncUtil
@ -41,6 +45,10 @@ import timber.log.Timber

class VectorApplication : Application() {

//font thread handler
private var mFontThreadHandler: Handler? = null


val vectorConfiguration: VectorConfiguration by inject()

override fun onCreate() {
@ -63,10 +71,20 @@ class VectorApplication : Application() {
val appModule = AppModule(applicationContext).definition
val homeModule = HomeModule().definition
val roomDirectoryModule = RoomDirectoryModule().definition
startKoin(listOf(appModule, homeModule, roomDirectoryModule), logger = EmptyLogger())
val koin = startKoin(listOf(appModule, homeModule, roomDirectoryModule), logger = EmptyLogger())

Matrix.getInstance().setApplicationFlavor(BuildConfig.FLAVOR_DESCRIPTION)

val fontRequest = FontRequest(
"com.google.android.gms.fonts",
"com.google.android.gms",
"Noto Color Emoji Compat",
R.array.com_google_android_gms_fonts_certs
)

// val efp = koin.koinContext.get<EmojiCompatFontProvider>()
FontsContractCompat.requestFont(this, fontRequest, koin.koinContext.get<EmojiCompatFontProvider>(), getFontThreadHandler())

vectorConfiguration.initConfiguration()
}

@ -81,4 +99,13 @@ class VectorApplication : Application() {
vectorConfiguration.onConfigurationChanged(newConfig)
}

private fun getFontThreadHandler(): Handler {
if (mFontThreadHandler == null) {
val handlerThread = HandlerThread("fonts")
handlerThread.start()
mFontThreadHandler = Handler(handlerThread.looper)
}
return mFontThreadHandler!!
}

}

View File

@ -20,6 +20,7 @@ import android.content.Context
import android.content.Context.MODE_PRIVATE
import androidx.fragment.app.Fragment
import im.vector.matrix.android.api.Matrix
import im.vector.riotredesign.EmojiCompatFontProvider
import im.vector.riotredesign.core.error.ErrorFormatter
import im.vector.riotredesign.core.resources.LocaleProvider
import im.vector.riotredesign.core.resources.StringArrayProvider
@ -90,5 +91,9 @@ class AppModule(private val context: Context) {
DefaultNavigator(fragment) as Navigator
}

single {
EmojiCompatFontProvider()
}

}
}

View File

@ -72,7 +72,8 @@ class HomeModule {
val timelineMediaSizeProvider = TimelineMediaSizeProvider()
val colorProvider = ColorProvider(fragment.requireContext())
val timelineDateFormatter = get<TimelineDateFormatter>()
val messageItemFactory = MessageItemFactory(colorProvider, timelineMediaSizeProvider, timelineDateFormatter, eventHtmlRenderer, get())
val messageItemFactory = MessageItemFactory(colorProvider, timelineMediaSizeProvider,
timelineDateFormatter, eventHtmlRenderer, get(), get())

val timelineItemFactory = TimelineItemFactory(
messageItemFactory = messageItemFactory,

View File

@ -15,6 +15,7 @@
*/
package im.vector.riotredesign.features.home.room.detail.timeline.action

import android.graphics.Typeface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -28,7 +29,9 @@ import com.airbnb.mvrx.BaseMvRxFragment
import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.riotredesign.EmojiCompatFontProvider
import im.vector.riotredesign.R
import org.koin.android.ext.android.inject

/**
* Quick Reaction Fragment (agree / like reactions)
@ -54,6 +57,8 @@ class QuickReactionFragment : BaseMvRxFragment() {

var interactionListener: InteractionListener? = null

val fontProvider by inject<EmojiCompatFontProvider>()

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.adapter_item_action_quick_reaction, container, false)
ButterKnife.bind(this, view)
@ -68,6 +73,10 @@ class QuickReactionFragment : BaseMvRxFragment() {
quickReact3Text.text = QuickReactionViewModel.likePositive
quickReact4Text.text = QuickReactionViewModel.likeNegative

listOf(quickReact1Text, quickReact2Text, quickReact3Text, quickReact4Text).forEach {
it.typeface = fontProvider.typeface ?: Typeface.DEFAULT
}

//configure click listeners
quickReact1Text.setOnClickListener {
viewModel.toggleAgree(true)
@ -88,11 +97,11 @@ class QuickReactionFragment : BaseMvRxFragment() {

TransitionManager.beginDelayedTransition(rootLayout)
when (it.agreeTrigleState) {
TriggleState.NONE -> {
TriggleState.NONE -> {
quickReact1Text.alpha = 1f
quickReact2Text.alpha = 1f
}
TriggleState.FIRST -> {
TriggleState.FIRST -> {
quickReact1Text.alpha = 1f
quickReact2Text.alpha = 0.2f

@ -103,11 +112,11 @@ class QuickReactionFragment : BaseMvRxFragment() {
}
}
when (it.likeTriggleState) {
TriggleState.NONE -> {
TriggleState.NONE -> {
quickReact3Text.alpha = 1f
quickReact4Text.alpha = 1f
}
TriggleState.FIRST -> {
TriggleState.FIRST -> {
quickReact3Text.alpha = 1f
quickReact4Text.alpha = 0.2f


View File

@ -33,6 +33,7 @@ import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
import im.vector.matrix.android.api.session.room.model.message.*
import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.EmojiCompatFontProvider
import im.vector.riotredesign.R
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
import im.vector.riotredesign.core.extensions.localDateTime
@ -55,7 +56,8 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
private val timelineDateFormatter: TimelineDateFormatter,
private val htmlRenderer: EventHtmlRenderer,
private val stringProvider: StringProvider) {
private val stringProvider: StringProvider,
private val emojiCompatFontProvider: EmojiCompatFontProvider) {

fun create(event: TimelineEvent,
nextEvent: TimelineEvent?,
@ -115,24 +117,24 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
// val all = event.root.toContent()
// val ev = all.toModel<Event>()
return when (messageContent) {
is MessageEmoteContent -> buildEmoteMessageItem(messageContent,
is MessageEmoteContent -> buildEmoteMessageItem(messageContent,
informationData,
hasBeenEdited,
event.annotations?.editSummary,
callback)
is MessageTextContent -> buildTextMessageItem(event.sendState,
is MessageTextContent -> buildTextMessageItem(event.sendState,
messageContent,
informationData,
hasBeenEdited,
event.annotations?.editSummary,
callback
)
is MessageImageContent -> buildImageMessageItem(messageContent, informationData, callback)
is MessageImageContent -> buildImageMessageItem(messageContent, informationData, callback)
is MessageNoticeContent -> buildNoticeMessageItem(messageContent, informationData, callback)
is MessageVideoContent -> buildVideoMessageItem(messageContent, informationData, callback)
is MessageFileContent -> buildFileMessageItem(messageContent, informationData, callback)
is MessageAudioContent -> buildAudioMessageItem(messageContent, informationData, callback)
else -> buildNotHandledMessageItem(messageContent)
is MessageVideoContent -> buildVideoMessageItem(messageContent, informationData, callback)
is MessageFileContent -> buildFileMessageItem(messageContent, informationData, callback)
is MessageAudioContent -> buildAudioMessageItem(messageContent, informationData, callback)
else -> buildNotHandledMessageItem(messageContent)
}
}

@ -144,20 +146,21 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
.filename(messageContent.body)
.iconRes(R.drawable.filetype_audio)
.reactionPillCallback(callback)
.emojiTypeFace(emojiCompatFontProvider.typeface)
.avatarClickListener(
DebouncedClickListener(View.OnClickListener { view ->
DebouncedClickListener(View.OnClickListener {
callback?.onAvatarClicked(informationData)
}))
.memberClickListener(
DebouncedClickListener(View.OnClickListener { view ->
DebouncedClickListener(View.OnClickListener {
callback?.onMemberNameClicked(informationData)
}))
.cellClickListener(
DebouncedClickListener(View.OnClickListener { view ->
DebouncedClickListener(View.OnClickListener { view: View ->
callback?.onEventCellClicked(informationData, messageContent, view)
}))
.clickListener(
DebouncedClickListener(View.OnClickListener { _ ->
DebouncedClickListener(View.OnClickListener {
callback?.onAudioMessageClicked(messageContent)
}))
.longClickListener { view ->
@ -173,6 +176,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
.informationData(informationData)
.filename(messageContent.body)
.reactionPillCallback(callback)
.emojiTypeFace(emojiCompatFontProvider.typeface)
.iconRes(R.drawable.filetype_attachment)
.avatarClickListener(
DebouncedClickListener(View.OnClickListener { view ->
@ -221,6 +225,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
.informationData(informationData)
.mediaData(data)
.reactionPillCallback(callback)
.emojiTypeFace(emojiCompatFontProvider.typeface)
.avatarClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onAvatarClicked(informationData)
@ -268,6 +273,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
.informationData(informationData)
.mediaData(thumbnailData)
.reactionPillCallback(callback)
.emojiTypeFace(emojiCompatFontProvider.typeface)
.avatarClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onAvatarClicked(informationData)
@ -311,6 +317,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
}
.informationData(informationData)
.reactionPillCallback(callback)
.emojiTypeFace(emojiCompatFontProvider.typeface)
.avatarClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onAvatarClicked(informationData)
@ -384,6 +391,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
.message(message)
.informationData(informationData)
.reactionPillCallback(callback)
.emojiTypeFace(emojiCompatFontProvider.typeface)
.avatarClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onAvatarClicked(informationData)
@ -423,6 +431,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
}
.informationData(informationData)
.reactionPillCallback(callback)
.emojiTypeFace(emojiCompatFontProvider.typeface)
.avatarClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onAvatarClicked(informationData)

View File

@ -16,6 +16,7 @@

package im.vector.riotredesign.features.home.room.detail.timeline.item

import android.graphics.Typeface
import android.os.Build
import android.view.View
import android.view.ViewGroup
@ -50,6 +51,9 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : BaseEventItem<H>() {
@EpoxyAttribute
var memberClickListener: View.OnClickListener? = null

@EpoxyAttribute
var emojiTypeFace: Typeface? = null

@EpoxyAttribute
var reactionPillCallback: TimelineEventController.ReactionPillCallback? = null

@ -116,6 +120,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : BaseEventItem<H>() {
idToRefInFlow.add(reactionButton.id)
reactionButton.reactionString = reaction.key
reactionButton.reactionCount = reaction.count
reactionButton.emojiTypeFace = emojiTypeFace
reactionButton.setChecked(reaction.addedByMe)
reactionButton.isEnabled = reaction.synced
}

View File

@ -19,23 +19,20 @@ import android.app.Activity
import android.content.Context
import android.content.Intent
import android.graphics.Typeface
import android.os.Handler
import android.os.HandlerThread
import android.util.TypedValue
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.widget.SearchView
import androidx.appcompat.widget.Toolbar
import androidx.core.provider.FontRequest
import androidx.core.provider.FontsContractCompat
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import com.google.android.material.tabs.TabLayout
import im.vector.riotredesign.EmojiCompatFontProvider
import im.vector.riotredesign.R
import im.vector.riotredesign.core.platform.VectorBaseActivity
import kotlinx.android.synthetic.main.activity_emoji_reaction_picker.*
import timber.log.Timber
import org.koin.android.ext.android.inject

/**
*
@ -44,20 +41,21 @@ import timber.log.Timber
* TODO: Finish Refactor to vector base activity
* TODO: Move font request to app
*/
class EmojiReactionPickerActivity : VectorBaseActivity() {
class EmojiReactionPickerActivity : VectorBaseActivity(), EmojiCompatFontProvider.FontProviderListener {


private lateinit var tabLayout: TabLayout

lateinit var viewModel: EmojiChooserViewModel

private var mHandler: Handler? = null

override fun getMenuRes(): Int = R.menu.menu_emoji_reaction_picker

override fun getLayoutRes(): Int = R.layout.activity_emoji_reaction_picker

override fun getTitleRes(): Int = R.string.title_activity_emoji_reaction_picker

val emojiCompatFontProvider by inject<EmojiCompatFontProvider>()

private var tabLayoutSelectionListener = object : TabLayout.BaseOnTabSelectedListener<TabLayout.Tab> {
override fun onTabReselected(p0: TabLayout.Tab) {
}
@ -71,19 +69,13 @@ class EmojiReactionPickerActivity : VectorBaseActivity() {

}

private fun getFontThreadHandler(): Handler {
if (mHandler == null) {
val handlerThread = HandlerThread("fonts")
handlerThread.start()
mHandler = Handler(handlerThread.looper)
}
return mHandler!!
}

override fun initUiAndData() {
configureToolbar(emojiPickerToolbar)

requestEmojivUnicode10CompatibleFont()
emojiCompatFontProvider.let {
EmojiDrawView.configureTextPaint(this, it.typeface)
it.addListener(this)
}

tabLayout = findViewById(R.id.tabs)

@ -124,27 +116,13 @@ class EmojiReactionPickerActivity : VectorBaseActivity() {
})
}

private fun requestEmojivUnicode10CompatibleFont() {
val fontRequest = FontRequest(
"com.google.android.gms.fonts",
"com.google.android.gms",
"Noto Color Emoji Compat",
R.array.com_google_android_gms_fonts_certs
)
override fun compatibilityFontUpdate(typeface: Typeface?) {
EmojiDrawView.configureTextPaint(this, typeface)
}

EmojiDrawView.configureTextPaint(this, null)
val callback = object : FontsContractCompat.FontRequestCallback() {

override fun onTypefaceRetrieved(typeface: Typeface) {
EmojiDrawView.configureTextPaint(this@EmojiReactionPickerActivity, typeface)
}

override fun onTypefaceRequestFailed(reason: Int) {
Timber.e("Failed to load Emoji Compatible font, reason:$reason")
}
}

FontsContractCompat.requestFont(this, fontRequest, callback, getFontThreadHandler())
override fun onDestroy() {
emojiCompatFontProvider.removeListener(this)
super.onDestroy()
}

override fun onCreateOptionsMenu(menu: Menu): Boolean {

View File

@ -21,6 +21,7 @@ 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
@ -56,6 +57,11 @@ class ReactionButton @JvmOverloads constructor(context: Context, attrs: Attribut

private var reactionSelector: View? = null

var emojiTypeFace: Typeface? = null
set(value) {
field = value
emojiView?.typeface = value ?: Typeface.DEFAULT
}

private var dotsView: DotsView
private var circleView: CircleView
@ -97,6 +103,8 @@ class ReactionButton @JvmOverloads constructor(context: Context, attrs: Attribut

countTextView?.text = reactionCount.toString()

emojiView?.typeface = this.emojiTypeFace ?: Typeface.DEFAULT

val array = context.obtainStyledAttributes(attrs, R.styleable.ReactionButton, defStyleAttr, 0)

onDrawable = ContextCompat.getDrawable(context, R.drawable.rounded_rect_shape)
@ -239,7 +247,7 @@ class ReactionButton @JvmOverloads constructor(context: Context, attrs: Attribut
return true

when (event.action) {
MotionEvent.ACTION_DOWN ->
MotionEvent.ACTION_DOWN ->
/*
Commented out this line and moved the animation effect to the action up event due to
conflicts that were occurring when library is used in sliding type views.
@ -248,7 +256,7 @@ class ReactionButton @JvmOverloads constructor(context: Context, attrs: Attribut
*/
isPressed = true

MotionEvent.ACTION_MOVE -> {
MotionEvent.ACTION_MOVE -> {
val x = event.x
val y = event.y
val isInside = x > 0 && x < width && y > 0 && y < height
@ -257,7 +265,7 @@ class ReactionButton @JvmOverloads constructor(context: Context, attrs: Attribut
}
}

MotionEvent.ACTION_UP -> {
MotionEvent.ACTION_UP -> {
emojiView!!.animate().scaleX(0.7f).scaleY(0.7f).setDuration(150).interpolator = DECCELERATE_INTERPOLATOR
emojiView!!.animate().scaleX(1f).scaleY(1f).interpolator = DECCELERATE_INTERPOLATOR
if (isPressed) {