Animated emoji support (#2064)
* Animated emoji support * Try to query preference only once * Revert to using SpannableStringBuilder
This commit is contained in:
parent
c685192d49
commit
9580870445
38 changed files with 225 additions and 120 deletions
|
@ -78,7 +78,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
||||||
|
|
||||||
private val viewModel: AccountViewModel by viewModels { viewModelFactory }
|
private val viewModel: AccountViewModel by viewModels { viewModelFactory }
|
||||||
|
|
||||||
private val accountFieldAdapter = AccountFieldAdapter(this)
|
private lateinit var accountFieldAdapter : AccountFieldAdapter
|
||||||
|
|
||||||
private var followState: FollowState = FollowState.NOT_FOLLOWING
|
private var followState: FollowState = FollowState.NOT_FOLLOWING
|
||||||
private var blocking: Boolean = false
|
private var blocking: Boolean = false
|
||||||
|
@ -89,6 +89,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
||||||
private var loadedAccount: Account? = null
|
private var loadedAccount: Account? = null
|
||||||
|
|
||||||
private var animateAvatar: Boolean = false
|
private var animateAvatar: Boolean = false
|
||||||
|
private var animateEmojis: Boolean = false
|
||||||
|
|
||||||
// fields for scroll animation
|
// fields for scroll animation
|
||||||
private var hideFab: Boolean = false
|
private var hideFab: Boolean = false
|
||||||
|
@ -124,6 +125,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
||||||
|
|
||||||
val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this)
|
val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
animateAvatar = sharedPrefs.getBoolean("animateGifAvatars", false)
|
animateAvatar = sharedPrefs.getBoolean("animateGifAvatars", false)
|
||||||
|
animateEmojis = sharedPrefs.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
||||||
hideFab = sharedPrefs.getBoolean("fabHide", false)
|
hideFab = sharedPrefs.getBoolean("fabHide", false)
|
||||||
|
|
||||||
setupToolbar()
|
setupToolbar()
|
||||||
|
@ -162,6 +164,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
||||||
accountFollowsYouTextView.hide()
|
accountFollowsYouTextView.hide()
|
||||||
|
|
||||||
// setup the RecyclerView for the account fields
|
// setup the RecyclerView for the account fields
|
||||||
|
accountFieldAdapter = AccountFieldAdapter(this, animateEmojis)
|
||||||
accountFieldList.isNestedScrollingEnabled = false
|
accountFieldList.isNestedScrollingEnabled = false
|
||||||
accountFieldList.layoutManager = LinearLayoutManager(this)
|
accountFieldList.layoutManager = LinearLayoutManager(this)
|
||||||
accountFieldList.adapter = accountFieldAdapter
|
accountFieldList.adapter = accountFieldAdapter
|
||||||
|
@ -375,9 +378,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 = account.name.emojify(account.emojis, accountDisplayNameTextView)
|
accountDisplayNameTextView.text = account.name.emojify(account.emojis, accountDisplayNameTextView, animateEmojis)
|
||||||
|
|
||||||
val emojifiedNote = account.note.emojify(account.emojis, accountNoteTextView)
|
val emojifiedNote = account.note.emojify(account.emojis, accountNoteTextView, animateEmojis)
|
||||||
LinkHelper.setClickableText(accountNoteTextView, emojifiedNote, null, this)
|
LinkHelper.setClickableText(accountNoteTextView, emojifiedNote, null, this)
|
||||||
|
|
||||||
// accountFieldAdapter.fields = account.fields ?: emptyList()
|
// accountFieldAdapter.fields = account.fields ?: emptyList()
|
||||||
|
@ -437,7 +440,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
||||||
private fun updateToolbar() {
|
private fun updateToolbar() {
|
||||||
loadedAccount?.let { account ->
|
loadedAccount?.let { account ->
|
||||||
|
|
||||||
val emojifiedName = account.name.emojify(account.emojis, accountToolbar)
|
val emojifiedName = account.name.emojify(account.emojis, accountToolbar, animateEmojis)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
supportActionBar?.title = EmojiCompat.get().process(emojifiedName)
|
supportActionBar?.title = EmojiCompat.get().process(emojifiedName)
|
||||||
|
|
|
@ -31,6 +31,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.keylesspalace.tusky.di.Injectable
|
import com.keylesspalace.tusky.di.Injectable
|
||||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||||
import com.keylesspalace.tusky.entity.Account
|
import com.keylesspalace.tusky.entity.Account
|
||||||
|
import com.keylesspalace.tusky.settings.PrefKeys
|
||||||
import com.keylesspalace.tusky.util.*
|
import com.keylesspalace.tusky.util.*
|
||||||
import com.keylesspalace.tusky.viewmodel.AccountsInListViewModel
|
import com.keylesspalace.tusky.viewmodel.AccountsInListViewModel
|
||||||
import com.keylesspalace.tusky.viewmodel.State
|
import com.keylesspalace.tusky.viewmodel.State
|
||||||
|
@ -71,7 +72,9 @@ class AccountsInListFragment : DialogFragment(), Injectable {
|
||||||
private val searchAdapter = SearchAdapter()
|
private val searchAdapter = SearchAdapter()
|
||||||
|
|
||||||
private val radius by lazy { resources.getDimensionPixelSize(R.dimen.avatar_radius_48dp) }
|
private val radius by lazy { resources.getDimensionPixelSize(R.dimen.avatar_radius_48dp) }
|
||||||
private val animateAvatar by lazy { PreferenceManager.getDefaultSharedPreferences(requireContext()).getBoolean("animateGifAvatars", false) }
|
private val pm by lazy { PreferenceManager.getDefaultSharedPreferences(requireContext()) }
|
||||||
|
private val animateAvatar by lazy { pm.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false) }
|
||||||
|
private val animateEmojis by lazy { pm.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) }
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -209,7 +212,7 @@ class AccountsInListFragment : DialogFragment(), Injectable {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bind(account: Account) {
|
fun bind(account: Account) {
|
||||||
displayNameTextView.text = account.name.emojify(account.emojis, displayNameTextView)
|
displayNameTextView.text = account.name.emojify(account.emojis, displayNameTextView, animateEmojis)
|
||||||
usernameTextView.text = account.username
|
usernameTextView.text = account.username
|
||||||
loadAvatar(account.avatar, avatar, radius, animateAvatar)
|
loadAvatar(account.avatar, avatar, radius, animateAvatar)
|
||||||
}
|
}
|
||||||
|
@ -252,7 +255,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 = account.name.emojify(account.emojis, displayNameTextView)
|
displayNameTextView.text = account.name.emojify(account.emojis, displayNameTextView, animateEmojis)
|
||||||
usernameTextView.text = account.username
|
usernameTextView.text = account.username
|
||||||
loadAvatar(account.avatar, avatar, radius, animateAvatar)
|
loadAvatar(account.avatar, avatar, radius, animateAvatar)
|
||||||
|
|
||||||
|
|
|
@ -723,8 +723,9 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateProfiles() {
|
private fun updateProfiles() {
|
||||||
|
val animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
||||||
val profiles: MutableList<IProfile> = accountManager.getAllAccountsOrderedByActive().map { acc ->
|
val profiles: MutableList<IProfile> = accountManager.getAllAccountsOrderedByActive().map { acc ->
|
||||||
val emojifiedName = EmojiCompat.get().process(acc.displayName.emojify(acc.emojis, header))
|
val emojifiedName = EmojiCompat.get().process(acc.displayName.emojify(acc.emojis, header, animateEmojis))
|
||||||
|
|
||||||
ProfileDrawerItem().apply {
|
ProfileDrawerItem().apply {
|
||||||
isSelected = acc.isActive
|
isSelected = acc.isActive
|
||||||
|
|
|
@ -33,10 +33,14 @@ public abstract class AccountAdapter extends RecyclerView.Adapter {
|
||||||
List<Account> accountList;
|
List<Account> accountList;
|
||||||
AccountActionListener accountActionListener;
|
AccountActionListener accountActionListener;
|
||||||
private boolean bottomLoading;
|
private boolean bottomLoading;
|
||||||
|
protected final boolean animateEmojis;
|
||||||
|
protected final boolean animateAvatar;
|
||||||
|
|
||||||
AccountAdapter(AccountActionListener accountActionListener) {
|
AccountAdapter(AccountActionListener accountActionListener, boolean animateAvatar, boolean animateEmojis) {
|
||||||
this.accountList = new ArrayList<>();
|
this.accountList = new ArrayList<>();
|
||||||
this.accountActionListener = accountActionListener;
|
this.accountActionListener = accountActionListener;
|
||||||
|
this.animateAvatar = animateAvatar;
|
||||||
|
this.animateEmojis = animateEmojis;
|
||||||
bottomLoading = false;
|
bottomLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ import com.keylesspalace.tusky.interfaces.LinkListener
|
||||||
import com.keylesspalace.tusky.util.*
|
import com.keylesspalace.tusky.util.*
|
||||||
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, private val animateEmojis: Boolean) : RecyclerView.Adapter<AccountFieldAdapter.ViewHolder>() {
|
||||||
|
|
||||||
var emojis: List<Emoji> = emptyList()
|
var emojis: List<Emoji> = emptyList()
|
||||||
var fields: List<Either<IdentityProof, Field>> = emptyList()
|
var fields: List<Either<IdentityProof, Field>> = emptyList()
|
||||||
|
@ -55,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 = field.name.emojify(emojis, viewHolder.nameTextView)
|
val emojifiedName = field.name.emojify(emojis, viewHolder.nameTextView, animateEmojis)
|
||||||
viewHolder.nameTextView.text = emojifiedName
|
viewHolder.nameTextView.text = emojifiedName
|
||||||
|
|
||||||
val emojifiedValue = field.value.emojify(emojis, viewHolder.valueTextView)
|
val emojifiedValue = field.value.emojify(emojis, viewHolder.valueTextView, animateEmojis)
|
||||||
LinkHelper.setClickableText(viewHolder.valueTextView, emojifiedValue, null, linkListener)
|
LinkHelper.setClickableText(viewHolder.valueTextView, emojifiedValue, null, linkListener)
|
||||||
|
|
||||||
if(field.verifiedAt != null) {
|
if(field.verifiedAt != null) {
|
||||||
|
|
|
@ -23,6 +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.settings.PrefKeys
|
||||||
import com.keylesspalace.tusky.util.*
|
import com.keylesspalace.tusky.util.*
|
||||||
import kotlinx.android.synthetic.main.item_autocomplete_account.view.*
|
import kotlinx.android.synthetic.main.item_autocomplete_account.view.*
|
||||||
|
|
||||||
|
@ -41,12 +42,14 @@ class AccountSelectionAdapter(context: Context) : ArrayAdapter<AccountEntity>(co
|
||||||
val username = view.username
|
val username = view.username
|
||||||
val displayName = view.display_name
|
val displayName = view.display_name
|
||||||
val avatar = view.avatar
|
val avatar = view.avatar
|
||||||
|
val pm = PreferenceManager.getDefaultSharedPreferences(avatar.context)
|
||||||
|
val animateEmojis = pm.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
||||||
|
|
||||||
username.text = account.fullName
|
username.text = account.fullName
|
||||||
displayName.text = account.displayName.emojify(account.emojis, displayName)
|
displayName.text = account.displayName.emojify(account.emojis, displayName, animateEmojis)
|
||||||
|
|
||||||
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 = pm.getBoolean("animateGifAvatars", false)
|
||||||
.getBoolean("animateGifAvatars", false)
|
|
||||||
|
|
||||||
loadAvatar(account.profilePictureUrl, avatar, avatarRadius, animateAvatar)
|
loadAvatar(account.profilePictureUrl, avatar, avatarRadius, animateAvatar)
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ public class AccountViewHolder extends RecyclerView.ViewHolder {
|
||||||
private ImageView avatarInset;
|
private ImageView avatarInset;
|
||||||
private String accountId;
|
private String accountId;
|
||||||
private boolean showBotOverlay;
|
private boolean showBotOverlay;
|
||||||
private boolean animateAvatar;
|
|
||||||
|
|
||||||
public AccountViewHolder(View itemView) {
|
public AccountViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
|
@ -32,15 +31,14 @@ public class AccountViewHolder extends RecyclerView.ViewHolder {
|
||||||
avatarInset = itemView.findViewById(R.id.account_avatar_inset);
|
avatarInset = itemView.findViewById(R.id.account_avatar_inset);
|
||||||
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(itemView.getContext());
|
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(itemView.getContext());
|
||||||
showBotOverlay = sharedPrefs.getBoolean("showBotOverlay", true);
|
showBotOverlay = sharedPrefs.getBoolean("showBotOverlay", true);
|
||||||
animateAvatar = sharedPrefs.getBoolean("animateGifAvatars", false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setupWithAccount(Account account) {
|
public void setupWithAccount(Account account, boolean animateAvatar, boolean animateEmojis) {
|
||||||
accountId = account.getId();
|
accountId = account.getId();
|
||||||
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.emojify(account.getName(), account.getEmojis(), displayName);
|
CharSequence emojifiedName = CustomEmojiHelper.emojify(account.getName(), account.getEmojis(), displayName, animateEmojis);
|
||||||
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);
|
||||||
|
|
|
@ -34,8 +34,8 @@ import com.keylesspalace.tusky.util.ImageLoadingHelper;
|
||||||
|
|
||||||
public class BlocksAdapter extends AccountAdapter {
|
public class BlocksAdapter extends AccountAdapter {
|
||||||
|
|
||||||
public BlocksAdapter(AccountActionListener accountActionListener) {
|
public BlocksAdapter(AccountActionListener accountActionListener, boolean animateAvatar, boolean animateEmojis) {
|
||||||
super(accountActionListener);
|
super(accountActionListener, animateAvatar, animateEmojis);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ -60,7 +60,7 @@ public class BlocksAdapter extends AccountAdapter {
|
||||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
|
||||||
if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) {
|
if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) {
|
||||||
BlockedUserViewHolder holder = (BlockedUserViewHolder) viewHolder;
|
BlockedUserViewHolder holder = (BlockedUserViewHolder) viewHolder;
|
||||||
holder.setupWithAccount(accountList.get(position));
|
holder.setupWithAccount(accountList.get(position), animateAvatar, animateEmojis);
|
||||||
holder.setupActionListener(accountActionListener);
|
holder.setupActionListener(accountActionListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,6 @@ public class BlocksAdapter extends AccountAdapter {
|
||||||
private TextView displayName;
|
private TextView displayName;
|
||||||
private ImageButton unblock;
|
private ImageButton unblock;
|
||||||
private String id;
|
private String id;
|
||||||
private boolean animateAvatar;
|
|
||||||
|
|
||||||
BlockedUserViewHolder(View itemView) {
|
BlockedUserViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
|
@ -79,14 +78,12 @@ public class BlocksAdapter extends AccountAdapter {
|
||||||
username = itemView.findViewById(R.id.blocked_user_username);
|
username = itemView.findViewById(R.id.blocked_user_username);
|
||||||
displayName = itemView.findViewById(R.id.blocked_user_display_name);
|
displayName = itemView.findViewById(R.id.blocked_user_display_name);
|
||||||
unblock = itemView.findViewById(R.id.blocked_user_unblock);
|
unblock = itemView.findViewById(R.id.blocked_user_unblock);
|
||||||
animateAvatar = PreferenceManager.getDefaultSharedPreferences(itemView.getContext())
|
|
||||||
.getBoolean("animateGifAvatars", false);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupWithAccount(Account account) {
|
void setupWithAccount(Account account, boolean animateAvatar, boolean animateEmojis) {
|
||||||
id = account.getId();
|
id = account.getId();
|
||||||
CharSequence emojifiedName = CustomEmojiHelper.emojify(account.getName(), account.getEmojis(), displayName);
|
CharSequence emojifiedName = CustomEmojiHelper.emojify(account.getName(), account.getEmojis(), displayName, animateEmojis);
|
||||||
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());
|
||||||
|
|
|
@ -27,8 +27,8 @@ import com.keylesspalace.tusky.interfaces.AccountActionListener;
|
||||||
/** Both for follows and following lists. */
|
/** Both for follows and following lists. */
|
||||||
public class FollowAdapter extends AccountAdapter {
|
public class FollowAdapter extends AccountAdapter {
|
||||||
|
|
||||||
public FollowAdapter(AccountActionListener accountActionListener) {
|
public FollowAdapter(AccountActionListener accountActionListener, boolean animateAvatar, boolean animateEmojis) {
|
||||||
super(accountActionListener);
|
super(accountActionListener, animateAvatar, animateEmojis);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ -53,7 +53,7 @@ public class FollowAdapter extends AccountAdapter {
|
||||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
|
||||||
if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) {
|
if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) {
|
||||||
AccountViewHolder holder = (AccountViewHolder) viewHolder;
|
AccountViewHolder holder = (AccountViewHolder) viewHolder;
|
||||||
holder.setupWithAccount(accountList.get(position));
|
holder.setupWithAccount(accountList.get(position), animateAvatar, animateEmojis);
|
||||||
holder.setupActionListener(accountActionListener);
|
holder.setupActionListener(accountActionListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,27 +10,24 @@ 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.emojify
|
import com.keylesspalace.tusky.util.*
|
||||||
import com.keylesspalace.tusky.util.loadAvatar
|
|
||||||
import com.keylesspalace.tusky.util.unicodeWrap
|
|
||||||
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) {
|
||||||
private var id: String? = null
|
private var id: String? = null
|
||||||
private val animateAvatar: Boolean = PreferenceManager.getDefaultSharedPreferences(itemView.context)
|
|
||||||
.getBoolean("animateGifAvatars", false)
|
|
||||||
|
|
||||||
fun setupWithAccount(account: Account) {
|
fun setupWithAccount(account: Account, animateAvatar: Boolean, animateEmojis: Boolean) {
|
||||||
id = account.id
|
id = account.id
|
||||||
val wrappedName = account.name.unicodeWrap()
|
val wrappedName = account.name.unicodeWrap()
|
||||||
val emojifiedName: CharSequence = wrappedName.emojify(account.emojis, itemView)
|
val emojifiedName: CharSequence = wrappedName.emojify(account.emojis, itemView, animateEmojis)
|
||||||
itemView.displayNameTextView.text = emojifiedName
|
itemView.displayNameTextView.text = emojifiedName
|
||||||
if (showHeader) {
|
if (showHeader) {
|
||||||
val wholeMessage: String = itemView.context.getString(R.string.notification_follow_request_format, wrappedName)
|
val wholeMessage: String = itemView.context.getString(R.string.notification_follow_request_format, wrappedName)
|
||||||
itemView.notificationTextView?.text = SpannableStringBuilder(wholeMessage).apply {
|
itemView.notificationTextView?.text = SpannableStringBuilder(wholeMessage).apply {
|
||||||
setSpan(StyleSpan(Typeface.BOLD), 0, wrappedName.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
setSpan(StyleSpan(Typeface.BOLD), 0, wrappedName.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
}.emojify(account.emojis, itemView)
|
}.emojify(account.emojis, itemView, animateEmojis)
|
||||||
}
|
}
|
||||||
itemView.notificationTextView?.visible(showHeader)
|
itemView.notificationTextView?.visible(showHeader)
|
||||||
val format = itemView.context.getString(R.string.status_username_format)
|
val format = itemView.context.getString(R.string.status_username_format)
|
||||||
|
|
|
@ -27,8 +27,8 @@ import com.keylesspalace.tusky.interfaces.AccountActionListener;
|
||||||
|
|
||||||
public class FollowRequestsAdapter extends AccountAdapter {
|
public class FollowRequestsAdapter extends AccountAdapter {
|
||||||
|
|
||||||
public FollowRequestsAdapter(AccountActionListener accountActionListener) {
|
public FollowRequestsAdapter(AccountActionListener accountActionListener, boolean animateAvatar, boolean animateEmojis) {
|
||||||
super(accountActionListener);
|
super(accountActionListener, animateAvatar, animateEmojis);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ -53,7 +53,7 @@ public class FollowRequestsAdapter extends AccountAdapter {
|
||||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
|
||||||
if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) {
|
if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) {
|
||||||
FollowRequestViewHolder holder = (FollowRequestViewHolder) viewHolder;
|
FollowRequestViewHolder holder = (FollowRequestViewHolder) viewHolder;
|
||||||
holder.setupWithAccount(accountList.get(position));
|
holder.setupWithAccount(accountList.get(position), animateAvatar, animateEmojis);
|
||||||
holder.setupActionListener(accountActionListener);
|
holder.setupActionListener(accountActionListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,8 @@ import java.util.HashMap;
|
||||||
public class MutesAdapter extends AccountAdapter {
|
public class MutesAdapter extends AccountAdapter {
|
||||||
private HashMap<String, Boolean> mutingNotificationsMap;
|
private HashMap<String, Boolean> mutingNotificationsMap;
|
||||||
|
|
||||||
public MutesAdapter(AccountActionListener accountActionListener) {
|
public MutesAdapter(AccountActionListener accountActionListener, boolean animateAvatar, boolean animateEmojis) {
|
||||||
super(accountActionListener);
|
super(accountActionListener, animateAvatar, animateEmojis);
|
||||||
mutingNotificationsMap = new HashMap<String, Boolean>();
|
mutingNotificationsMap = new HashMap<String, Boolean>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ public class MutesAdapter extends AccountAdapter {
|
||||||
if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) {
|
if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) {
|
||||||
MutedUserViewHolder holder = (MutedUserViewHolder) viewHolder;
|
MutedUserViewHolder holder = (MutedUserViewHolder) viewHolder;
|
||||||
Account account = accountList.get(position);
|
Account account = accountList.get(position);
|
||||||
holder.setupWithAccount(account, mutingNotificationsMap.get(account.getId()));
|
holder.setupWithAccount(account, mutingNotificationsMap.get(account.getId()), animateAvatar, animateEmojis);
|
||||||
holder.setupActionListener(accountActionListener);
|
holder.setupActionListener(accountActionListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,6 @@ public class MutesAdapter extends AccountAdapter {
|
||||||
private ImageButton unmute;
|
private ImageButton unmute;
|
||||||
private ImageButton muteNotifications;
|
private ImageButton muteNotifications;
|
||||||
private String id;
|
private String id;
|
||||||
private boolean animateAvatar;
|
|
||||||
private boolean notifications;
|
private boolean notifications;
|
||||||
|
|
||||||
MutedUserViewHolder(View itemView) {
|
MutedUserViewHolder(View itemView) {
|
||||||
|
@ -83,13 +82,11 @@ public class MutesAdapter extends AccountAdapter {
|
||||||
displayName = itemView.findViewById(R.id.muted_user_display_name);
|
displayName = itemView.findViewById(R.id.muted_user_display_name);
|
||||||
unmute = itemView.findViewById(R.id.muted_user_unmute);
|
unmute = itemView.findViewById(R.id.muted_user_unmute);
|
||||||
muteNotifications = itemView.findViewById(R.id.muted_user_mute_notifications);
|
muteNotifications = itemView.findViewById(R.id.muted_user_mute_notifications);
|
||||||
animateAvatar = PreferenceManager.getDefaultSharedPreferences(itemView.getContext())
|
|
||||||
.getBoolean("animateGifAvatars", false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupWithAccount(Account account, Boolean mutingNotifications) {
|
void setupWithAccount(Account account, Boolean mutingNotifications, boolean animateAvatar, boolean animateEmojis) {
|
||||||
id = account.getId();
|
id = account.getId();
|
||||||
CharSequence emojifiedName = CustomEmojiHelper.emojify(account.getName(), account.getEmojis(), displayName);
|
CharSequence emojifiedName = CustomEmojiHelper.emojify(account.getName(), account.getEmojis(), displayName, animateEmojis);
|
||||||
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());
|
||||||
|
|
|
@ -232,7 +232,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||||
case VIEW_TYPE_FOLLOW_REQUEST: {
|
case VIEW_TYPE_FOLLOW_REQUEST: {
|
||||||
if (payloadForHolder == null) {
|
if (payloadForHolder == null) {
|
||||||
FollowRequestViewHolder holder = (FollowRequestViewHolder) viewHolder;
|
FollowRequestViewHolder holder = (FollowRequestViewHolder) viewHolder;
|
||||||
holder.setupWithAccount(concreteNotificaton.getAccount());
|
holder.setupWithAccount(concreteNotificaton.getAccount(), statusDisplayOptions.animateAvatars(), statusDisplayOptions.animateEmojis());
|
||||||
holder.setupActionListener(accountActionListener);
|
holder.setupActionListener(accountActionListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,7 +255,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||||
statusDisplayOptions.useBlurhash(),
|
statusDisplayOptions.useBlurhash(),
|
||||||
CardViewMode.NONE,
|
CardViewMode.NONE,
|
||||||
statusDisplayOptions.confirmReblogs(),
|
statusDisplayOptions.confirmReblogs(),
|
||||||
statusDisplayOptions.hideStats()
|
statusDisplayOptions.hideStats(),
|
||||||
|
statusDisplayOptions.animateEmojis()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,13 +337,17 @@ 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 = StringUtils.unicodeWrap(account.getName());
|
String wrappedDisplayName = StringUtils.unicodeWrap(account.getName());
|
||||||
String wholeMessage = String.format(format, wrappedDisplayName);
|
String wholeMessage = String.format(format, wrappedDisplayName);
|
||||||
CharSequence emojifiedMessage = CustomEmojiHelper.emojify(wholeMessage, account.getEmojis(), message);
|
CharSequence emojifiedMessage = CustomEmojiHelper.emojify(
|
||||||
|
wholeMessage, account.getEmojis(), message, statusDisplayOptions.animateEmojis()
|
||||||
|
);
|
||||||
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.emojify(wrappedDisplayName, account.getEmojis(), usernameView);
|
CharSequence emojifiedDisplayName = CustomEmojiHelper.emojify(
|
||||||
|
wrappedDisplayName, account.getEmojis(), usernameView, statusDisplayOptions.animateEmojis()
|
||||||
|
);
|
||||||
|
|
||||||
displayNameView.setText(emojifiedDisplayName);
|
displayNameView.setText(emojifiedDisplayName);
|
||||||
|
|
||||||
|
@ -425,7 +430,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.emojify(name, emojis, displayName);
|
CharSequence emojifiedName = CustomEmojiHelper.emojify(name, emojis, displayName, statusDisplayOptions.animateEmojis());
|
||||||
displayName.setText(emojifiedName);
|
displayName.setText(emojifiedName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,7 +524,9 @@ 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.emojify(str, notificationViewData.getAccount().getEmojis(), message);
|
CharSequence emojifiedText = CustomEmojiHelper.emojify(
|
||||||
|
str, notificationViewData.getAccount().getEmojis(), message, statusDisplayOptions.animateEmojis()
|
||||||
|
);
|
||||||
message.setText(emojifiedText);
|
message.setText(emojifiedText);
|
||||||
|
|
||||||
if (statusViewData != null) {
|
if (statusViewData != null) {
|
||||||
|
@ -630,11 +637,17 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||||
statusContent.setFilters(NO_INPUT_FILTER);
|
statusContent.setFilters(NO_INPUT_FILTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
CharSequence emojifiedText = CustomEmojiHelper.emojify(content, emojis, statusContent);
|
CharSequence emojifiedText = CustomEmojiHelper.emojify(
|
||||||
|
content, emojis, statusContent, statusDisplayOptions.animateEmojis()
|
||||||
|
);
|
||||||
LinkHelper.setClickableText(statusContent, emojifiedText, statusViewData.getMentions(), listener);
|
LinkHelper.setClickableText(statusContent, emojifiedText, statusViewData.getMentions(), listener);
|
||||||
|
|
||||||
CharSequence emojifiedContentWarning =
|
CharSequence emojifiedContentWarning = CustomEmojiHelper.emojify(
|
||||||
CustomEmojiHelper.emojify(statusViewData.getSpoilerText(), statusViewData.getStatusEmojis(), contentWarningDescriptionTextView);
|
statusViewData.getSpoilerText(),
|
||||||
|
statusViewData.getStatusEmojis(),
|
||||||
|
contentWarningDescriptionTextView,
|
||||||
|
statusDisplayOptions.animateEmojis()
|
||||||
|
);
|
||||||
contentWarningDescriptionTextView.setText(emojifiedContentWarning);
|
contentWarningDescriptionTextView.setText(emojifiedContentWarning);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ class PollAdapter: RecyclerView.Adapter<PollViewHolder>() {
|
||||||
private var mode = RESULT
|
private var mode = RESULT
|
||||||
private var emojis: List<Emoji> = emptyList()
|
private var emojis: List<Emoji> = emptyList()
|
||||||
private var resultClickListener: View.OnClickListener? = null
|
private var resultClickListener: View.OnClickListener? = null
|
||||||
|
private var animateEmojis = false
|
||||||
|
|
||||||
fun setup(
|
fun setup(
|
||||||
options: List<PollOptionViewData>,
|
options: List<PollOptionViewData>,
|
||||||
|
@ -45,13 +46,15 @@ class PollAdapter: RecyclerView.Adapter<PollViewHolder>() {
|
||||||
votersCount: Int?,
|
votersCount: Int?,
|
||||||
emojis: List<Emoji>,
|
emojis: List<Emoji>,
|
||||||
mode: Int,
|
mode: Int,
|
||||||
resultClickListener: View.OnClickListener?) {
|
resultClickListener: View.OnClickListener?,
|
||||||
|
animateEmojis: Boolean) {
|
||||||
this.pollOptions = options
|
this.pollOptions = options
|
||||||
this.voteCount = voteCount
|
this.voteCount = voteCount
|
||||||
this.votersCount = votersCount
|
this.votersCount = votersCount
|
||||||
this.emojis = emojis
|
this.emojis = emojis
|
||||||
this.mode = mode
|
this.mode = mode
|
||||||
this.resultClickListener = resultClickListener
|
this.resultClickListener = resultClickListener
|
||||||
|
this.animateEmojis = animateEmojis
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +84,7 @@ class PollAdapter: RecyclerView.Adapter<PollViewHolder>() {
|
||||||
RESULT -> {
|
RESULT -> {
|
||||||
val percent = calculatePercent(option.votesCount, votersCount, voteCount)
|
val percent = calculatePercent(option.votesCount, votersCount, voteCount)
|
||||||
val emojifiedPollOptionText = buildDescription(option.title, percent, holder.resultTextView.context)
|
val emojifiedPollOptionText = buildDescription(option.title, percent, holder.resultTextView.context)
|
||||||
.emojify(emojis, holder.resultTextView)
|
.emojify(emojis, holder.resultTextView, animateEmojis)
|
||||||
holder.resultTextView.text = EmojiCompat.get().process(emojifiedPollOptionText)
|
holder.resultTextView.text = EmojiCompat.get().process(emojifiedPollOptionText)
|
||||||
|
|
||||||
val level = percent * 100
|
val level = percent * 100
|
||||||
|
@ -90,7 +93,7 @@ class PollAdapter: RecyclerView.Adapter<PollViewHolder>() {
|
||||||
holder.resultTextView.setOnClickListener(resultClickListener)
|
holder.resultTextView.setOnClickListener(resultClickListener)
|
||||||
}
|
}
|
||||||
SINGLE -> {
|
SINGLE -> {
|
||||||
val emojifiedPollOptionText = option.title.emojify(emojis, holder.radioButton)
|
val emojifiedPollOptionText = option.title.emojify(emojis, holder.radioButton, animateEmojis)
|
||||||
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 {
|
||||||
|
@ -101,7 +104,7 @@ class PollAdapter: RecyclerView.Adapter<PollViewHolder>() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MULTIPLE -> {
|
MULTIPLE -> {
|
||||||
val emojifiedPollOptionText = option.title.emojify(emojis, holder.checkBox)
|
val emojifiedPollOptionText = option.title.emojify(emojis, holder.checkBox, animateEmojis)
|
||||||
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 ->
|
||||||
|
|
|
@ -181,8 +181,10 @@ 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, StatusDisplayOptions statusDisplayOptions) {
|
||||||
CharSequence emojifiedName = CustomEmojiHelper.emojify(name, customEmojis, displayName);
|
CharSequence emojifiedName = CustomEmojiHelper.emojify(
|
||||||
|
name, customEmojis, displayName, statusDisplayOptions.animateEmojis()
|
||||||
|
);
|
||||||
displayName.setText(emojifiedName);
|
displayName.setText(emojifiedName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +208,9 @@ 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.emojify(spoilerText, emojis, contentWarningDescription);
|
CharSequence emojiSpoiler = CustomEmojiHelper.emojify(
|
||||||
|
spoilerText, emojis, contentWarningDescription, statusDisplayOptions.animateEmojis()
|
||||||
|
);
|
||||||
contentWarningDescription.setText(emojiSpoiler);
|
contentWarningDescription.setText(emojiSpoiler);
|
||||||
contentWarningDescription.setVisibility(View.VISIBLE);
|
contentWarningDescription.setVisibility(View.VISIBLE);
|
||||||
contentWarningButton.setVisibility(View.VISIBLE);
|
contentWarningButton.setVisibility(View.VISIBLE);
|
||||||
|
@ -245,7 +249,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
StatusDisplayOptions statusDisplayOptions,
|
StatusDisplayOptions statusDisplayOptions,
|
||||||
final StatusActionListener listener) {
|
final StatusActionListener listener) {
|
||||||
if (expanded) {
|
if (expanded) {
|
||||||
CharSequence emojifiedText = CustomEmojiHelper.emojify(content, emojis, this.content);
|
CharSequence emojifiedText = CustomEmojiHelper.emojify(content, emojis, this.content, statusDisplayOptions.animateEmojis());
|
||||||
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);
|
||||||
|
@ -709,7 +713,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
StatusDisplayOptions statusDisplayOptions,
|
StatusDisplayOptions statusDisplayOptions,
|
||||||
@Nullable Object payloads) {
|
@Nullable Object payloads) {
|
||||||
if (payloads == null) {
|
if (payloads == null) {
|
||||||
setDisplayName(status.getUserFullName(), status.getAccountEmojis());
|
setDisplayName(status.getUserFullName(), status.getAccountEmojis(), statusDisplayOptions);
|
||||||
setUsername(status.getNickname());
|
setUsername(status.getNickname());
|
||||||
setCreatedAt(status.getCreatedAt(), statusDisplayOptions);
|
setCreatedAt(status.getCreatedAt(), statusDisplayOptions);
|
||||||
setIsReply(status.getInReplyToId() != null);
|
setIsReply(status.getInReplyToId() != null);
|
||||||
|
@ -927,12 +931,28 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
listener.onViewThread(position);
|
listener.onViewThread(position);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
pollAdapter.setup(poll.getOptions(), poll.getVotesCount(), poll.getVotersCount(), emojis, PollAdapter.RESULT, viewThreadListener);
|
pollAdapter.setup(
|
||||||
|
poll.getOptions(),
|
||||||
|
poll.getVotesCount(),
|
||||||
|
poll.getVotersCount(),
|
||||||
|
emojis,
|
||||||
|
PollAdapter.RESULT,
|
||||||
|
viewThreadListener,
|
||||||
|
statusDisplayOptions.animateEmojis()
|
||||||
|
);
|
||||||
|
|
||||||
pollButton.setVisibility(View.GONE);
|
pollButton.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
// voting possible
|
// voting possible
|
||||||
pollAdapter.setup(poll.getOptions(), poll.getVotesCount(), poll.getVotersCount(), emojis, poll.getMultiple() ? PollAdapter.MULTIPLE : PollAdapter.SINGLE, null);
|
pollAdapter.setup(
|
||||||
|
poll.getOptions(),
|
||||||
|
poll.getVotesCount(),
|
||||||
|
poll.getVotersCount(),
|
||||||
|
emojis,
|
||||||
|
poll.getMultiple() ? PollAdapter.MULTIPLE : PollAdapter.SINGLE,
|
||||||
|
null,
|
||||||
|
statusDisplayOptions.animateEmojis()
|
||||||
|
);
|
||||||
|
|
||||||
pollButton.setVisibility(View.VISIBLE);
|
pollButton.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ public class StatusViewHolder extends StatusBaseViewHolder {
|
||||||
if (rebloggedByDisplayName == null) {
|
if (rebloggedByDisplayName == null) {
|
||||||
hideStatusInfo();
|
hideStatusInfo();
|
||||||
} else {
|
} else {
|
||||||
setRebloggedByDisplayName(rebloggedByDisplayName, status);
|
setRebloggedByDisplayName(rebloggedByDisplayName, status, statusDisplayOptions);
|
||||||
statusInfo.setOnClickListener(v -> listener.onOpenReblog(getAdapterPosition()));
|
statusInfo.setOnClickListener(v -> listener.onOpenReblog(getAdapterPosition()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,11 +75,15 @@ public class StatusViewHolder extends StatusBaseViewHolder {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setRebloggedByDisplayName(final CharSequence name, final StatusViewData.Concrete status) {
|
private void setRebloggedByDisplayName(final CharSequence name,
|
||||||
|
final StatusViewData.Concrete status,
|
||||||
|
final StatusDisplayOptions statusDisplayOptions) {
|
||||||
Context context = statusInfo.getContext();
|
Context context = statusInfo.getContext();
|
||||||
CharSequence wrappedName = StringUtils.unicodeWrap(name);
|
CharSequence wrappedName = StringUtils.unicodeWrap(name);
|
||||||
CharSequence boostedText = context.getString(R.string.status_boosted_format, wrappedName);
|
CharSequence boostedText = context.getString(R.string.status_boosted_format, wrappedName);
|
||||||
CharSequence emojifiedText = CustomEmojiHelper.emojify(boostedText, status.getRebloggedByAccountEmojis(), statusInfo);
|
CharSequence emojifiedText = CustomEmojiHelper.emojify(
|
||||||
|
boostedText, status.getRebloggedByAccountEmojis(), statusInfo, statusDisplayOptions.animateEmojis()
|
||||||
|
);
|
||||||
statusInfo.setText(emojifiedText);
|
statusInfo.setText(emojifiedText);
|
||||||
statusInfo.setVisibility(View.VISIBLE);
|
statusInfo.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,8 @@ public final class TimelineAdapter extends RecyclerView.Adapter {
|
||||||
statusDisplayOptions.useBlurhash(),
|
statusDisplayOptions.useBlurhash(),
|
||||||
statusDisplayOptions.cardViewMode(),
|
statusDisplayOptions.cardViewMode(),
|
||||||
statusDisplayOptions.confirmReblogs(),
|
statusDisplayOptions.confirmReblogs(),
|
||||||
statusDisplayOptions.hideStats()
|
statusDisplayOptions.hideStats(),
|
||||||
|
statusDisplayOptions.animateEmojis()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,8 @@ interface AnnouncementActionListener: LinkListener {
|
||||||
class AnnouncementAdapter(
|
class AnnouncementAdapter(
|
||||||
private var items: List<Announcement> = emptyList(),
|
private var items: List<Announcement> = emptyList(),
|
||||||
private val listener: AnnouncementActionListener,
|
private val listener: AnnouncementActionListener,
|
||||||
private val wellbeingEnabled: Boolean = false
|
private val wellbeingEnabled: Boolean = false,
|
||||||
|
private val animateEmojis: Boolean = false
|
||||||
) : RecyclerView.Adapter<AnnouncementAdapter.AnnouncementViewHolder>() {
|
) : RecyclerView.Adapter<AnnouncementAdapter.AnnouncementViewHolder>() {
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnnouncementViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnnouncementViewHolder {
|
||||||
|
@ -99,7 +100,8 @@ class AnnouncementAdapter(
|
||||||
reaction.staticUrl ?: "",
|
reaction.staticUrl ?: "",
|
||||||
null
|
null
|
||||||
)),
|
)),
|
||||||
this
|
this,
|
||||||
|
animateEmojis
|
||||||
)
|
)
|
||||||
|
|
||||||
isChecked = reaction.me
|
isChecked = reaction.me
|
||||||
|
|
|
@ -82,8 +82,9 @@ class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener,
|
||||||
|
|
||||||
val preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
val preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
val wellbeingEnabled = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false)
|
val wellbeingEnabled = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false)
|
||||||
|
val animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
||||||
|
|
||||||
adapter = AnnouncementAdapter(emptyList(), this, wellbeingEnabled)
|
adapter = AnnouncementAdapter(emptyList(), this, wellbeingEnabled, animateEmojis)
|
||||||
|
|
||||||
announcementsList.adapter = adapter
|
announcementsList.adapter = adapter
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,7 @@ import com.keylesspalace.tusky.entity.Attachment
|
||||||
import com.keylesspalace.tusky.entity.Emoji
|
import com.keylesspalace.tusky.entity.Emoji
|
||||||
import com.keylesspalace.tusky.entity.NewPoll
|
import com.keylesspalace.tusky.entity.NewPoll
|
||||||
import com.keylesspalace.tusky.entity.Status
|
import com.keylesspalace.tusky.entity.Status
|
||||||
|
import com.keylesspalace.tusky.settings.PrefKeys
|
||||||
import com.keylesspalace.tusky.util.*
|
import com.keylesspalace.tusky.util.*
|
||||||
import com.mikepenz.iconics.IconicsDrawable
|
import com.mikepenz.iconics.IconicsDrawable
|
||||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||||
|
@ -160,7 +161,7 @@ class ComposeActivity : BaseActivity(),
|
||||||
composeScheduleView.setDateTime(composeOptions?.scheduledAt)
|
composeScheduleView.setDateTime(composeOptions?.scheduledAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
setupComposeField(viewModel.startingText)
|
setupComposeField(preferences, viewModel.startingText)
|
||||||
setupContentWarningField(composeOptions?.contentWarning)
|
setupContentWarningField(composeOptions?.contentWarning)
|
||||||
setupPollView()
|
setupPollView()
|
||||||
applyShareIntent(intent, savedInstanceState)
|
applyShareIntent(intent, savedInstanceState)
|
||||||
|
@ -245,13 +246,18 @@ class ComposeActivity : BaseActivity(),
|
||||||
composeContentWarningField.onTextChanged { _, _, _, _ -> updateVisibleCharactersLeft() }
|
composeContentWarningField.onTextChanged { _, _, _, _ -> updateVisibleCharactersLeft() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupComposeField(startingText: String?) {
|
private fun setupComposeField(preferences: SharedPreferences, startingText: String?) {
|
||||||
composeEditField.setOnCommitContentListener(this)
|
composeEditField.setOnCommitContentListener(this)
|
||||||
|
|
||||||
composeEditField.setOnKeyListener { _, keyCode, event -> this.onKeyDown(keyCode, event) }
|
composeEditField.setOnKeyListener { _, keyCode, event -> this.onKeyDown(keyCode, event) }
|
||||||
|
|
||||||
composeEditField.setAdapter(
|
composeEditField.setAdapter(
|
||||||
ComposeAutoCompleteAdapter(this))
|
ComposeAutoCompleteAdapter(
|
||||||
|
this,
|
||||||
|
preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false),
|
||||||
|
preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
||||||
|
)
|
||||||
|
)
|
||||||
composeEditField.setTokenizer(ComposeTokenizer())
|
composeEditField.setTokenizer(ComposeTokenizer())
|
||||||
|
|
||||||
composeEditField.setText(startingText)
|
composeEditField.setText(startingText)
|
||||||
|
|
|
@ -53,11 +53,15 @@ public class ComposeAutoCompleteAdapter extends BaseAdapter
|
||||||
|
|
||||||
private final ArrayList<AutocompleteResult> resultList;
|
private final ArrayList<AutocompleteResult> resultList;
|
||||||
private final AutocompletionProvider autocompletionProvider;
|
private final AutocompletionProvider autocompletionProvider;
|
||||||
|
private final boolean animateAvatar;
|
||||||
|
private final boolean animateEmojis;
|
||||||
|
|
||||||
public ComposeAutoCompleteAdapter(AutocompletionProvider autocompletionProvider) {
|
public ComposeAutoCompleteAdapter(AutocompletionProvider autocompletionProvider, boolean animateAvatar, boolean animateEmojis) {
|
||||||
super();
|
super();
|
||||||
resultList = new ArrayList<>();
|
resultList = new ArrayList<>();
|
||||||
this.autocompletionProvider = autocompletionProvider;
|
this.autocompletionProvider = autocompletionProvider;
|
||||||
|
this.animateAvatar = animateAvatar;
|
||||||
|
this.animateEmojis = animateEmojis;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -147,15 +151,12 @@ public class ComposeAutoCompleteAdapter extends BaseAdapter
|
||||||
);
|
);
|
||||||
accountViewHolder.username.setText(formattedUsername);
|
accountViewHolder.username.setText(formattedUsername);
|
||||||
CharSequence emojifiedName = CustomEmojiHelper.emojify(account.getName(),
|
CharSequence emojifiedName = CustomEmojiHelper.emojify(account.getName(),
|
||||||
account.getEmojis(), accountViewHolder.displayName);
|
account.getEmojis(), accountViewHolder.displayName, animateEmojis);
|
||||||
accountViewHolder.displayName.setText(emojifiedName);
|
accountViewHolder.displayName.setText(emojifiedName);
|
||||||
|
|
||||||
int avatarRadius = accountViewHolder.avatar.getContext().getResources()
|
int avatarRadius = accountViewHolder.avatar.getContext().getResources()
|
||||||
.getDimensionPixelSize(R.dimen.avatar_radius_42dp);
|
.getDimensionPixelSize(R.dimen.avatar_radius_42dp);
|
||||||
|
|
||||||
boolean animateAvatar = PreferenceManager.getDefaultSharedPreferences(accountViewHolder.avatar.getContext())
|
|
||||||
.getBoolean("animateGifAvatars", false);
|
|
||||||
|
|
||||||
ImageLoadingHelper.loadAvatar(
|
ImageLoadingHelper.loadAvatar(
|
||||||
account.getAvatar(),
|
account.getAvatar(),
|
||||||
accountViewHolder.avatar,
|
accountViewHolder.avatar,
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
|
||||||
|
|
||||||
setupCollapsedState(status.getCollapsible(), status.getCollapsed(), status.getExpanded(), status.getSpoilerText(), listener);
|
setupCollapsedState(status.getCollapsible(), status.getCollapsed(), status.getExpanded(), status.getSpoilerText(), listener);
|
||||||
|
|
||||||
setDisplayName(account.getDisplayName(), account.getEmojis());
|
setDisplayName(account.getDisplayName(), account.getEmojis(), statusDisplayOptions);
|
||||||
setUsername(account.getUsername());
|
setUsername(account.getUsername());
|
||||||
setCreatedAt(status.getCreatedAt(), statusDisplayOptions);
|
setCreatedAt(status.getCreatedAt(), statusDisplayOptions);
|
||||||
setIsReply(status.getInReplyToId() != null);
|
setIsReply(status.getInReplyToId() != null);
|
||||||
|
|
|
@ -67,8 +67,8 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
|
||||||
useBlurhash = preferences.getBoolean("useBlurhash", true),
|
useBlurhash = preferences.getBoolean("useBlurhash", true),
|
||||||
cardViewMode = CardViewMode.NONE,
|
cardViewMode = CardViewMode.NONE,
|
||||||
confirmReblogs = preferences.getBoolean("confirmReblogs", true),
|
confirmReblogs = preferences.getBoolean("confirmReblogs", true),
|
||||||
hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false)
|
hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false),
|
||||||
|
animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
||||||
)
|
)
|
||||||
|
|
||||||
adapter = ConversationAdapter(statusDisplayOptions, this, ::onTopLoaded, viewModel::retry)
|
adapter = ConversationAdapter(statusDisplayOptions, this, ::onTopLoaded, viewModel::retry)
|
||||||
|
|
|
@ -174,6 +174,13 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
||||||
setTitle(R.string.pref_title_enable_swipe_for_tabs)
|
setTitle(R.string.pref_title_enable_swipe_for_tabs)
|
||||||
isSingleLineTitle = false
|
isSingleLineTitle = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switchPreference {
|
||||||
|
setDefaultValue(false)
|
||||||
|
key = PrefKeys.ANIMATE_CUSTOM_EMOJIS
|
||||||
|
setTitle(R.string.pref_title_animate_custom_emojis)
|
||||||
|
isSingleLineTitle = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
preferenceCategory(R.string.pref_title_browser_settings) {
|
preferenceCategory(R.string.pref_title_browser_settings) {
|
||||||
|
|
|
@ -75,7 +75,7 @@ class StatusViewHolder(
|
||||||
sensitive, previewListener, viewState.isMediaShow(status.id, status.sensitive),
|
sensitive, previewListener, viewState.isMediaShow(status.id, status.sensitive),
|
||||||
mediaViewHeight)
|
mediaViewHeight)
|
||||||
|
|
||||||
statusViewHelper.setupPollReadonly(status.poll.toViewData(), status.emojis, statusDisplayOptions.useAbsoluteTime)
|
statusViewHelper.setupPollReadonly(status.poll.toViewData(), status.emojis, statusDisplayOptions)
|
||||||
setCreatedAt(status.createdAt)
|
setCreatedAt(status.createdAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ class StatusViewHolder(
|
||||||
itemView.statusContentWarningButton.hide()
|
itemView.statusContentWarningButton.hide()
|
||||||
itemView.statusContentWarningDescription.hide()
|
itemView.statusContentWarningDescription.hide()
|
||||||
} else {
|
} else {
|
||||||
val emojiSpoiler = status.spoilerText.emojify(status.emojis, itemView.statusContentWarningDescription)
|
val emojiSpoiler = status.spoilerText.emojify(status.emojis, itemView.statusContentWarningDescription, statusDisplayOptions.animateEmojis)
|
||||||
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 = content.emojify(emojis, itemView.statusContent)
|
val emojifiedText = content.emojify(emojis, itemView.statusContent, statusDisplayOptions.animateEmojis)
|
||||||
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)
|
||||||
|
|
|
@ -111,7 +111,8 @@ class ReportStatusesFragment : Fragment(R.layout.fragment_report_statuses), Inje
|
||||||
useBlurhash = preferences.getBoolean("useBlurhash", true),
|
useBlurhash = preferences.getBoolean("useBlurhash", true),
|
||||||
cardViewMode = CardViewMode.NONE,
|
cardViewMode = CardViewMode.NONE,
|
||||||
confirmReblogs = preferences.getBoolean("confirmReblogs", true),
|
confirmReblogs = preferences.getBoolean("confirmReblogs", true),
|
||||||
hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false)
|
hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false),
|
||||||
|
animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
||||||
)
|
)
|
||||||
|
|
||||||
adapter = StatusesAdapter(statusDisplayOptions,
|
adapter = StatusesAdapter(statusDisplayOptions,
|
||||||
|
|
|
@ -25,7 +25,7 @@ import com.keylesspalace.tusky.adapter.AccountViewHolder
|
||||||
import com.keylesspalace.tusky.entity.Account
|
import com.keylesspalace.tusky.entity.Account
|
||||||
import com.keylesspalace.tusky.interfaces.LinkListener
|
import com.keylesspalace.tusky.interfaces.LinkListener
|
||||||
|
|
||||||
class SearchAccountsAdapter(private val linkListener: LinkListener)
|
class SearchAccountsAdapter(private val linkListener: LinkListener, private val animateAvatars: Boolean, private val animateEmojis: Boolean)
|
||||||
: PagedListAdapter<Account, RecyclerView.ViewHolder>(ACCOUNT_COMPARATOR) {
|
: PagedListAdapter<Account, RecyclerView.ViewHolder>(ACCOUNT_COMPARATOR) {
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
@ -37,7 +37,7 @@ class SearchAccountsAdapter(private val linkListener: LinkListener)
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
getItem(position)?.let { item ->
|
getItem(position)?.let { item ->
|
||||||
(holder as AccountViewHolder).apply {
|
(holder as AccountViewHolder).apply {
|
||||||
setupWithAccount(item)
|
setupWithAccount(item, animateAvatars, animateEmojis)
|
||||||
setupLinkListener(linkListener)
|
setupLinkListener(linkListener)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,23 @@ package com.keylesspalace.tusky.components.search.fragments
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.paging.PagedList
|
import androidx.paging.PagedList
|
||||||
import androidx.paging.PagedListAdapter
|
import androidx.paging.PagedListAdapter
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
import com.keylesspalace.tusky.components.search.adapter.SearchAccountsAdapter
|
import com.keylesspalace.tusky.components.search.adapter.SearchAccountsAdapter
|
||||||
import com.keylesspalace.tusky.entity.Account
|
import com.keylesspalace.tusky.entity.Account
|
||||||
|
import com.keylesspalace.tusky.settings.PrefKeys
|
||||||
import com.keylesspalace.tusky.util.NetworkState
|
import com.keylesspalace.tusky.util.NetworkState
|
||||||
|
import kotlinx.android.synthetic.main.fragment_search.*
|
||||||
|
|
||||||
class SearchAccountsFragment : SearchFragment<Account>() {
|
class SearchAccountsFragment : SearchFragment<Account>() {
|
||||||
override fun createAdapter(): PagedListAdapter<Account, *> = SearchAccountsAdapter(this)
|
override fun createAdapter(): PagedListAdapter<Account, *> {
|
||||||
|
val preferences = PreferenceManager.getDefaultSharedPreferences(searchRecyclerView.context)
|
||||||
|
|
||||||
|
return SearchAccountsAdapter(
|
||||||
|
this,
|
||||||
|
preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false),
|
||||||
|
preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override val networkStateRefresh: LiveData<NetworkState>
|
override val networkStateRefresh: LiveData<NetworkState>
|
||||||
get() = viewModel.networkStateAccountRefresh
|
get() = viewModel.networkStateAccountRefresh
|
||||||
|
|
|
@ -87,7 +87,8 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||||
useBlurhash = preferences.getBoolean("useBlurhash", true),
|
useBlurhash = preferences.getBoolean("useBlurhash", true),
|
||||||
cardViewMode = CardViewMode.NONE,
|
cardViewMode = CardViewMode.NONE,
|
||||||
confirmReblogs = preferences.getBoolean("confirmReblogs", true),
|
confirmReblogs = preferences.getBoolean("confirmReblogs", true),
|
||||||
hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false)
|
hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false),
|
||||||
|
animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
||||||
)
|
)
|
||||||
|
|
||||||
searchRecyclerView.addItemDecoration(DividerItemDecoration(searchRecyclerView.context, DividerItemDecoration.VERTICAL))
|
searchRecyclerView.addItemDecoration(DividerItemDecoration(searchRecyclerView.context, DividerItemDecoration.VERTICAL))
|
||||||
|
|
|
@ -20,6 +20,7 @@ import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
@ -35,6 +36,7 @@ import com.keylesspalace.tusky.entity.Account
|
||||||
import com.keylesspalace.tusky.entity.Relationship
|
import com.keylesspalace.tusky.entity.Relationship
|
||||||
import com.keylesspalace.tusky.interfaces.AccountActionListener
|
import com.keylesspalace.tusky.interfaces.AccountActionListener
|
||||||
import com.keylesspalace.tusky.network.MastodonApi
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
|
import com.keylesspalace.tusky.settings.PrefKeys
|
||||||
import com.keylesspalace.tusky.util.HttpHeaderLink
|
import com.keylesspalace.tusky.util.HttpHeaderLink
|
||||||
import com.keylesspalace.tusky.util.hide
|
import com.keylesspalace.tusky.util.hide
|
||||||
import com.keylesspalace.tusky.util.show
|
import com.keylesspalace.tusky.util.show
|
||||||
|
@ -78,11 +80,15 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct
|
||||||
|
|
||||||
recyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL))
|
recyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL))
|
||||||
|
|
||||||
|
val pm = PreferenceManager.getDefaultSharedPreferences(view.context)
|
||||||
|
val animateAvatar = pm.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false)
|
||||||
|
val animateEmojis = pm.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
||||||
|
|
||||||
adapter = when (type) {
|
adapter = when (type) {
|
||||||
Type.BLOCKS -> BlocksAdapter(this)
|
Type.BLOCKS -> BlocksAdapter(this, animateAvatar, animateEmojis)
|
||||||
Type.MUTES -> MutesAdapter(this)
|
Type.MUTES -> MutesAdapter(this, animateAvatar, animateEmojis)
|
||||||
Type.FOLLOW_REQUESTS -> FollowRequestsAdapter(this)
|
Type.FOLLOW_REQUESTS -> FollowRequestsAdapter(this, animateAvatar, animateEmojis)
|
||||||
else -> FollowAdapter(this)
|
else -> FollowAdapter(this, animateAvatar, animateEmojis)
|
||||||
}
|
}
|
||||||
recyclerView.adapter = adapter
|
recyclerView.adapter = adapter
|
||||||
|
|
||||||
|
|
|
@ -252,7 +252,8 @@ public class NotificationsFragment extends SFragment implements
|
||||||
preferences.getBoolean("useBlurhash", true),
|
preferences.getBoolean("useBlurhash", true),
|
||||||
CardViewMode.NONE,
|
CardViewMode.NONE,
|
||||||
preferences.getBoolean("confirmReblogs", true),
|
preferences.getBoolean("confirmReblogs", true),
|
||||||
preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false)
|
preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false),
|
||||||
|
preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
||||||
);
|
);
|
||||||
|
|
||||||
adapter = new NotificationsAdapter(accountManager.getActiveAccount().getAccountId(),
|
adapter = new NotificationsAdapter(accountManager.getActiveAccount().getAccountId(),
|
||||||
|
|
|
@ -252,7 +252,8 @@ public class TimelineFragment extends SFragment implements
|
||||||
CardViewMode.INDENTED :
|
CardViewMode.INDENTED :
|
||||||
CardViewMode.NONE,
|
CardViewMode.NONE,
|
||||||
preferences.getBoolean("confirmReblogs", true),
|
preferences.getBoolean("confirmReblogs", true),
|
||||||
preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false)
|
preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false),
|
||||||
|
preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
||||||
);
|
);
|
||||||
adapter = new TimelineAdapter(dataSource, statusDisplayOptions, this);
|
adapter = new TimelineAdapter(dataSource, statusDisplayOptions, this);
|
||||||
|
|
||||||
|
|
|
@ -134,7 +134,8 @@ public final class ViewThreadFragment extends SFragment implements
|
||||||
CardViewMode.INDENTED :
|
CardViewMode.INDENTED :
|
||||||
CardViewMode.NONE,
|
CardViewMode.NONE,
|
||||||
preferences.getBoolean("confirmReblogs", true),
|
preferences.getBoolean("confirmReblogs", true),
|
||||||
preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false)
|
preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false),
|
||||||
|
preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
|
||||||
);
|
);
|
||||||
adapter = new ThreadAdapter(statusDisplayOptions, this);
|
adapter = new ThreadAdapter(statusDisplayOptions, this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ object PrefKeys {
|
||||||
const val SHOW_CARDS_IN_TIMELINES = "showCardsInTimelines"
|
const val SHOW_CARDS_IN_TIMELINES = "showCardsInTimelines"
|
||||||
const val CONFIRM_REBLOGS = "confirmReblogs"
|
const val CONFIRM_REBLOGS = "confirmReblogs"
|
||||||
const val ENABLE_SWIPE_FOR_TABS = "enableSwipeForTabs"
|
const val ENABLE_SWIPE_FOR_TABS = "enableSwipeForTabs"
|
||||||
|
const val ANIMATE_CUSTOM_EMOJIS = "animateCustomEmojis"
|
||||||
|
|
||||||
const val CUSTOM_TABS = "customTabs"
|
const val CUSTOM_TABS = "customTabs"
|
||||||
const val WELLBEING_LIMITED_NOTIFICATIONS = "wellbeingModeLimitedNotifications"
|
const val WELLBEING_LIMITED_NOTIFICATIONS = "wellbeingModeLimitedNotifications"
|
||||||
|
|
|
@ -16,11 +16,9 @@
|
||||||
@file:JvmName("CustomEmojiHelper")
|
@file:JvmName("CustomEmojiHelper")
|
||||||
package com.keylesspalace.tusky.util
|
package com.keylesspalace.tusky.util
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.Paint
|
import android.graphics.Paint
|
||||||
import android.graphics.drawable.BitmapDrawable
|
import android.graphics.drawable.*
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
import android.text.style.ReplacementSpan
|
import android.text.style.ReplacementSpan
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -33,6 +31,8 @@ import com.keylesspalace.tusky.entity.Emoji
|
||||||
|
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.keylesspalace.tusky.settings.PrefKeys
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* replaces emoji shortcodes in a text with EmojiSpans
|
* replaces emoji shortcodes in a text with EmojiSpans
|
||||||
|
@ -41,7 +41,7 @@ import java.util.regex.Pattern
|
||||||
* @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)
|
* @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
|
* @return the text with the shortcodes replaced by EmojiSpans
|
||||||
*/
|
*/
|
||||||
fun CharSequence.emojify(emojis: List<Emoji>?, view: View) : CharSequence {
|
fun CharSequence.emojify(emojis: List<Emoji>?, view: View, animate: Boolean) : CharSequence {
|
||||||
if(emojis.isNullOrEmpty())
|
if(emojis.isNullOrEmpty())
|
||||||
return this
|
return this
|
||||||
|
|
||||||
|
@ -56,9 +56,9 @@ fun CharSequence.emojify(emojis: List<Emoji>?, view: View) : CharSequence {
|
||||||
|
|
||||||
builder.setSpan(span, matcher.start(), matcher.end(), 0)
|
builder.setSpan(span, matcher.start(), matcher.end(), 0)
|
||||||
Glide.with(view)
|
Glide.with(view)
|
||||||
.asBitmap()
|
.asDrawable()
|
||||||
.load(url)
|
.load(url)
|
||||||
.into(span.getTarget())
|
.into(span.getTarget(animate))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return builder
|
return builder
|
||||||
|
@ -97,11 +97,29 @@ class EmojiSpan(val viewWeakReference: WeakReference<View>) : ReplacementSpan()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTarget(): Target<Bitmap> {
|
fun getTarget(animate : Boolean): Target<Drawable> {
|
||||||
return object : CustomTarget<Bitmap>() {
|
return object : CustomTarget<Drawable>() {
|
||||||
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
|
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
|
||||||
viewWeakReference.get()?.let { view ->
|
viewWeakReference.get()?.let { view ->
|
||||||
imageDrawable = BitmapDrawable(view.context.resources, resource)
|
if(animate && resource is Animatable) {
|
||||||
|
val callback = resource.callback
|
||||||
|
|
||||||
|
resource.callback = object: Drawable.Callback {
|
||||||
|
override fun unscheduleDrawable(p0: Drawable, p1: Runnable) {
|
||||||
|
callback?.unscheduleDrawable(p0, p1)
|
||||||
|
}
|
||||||
|
override fun scheduleDrawable(p0: Drawable, p1: Runnable, p2: Long) {
|
||||||
|
callback?.scheduleDrawable(p0, p1, p2)
|
||||||
|
}
|
||||||
|
override fun invalidateDrawable(p0: Drawable) {
|
||||||
|
callback?.invalidateDrawable(p0)
|
||||||
|
view.invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resource.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
imageDrawable = resource
|
||||||
view.invalidate()
|
view.invalidate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,5 +16,7 @@ data class StatusDisplayOptions(
|
||||||
@get:JvmName("confirmReblogs")
|
@get:JvmName("confirmReblogs")
|
||||||
val confirmReblogs: Boolean,
|
val confirmReblogs: Boolean,
|
||||||
@get:JvmName("hideStats")
|
@get:JvmName("hideStats")
|
||||||
val hideStats: Boolean
|
val hideStats: Boolean,
|
||||||
|
@get:JvmName("animateEmojis")
|
||||||
|
val animateEmojis: Boolean
|
||||||
)
|
)
|
|
@ -243,7 +243,7 @@ class StatusViewHelper(private val itemView: View) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setupPollReadonly(poll: PollViewData?, emojis: List<Emoji>, useAbsoluteTime: Boolean) {
|
fun setupPollReadonly(poll: PollViewData?, emojis: List<Emoji>, statusDisplayOptions: StatusDisplayOptions) {
|
||||||
val pollResults = listOf<TextView>(
|
val pollResults = listOf<TextView>(
|
||||||
itemView.findViewById(R.id.status_poll_option_result_0),
|
itemView.findViewById(R.id.status_poll_option_result_0),
|
||||||
itemView.findViewById(R.id.status_poll_option_result_1),
|
itemView.findViewById(R.id.status_poll_option_result_1),
|
||||||
|
@ -261,10 +261,10 @@ class StatusViewHelper(private val itemView: View) {
|
||||||
val timestamp = System.currentTimeMillis()
|
val timestamp = System.currentTimeMillis()
|
||||||
|
|
||||||
|
|
||||||
setupPollResult(poll, emojis, pollResults)
|
setupPollResult(poll, emojis, pollResults, statusDisplayOptions.animateEmojis)
|
||||||
|
|
||||||
pollDescription.visibility = View.VISIBLE
|
pollDescription.visibility = View.VISIBLE
|
||||||
pollDescription.text = getPollInfoText(timestamp, poll, pollDescription, useAbsoluteTime)
|
pollDescription.text = getPollInfoText(timestamp, poll, pollDescription, statusDisplayOptions.useAbsoluteTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,7 +292,7 @@ class StatusViewHelper(private val itemView: View) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun setupPollResult(poll: PollViewData, emojis: List<Emoji>, pollResults: List<TextView>) {
|
private fun setupPollResult(poll: PollViewData, emojis: List<Emoji>, pollResults: List<TextView>, animateEmojis: Boolean) {
|
||||||
val options = poll.options
|
val options = poll.options
|
||||||
|
|
||||||
for (i in 0 until Status.MAX_POLL_OPTIONS) {
|
for (i in 0 until Status.MAX_POLL_OPTIONS) {
|
||||||
|
@ -300,7 +300,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 = pollOptionText.emojify(emojis, pollResults[i])
|
pollResults[i].text = pollOptionText.emojify(emojis, pollResults[i], animateEmojis)
|
||||||
pollResults[i].visibility = View.VISIBLE
|
pollResults[i].visibility = View.VISIBLE
|
||||||
|
|
||||||
val level = percent * 100
|
val level = percent * 100
|
||||||
|
|
|
@ -243,6 +243,7 @@
|
||||||
<string name="pref_title_bot_overlay">Show indicator for bots</string>
|
<string name="pref_title_bot_overlay">Show indicator for bots</string>
|
||||||
<string name="pref_title_animate_gif_avatars">Animate GIF avatars</string>
|
<string name="pref_title_animate_gif_avatars">Animate GIF avatars</string>
|
||||||
<string name="pref_title_gradient_for_media">Show colorful gradients for hidden media</string>
|
<string name="pref_title_gradient_for_media">Show colorful gradients for hidden media</string>
|
||||||
|
<string name="pref_title_animate_custom_emojis">Animate custom emojis</string>
|
||||||
|
|
||||||
<string name="pref_title_status_filter">Timeline filtering</string>
|
<string name="pref_title_status_filter">Timeline filtering</string>
|
||||||
<string name="pref_title_status_tabs">Tabs</string>
|
<string name="pref_title_status_tabs">Tabs</string>
|
||||||
|
|
Loading…
Reference in a new issue