diff --git a/vector/src/main/java/im/vector/riotredesign/EmojiCompatFontProvider.kt b/vector/src/main/java/im/vector/riotredesign/EmojiCompatFontProvider.kt new file mode 100644 index 00000000..a928d48b --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/EmojiCompatFontProvider.kt @@ -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() + + 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?) + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/VectorApplication.kt b/vector/src/main/java/im/vector/riotredesign/VectorApplication.kt index 88cee674..e1d85fe3 100644 --- a/vector/src/main/java/im/vector/riotredesign/VectorApplication.kt +++ b/vector/src/main/java/im/vector/riotredesign/VectorApplication.kt @@ -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() + FontsContractCompat.requestFont(this, fontRequest, koin.koinContext.get(), 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!! + } + } \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/core/di/AppModule.kt b/vector/src/main/java/im/vector/riotredesign/core/di/AppModule.kt index dc3068cf..542c0953 100644 --- a/vector/src/main/java/im/vector/riotredesign/core/di/AppModule.kt +++ b/vector/src/main/java/im/vector/riotredesign/core/di/AppModule.kt @@ -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() + } + } } \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt index d6297b6f..ce985abb 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt @@ -72,7 +72,8 @@ class HomeModule { val timelineMediaSizeProvider = TimelineMediaSizeProvider() val colorProvider = ColorProvider(fragment.requireContext()) val timelineDateFormatter = get() - val messageItemFactory = MessageItemFactory(colorProvider, timelineMediaSizeProvider, timelineDateFormatter, eventHtmlRenderer, get()) + val messageItemFactory = MessageItemFactory(colorProvider, timelineMediaSizeProvider, + timelineDateFormatter, eventHtmlRenderer, get(), get()) val timelineItemFactory = TimelineItemFactory( messageItemFactory = messageItemFactory, diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionFragment.kt index eac10a90..2d601544 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionFragment.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/action/QuickReactionFragment.kt @@ -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() + 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 diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/MessageItemFactory.kt index d61848af..7571fead 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -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() 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) diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/AbsMessageItem.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/AbsMessageItem.kt index 0ce01a88..b74f7bca 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/AbsMessageItem.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/item/AbsMessageItem.kt @@ -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 : BaseEventItem() { @EpoxyAttribute var memberClickListener: View.OnClickListener? = null + @EpoxyAttribute + var emojiTypeFace: Typeface? = null + @EpoxyAttribute var reactionPillCallback: TimelineEventController.ReactionPillCallback? = null @@ -116,6 +120,7 @@ abstract class AbsMessageItem : BaseEventItem() { idToRefInFlow.add(reactionButton.id) reactionButton.reactionString = reaction.key reactionButton.reactionCount = reaction.count + reactionButton.emojiTypeFace = emojiTypeFace reactionButton.setChecked(reaction.addedByMe) reactionButton.isEnabled = reaction.synced } diff --git a/vector/src/main/java/im/vector/riotredesign/features/reactions/EmojiReactionPickerActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/reactions/EmojiReactionPickerActivity.kt index a75accd9..7ad57d53 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/reactions/EmojiReactionPickerActivity.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/reactions/EmojiReactionPickerActivity.kt @@ -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() + private var tabLayoutSelectionListener = object : TabLayout.BaseOnTabSelectedListener { 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 { diff --git a/vector/src/main/java/im/vector/riotredesign/features/reactions/widget/ReactionButton.kt b/vector/src/main/java/im/vector/riotredesign/features/reactions/widget/ReactionButton.kt index c4a87d0f..b0bf08e3 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/reactions/widget/ReactionButton.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/reactions/widget/ReactionButton.kt @@ -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) {