Animated emoji support (#2064)

* Animated emoji support

* Try to query preference only once

* Revert to using SpannableStringBuilder
This commit is contained in:
Alibek Omarov 2021-02-06 10:14:51 +03:00 committed by GitHub
commit 9580870445
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 225 additions and 120 deletions

View file

@ -16,11 +16,9 @@
@file:JvmName("CustomEmojiHelper")
package com.keylesspalace.tusky.util
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.*
import android.text.SpannableStringBuilder
import android.text.style.ReplacementSpan
import android.view.View
@ -33,6 +31,8 @@ import com.keylesspalace.tusky.entity.Emoji
import java.lang.ref.WeakReference
import java.util.regex.Pattern
import androidx.preference.PreferenceManager
import com.keylesspalace.tusky.settings.PrefKeys
/**
* replaces emoji shortcodes in a text with EmojiSpans
@ -41,7 +41,7 @@ import java.util.regex.Pattern
* @param view a reference to the a view the emojis will be shown in (should be the TextView, but parents of the TextView are also acceptable)
* @return the text with the shortcodes replaced by EmojiSpans
*/
fun CharSequence.emojify(emojis: List<Emoji>?, view: View) : CharSequence {
fun CharSequence.emojify(emojis: List<Emoji>?, view: View, animate: Boolean) : CharSequence {
if(emojis.isNullOrEmpty())
return this
@ -56,9 +56,9 @@ fun CharSequence.emojify(emojis: List<Emoji>?, view: View) : CharSequence {
builder.setSpan(span, matcher.start(), matcher.end(), 0)
Glide.with(view)
.asBitmap()
.asDrawable()
.load(url)
.into(span.getTarget())
.into(span.getTarget(animate))
}
}
return builder
@ -97,11 +97,29 @@ class EmojiSpan(val viewWeakReference: WeakReference<View>) : ReplacementSpan()
}
}
fun getTarget(): Target<Bitmap> {
return object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
fun getTarget(animate : Boolean): Target<Drawable> {
return object : CustomTarget<Drawable>() {
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
viewWeakReference.get()?.let { view ->
imageDrawable = BitmapDrawable(view.context.resources, resource)
if(animate && resource is Animatable) {
val callback = resource.callback
resource.callback = object: Drawable.Callback {
override fun unscheduleDrawable(p0: Drawable, p1: Runnable) {
callback?.unscheduleDrawable(p0, p1)
}
override fun scheduleDrawable(p0: Drawable, p1: Runnable, p2: Long) {
callback?.scheduleDrawable(p0, p1, p2)
}
override fun invalidateDrawable(p0: Drawable) {
callback?.invalidateDrawable(p0)
view.invalidate()
}
}
resource.start()
}
imageDrawable = resource
view.invalidate()
}
}

View file

@ -16,5 +16,7 @@ data class StatusDisplayOptions(
@get:JvmName("confirmReblogs")
val confirmReblogs: Boolean,
@get:JvmName("hideStats")
val hideStats: Boolean
val hideStats: Boolean,
@get:JvmName("animateEmojis")
val animateEmojis: Boolean
)

View file

@ -243,7 +243,7 @@ class StatusViewHelper(private val itemView: View) {
}
}
fun setupPollReadonly(poll: PollViewData?, emojis: List<Emoji>, useAbsoluteTime: Boolean) {
fun setupPollReadonly(poll: PollViewData?, emojis: List<Emoji>, statusDisplayOptions: StatusDisplayOptions) {
val pollResults = listOf<TextView>(
itemView.findViewById(R.id.status_poll_option_result_0),
itemView.findViewById(R.id.status_poll_option_result_1),
@ -261,10 +261,10 @@ class StatusViewHelper(private val itemView: View) {
val timestamp = System.currentTimeMillis()
setupPollResult(poll, emojis, pollResults)
setupPollResult(poll, emojis, pollResults, statusDisplayOptions.animateEmojis)
pollDescription.visibility = View.VISIBLE
pollDescription.text = getPollInfoText(timestamp, poll, pollDescription, useAbsoluteTime)
pollDescription.text = getPollInfoText(timestamp, poll, pollDescription, statusDisplayOptions.useAbsoluteTime)
}
}
@ -292,7 +292,7 @@ class StatusViewHelper(private val itemView: View) {
}
private fun setupPollResult(poll: PollViewData, emojis: List<Emoji>, pollResults: List<TextView>) {
private fun setupPollResult(poll: PollViewData, emojis: List<Emoji>, pollResults: List<TextView>, animateEmojis: Boolean) {
val options = poll.options
for (i in 0 until Status.MAX_POLL_OPTIONS) {
@ -300,7 +300,7 @@ class StatusViewHelper(private val itemView: View) {
val percent = calculatePercent(options[i].votesCount, poll.votersCount, poll.votesCount)
val pollOptionText = buildDescription(options[i].title, percent, pollResults[i].context)
pollResults[i].text = pollOptionText.emojify(emojis, pollResults[i])
pollResults[i].text = pollOptionText.emojify(emojis, pollResults[i], animateEmojis)
pollResults[i].visibility = View.VISIBLE
val level = percent * 100