CustomEmojiHelper: rewrite to Kotlin (#1787)

* CustomEmojiHelper: rewrite to Kotlin

* CustomEmojiHelper: PR fixes
This commit is contained in:
Alibek Omarov 2020-05-15 23:09:12 +03:00 committed by GitHub
parent e72bdcaf42
commit 2fc7ad13bb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 151 additions and 190 deletions

View file

@ -360,9 +360,9 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
val usernameFormatted = getString(R.string.status_username_format, account.username)
accountUsernameTextView.text = usernameFormatted
accountDisplayNameTextView.text = CustomEmojiHelper.emojifyString(account.name, account.emojis, accountDisplayNameTextView)
accountDisplayNameTextView.text = account.name.emojify(account.emojis, accountDisplayNameTextView)
val emojifiedNote = CustomEmojiHelper.emojifyText(account.note, account.emojis, accountNoteTextView)
val emojifiedNote = account.note.emojify(account.emojis, accountNoteTextView)
LinkHelper.setClickableText(accountNoteTextView, emojifiedNote, null, this)
// accountFieldAdapter.fields = account.fields ?: emptyList()
@ -423,7 +423,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
private fun updateToolbar() {
loadedAccount?.let { account ->
val emojifiedName = CustomEmojiHelper.emojifyString(account.name, account.emojis, accountToolbar)
val emojifiedName = account.name.emojify(account.emojis, accountToolbar)
try {
supportActionBar?.title = EmojiCompat.get().process(emojifiedName)

View file

@ -209,7 +209,7 @@ class AccountsInListFragment : DialogFragment(), Injectable {
}
fun bind(account: Account) {
displayNameTextView.text = CustomEmojiHelper.emojifyString(account.name, account.emojis, displayNameTextView)
displayNameTextView.text = account.name.emojify(account.emojis, displayNameTextView)
usernameTextView.text = account.username
loadAvatar(account.avatar, avatar, radius, animateAvatar)
}
@ -252,7 +252,7 @@ class AccountsInListFragment : DialogFragment(), Injectable {
override val containerView = itemView
fun bind(account: Account, inAList: Boolean) {
displayNameTextView.text = CustomEmojiHelper.emojifyString(account.name, account.emojis, displayNameTextView)
displayNameTextView.text = account.name.emojify(account.emojis, displayNameTextView)
usernameTextView.text = account.username
loadAvatar(account.avatar, avatar, radius, animateAvatar)

View file

@ -591,7 +591,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
private fun updateProfiles() {
val profiles: MutableList<IProfile> = accountManager.getAllAccountsOrderedByActive().map { acc ->
val emojifiedName = EmojiCompat.get().process(CustomEmojiHelper.emojifyString(acc.displayName, acc.emojis, header))
val emojifiedName = EmojiCompat.get().process(acc.displayName.emojify(acc.emojis, header))
ProfileDrawerItem().apply {
isSelected = acc.isActive

View file

@ -26,9 +26,7 @@ import com.keylesspalace.tusky.entity.Emoji
import com.keylesspalace.tusky.entity.Field
import com.keylesspalace.tusky.entity.IdentityProof
import com.keylesspalace.tusky.interfaces.LinkListener
import com.keylesspalace.tusky.util.CustomEmojiHelper
import com.keylesspalace.tusky.util.Either
import com.keylesspalace.tusky.util.LinkHelper
import com.keylesspalace.tusky.util.*
import kotlinx.android.synthetic.main.item_account_field.view.*
class AccountFieldAdapter(private val linkListener: LinkListener) : RecyclerView.Adapter<AccountFieldAdapter.ViewHolder>() {
@ -57,10 +55,10 @@ class AccountFieldAdapter(private val linkListener: LinkListener) : RecyclerView
viewHolder.valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_check_circle, 0)
} else {
val field = proofOrField.asRight()
val emojifiedName = CustomEmojiHelper.emojifyString(field.name, emojis, viewHolder.nameTextView)
val emojifiedName = field.name.emojify(emojis, viewHolder.nameTextView)
viewHolder.nameTextView.text = emojifiedName
val emojifiedValue = CustomEmojiHelper.emojifyText(field.value, emojis, viewHolder.valueTextView)
val emojifiedValue = field.value.emojify(emojis, viewHolder.valueTextView)
LinkHelper.setClickableText(viewHolder.valueTextView, emojifiedValue, null, linkListener)
if(field.verifiedAt != null) {

View file

@ -23,8 +23,7 @@ import android.widget.ArrayAdapter
import androidx.preference.PreferenceManager
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.db.AccountEntity
import com.keylesspalace.tusky.util.CustomEmojiHelper
import com.keylesspalace.tusky.util.loadAvatar
import com.keylesspalace.tusky.util.*
import kotlinx.android.synthetic.main.item_autocomplete_account.view.*
class AccountSelectionAdapter(context: Context) : ArrayAdapter<AccountEntity>(context, R.layout.item_autocomplete_account) {
@ -43,7 +42,7 @@ class AccountSelectionAdapter(context: Context) : ArrayAdapter<AccountEntity>(co
val displayName = view.display_name
val avatar = view.avatar
username.text = account.fullName
displayName.text = CustomEmojiHelper.emojifyString(account.displayName, account.emojis, displayName)
displayName.text = account.displayName.emojify(account.emojis, displayName)
val avatarRadius = avatar.context.resources.getDimensionPixelSize(R.dimen.avatar_radius_42dp)
val animateAvatar = PreferenceManager.getDefaultSharedPreferences(avatar.context)

View file

@ -40,7 +40,7 @@ public class AccountViewHolder extends RecyclerView.ViewHolder {
String format = username.getContext().getString(R.string.status_username_format);
String formattedUsername = String.format(format, account.getUsername());
username.setText(formattedUsername);
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(account.getName(), account.getEmojis(), displayName);
CharSequence emojifiedName = CustomEmojiHelper.emojify(account.getName(), account.getEmojis(), displayName);
displayName.setText(emojifiedName);
int avatarRadius = avatar.getContext().getResources()
.getDimensionPixelSize(R.dimen.avatar_radius_48dp);

View file

@ -86,7 +86,7 @@ public class BlocksAdapter extends AccountAdapter {
void setupWithAccount(Account account) {
id = account.getId();
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(account.getName(), account.getEmojis(), displayName);
CharSequence emojifiedName = CustomEmojiHelper.emojify(account.getName(), account.getEmojis(), displayName);
displayName.setText(emojifiedName);
String format = username.getContext().getString(R.string.status_username_format);
String formattedUsername = String.format(format, account.getUsername());

View file

@ -146,7 +146,7 @@ public class ComposeAutoCompleteAdapter extends BaseAdapter
account.getUsername()
);
accountViewHolder.username.setText(formattedUsername);
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(account.getName(),
CharSequence emojifiedName = CustomEmojiHelper.emojify(account.getName(),
account.getEmojis(), accountViewHolder.displayName);
accountViewHolder.displayName.setText(emojifiedName);

View file

@ -7,9 +7,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Account
import com.keylesspalace.tusky.interfaces.AccountActionListener
import com.keylesspalace.tusky.util.CustomEmojiHelper
import com.keylesspalace.tusky.util.loadAvatar
import com.keylesspalace.tusky.util.visible
import com.keylesspalace.tusky.util.*
import kotlinx.android.synthetic.main.item_follow_request_notification.view.*
internal class FollowRequestViewHolder(itemView: View, private val showHeader: Boolean) : RecyclerView.ViewHolder(itemView) {
@ -20,7 +18,7 @@ internal class FollowRequestViewHolder(itemView: View, private val showHeader: B
fun setupWithAccount(account: Account, formatter: BidiFormatter?) {
id = account.id
val wrappedName = formatter?.unicodeWrap(account.name) ?: account.name
val emojifiedName: CharSequence = CustomEmojiHelper.emojifyString(wrappedName, account.emojis, itemView)
val emojifiedName: CharSequence = wrappedName.emojify(account.emojis, itemView)
itemView.displayNameTextView.text = emojifiedName
if (showHeader) {
itemView.notificationTextView?.text = itemView.context.getString(R.string.notification_follow_request_format, emojifiedName)

View file

@ -71,7 +71,7 @@ public class MutesAdapter extends AccountAdapter {
void setupWithAccount(Account account) {
id = account.getId();
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(account.getName(), account.getEmojis(), displayName);
CharSequence emojifiedName = CustomEmojiHelper.emojify(account.getName(), account.getEmojis(), displayName);
displayName.setText(emojifiedName);
String format = username.getContext().getString(R.string.status_username_format);
String formattedUsername = String.format(format, account.getUsername());

View file

@ -331,13 +331,13 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
String format = context.getString(R.string.notification_follow_format);
String wrappedDisplayName = bidiFormatter.unicodeWrap(account.getName());
String wholeMessage = String.format(format, wrappedDisplayName);
CharSequence emojifiedMessage = CustomEmojiHelper.emojifyString(wholeMessage, account.getEmojis(), message);
CharSequence emojifiedMessage = CustomEmojiHelper.emojify(wholeMessage, account.getEmojis(), message);
message.setText(emojifiedMessage);
String username = context.getString(R.string.status_username_format, account.getUsername());
usernameView.setText(username);
CharSequence emojifiedDisplayName = CustomEmojiHelper.emojifyString(wrappedDisplayName, account.getEmojis(), usernameView);
CharSequence emojifiedDisplayName = CustomEmojiHelper.emojify(wrappedDisplayName, account.getEmojis(), usernameView);
displayNameView.setText(emojifiedDisplayName);
@ -412,7 +412,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
}
private void setDisplayName(String name, List<Emoji> emojis) {
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(name, emojis, displayName);
CharSequence emojifiedName = CustomEmojiHelper.emojify(name, emojis, displayName);
displayName.setText(emojifiedName);
}
@ -496,7 +496,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
final SpannableStringBuilder str = new SpannableStringBuilder(wholeMessage);
str.setSpan(new StyleSpan(Typeface.BOLD), 0, displayName.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
CharSequence emojifiedText = CustomEmojiHelper.emojifyText(str, notificationViewData.getAccount().getEmojis(), message);
CharSequence emojifiedText = CustomEmojiHelper.emojify(str, notificationViewData.getAccount().getEmojis(), message);
message.setText(emojifiedText);
if (statusViewData != null) {
@ -592,11 +592,11 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
statusContent.setFilters(NO_INPUT_FILTER);
}
Spanned emojifiedText = CustomEmojiHelper.emojifyText(content, emojis, statusContent);
CharSequence emojifiedText = CustomEmojiHelper.emojify(content, emojis, statusContent);
LinkHelper.setClickableText(statusContent, emojifiedText, statusViewData.getMentions(), listener);
Spanned emojifiedContentWarning =
CustomEmojiHelper.emojifyString(statusViewData.getSpoilerText(), statusViewData.getStatusEmojis(), contentWarningDescriptionTextView);
CharSequence emojifiedContentWarning =
CustomEmojiHelper.emojify(statusViewData.getSpoilerText(), statusViewData.getStatusEmojis(), contentWarningDescriptionTextView);
contentWarningDescriptionTextView.setText(emojifiedContentWarning);
}

View file

@ -25,8 +25,7 @@ import androidx.emoji.text.EmojiCompat
import androidx.recyclerview.widget.RecyclerView
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Emoji
import com.keylesspalace.tusky.util.CustomEmojiHelper
import com.keylesspalace.tusky.util.visible
import com.keylesspalace.tusky.util.*
import com.keylesspalace.tusky.viewdata.PollOptionViewData
import com.keylesspalace.tusky.viewdata.buildDescription
import com.keylesspalace.tusky.viewdata.calculatePercent
@ -78,7 +77,8 @@ class PollAdapter: RecyclerView.Adapter<PollViewHolder>() {
when(mode) {
RESULT -> {
val percent = calculatePercent(option.votesCount, votersCount, voteCount)
val emojifiedPollOptionText = CustomEmojiHelper.emojifyText(buildDescription(option.title, percent, holder.resultTextView.context), emojis, holder.resultTextView)
val emojifiedPollOptionText = buildDescription(option.title, percent, holder.resultTextView.context)
.emojify(emojis, holder.resultTextView)
holder.resultTextView.text = EmojiCompat.get().process(emojifiedPollOptionText)
val level = percent * 100
@ -87,7 +87,7 @@ class PollAdapter: RecyclerView.Adapter<PollViewHolder>() {
}
SINGLE -> {
val emojifiedPollOptionText = CustomEmojiHelper.emojifyString(option.title, emojis, holder.radioButton)
val emojifiedPollOptionText = option.title.emojify(emojis, holder.radioButton)
holder.radioButton.text = EmojiCompat.get().process(emojifiedPollOptionText)
holder.radioButton.isChecked = option.selected
holder.radioButton.setOnClickListener {
@ -98,7 +98,7 @@ class PollAdapter: RecyclerView.Adapter<PollViewHolder>() {
}
}
MULTIPLE -> {
val emojifiedPollOptionText = CustomEmojiHelper.emojifyString(option.title, emojis, holder.checkBox)
val emojifiedPollOptionText = option.title.emojify(emojis, holder.checkBox)
holder.checkBox.text = EmojiCompat.get().process(emojifiedPollOptionText)
holder.checkBox.isChecked = option.selected
holder.checkBox.setOnCheckedChangeListener { _, isChecked ->
@ -124,4 +124,4 @@ class PollViewHolder(view: View): RecyclerView.ViewHolder(view) {
val radioButton: RadioButton = view.findViewById(R.id.status_poll_radio_button)
val checkBox: CheckBox = view.findViewById(R.id.status_poll_checkbox)
}
}

View file

@ -181,7 +181,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
protected abstract int getMediaPreviewHeight(Context context);
protected void setDisplayName(String name, List<Emoji> customEmojis) {
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(name, customEmojis, displayName);
CharSequence emojifiedName = CustomEmojiHelper.emojify(name, customEmojis, displayName);
displayName.setText(emojifiedName);
}
@ -205,7 +205,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
final StatusActionListener listener) {
boolean sensitive = !TextUtils.isEmpty(spoilerText);
if (sensitive) {
CharSequence emojiSpoiler = CustomEmojiHelper.emojifyString(spoilerText, emojis, contentWarningDescription);
CharSequence emojiSpoiler = CustomEmojiHelper.emojify(spoilerText, emojis, contentWarningDescription);
contentWarningDescription.setText(emojiSpoiler);
contentWarningDescription.setVisibility(View.VISIBLE);
contentWarningButton.setVisibility(View.VISIBLE);
@ -244,7 +244,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
StatusDisplayOptions statusDisplayOptions,
final StatusActionListener listener) {
if (expanded) {
Spanned emojifiedText = CustomEmojiHelper.emojifyText(content, emojis, this.content);
CharSequence emojifiedText = CustomEmojiHelper.emojify(content, emojis, this.content);
LinkHelper.setClickableText(this.content, emojifiedText, mentions, listener);
for (int i = 0; i < mediaLabels.length; ++i) {
updateMediaLabel(i, sensitive, expanded);

View file

@ -89,7 +89,7 @@ class StatusViewHolder(
itemView.statusContentWarningButton.hide()
itemView.statusContentWarningDescription.hide()
} else {
val emojiSpoiler = CustomEmojiHelper.emojifyString(status.spoilerText, status.emojis, itemView.statusContentWarningDescription)
val emojiSpoiler = status.spoilerText.emojify(status.emojis, itemView.statusContentWarningDescription)
itemView.statusContentWarningDescription.text = emojiSpoiler
itemView.statusContentWarningDescription.show()
itemView.statusContentWarningButton.show()
@ -122,7 +122,7 @@ class StatusViewHolder(
emojis: List<Emoji>,
listener: LinkListener) {
if (expanded) {
val emojifiedText = CustomEmojiHelper.emojifyText(content, emojis, itemView.statusContent)
val emojifiedText = content.emojify(emojis, itemView.statusContent)
LinkHelper.setClickableText(itemView.statusContent, emojifiedText, mentions, listener)
} else {
LinkHelper.setClickableMentions(itemView.statusContent, mentions, listener)

View file

@ -1,146 +0,0 @@
/* Copyright 2017 Andrew Dawson
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
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.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.SpannedString;
import android.text.style.ReplacementSpan;
import android.view.View;
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.entity.Emoji;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class CustomEmojiHelper {
/**
* replaces emoji shortcodes in a text with EmojiSpans
* @param text the text containing custom emojis
* @param emojis a list of the custom emojis (nullable for backward compatibility with old mastodon instances)
* @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
*/
@NonNull
public static Spanned emojifyText(@NonNull Spanned text, @Nullable List<Emoji> emojis, @NonNull final View view) {
if (emojis != null && !emojis.isEmpty()) {
SpannableStringBuilder builder = new SpannableStringBuilder(text);
for (Emoji emoji : emojis) {
CharSequence pattern = new StringBuilder(":").append(emoji.getShortcode()).append(':');
Matcher matcher = Pattern.compile(pattern.toString()).matcher(text);
while (matcher.find()) {
EmojiSpan span = new EmojiSpan(view);
builder.setSpan(span, matcher.start(), matcher.end(), 0);
Glide.with(view)
.asBitmap()
.load(emoji.getUrl())
.into(span.getTarget());
}
}
return builder;
}
return text;
}
@NonNull
public static Spanned emojifyString(@NonNull String string, @Nullable List<Emoji> emojis, @NonNull final View ciew) {
return emojifyText(new SpannedString(string), emojis, ciew);
}
public static class EmojiSpan extends ReplacementSpan {
@Nullable
private Drawable imageDrawable;
private WeakReference<View> viewWeakReference;
EmojiSpan(View view) {
this.viewWeakReference = new WeakReference<>(view);
}
@Override
public int getSize(@NonNull Paint paint, CharSequence text, int start, int end,
@Nullable Paint.FontMetricsInt fm) {
/* update FontMetricsInt or otherwise span does not get drawn when
it covers the whole text */
Paint.FontMetricsInt metrics = paint.getFontMetricsInt();
if (fm != null) {
fm.top = metrics.top;
fm.ascent = metrics.ascent;
fm.descent = metrics.descent;
fm.bottom = metrics.bottom;
}
return (int) (paint.getTextSize()*1.2);
}
@Override
public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x,
int top, int y, int bottom, @NonNull Paint paint) {
if (imageDrawable == null) return;
canvas.save();
int emojiSize = (int) (paint.getTextSize() * 1.1);
imageDrawable.setBounds(0, 0, emojiSize, emojiSize);
int transY = bottom - imageDrawable.getBounds().bottom;
transY -= paint.getFontMetricsInt().descent/2;
canvas.translate(x, transY);
imageDrawable.draw(canvas);
canvas.restore();
}
Target<Bitmap> getTarget(){
return new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
View view = viewWeakReference.get();
if (view != null) {
imageDrawable = new BitmapDrawable(view.getContext().getResources(), resource);
view.invalidate();
}
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
//Do nothing on load cleared
}
};
}
}
}

View file

@ -0,0 +1,112 @@
/* Copyright 2020 Tusky Contributors
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
@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.text.SpannableStringBuilder
import android.text.style.ReplacementSpan
import android.view.View
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.entity.Emoji
import java.lang.ref.WeakReference
import java.util.regex.Pattern
/**
* replaces emoji shortcodes in a text with EmojiSpans
* @param text the text containing custom emojis
* @param emojis a list of the custom emojis (nullable for backward compatibility with old mastodon instances)
* @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 {
if(emojis.isNullOrEmpty())
return this
val builder = SpannableStringBuilder.valueOf(this)
emojis.forEach { (shortcode, url) ->
val matcher = Pattern.compile(":$shortcode:", Pattern.LITERAL)
.matcher(this)
while(matcher.find()) {
val span = EmojiSpan(WeakReference(view))
builder.setSpan(span, matcher.start(), matcher.end(), 0);
Glide.with(view)
.asBitmap()
.load(url)
.into(span.getTarget())
}
}
return builder
}
class EmojiSpan(val viewWeakReference: WeakReference<View>) : ReplacementSpan() {
var imageDrawable: Drawable? = null
override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?) : Int {
if (fm != null) {
/* update FontMetricsInt or otherwise span does not get drawn when
* it covers the whole text */
val metrics = paint.fontMetricsInt
fm.top = metrics.top
fm.ascent = metrics.ascent
fm.descent = metrics.descent
fm.bottom = metrics.bottom
}
return (paint.textSize * 1.2).toInt()
}
override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
imageDrawable?.let { drawable ->
canvas.save()
val emojiSize = (paint.textSize * 1.1).toInt()
drawable.setBounds(0, 0, emojiSize, emojiSize)
var transY = bottom - drawable.bounds.bottom
transY -= paint.fontMetricsInt.descent / 2;
canvas.translate(x, transY.toFloat())
drawable.draw(canvas)
canvas.restore()
}
}
fun getTarget(): Target<Bitmap> {
return object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
viewWeakReference.get()?.let { view ->
imageDrawable = BitmapDrawable(view.context.resources, resource)
view.invalidate()
}
}
override fun onLoadCleared(placeholder: Drawable?) {}
}
}
}

View file

@ -68,10 +68,10 @@ public class LinkHelper {
* @param mentions any '@' mentions which are known to be in the content
* @param listener to notify about particular spans that are clicked
*/
public static void setClickableText(TextView view, Spanned content,
public static void setClickableText(TextView view, CharSequence content,
@Nullable Status.Mention[] mentions, final LinkListener listener) {
SpannableStringBuilder builder = new SpannableStringBuilder(content);
URLSpan[] urlSpans = content.getSpans(0, content.length(), URLSpan.class);
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(content);
URLSpan[] urlSpans = builder.getSpans(0, content.length(), URLSpan.class);
for (URLSpan span : urlSpans) {
int start = builder.getSpanStart(span);
int end = builder.getSpanEnd(span);

View file

@ -302,7 +302,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 = CustomEmojiHelper.emojifyText(pollOptionText, emojis, pollResults[i])
pollResults[i].text = pollOptionText.emojify(emojis, pollResults[i])
pollResults[i].visibility = View.VISIBLE
val level = percent * 100