1
0
mirror of https://github.com/tuskyapp/Tusky synced 2025-02-04 09:27:33 +01:00

don't load custom emojis in their full size (#4429)

This should save quite some memory, but most importantly it gets rid of
this crash:

```
java.lang.RuntimeException: Canvas: trying to draw too large(121969936bytes) bitmap.
           at android.graphics.RecordingCanvas.throwIfCannotDraw(RecordingCanvas.java:266)
           at android.graphics.BaseRecordingCanvas.drawBitmap(BaseRecordingCanvas.java:94)
           at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:549)
           at com.keylesspalace.tusky.util.EmojiSpan.draw(CustomEmojiHelper.kt:131)
           ...
```
This commit is contained in:
Konrad Pozniak 2024-05-10 12:22:07 +02:00 committed by GitHub
parent 3736034952
commit 4dec228926
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 28 additions and 14 deletions

View File

@ -16,7 +16,7 @@
package com.keylesspalace.tusky.adapter package com.keylesspalace.tusky.adapter
import android.graphics.Typeface import android.graphics.Typeface
import android.text.SpannableStringBuilder import android.text.SpannableString
import android.text.Spanned import android.text.Spanned
import android.text.style.StyleSpan import android.text.style.StyleSpan
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -67,7 +67,7 @@ class FollowRequestViewHolder(
val wrappedName = account.name.unicodeWrap() val wrappedName = account.name.unicodeWrap()
val emojifiedName: CharSequence = wrappedName.emojify( val emojifiedName: CharSequence = wrappedName.emojify(
account.emojis, account.emojis,
itemView, binding.displayNameTextView,
animateEmojis animateEmojis
) )
binding.displayNameTextView.text = emojifiedName binding.displayNameTextView.text = emojifiedName
@ -76,9 +76,9 @@ class FollowRequestViewHolder(
R.string.notification_follow_request_format, R.string.notification_follow_request_format,
wrappedName wrappedName
) )
binding.notificationTextView.text = SpannableStringBuilder(wholeMessage).apply { binding.notificationTextView.text = SpannableString(wholeMessage).apply {
setSpan(StyleSpan(Typeface.BOLD), 0, wrappedName.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) setSpan(StyleSpan(Typeface.BOLD), 0, wrappedName.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}.emojify(account.emojis, itemView, animateEmojis) }.emojify(account.emojis, binding.notificationTextView, animateEmojis)
} }
binding.notificationTextView.visible(showHeader) binding.notificationTextView.visible(showHeader)
val formattedUsername = itemView.context.getString( val formattedUsername = itemView.context.getString(

View File

@ -37,7 +37,6 @@ import com.keylesspalace.tusky.util.emojify
import com.keylesspalace.tusky.util.parseAsMastodonHtml import com.keylesspalace.tusky.util.parseAsMastodonHtml
import com.keylesspalace.tusky.util.setClickableText import com.keylesspalace.tusky.util.setClickableText
import com.keylesspalace.tusky.util.visible import com.keylesspalace.tusky.util.visible
import java.lang.ref.WeakReference
interface AnnouncementActionListener : LinkListener { interface AnnouncementActionListener : LinkListener {
fun openReactionPicker(announcementId: String, target: View) fun openReactionPicker(announcementId: String, target: View)
@ -111,7 +110,7 @@ class AnnouncementAdapter(
// we set the EmojiSpan on a space, because otherwise the Chip won't have the right size // we set the EmojiSpan on a space, because otherwise the Chip won't have the right size
// https://github.com/tuskyapp/Tusky/issues/2308 // https://github.com/tuskyapp/Tusky/issues/2308
val spanBuilder = SpannableStringBuilder(" ${reaction.count}") val spanBuilder = SpannableStringBuilder(" ${reaction.count}")
val span = EmojiSpan(WeakReference(this)) val span = EmojiSpan(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
span.contentDescription = reaction.name span.contentDescription = reaction.name
} }

View File

@ -41,8 +41,8 @@ class ReportNotificationViewHolder(
val report = viewData.report!! val report = viewData.report!!
val reporter = viewData.account val reporter = viewData.account
val reporterName = reporter.name.unicodeWrap().emojify(reporter.emojis, itemView, statusDisplayOptions.animateEmojis) val reporterName = reporter.name.unicodeWrap().emojify(reporter.emojis, binding.notificationTopText, statusDisplayOptions.animateEmojis)
val reporteeName = report.targetAccount.name.unicodeWrap().emojify(report.targetAccount.emojis, itemView, statusDisplayOptions.animateEmojis) val reporteeName = report.targetAccount.name.unicodeWrap().emojify(report.targetAccount.emojis, binding.notificationTopText, statusDisplayOptions.animateEmojis)
binding.notificationTopText.text = itemView.context.getString(R.string.notification_header_report_format, reporterName, reporteeName) binding.notificationTopText.text = itemView.context.getString(R.string.notification_header_report_format, reporterName, reporteeName)
binding.notificationSummary.text = itemView.context.getString(R.string.notification_summary_report_format, getRelativeTimeSpanString(itemView.context, report.createdAt.time, System.currentTimeMillis()), report.statusIds?.size ?: 0) binding.notificationSummary.text = itemView.context.getString(R.string.notification_summary_report_format, getRelativeTimeSpanString(itemView.context, report.createdAt.time, System.currentTimeMillis()), report.statusIds?.size ?: 0)

View File

@ -24,10 +24,12 @@ import android.graphics.drawable.Drawable
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.text.style.ReplacementSpan import android.text.style.ReplacementSpan
import android.view.View import android.view.View
import android.widget.TextView
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.target.Target import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.transition.Transition import com.bumptech.glide.request.transition.Transition
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Emoji import com.keylesspalace.tusky.entity.Emoji
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.regex.Pattern import java.util.regex.Pattern
@ -51,7 +53,7 @@ fun CharSequence.emojify(emojis: List<Emoji>, view: View, animate: Boolean): Cha
.matcher(this) .matcher(this)
while (matcher.find()) { while (matcher.find()) {
val span = EmojiSpan(WeakReference(view)) val span = EmojiSpan(view)
builder.setSpan(span, matcher.start(), matcher.end(), 0) builder.setSpan(span, matcher.start(), matcher.end(), 0)
Glide.with(view) Glide.with(view)
@ -69,7 +71,19 @@ fun CharSequence.emojify(emojis: List<Emoji>, view: View, animate: Boolean): Cha
return builder return builder
} }
class EmojiSpan(val viewWeakReference: WeakReference<View>) : ReplacementSpan() { class EmojiSpan(view: View) : ReplacementSpan() {
private val viewWeakReference = WeakReference(view)
private val emojiSize: Int = if (view is TextView) {
view.paint.textSize
} else {
// sometimes it is not possible to determine the TextView the emoji will be shown in,
// e.g. because it is passed to a library, so we fallback to a size that should be large
// enough in most cases
view.context.resources.getDimension(R.dimen.fallback_emoji_size)
}.times(1.2).toInt()
var imageDrawable: Drawable? = null var imageDrawable: Drawable? = null
override fun getSize( override fun getSize(
@ -89,7 +103,7 @@ class EmojiSpan(val viewWeakReference: WeakReference<View>) : ReplacementSpan()
fm.bottom = metrics.bottom fm.bottom = metrics.bottom
} }
return (paint.textSize * 1.2).toInt() return emojiSize
} }
override fun draw( override fun draw(
@ -134,7 +148,7 @@ class EmojiSpan(val viewWeakReference: WeakReference<View>) : ReplacementSpan()
} }
fun getTarget(animate: Boolean): Target<Drawable> { fun getTarget(animate: Boolean): Target<Drawable> {
return object : CustomTarget<Drawable>() { return object : CustomTarget<Drawable>(emojiSize, emojiSize) {
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) { override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
viewWeakReference.get()?.let { view -> viewWeakReference.get()?.let { view ->
if (animate && resource is Animatable) { if (animate && resource is Animatable) {

View File

@ -48,7 +48,6 @@ import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.HashTag import com.keylesspalace.tusky.entity.HashTag
import com.keylesspalace.tusky.entity.Status.Mention import com.keylesspalace.tusky.entity.Status.Mention
import com.keylesspalace.tusky.interfaces.LinkListener import com.keylesspalace.tusky.interfaces.LinkListener
import java.lang.ref.WeakReference
import java.net.URI import java.net.URI
import java.net.URISyntaxException import java.net.URISyntaxException
@ -128,7 +127,7 @@ fun markupHiddenUrls(view: TextView, content: CharSequence): SpannableStringBuil
val linkDrawable = AppCompatResources.getDrawable(view.context, R.drawable.ic_link)!! val linkDrawable = AppCompatResources.getDrawable(view.context, R.drawable.ic_link)!!
// ImageSpan does not always align the icon correctly in the line, let's use our custom emoji span for this // ImageSpan does not always align the icon correctly in the line, let's use our custom emoji span for this
val linkDrawableSpan = EmojiSpan(WeakReference(view)) val linkDrawableSpan = EmojiSpan(view)
linkDrawableSpan.imageDrawable = linkDrawable linkDrawableSpan.imageDrawable = linkDrawable
val placeholderIndex = replacementText.indexOf("🔗") val placeholderIndex = replacementText.indexOf("🔗")

View File

@ -75,4 +75,6 @@
<dimen name="account_swiperefresh_distance">64dp</dimen> <dimen name="account_swiperefresh_distance">64dp</dimen>
<dimen name="fallback_emoji_size">16sp</dimen>
</resources> </resources>