diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt index 8c5a37014e..5801771aec 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt @@ -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 @@ -67,7 +67,7 @@ class FollowRequestViewHolder( val wrappedName = account.name.unicodeWrap() val emojifiedName: CharSequence = wrappedName.emojify( account.emojis, - itemView, + binding.displayNameTextView, animateEmojis ) binding.displayNameTextView.text = emojifiedName @@ -76,9 +76,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( diff --git a/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementAdapter.kt index 4e835dbcbe..1ce5389279 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementAdapter.kt @@ -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) @@ -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 } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/notifications/ReportNotificationViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/components/notifications/ReportNotificationViewHolder.kt index df897e2434..082ade3d6c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/notifications/ReportNotificationViewHolder.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/notifications/ReportNotificationViewHolder.kt @@ -41,8 +41,8 @@ class ReportNotificationViewHolder( val report = viewData.report!! val reporter = viewData.account - val reporterName = reporter.name.unicodeWrap().emojify(reporter.emojis, itemView, statusDisplayOptions.animateEmojis) - val reporteeName = report.targetAccount.name.unicodeWrap().emojify(report.targetAccount.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, binding.notificationTopText, statusDisplayOptions.animateEmojis) 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) diff --git a/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.kt b/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.kt index 2c462caa81..0ef40b312a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.kt @@ -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 @@ -51,7 +53,7 @@ fun CharSequence.emojify(emojis: List, 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) @@ -69,7 +71,19 @@ fun CharSequence.emojify(emojis: List, view: View, animate: Boolean): Cha return builder } -class EmojiSpan(val viewWeakReference: WeakReference) : 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( @@ -89,7 +103,7 @@ class EmojiSpan(val viewWeakReference: WeakReference) : ReplacementSpan() fm.bottom = metrics.bottom } - return (paint.textSize * 1.2).toInt() + return emojiSize } override fun draw( @@ -134,7 +148,7 @@ class EmojiSpan(val viewWeakReference: WeakReference) : ReplacementSpan() } fun getTarget(animate: Boolean): Target { - return object : CustomTarget() { + return object : CustomTarget(emojiSize, emojiSize) { override fun onResourceReady(resource: Drawable, transition: Transition?) { viewWeakReference.get()?.let { view -> if (animate && resource is Animatable) { diff --git a/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt b/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt index 7770d54dcd..50d3ccfa77 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt @@ -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 @@ -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("🔗") diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index c6f26d4812..7136914bf2 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -75,4 +75,6 @@ 64dp + 16sp +