CustomEmojiHelper: rewrite to Kotlin (#1787)
* CustomEmojiHelper: rewrite to Kotlin * CustomEmojiHelper: PR fixes
This commit is contained in:
parent
e72bdcaf42
commit
2fc7ad13bb
18 changed files with 151 additions and 190 deletions
|
@ -360,9 +360,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 = 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)
|
LinkHelper.setClickableText(accountNoteTextView, emojifiedNote, null, this)
|
||||||
|
|
||||||
// accountFieldAdapter.fields = account.fields ?: emptyList()
|
// accountFieldAdapter.fields = account.fields ?: emptyList()
|
||||||
|
@ -423,7 +423,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 = account.name.emojify(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 = account.name.emojify(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 = account.name.emojify(account.emojis, displayNameTextView)
|
||||||
usernameTextView.text = account.username
|
usernameTextView.text = account.username
|
||||||
loadAvatar(account.avatar, avatar, radius, animateAvatar)
|
loadAvatar(account.avatar, avatar, radius, animateAvatar)
|
||||||
|
|
||||||
|
|
|
@ -591,7 +591,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(acc.displayName.emojify(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 = field.name.emojify(emojis, viewHolder.nameTextView)
|
||||||
viewHolder.nameTextView.text = emojifiedName
|
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)
|
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 = account.displayName.emojify(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)
|
||||||
|
|
|
@ -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 = CustomEmojiHelper.emojify(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);
|
||||||
|
|
|
@ -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 = CustomEmojiHelper.emojify(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());
|
||||||
|
|
|
@ -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 = CustomEmojiHelper.emojify(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 = wrappedName.emojify(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)
|
||||||
|
|
|
@ -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 = CustomEmojiHelper.emojify(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());
|
||||||
|
|
|
@ -331,13 +331,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 = CustomEmojiHelper.emojify(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 = CustomEmojiHelper.emojify(wrappedDisplayName, account.getEmojis(), usernameView);
|
||||||
|
|
||||||
displayNameView.setText(emojifiedDisplayName);
|
displayNameView.setText(emojifiedDisplayName);
|
||||||
|
|
||||||
|
@ -412,7 +412,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 = CustomEmojiHelper.emojify(name, emojis, displayName);
|
||||||
displayName.setText(emojifiedName);
|
displayName.setText(emojifiedName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,7 +496,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 = CustomEmojiHelper.emojify(str, notificationViewData.getAccount().getEmojis(), message);
|
||||||
message.setText(emojifiedText);
|
message.setText(emojifiedText);
|
||||||
|
|
||||||
if (statusViewData != null) {
|
if (statusViewData != null) {
|
||||||
|
@ -592,11 +592,11 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||||
statusContent.setFilters(NO_INPUT_FILTER);
|
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);
|
LinkHelper.setClickableText(statusContent, emojifiedText, statusViewData.getMentions(), listener);
|
||||||
|
|
||||||
Spanned emojifiedContentWarning =
|
CharSequence emojifiedContentWarning =
|
||||||
CustomEmojiHelper.emojifyString(statusViewData.getSpoilerText(), statusViewData.getStatusEmojis(), contentWarningDescriptionTextView);
|
CustomEmojiHelper.emojify(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
|
||||||
|
@ -78,7 +77,8 @@ class PollAdapter: RecyclerView.Adapter<PollViewHolder>() {
|
||||||
when(mode) {
|
when(mode) {
|
||||||
RESULT -> {
|
RESULT -> {
|
||||||
val percent = calculatePercent(option.votesCount, votersCount, voteCount)
|
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)
|
holder.resultTextView.text = EmojiCompat.get().process(emojifiedPollOptionText)
|
||||||
|
|
||||||
val level = percent * 100
|
val level = percent * 100
|
||||||
|
@ -87,7 +87,7 @@ class PollAdapter: RecyclerView.Adapter<PollViewHolder>() {
|
||||||
|
|
||||||
}
|
}
|
||||||
SINGLE -> {
|
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.text = EmojiCompat.get().process(emojifiedPollOptionText)
|
||||||
holder.radioButton.isChecked = option.selected
|
holder.radioButton.isChecked = option.selected
|
||||||
holder.radioButton.setOnClickListener {
|
holder.radioButton.setOnClickListener {
|
||||||
|
@ -98,7 +98,7 @@ class PollAdapter: RecyclerView.Adapter<PollViewHolder>() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MULTIPLE -> {
|
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.text = EmojiCompat.get().process(emojifiedPollOptionText)
|
||||||
holder.checkBox.isChecked = option.selected
|
holder.checkBox.isChecked = option.selected
|
||||||
holder.checkBox.setOnCheckedChangeListener { _, isChecked ->
|
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 radioButton: RadioButton = view.findViewById(R.id.status_poll_radio_button)
|
||||||
val checkBox: CheckBox = view.findViewById(R.id.status_poll_checkbox)
|
val checkBox: CheckBox = view.findViewById(R.id.status_poll_checkbox)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,7 +181,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 = CustomEmojiHelper.emojify(name, customEmojis, displayName);
|
||||||
displayName.setText(emojifiedName);
|
displayName.setText(emojifiedName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +205,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 = CustomEmojiHelper.emojify(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);
|
||||||
|
@ -244,7 +244,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);
|
CharSequence emojifiedText = CustomEmojiHelper.emojify(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 = status.spoilerText.emojify(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 = content.emojify(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,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
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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?) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,10 +68,10 @@ public class LinkHelper {
|
||||||
* @param mentions any '@' mentions which are known to be in the content
|
* @param mentions any '@' mentions which are known to be in the content
|
||||||
* @param listener to notify about particular spans that are clicked
|
* @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) {
|
@Nullable Status.Mention[] mentions, final LinkListener listener) {
|
||||||
SpannableStringBuilder builder = new SpannableStringBuilder(content);
|
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(content);
|
||||||
URLSpan[] urlSpans = content.getSpans(0, content.length(), URLSpan.class);
|
URLSpan[] urlSpans = builder.getSpans(0, content.length(), URLSpan.class);
|
||||||
for (URLSpan span : urlSpans) {
|
for (URLSpan span : urlSpans) {
|
||||||
int start = builder.getSpanStart(span);
|
int start = builder.getSpanStart(span);
|
||||||
int end = builder.getSpanEnd(span);
|
int end = builder.getSpanEnd(span);
|
||||||
|
|
|
@ -302,7 +302,7 @@ class StatusViewHelper(private val itemView: View) {
|
||||||
val percent = calculatePercent(options[i].votesCount, poll.votersCount, poll.votesCount)
|
val percent = calculatePercent(options[i].votesCount, poll.votersCount, 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 = pollOptionText.emojify(emojis, pollResults[i])
|
||||||
pollResults[i].visibility = View.VISIBLE
|
pollResults[i].visibility = View.VISIBLE
|
||||||
|
|
||||||
val level = percent * 100
|
val level = percent * 100
|
||||||
|
|
Loading…
Add table
Reference in a new issue