From 2fc7ad13bb237f5f6ae80b3d6500117668022b95 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 15 May 2020 23:09:12 +0300 Subject: [PATCH] CustomEmojiHelper: rewrite to Kotlin (#1787) * CustomEmojiHelper: rewrite to Kotlin * CustomEmojiHelper: PR fixes --- .../keylesspalace/tusky/AccountActivity.kt | 6 +- .../tusky/AccountsInListFragment.kt | 4 +- .../com/keylesspalace/tusky/MainActivity.kt | 2 +- .../tusky/adapter/AccountFieldAdapter.kt | 8 +- .../tusky/adapter/AccountSelectionAdapter.kt | 5 +- .../tusky/adapter/AccountViewHolder.java | 2 +- .../tusky/adapter/BlocksAdapter.java | 2 +- .../adapter/ComposeAutoCompleteAdapter.java | 2 +- .../tusky/adapter/FollowRequestViewHolder.kt | 6 +- .../tusky/adapter/MutesAdapter.java | 2 +- .../tusky/adapter/NotificationsAdapter.java | 14 +- .../tusky/adapter/PollAdapter.kt | 12 +- .../tusky/adapter/StatusBaseViewHolder.java | 6 +- .../report/adapter/StatusViewHolder.kt | 4 +- .../tusky/util/CustomEmojiHelper.java | 146 ------------------ .../tusky/util/CustomEmojiHelper.kt | 112 ++++++++++++++ .../keylesspalace/tusky/util/LinkHelper.java | 6 +- .../tusky/util/StatusViewHelper.kt | 2 +- 18 files changed, 151 insertions(+), 190 deletions(-) delete mode 100644 app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.java create mode 100644 app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.kt diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt index 97d87b358..9c72da3f8 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt @@ -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) diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt b/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt index bd5a12c30..2933d6893 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt @@ -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) diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt b/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt index 17ad57be8..d0d2bf5bc 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt @@ -591,7 +591,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje private fun updateProfiles() { val profiles: MutableList = 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 diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldAdapter.kt index 710b0d9b8..e80129c62 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldAdapter.kt @@ -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() { @@ -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) { diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountSelectionAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountSelectionAdapter.kt index f4892f9ba..dae0db4b6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountSelectionAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountSelectionAdapter.kt @@ -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(context, R.layout.item_autocomplete_account) { @@ -43,7 +42,7 @@ class AccountSelectionAdapter(context: Context) : ArrayAdapter(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) diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountViewHolder.java index edd93082b..7b07d5bd9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountViewHolder.java @@ -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); diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.java index 70a44ba8c..3cc686ffc 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.java @@ -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()); diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/ComposeAutoCompleteAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/ComposeAutoCompleteAdapter.java index f8018ba7e..ebc292ab4 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/ComposeAutoCompleteAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/ComposeAutoCompleteAdapter.java @@ -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); 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 fb303354e..41a96a6f9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt @@ -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) diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java index a7d145454..db3a0ae55 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java @@ -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()); diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java index 10bf1a070..2e168a7f6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java @@ -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 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); } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt index ea75c16a9..8da82d22e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt @@ -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() { 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() { } 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() { } } 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) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java index 82d1dbabb..3c03c03fa 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java @@ -181,7 +181,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { protected abstract int getMediaPreviewHeight(Context context); protected void setDisplayName(String name, List 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); diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusViewHolder.kt index b4120ca8c..93b3a7d21 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusViewHolder.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusViewHolder.kt @@ -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, 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) diff --git a/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.java b/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.java deleted file mode 100644 index a808b0f46..000000000 --- a/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.java +++ /dev/null @@ -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 . */ - -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 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 emojis, @NonNull final View ciew) { - return emojifyText(new SpannedString(string), emojis, ciew); - } - - - public static class EmojiSpan extends ReplacementSpan { - - @Nullable - private Drawable imageDrawable; - private WeakReference 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 getTarget(){ - return new CustomTarget() { - @Override - public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition 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 - } - }; - } - } - -} diff --git a/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.kt b/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.kt new file mode 100644 index 000000000..14a0ceb57 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.kt @@ -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 . */ + +@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?, 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) : 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 { + return object : CustomTarget() { + override fun onResourceReady(resource: Bitmap, transition: Transition?) { + viewWeakReference.get()?.let { view -> + imageDrawable = BitmapDrawable(view.context.resources, resource) + view.invalidate() + } + } + + override fun onLoadCleared(placeholder: Drawable?) {} + } + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.java b/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.java index e74fde30f..64e329c80 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.java @@ -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); diff --git a/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt b/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt index 79c0c4b84..e747c3e30 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt @@ -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