CustomEmojiHelper: rewrite to Kotlin

main
Alibek Omarov 5 years ago
parent 4b0c7789c2
commit 8e2b421b1f
  1. 6
      app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt
  2. 4
      app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt
  3. 2
      app/src/main/java/com/keylesspalace/tusky/MainActivity.kt
  4. 8
      app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldAdapter.kt
  5. 5
      app/src/main/java/com/keylesspalace/tusky/adapter/AccountSelectionAdapter.kt
  6. 4
      app/src/main/java/com/keylesspalace/tusky/adapter/AccountViewHolder.java
  7. 4
      app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.java
  8. 4
      app/src/main/java/com/keylesspalace/tusky/adapter/ComposeAutoCompleteAdapter.java
  9. 6
      app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt
  10. 35
      app/src/main/java/com/keylesspalace/tusky/adapter/MutedStatusViewHolder.java
  11. 4
      app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java
  12. 14
      app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java
  13. 9
      app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt
  14. 15
      app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
  15. 4
      app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusViewHolder.kt
  16. 148
      app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.java
  17. 120
      app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.kt
  18. 2
      app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt

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

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

@ -592,7 +592,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
private fun updateProfiles() { private fun updateProfiles() {
val profiles: MutableList<IProfile> = accountManager.getAllAccountsOrderedByActive().map { acc -> 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(emojifyString(acc.displayName, acc.emojis, header))
ProfileDrawerItem().apply { ProfileDrawerItem().apply {
isSelected = acc.isActive isSelected = acc.isActive

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

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

@ -12,7 +12,7 @@ import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.entity.Account; import com.keylesspalace.tusky.entity.Account;
import com.keylesspalace.tusky.interfaces.AccountActionListener; import com.keylesspalace.tusky.interfaces.AccountActionListener;
import com.keylesspalace.tusky.interfaces.LinkListener; import com.keylesspalace.tusky.interfaces.LinkListener;
import com.keylesspalace.tusky.util.CustomEmojiHelper; import com.keylesspalace.tusky.util.CustomEmojiHelperKt;
import com.keylesspalace.tusky.util.ImageLoadingHelper; import com.keylesspalace.tusky.util.ImageLoadingHelper;
public class AccountViewHolder extends RecyclerView.ViewHolder { public class AccountViewHolder extends RecyclerView.ViewHolder {
@ -40,7 +40,7 @@ public class AccountViewHolder extends RecyclerView.ViewHolder {
String format = username.getContext().getString(R.string.status_username_format); String format = username.getContext().getString(R.string.status_username_format);
String formattedUsername = String.format(format, account.getUsername()); String formattedUsername = String.format(format, account.getUsername());
username.setText(formattedUsername); username.setText(formattedUsername);
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(account.getName(), account.getEmojis(), displayName); CharSequence emojifiedName = CustomEmojiHelperKt.emojifyString(account.getName(), account.getEmojis(), displayName);
displayName.setText(emojifiedName); displayName.setText(emojifiedName);
int avatarRadius = avatar.getContext().getResources() int avatarRadius = avatar.getContext().getResources()
.getDimensionPixelSize(R.dimen.avatar_radius_48dp); .getDimensionPixelSize(R.dimen.avatar_radius_48dp);

@ -29,7 +29,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.keylesspalace.tusky.R; import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.entity.Account; import com.keylesspalace.tusky.entity.Account;
import com.keylesspalace.tusky.interfaces.AccountActionListener; import com.keylesspalace.tusky.interfaces.AccountActionListener;
import com.keylesspalace.tusky.util.CustomEmojiHelper; import com.keylesspalace.tusky.util.CustomEmojiHelperKt;
import com.keylesspalace.tusky.util.ImageLoadingHelper; import com.keylesspalace.tusky.util.ImageLoadingHelper;
public class BlocksAdapter extends AccountAdapter { public class BlocksAdapter extends AccountAdapter {
@ -86,7 +86,7 @@ public class BlocksAdapter extends AccountAdapter {
void setupWithAccount(Account account) { void setupWithAccount(Account account) {
id = account.getId(); id = account.getId();
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(account.getName(), account.getEmojis(), displayName); CharSequence emojifiedName = CustomEmojiHelperKt.emojifyString(account.getName(), account.getEmojis(), displayName);
displayName.setText(emojifiedName); displayName.setText(emojifiedName);
String format = username.getContext().getString(R.string.status_username_format); String format = username.getContext().getString(R.string.status_username_format);
String formattedUsername = String.format(format, account.getUsername()); String formattedUsername = String.format(format, account.getUsername());

@ -31,7 +31,7 @@ import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.entity.Account; import com.keylesspalace.tusky.entity.Account;
import com.keylesspalace.tusky.entity.Emoji; import com.keylesspalace.tusky.entity.Emoji;
import com.keylesspalace.tusky.entity.HashTag; import com.keylesspalace.tusky.entity.HashTag;
import com.keylesspalace.tusky.util.CustomEmojiHelper; import com.keylesspalace.tusky.util.CustomEmojiHelperKt;
import com.keylesspalace.tusky.util.ImageLoadingHelper; import com.keylesspalace.tusky.util.ImageLoadingHelper;
import java.util.ArrayList; import java.util.ArrayList;
@ -146,7 +146,7 @@ public class ComposeAutoCompleteAdapter extends BaseAdapter
account.getUsername() account.getUsername()
); );
accountViewHolder.username.setText(formattedUsername); accountViewHolder.username.setText(formattedUsername);
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(account.getName(), CharSequence emojifiedName = CustomEmojiHelperKt.emojifyString(account.getName(),
account.getEmojis(), accountViewHolder.displayName); account.getEmojis(), accountViewHolder.displayName);
accountViewHolder.displayName.setText(emojifiedName); accountViewHolder.displayName.setText(emojifiedName);

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

@ -1,57 +1,27 @@
package com.keylesspalace.tusky.adapter; package com.keylesspalace.tusky.adapter;
import android.content.Context; import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.view.View; import android.view.View;
import android.widget.Button;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.google.android.material.button.MaterialButton;
import com.keylesspalace.tusky.R; import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.entity.Attachment;
import com.keylesspalace.tusky.entity.Attachment.Focus;
import com.keylesspalace.tusky.entity.Attachment.MetaData;
import com.keylesspalace.tusky.entity.Emoji; import com.keylesspalace.tusky.entity.Emoji;
import com.keylesspalace.tusky.entity.Status;
import com.keylesspalace.tusky.interfaces.StatusActionListener; import com.keylesspalace.tusky.interfaces.StatusActionListener;
import com.keylesspalace.tusky.util.CustomEmojiHelper; import com.keylesspalace.tusky.util.CustomEmojiHelperKt;
import com.keylesspalace.tusky.util.ImageLoadingHelper;
import com.keylesspalace.tusky.util.LinkHelper;
import com.keylesspalace.tusky.util.StatusDisplayOptions; import com.keylesspalace.tusky.util.StatusDisplayOptions;
import com.keylesspalace.tusky.util.ThemeUtils;
import com.keylesspalace.tusky.util.TimestampUtils; import com.keylesspalace.tusky.util.TimestampUtils;
import com.keylesspalace.tusky.view.MediaPreviewImageView;
import com.keylesspalace.tusky.viewdata.PollOptionViewData;
import com.keylesspalace.tusky.viewdata.PollViewData;
import com.keylesspalace.tusky.viewdata.PollViewDataKt;
import com.keylesspalace.tusky.viewdata.StatusViewData; import com.keylesspalace.tusky.viewdata.StatusViewData;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Objects;
import at.connyduck.sparkbutton.SparkButton;
import kotlin.collections.CollectionsKt;
import static com.keylesspalace.tusky.viewdata.PollViewDataKt.buildDescription;
public class MutedStatusViewHolder extends RecyclerView.ViewHolder { public class MutedStatusViewHolder extends RecyclerView.ViewHolder {
public static class Key { public static class Key {
@ -60,7 +30,6 @@ public class MutedStatusViewHolder extends RecyclerView.ViewHolder {
private TextView displayName; private TextView displayName;
private TextView username; private TextView username;
private TextView message;
private ImageButton unmuteButton; private ImageButton unmuteButton;
public TextView timestampInfo; public TextView timestampInfo;
@ -79,7 +48,7 @@ public class MutedStatusViewHolder extends RecyclerView.ViewHolder {
} }
protected void setDisplayName(String name, List<Emoji> customEmojis) { protected void setDisplayName(String name, List<Emoji> customEmojis) {
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(name, customEmojis, displayName); CharSequence emojifiedName = CustomEmojiHelperKt.emojifyString(name, customEmojis, displayName);
displayName.setText(emojifiedName); displayName.setText(emojifiedName);
} }

@ -14,7 +14,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.keylesspalace.tusky.R; import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.entity.Account; import com.keylesspalace.tusky.entity.Account;
import com.keylesspalace.tusky.interfaces.AccountActionListener; import com.keylesspalace.tusky.interfaces.AccountActionListener;
import com.keylesspalace.tusky.util.CustomEmojiHelper; import com.keylesspalace.tusky.util.CustomEmojiHelperKt;
import com.keylesspalace.tusky.util.ImageLoadingHelper; import com.keylesspalace.tusky.util.ImageLoadingHelper;
public class MutesAdapter extends AccountAdapter { public class MutesAdapter extends AccountAdapter {
@ -71,7 +71,7 @@ public class MutesAdapter extends AccountAdapter {
void setupWithAccount(Account account) { void setupWithAccount(Account account) {
id = account.getId(); id = account.getId();
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(account.getName(), account.getEmojis(), displayName); CharSequence emojifiedName = CustomEmojiHelperKt.emojifyString(account.getName(), account.getEmojis(), displayName);
displayName.setText(emojifiedName); displayName.setText(emojifiedName);
String format = username.getContext().getString(R.string.status_username_format); String format = username.getContext().getString(R.string.status_username_format);
String formattedUsername = String.format(format, account.getUsername()); String formattedUsername = String.format(format, account.getUsername());

@ -46,7 +46,7 @@ import com.keylesspalace.tusky.interfaces.AccountActionListener;
import com.keylesspalace.tusky.interfaces.LinkListener; import com.keylesspalace.tusky.interfaces.LinkListener;
import com.keylesspalace.tusky.interfaces.StatusActionListener; import com.keylesspalace.tusky.interfaces.StatusActionListener;
import com.keylesspalace.tusky.util.CardViewMode; import com.keylesspalace.tusky.util.CardViewMode;
import com.keylesspalace.tusky.util.CustomEmojiHelper; import com.keylesspalace.tusky.util.CustomEmojiHelperKt;
import com.keylesspalace.tusky.util.ImageLoadingHelper; import com.keylesspalace.tusky.util.ImageLoadingHelper;
import com.keylesspalace.tusky.util.LinkHelper; import com.keylesspalace.tusky.util.LinkHelper;
import com.keylesspalace.tusky.util.SmartLengthInputFilter; import com.keylesspalace.tusky.util.SmartLengthInputFilter;
@ -347,13 +347,13 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
String format = context.getString(R.string.notification_follow_format); String format = context.getString(R.string.notification_follow_format);
String wrappedDisplayName = bidiFormatter.unicodeWrap(account.getName()); String wrappedDisplayName = bidiFormatter.unicodeWrap(account.getName());
String wholeMessage = String.format(format, wrappedDisplayName); String wholeMessage = String.format(format, wrappedDisplayName);
CharSequence emojifiedMessage = CustomEmojiHelper.emojifyString(wholeMessage, account.getEmojis(), message); CharSequence emojifiedMessage = CustomEmojiHelperKt.emojifyString(wholeMessage, account.getEmojis(), message);
message.setText(emojifiedMessage); message.setText(emojifiedMessage);
String username = context.getString(R.string.status_username_format, account.getUsername()); String username = context.getString(R.string.status_username_format, account.getUsername());
usernameView.setText(username); usernameView.setText(username);
CharSequence emojifiedDisplayName = CustomEmojiHelper.emojifyString(wrappedDisplayName, account.getEmojis(), usernameView); CharSequence emojifiedDisplayName = CustomEmojiHelperKt.emojifyString(wrappedDisplayName, account.getEmojis(), usernameView);
displayNameView.setText(emojifiedDisplayName); displayNameView.setText(emojifiedDisplayName);
@ -429,7 +429,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
} }
private void setDisplayName(String name, List<Emoji> emojis) { private void setDisplayName(String name, List<Emoji> emojis) {
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(name, emojis, displayName); CharSequence emojifiedName = CustomEmojiHelperKt.emojifyString(name, emojis, displayName);
displayName.setText(emojifiedName); displayName.setText(emojifiedName);
} }
@ -526,7 +526,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
final SpannableStringBuilder str = new SpannableStringBuilder(wholeMessage); final SpannableStringBuilder str = new SpannableStringBuilder(wholeMessage);
str.setSpan(new StyleSpan(Typeface.BOLD), 0, displayName.length(), str.setSpan(new StyleSpan(Typeface.BOLD), 0, displayName.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
CharSequence emojifiedText = CustomEmojiHelper.emojifyText(str, notificationViewData.getAccount().getEmojis(), message); CharSequence emojifiedText = CustomEmojiHelperKt.emojifyText(str, notificationViewData.getAccount().getEmojis(), message);
message.setText(emojifiedText); message.setText(emojifiedText);
if (statusViewData != null) { if (statusViewData != null) {
@ -621,11 +621,11 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
statusContent.setFilters(NO_INPUT_FILTER); statusContent.setFilters(NO_INPUT_FILTER);
} }
Spanned emojifiedText = CustomEmojiHelper.emojifyText(content, emojis, statusContent); Spanned emojifiedText = CustomEmojiHelperKt.emojifyText(content, emojis, statusContent);
LinkHelper.setClickableText(statusContent, emojifiedText, statusViewData.getMentions(), listener); LinkHelper.setClickableText(statusContent, emojifiedText, statusViewData.getMentions(), listener);
Spanned emojifiedContentWarning = Spanned emojifiedContentWarning =
CustomEmojiHelper.emojifyString(statusViewData.getSpoilerText(), statusViewData.getStatusEmojis(), contentWarningDescriptionTextView); CustomEmojiHelperKt.emojifyString(statusViewData.getSpoilerText(), statusViewData.getStatusEmojis(), contentWarningDescriptionTextView);
contentWarningDescriptionTextView.setText(emojifiedContentWarning); contentWarningDescriptionTextView.setText(emojifiedContentWarning);
} }
} }

@ -25,8 +25,7 @@ import androidx.emoji.text.EmojiCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Emoji import com.keylesspalace.tusky.entity.Emoji
import com.keylesspalace.tusky.util.CustomEmojiHelper import com.keylesspalace.tusky.util.*
import com.keylesspalace.tusky.util.visible
import com.keylesspalace.tusky.viewdata.PollOptionViewData import com.keylesspalace.tusky.viewdata.PollOptionViewData
import com.keylesspalace.tusky.viewdata.buildDescription import com.keylesspalace.tusky.viewdata.buildDescription
import com.keylesspalace.tusky.viewdata.calculatePercent import com.keylesspalace.tusky.viewdata.calculatePercent
@ -71,7 +70,7 @@ class PollAdapter: RecyclerView.Adapter<PollViewHolder>() {
when(mode) { when(mode) {
RESULT -> { RESULT -> {
val percent = calculatePercent(option.votesCount, voteCount) val percent = calculatePercent(option.votesCount, voteCount)
val emojifiedPollOptionText = CustomEmojiHelper.emojifyText(buildDescription(option.title, percent, holder.resultTextView.context), emojis, holder.resultTextView) val emojifiedPollOptionText = emojifyText(buildDescription(option.title, percent, holder.resultTextView.context), emojis, holder.resultTextView)
holder.resultTextView.text = EmojiCompat.get().process(emojifiedPollOptionText) holder.resultTextView.text = EmojiCompat.get().process(emojifiedPollOptionText)
val level = percent * 100 val level = percent * 100
@ -80,7 +79,7 @@ class PollAdapter: RecyclerView.Adapter<PollViewHolder>() {
} }
SINGLE -> { SINGLE -> {
val emojifiedPollOptionText = CustomEmojiHelper.emojifyString(option.title, emojis, holder.radioButton) val emojifiedPollOptionText = emojifyString(option.title, emojis, holder.radioButton)
holder.radioButton.text = EmojiCompat.get().process(emojifiedPollOptionText) holder.radioButton.text = EmojiCompat.get().process(emojifiedPollOptionText)
holder.radioButton.isChecked = option.selected holder.radioButton.isChecked = option.selected
holder.radioButton.setOnClickListener { holder.radioButton.setOnClickListener {
@ -91,7 +90,7 @@ class PollAdapter: RecyclerView.Adapter<PollViewHolder>() {
} }
} }
MULTIPLE -> { MULTIPLE -> {
val emojifiedPollOptionText = CustomEmojiHelper.emojifyString(option.title, emojis, holder.checkBox) val emojifiedPollOptionText = emojifyString(option.title, emojis, holder.checkBox)
holder.checkBox.text = EmojiCompat.get().process(emojifiedPollOptionText) holder.checkBox.text = EmojiCompat.get().process(emojifiedPollOptionText)
holder.checkBox.isChecked = option.selected holder.checkBox.isChecked = option.selected
holder.checkBox.setOnCheckedChangeListener { _, isChecked -> holder.checkBox.setOnCheckedChangeListener { _, isChecked ->

@ -41,14 +41,7 @@ import com.keylesspalace.tusky.entity.Emoji;
import com.keylesspalace.tusky.entity.Status; import com.keylesspalace.tusky.entity.Status;
import com.keylesspalace.tusky.entity.EmojiReaction; import com.keylesspalace.tusky.entity.EmojiReaction;
import com.keylesspalace.tusky.interfaces.StatusActionListener; import com.keylesspalace.tusky.interfaces.StatusActionListener;
import com.keylesspalace.tusky.util.CardViewMode; import com.keylesspalace.tusky.util.*;
import com.keylesspalace.tusky.util.CustomEmojiHelper;
import com.keylesspalace.tusky.util.ImageLoadingHelper;
import com.keylesspalace.tusky.util.LinkHelper;
import com.keylesspalace.tusky.util.StatusDisplayOptions;
import com.keylesspalace.tusky.util.ThemeUtils;
import com.keylesspalace.tusky.util.TimestampUtils;
import com.keylesspalace.tusky.util.ViewExtensionsKt;
import com.keylesspalace.tusky.view.MediaPreviewImageView; import com.keylesspalace.tusky.view.MediaPreviewImageView;
import com.keylesspalace.tusky.view.EmojiKeyboard; import com.keylesspalace.tusky.view.EmojiKeyboard;
import com.keylesspalace.tusky.viewdata.PollOptionViewData; import com.keylesspalace.tusky.viewdata.PollOptionViewData;
@ -202,7 +195,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
protected abstract int getMediaPreviewHeight(Context context); protected abstract int getMediaPreviewHeight(Context context);
protected void setDisplayName(String name, List<Emoji> customEmojis) { protected void setDisplayName(String name, List<Emoji> customEmojis) {
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(name, customEmojis, displayName); CharSequence emojifiedName = CustomEmojiHelperKt.emojifyString(name, customEmojis, displayName);
displayName.setText(emojifiedName); displayName.setText(emojifiedName);
} }
@ -226,7 +219,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
final StatusActionListener listener) { final StatusActionListener listener) {
boolean sensitive = !TextUtils.isEmpty(spoilerText); boolean sensitive = !TextUtils.isEmpty(spoilerText);
if (sensitive) { if (sensitive) {
CharSequence emojiSpoiler = CustomEmojiHelper.emojifyString(spoilerText, emojis, contentWarningDescription); CharSequence emojiSpoiler = CustomEmojiHelperKt.emojifyString(spoilerText, emojis, contentWarningDescription);
contentWarningDescription.setText(emojiSpoiler); contentWarningDescription.setText(emojiSpoiler);
contentWarningDescription.setVisibility(View.VISIBLE); contentWarningDescription.setVisibility(View.VISIBLE);
contentWarningButton.setVisibility(View.VISIBLE); contentWarningButton.setVisibility(View.VISIBLE);
@ -265,7 +258,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
StatusDisplayOptions statusDisplayOptions, StatusDisplayOptions statusDisplayOptions,
final StatusActionListener listener) { final StatusActionListener listener) {
if (expanded) { if (expanded) {
Spanned emojifiedText = CustomEmojiHelper.emojifyText(content, emojis, this.content); Spanned emojifiedText = CustomEmojiHelperKt.emojifyText(content, emojis, this.content);
LinkHelper.setClickableText(this.content, emojifiedText, mentions, listener); LinkHelper.setClickableText(this.content, emojifiedText, mentions, listener);
for (int i = 0; i < mediaLabels.length; ++i) { for (int i = 0; i < mediaLabels.length; ++i) {
updateMediaLabel(i, sensitive, expanded); updateMediaLabel(i, sensitive, expanded);

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

@ -1,148 +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(), Pattern.LITERAL)
.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 = (int)((float)metrics.top * 1.3);
fm.ascent = (int)((float)metrics.ascent * 1.3);
fm.descent = (int)((float)metrics.descent * 2.0);
fm.bottom = (int)((float)metrics.bottom * 3.5);
// fm.leading = (int)((float)metrics.leading); // useless to change
}
return (int) (paint.getTextSize()*2.0);
}
@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() * 2.0);
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
}
};
}
}
}

@ -0,0 +1,120 @@
/* 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.regex.Pattern
import androidx.preference.PreferenceManager
/**
* 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 emojifyText(text: Spanned, emojis: List<Emoji>?, view: View) : Spanned {
if (emojis != null && emojis.isNotEmpty()) {
val builder = SpannableStringBuilder(text)
for (emoji in emojis) {
val pattern = StringBuilder(":").append(emoji.shortcode).append(":")
val matcher = Pattern.compile(pattern.toString(), Pattern.LITERAL)
.matcher(text)
while(matcher.find()) {
val span = EmojiSpan(WeakReference<View>(view))
builder.setSpan(span, matcher.start(), matcher.end(), 0);
Glide.with(view)
.asBitmap()
.load(emoji.url)
.into(span.getTarget())
}
}
return builder
}
return text
}
fun emojifyString(string: String, emojis: List<Emoji>?, view: View) : Spanned {
return emojifyText(SpannedString(string), emojis, view)
}
public 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 * 1.3f).toInt()
fm.ascent = (metrics.ascent * 1.3f).toInt()
fm.descent = (metrics.descent * 2.0f).toInt()
fm.bottom = (metrics.bottom * 3.5f).toInt()
}
return (paint.textSize * 2.0).toInt()
}
override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
if (imageDrawable == null)
return
canvas.save()
val emojiSize = getSize(paint, text, start, end, null)
imageDrawable!!.setBounds(0, 0, emojiSize, emojiSize)
var transY = bottom - imageDrawable!!.bounds.bottom
transY -= paint.fontMetricsInt.descent / 2;
canvas.translate(x, transY.toFloat())
imageDrawable!!.draw(canvas)
canvas.restore()
}
fun getTarget(): Target<Bitmap> {
return object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
val view = viewWeakReference.get()
if (view != null) {
imageDrawable = BitmapDrawable(view.context.resources, resource)
view.invalidate()
}
}
override fun onLoadCleared(placeholder: Drawable?) {}
}
}
}

@ -298,7 +298,7 @@ class StatusViewHelper(private val itemView: View) {
val percent = calculatePercent(options[i].votesCount, poll.votesCount) val percent = calculatePercent(options[i].votesCount, poll.votesCount)
val pollOptionText = buildDescription(options[i].title, percent, pollResults[i].context) val pollOptionText = buildDescription(options[i].title, percent, pollResults[i].context)
pollResults[i].text = CustomEmojiHelper.emojifyText(pollOptionText, emojis, pollResults[i]) pollResults[i].text = emojifyText(pollOptionText, emojis, pollResults[i])
pollResults[i].visibility = View.VISIBLE pollResults[i].visibility = View.VISIBLE
val level = percent * 100 val level = percent * 100

Loading…
Cancel
Save