Skip to content

Commit

Permalink
don't load custom emojis in their full size (#4429)
Browse files Browse the repository at this point in the history
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)
           ...
```
  • Loading branch information
connyduck committed May 10, 2024
1 parent ba575db commit f483cf7
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
package com.keylesspalace.tusky.adapter

import android.graphics.Typeface
import android.text.SpannableStringBuilder
import android.text.SpannableString
import android.text.Spanned
import android.text.style.StyleSpan
import androidx.recyclerview.widget.RecyclerView
Expand Down Expand Up @@ -49,7 +49,7 @@ class FollowRequestViewHolder(
val wrappedName = account.name.unicodeWrap()
val emojifiedName: CharSequence = wrappedName.emojify(
account.emojis,
itemView,
binding.displayNameTextView,
animateEmojis
)
binding.displayNameTextView.text = emojifiedName
Expand All @@ -58,9 +58,9 @@ class FollowRequestViewHolder(
R.string.notification_follow_request_format,
wrappedName
)
binding.notificationTextView.text = SpannableStringBuilder(wholeMessage).apply {
binding.notificationTextView.text = SpannableString(wholeMessage).apply {
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)
val formattedUsername = itemView.context.getString(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,9 @@ class ReportNotificationViewHolder(
animateAvatar: Boolean,
animateEmojis: Boolean
) {
val reporterName = reporter.name.unicodeWrap().emojify(
reporter.emojis,
itemView,
animateEmojis
)
val reporteeName = report.targetAccount.name.unicodeWrap().emojify(
report.targetAccount.emojis,
itemView,
animateEmojis
)
val reporterName = reporter.name.unicodeWrap().emojify(reporter.emojis, binding.notificationTopText, animateEmojis)
val reporteeName = report.targetAccount.name.unicodeWrap().emojify(report.targetAccount.emojis, binding.notificationTopText, animateEmojis)

val icon = ContextCompat.getDrawable(itemView.context, R.drawable.ic_flag_24dp)

binding.notificationTopText.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import com.keylesspalace.tusky.util.emojify
import com.keylesspalace.tusky.util.parseAsMastodonHtml
import com.keylesspalace.tusky.util.setClickableText
import com.keylesspalace.tusky.util.visible
import java.lang.ref.WeakReference

interface AnnouncementActionListener : LinkListener {
fun openReactionPicker(announcementId: String, target: View)
Expand Down Expand Up @@ -111,7 +110,7 @@ class AnnouncementAdapter(
// we set the EmojiSpan on a space, because otherwise the Chip won't have the right size
// https://github.com/tuskyapp/Tusky/issues/2308
val spanBuilder = SpannableStringBuilder(" ${reaction.count}")
val span = EmojiSpan(WeakReference(this))
val span = EmojiSpan(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
span.contentDescription = reaction.name
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ import android.graphics.drawable.Drawable
import android.text.SpannableStringBuilder
import android.text.style.ReplacementSpan
import android.view.View
import android.widget.TextView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.transition.Transition
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Emoji
import java.lang.ref.WeakReference
import java.util.regex.Pattern
Expand All @@ -51,7 +53,7 @@ fun CharSequence.emojify(emojis: List<Emoji>, view: View, animate: Boolean): Cha
.matcher(this)

while (matcher.find()) {
val span = EmojiSpan(WeakReference(view))
val span = EmojiSpan(view)

builder.setSpan(span, matcher.start(), matcher.end(), 0)
Glide.with(view)
Expand All @@ -69,7 +71,19 @@ fun CharSequence.emojify(emojis: List<Emoji>, view: View, animate: Boolean): Cha
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

override fun getSize(
Expand All @@ -89,7 +103,7 @@ class EmojiSpan(val viewWeakReference: WeakReference<View>) : ReplacementSpan()
fm.bottom = metrics.bottom
}

return (paint.textSize * 1.2).toInt()
return emojiSize
}

override fun draw(
Expand Down Expand Up @@ -134,7 +148,7 @@ class EmojiSpan(val viewWeakReference: WeakReference<View>) : ReplacementSpan()
}

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>?) {
viewWeakReference.get()?.let { view ->
if (animate && resource is Animatable) {
Expand Down
3 changes: 1 addition & 2 deletions app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.HashTag
import com.keylesspalace.tusky.entity.Status.Mention
import com.keylesspalace.tusky.interfaces.LinkListener
import java.lang.ref.WeakReference
import java.net.URI
import java.net.URISyntaxException

Expand Down Expand Up @@ -128,7 +127,7 @@ fun markupHiddenUrls(view: TextView, content: CharSequence): SpannableStringBuil

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
val linkDrawableSpan = EmojiSpan(WeakReference(view))
val linkDrawableSpan = EmojiSpan(view)
linkDrawableSpan.imageDrawable = linkDrawable

val placeholderIndex = replacementText.indexOf("🔗")
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/dimens.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,6 @@

<dimen name="account_swiperefresh_distance">64dp</dimen>

<dimen name="fallback_emoji_size">16sp</dimen>

</resources>

0 comments on commit f483cf7

Please sign in to comment.