Animate gif avatars (#1279)
* animate gif avatars * add setting to enable avatar animation * cleanup code
This commit is contained in:
parent
df401e90b0
commit
fb45e0e2bb
@ -78,6 +78,8 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||
private var showingReblogs: Boolean = false
|
||||
private var loadedAccount: Account? = null
|
||||
|
||||
private var animateAvatar: Boolean = false
|
||||
|
||||
// fields for scroll animation
|
||||
private var hideFab: Boolean = false
|
||||
private var oldOffset: Int = 0
|
||||
@ -120,7 +122,9 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||
updateButtons()
|
||||
}
|
||||
|
||||
hideFab = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("fabHide", false)
|
||||
val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
animateAvatar = sharedPrefs.getBoolean("animateGifAvatars", false)
|
||||
hideFab = sharedPrefs.getBoolean("fabHide", false)
|
||||
|
||||
loadResources()
|
||||
setupToolbar()
|
||||
@ -379,11 +383,16 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||
*/
|
||||
private fun updateAccountAvatar() {
|
||||
loadedAccount?.let { account ->
|
||||
|
||||
loadAvatar(
|
||||
account.avatar,
|
||||
accountAvatarImageView,
|
||||
resources.getDimensionPixelSize(R.dimen.avatar_radius_94dp),
|
||||
animateAvatar
|
||||
)
|
||||
|
||||
Glide.with(this)
|
||||
.load(account.avatar)
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(accountAvatarImageView)
|
||||
Glide.with(this)
|
||||
.asBitmap()
|
||||
.load(account.header)
|
||||
.centerCrop()
|
||||
.into(accountHeaderImageView)
|
||||
@ -430,10 +439,9 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||
accountMovedDisplayName.text = movedAccount.name
|
||||
accountMovedUsername.text = getString(R.string.status_username_format, movedAccount.username)
|
||||
|
||||
Glide.with(this)
|
||||
.load(movedAccount.avatar)
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(accountMovedAvatar)
|
||||
val avatarRadius = resources.getDimensionPixelSize(R.dimen.avatar_radius_48dp)
|
||||
|
||||
loadAvatar(movedAccount.avatar, accountMovedAvatar, avatarRadius, animateAvatar)
|
||||
|
||||
accountMovedText.text = getString(R.string.account_moved_description, movedAccount.displayName)
|
||||
|
||||
|
@ -26,6 +26,7 @@ import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
@ -60,25 +61,6 @@ import android.widget.PopupMenu;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.Px;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.core.view.inputmethod.InputConnectionCompat;
|
||||
import androidx.core.view.inputmethod.InputContentInfoCompat;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.transition.TransitionManager;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
@ -103,6 +85,7 @@ import com.keylesspalace.tusky.service.SendTootService;
|
||||
import com.keylesspalace.tusky.util.ComposeTokenizer;
|
||||
import com.keylesspalace.tusky.util.CountUpDownLatch;
|
||||
import com.keylesspalace.tusky.util.DownsizeImageTask;
|
||||
import com.keylesspalace.tusky.util.ImageLoadingHelper;
|
||||
import com.keylesspalace.tusky.util.ListUtils;
|
||||
import com.keylesspalace.tusky.util.SaveTootHelper;
|
||||
import com.keylesspalace.tusky.util.SpanUtilsKt;
|
||||
@ -303,14 +286,20 @@ public final class ComposeActivity
|
||||
if (activeAccount != null) {
|
||||
ImageView composeAvatar = findViewById(R.id.composeAvatar);
|
||||
|
||||
if (TextUtils.isEmpty(activeAccount.getProfilePictureUrl())) {
|
||||
composeAvatar.setImageResource(R.drawable.avatar_default);
|
||||
} else {
|
||||
Glide.with(this).load(activeAccount.getProfilePictureUrl())
|
||||
.error(R.drawable.avatar_default)
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(composeAvatar);
|
||||
}
|
||||
|
||||
int[] actionBarSizeAttr = new int[] { R.attr.actionBarSize };
|
||||
TypedArray a = obtainStyledAttributes(null, actionBarSizeAttr);
|
||||
int avatarSize = a.getDimensionPixelSize(0, 1);
|
||||
a.recycle();
|
||||
|
||||
boolean animateAvatars = preferences.getBoolean("animateGifAvatars", false);
|
||||
|
||||
ImageLoadingHelper.loadAvatar(
|
||||
activeAccount.getProfilePictureUrl(),
|
||||
composeAvatar,
|
||||
avatarSize / 8,
|
||||
animateAvatars
|
||||
);
|
||||
|
||||
composeAvatar.setContentDescription(
|
||||
getString(R.string.compose_active_account_description,
|
||||
|
@ -35,6 +35,8 @@ import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.resource.bitmap.FitCenter
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
import com.keylesspalace.tusky.adapter.AccountFieldEditAdapter
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
@ -136,6 +138,10 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
||||
Glide.with(this)
|
||||
.load(me.avatar)
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.transform(
|
||||
FitCenter(),
|
||||
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_80dp))
|
||||
)
|
||||
.into(avatarPreview)
|
||||
}
|
||||
|
||||
@ -158,8 +164,8 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
||||
}
|
||||
})
|
||||
|
||||
observeImage(viewModel.avatarData, avatarPreview, avatarProgressBar)
|
||||
observeImage(viewModel.headerData, headerPreview, headerProgressBar)
|
||||
observeImage(viewModel.avatarData, avatarPreview, avatarProgressBar, true)
|
||||
observeImage(viewModel.headerData, headerPreview, headerProgressBar, false)
|
||||
|
||||
viewModel.saveData.observe(this, Observer<Resource<Nothing>> {
|
||||
when(it) {
|
||||
@ -192,12 +198,26 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeImage(liveData: LiveData<Resource<Bitmap>>, imageView: ImageView, progressBar: View) {
|
||||
private fun observeImage(liveData: LiveData<Resource<Bitmap>>,
|
||||
imageView: ImageView,
|
||||
progressBar: View,
|
||||
roundedCorners: Boolean) {
|
||||
liveData.observe(this, Observer<Resource<Bitmap>> {
|
||||
|
||||
when (it) {
|
||||
is Success -> {
|
||||
imageView.setImageBitmap(it.data)
|
||||
val glide = Glide.with(imageView)
|
||||
.load(it.data)
|
||||
|
||||
if (roundedCorners) {
|
||||
glide.transform(
|
||||
FitCenter(),
|
||||
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_80dp))
|
||||
)
|
||||
}
|
||||
|
||||
glide.into(imageView)
|
||||
|
||||
imageView.show()
|
||||
progressBar.hide()
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import androidx.viewpager.widget.ViewPager;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.widget.ImageButton;
|
||||
@ -78,9 +79,6 @@ import dagger.android.AndroidInjector;
|
||||
import dagger.android.DispatchingAndroidInjector;
|
||||
import dagger.android.support.HasSupportFragmentInjector;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
import static com.keylesspalace.tusky.util.MediaUtilsKt.deleteStaleCachedMedia;
|
||||
import static com.uber.autodispose.AutoDispose.autoDisposable;
|
||||
@ -330,14 +328,25 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
||||
background.setColorFilter(ContextCompat.getColor(this, R.color.header_background_filter));
|
||||
background.setBackgroundColor(ContextCompat.getColor(this, R.color.window_background_dark));
|
||||
|
||||
final boolean animateAvatars = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getBoolean("animateGifAvatars", false);
|
||||
|
||||
DrawerImageLoader.init(new AbstractDrawerImageLoader() {
|
||||
@Override
|
||||
public void set(ImageView imageView, Uri uri, Drawable placeholder, String tag) {
|
||||
Glide.with(MainActivity.this)
|
||||
.asBitmap()
|
||||
.load(uri)
|
||||
.placeholder(placeholder)
|
||||
.into(imageView);
|
||||
if(animateAvatars) {
|
||||
Glide.with(MainActivity.this)
|
||||
.load(uri)
|
||||
.placeholder(placeholder)
|
||||
.into(imageView);
|
||||
} else {
|
||||
Glide.with(MainActivity.this)
|
||||
.asBitmap()
|
||||
.load(uri)
|
||||
.placeholder(placeholder)
|
||||
.into(imageView);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -137,13 +137,7 @@ class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreference
|
||||
}
|
||||
//workaround end
|
||||
}
|
||||
"statusTextSize" -> {
|
||||
restartActivitiesOnExit = true
|
||||
}
|
||||
"absoluteTimeView" -> {
|
||||
restartActivitiesOnExit = true
|
||||
}
|
||||
"showBotOverlay" -> {
|
||||
"statusTextSize", "absoluteTimeView", "showBotOverlay", "animateGifAvatars" -> {
|
||||
restartActivitiesOnExit = true
|
||||
}
|
||||
"language" -> {
|
||||
|
@ -16,19 +16,19 @@
|
||||
package com.keylesspalace.tusky.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.text.TextUtils
|
||||
import android.preference.PreferenceManager
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import com.bumptech.glide.Glide
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.db.AccountEntity
|
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper
|
||||
import com.keylesspalace.tusky.util.loadAvatar
|
||||
|
||||
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) {
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
var view = convertView
|
||||
|
||||
@ -45,13 +45,13 @@ class AccountSelectionAdapter(context: Context): ArrayAdapter<AccountEntity>(con
|
||||
val avatar = view.avatar
|
||||
username.text = account.fullName
|
||||
displayName.text = CustomEmojiHelper.emojifyString(account.displayName, account.emojis, displayName)
|
||||
if (!TextUtils.isEmpty(account.profilePictureUrl)) {
|
||||
Glide.with(avatar)
|
||||
.asBitmap()
|
||||
.load(account.profilePictureUrl)
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(avatar)
|
||||
}
|
||||
|
||||
val avatarRadius = avatar.context.resources.getDimensionPixelSize(R.dimen.avatar_radius_42dp)
|
||||
val animateAvatar = PreferenceManager.getDefaultSharedPreferences(avatar.context)
|
||||
.getBoolean("animateGifAvatars", false)
|
||||
|
||||
loadAvatar(account.profilePictureUrl, avatar, avatarRadius, animateAvatar)
|
||||
|
||||
}
|
||||
|
||||
return view
|
||||
|
@ -2,17 +2,18 @@ package com.keylesspalace.tusky.adapter;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.entity.Account;
|
||||
import com.keylesspalace.tusky.interfaces.AccountActionListener;
|
||||
import com.keylesspalace.tusky.interfaces.LinkListener;
|
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||
import com.keylesspalace.tusky.util.ImageLoadingHelper;
|
||||
|
||||
class AccountViewHolder extends RecyclerView.ViewHolder {
|
||||
private TextView username;
|
||||
@ -21,6 +22,7 @@ class AccountViewHolder extends RecyclerView.ViewHolder {
|
||||
private ImageView avatarInset;
|
||||
private String accountId;
|
||||
private boolean showBotOverlay;
|
||||
private boolean animateAvatar;
|
||||
|
||||
AccountViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
@ -28,7 +30,9 @@ class AccountViewHolder extends RecyclerView.ViewHolder {
|
||||
displayName = itemView.findViewById(R.id.account_display_name);
|
||||
avatar = itemView.findViewById(R.id.account_avatar);
|
||||
avatarInset = itemView.findViewById(R.id.account_avatar_inset);
|
||||
showBotOverlay = PreferenceManager.getDefaultSharedPreferences(itemView.getContext()).getBoolean("showBotOverlay", true);
|
||||
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(itemView.getContext());
|
||||
showBotOverlay = sharedPrefs.getBoolean("showBotOverlay", true);
|
||||
animateAvatar = sharedPrefs.getBoolean("animateGifAvatars", false);
|
||||
}
|
||||
|
||||
void setupWithAccount(Account account) {
|
||||
@ -38,11 +42,9 @@ class AccountViewHolder extends RecyclerView.ViewHolder {
|
||||
username.setText(formattedUsername);
|
||||
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(account.getName(), account.getEmojis(), displayName);
|
||||
displayName.setText(emojifiedName);
|
||||
Glide.with(avatar)
|
||||
.asBitmap()
|
||||
.load(account.getAvatar())
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(avatar);
|
||||
int avatarRadius = avatar.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.avatar_radius_48dp);
|
||||
ImageLoadingHelper.loadAvatar(account.getAvatar(), avatar, avatarRadius, animateAvatar);
|
||||
if (showBotOverlay && account.getBot()) {
|
||||
avatarInset.setVisibility(View.VISIBLE);
|
||||
avatarInset.setImageResource(R.drawable.ic_bot_24dp);
|
||||
|
@ -17,6 +17,8 @@ package com.keylesspalace.tusky.adapter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -24,11 +26,11 @@ import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.entity.Account;
|
||||
import com.keylesspalace.tusky.interfaces.AccountActionListener;
|
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||
import com.keylesspalace.tusky.util.ImageLoadingHelper;
|
||||
|
||||
public class BlocksAdapter extends AccountAdapter {
|
||||
|
||||
@ -69,6 +71,7 @@ public class BlocksAdapter extends AccountAdapter {
|
||||
private TextView displayName;
|
||||
private ImageButton unblock;
|
||||
private String id;
|
||||
private boolean animateAvatar;
|
||||
|
||||
BlockedUserViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
@ -76,6 +79,9 @@ public class BlocksAdapter extends AccountAdapter {
|
||||
username = itemView.findViewById(R.id.blocked_user_username);
|
||||
displayName = itemView.findViewById(R.id.blocked_user_display_name);
|
||||
unblock = itemView.findViewById(R.id.blocked_user_unblock);
|
||||
animateAvatar = PreferenceManager.getDefaultSharedPreferences(itemView.getContext())
|
||||
.getBoolean("animateGifAvatars", false);
|
||||
|
||||
}
|
||||
|
||||
void setupWithAccount(Account account) {
|
||||
@ -85,11 +91,9 @@ public class BlocksAdapter extends AccountAdapter {
|
||||
String format = username.getContext().getString(R.string.status_username_format);
|
||||
String formattedUsername = String.format(format, account.getUsername());
|
||||
username.setText(formattedUsername);
|
||||
Glide.with(avatar)
|
||||
.asBitmap()
|
||||
.load(account.getAvatar())
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(avatar);
|
||||
int avatarRadius = avatar.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.avatar_radius_48dp);
|
||||
ImageLoadingHelper.loadAvatar(account.getAvatar(), avatar, avatarRadius, animateAvatar);
|
||||
}
|
||||
|
||||
void setupActionListener(final AccountActionListener listener) {
|
||||
|
@ -16,6 +16,7 @@
|
||||
package com.keylesspalace.tusky.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -30,6 +31,7 @@ import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.entity.Account;
|
||||
import com.keylesspalace.tusky.entity.Emoji;
|
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||
import com.keylesspalace.tusky.util.ImageLoadingHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -146,13 +148,19 @@ public class ComposeAutoCompleteAdapter extends BaseAdapter
|
||||
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(account.getName(),
|
||||
account.getEmojis(), accountViewHolder.displayName);
|
||||
accountViewHolder.displayName.setText(emojifiedName);
|
||||
if (!account.getAvatar().isEmpty()) {
|
||||
Glide.with(accountViewHolder.avatar)
|
||||
.asBitmap()
|
||||
.load(account.getAvatar())
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(accountViewHolder.avatar);
|
||||
}
|
||||
|
||||
int avatarRadius = accountViewHolder.avatar.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.avatar_radius_42dp);
|
||||
|
||||
boolean animateAvatar = PreferenceManager.getDefaultSharedPreferences(accountViewHolder.avatar.getContext())
|
||||
.getBoolean("animateGifAvatars", false);
|
||||
|
||||
ImageLoadingHelper.loadAvatar(
|
||||
account.getAvatar(),
|
||||
accountViewHolder.avatar,
|
||||
avatarRadius,
|
||||
animateAvatar
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -17,6 +17,8 @@ package com.keylesspalace.tusky.adapter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -24,11 +26,11 @@ import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.entity.Account;
|
||||
import com.keylesspalace.tusky.interfaces.AccountActionListener;
|
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||
import com.keylesspalace.tusky.util.ImageLoadingHelper;
|
||||
|
||||
public class FollowRequestsAdapter extends AccountAdapter {
|
||||
|
||||
@ -70,6 +72,7 @@ public class FollowRequestsAdapter extends AccountAdapter {
|
||||
private ImageButton accept;
|
||||
private ImageButton reject;
|
||||
private String id;
|
||||
private boolean animateAvatar;
|
||||
|
||||
FollowRequestViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
@ -78,6 +81,8 @@ public class FollowRequestsAdapter extends AccountAdapter {
|
||||
displayName = itemView.findViewById(R.id.displayNameTextView);
|
||||
accept = itemView.findViewById(R.id.acceptButton);
|
||||
reject = itemView.findViewById(R.id.rejectButton);
|
||||
animateAvatar = PreferenceManager.getDefaultSharedPreferences(itemView.getContext())
|
||||
.getBoolean("animateGifAvatars", false);
|
||||
}
|
||||
|
||||
void setupWithAccount(Account account) {
|
||||
@ -87,11 +92,9 @@ public class FollowRequestsAdapter extends AccountAdapter {
|
||||
String format = username.getContext().getString(R.string.status_username_format);
|
||||
String formattedUsername = String.format(format, account.getUsername());
|
||||
username.setText(formattedUsername);
|
||||
Glide.with(avatar)
|
||||
.asBitmap()
|
||||
.load(account.getAvatar())
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(avatar);
|
||||
int avatarRadius = avatar.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.avatar_radius_48dp);
|
||||
ImageLoadingHelper.loadAvatar(account.getAvatar(), avatar, avatarRadius, animateAvatar);
|
||||
}
|
||||
|
||||
void setupActionListener(final AccountActionListener listener) {
|
||||
|
@ -2,6 +2,8 @@ package com.keylesspalace.tusky.adapter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -9,11 +11,11 @@ import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.entity.Account;
|
||||
import com.keylesspalace.tusky.interfaces.AccountActionListener;
|
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||
import com.keylesspalace.tusky.util.ImageLoadingHelper;
|
||||
|
||||
public class MutesAdapter extends AccountAdapter {
|
||||
|
||||
@ -55,6 +57,7 @@ public class MutesAdapter extends AccountAdapter {
|
||||
private TextView displayName;
|
||||
private ImageButton unmute;
|
||||
private String id;
|
||||
private boolean animateAvatar;
|
||||
|
||||
MutedUserViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
@ -62,6 +65,8 @@ public class MutesAdapter extends AccountAdapter {
|
||||
username = itemView.findViewById(R.id.muted_user_username);
|
||||
displayName = itemView.findViewById(R.id.muted_user_display_name);
|
||||
unmute = itemView.findViewById(R.id.muted_user_unmute);
|
||||
animateAvatar = PreferenceManager.getDefaultSharedPreferences(itemView.getContext())
|
||||
.getBoolean("animateGifAvatars", false);
|
||||
}
|
||||
|
||||
void setupWithAccount(Account account) {
|
||||
@ -71,11 +76,9 @@ public class MutesAdapter extends AccountAdapter {
|
||||
String format = username.getContext().getString(R.string.status_username_format);
|
||||
String formattedUsername = String.format(format, account.getUsername());
|
||||
username.setText(formattedUsername);
|
||||
Glide.with(avatar)
|
||||
.asBitmap()
|
||||
.load(account.getAvatar())
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(avatar);
|
||||
int avatarRadius = avatar.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.avatar_radius_48dp);
|
||||
ImageLoadingHelper.loadAvatar(account.getAvatar(), avatar, avatarRadius, animateAvatar);
|
||||
}
|
||||
|
||||
void setupActionListener(final AccountActionListener listener) {
|
||||
|
@ -33,7 +33,6 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.ToggleButton;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.entity.Account;
|
||||
import com.keylesspalace.tusky.entity.Emoji;
|
||||
@ -42,6 +41,7 @@ import com.keylesspalace.tusky.interfaces.LinkListener;
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||
import com.keylesspalace.tusky.util.DateUtils;
|
||||
import com.keylesspalace.tusky.util.ImageLoadingHelper;
|
||||
import com.keylesspalace.tusky.util.LinkHelper;
|
||||
import com.keylesspalace.tusky.util.SmartLengthInputFilter;
|
||||
import com.keylesspalace.tusky.viewdata.NotificationViewData;
|
||||
@ -82,6 +82,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||
private NotificationActionListener notificationActionListener;
|
||||
private boolean mediaPreviewEnabled;
|
||||
private boolean useAbsoluteTime;
|
||||
private boolean showBotOverlay;
|
||||
private boolean animateAvatar;
|
||||
private BidiFormatter bidiFormatter;
|
||||
private AdapterDataSource<NotificationViewData> dataSource;
|
||||
|
||||
@ -96,6 +98,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||
this.notificationActionListener = notificationActionListener;
|
||||
mediaPreviewEnabled = true;
|
||||
useAbsoluteTime = false;
|
||||
showBotOverlay = true;
|
||||
animateAvatar = false;
|
||||
bidiFormatter = BidiFormatter.getInstance();
|
||||
}
|
||||
|
||||
@ -112,12 +116,12 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||
case VIEW_TYPE_STATUS_NOTIFICATION: {
|
||||
View view = inflater
|
||||
.inflate(R.layout.item_status_notification, parent, false);
|
||||
return new StatusNotificationViewHolder(view, useAbsoluteTime);
|
||||
return new StatusNotificationViewHolder(view, useAbsoluteTime, animateAvatar);
|
||||
}
|
||||
case VIEW_TYPE_FOLLOW: {
|
||||
View view = inflater
|
||||
.inflate(R.layout.item_follow, parent, false);
|
||||
return new FollowViewHolder(view);
|
||||
return new FollowViewHolder(view, animateAvatar);
|
||||
}
|
||||
case VIEW_TYPE_PLACEHOLDER: {
|
||||
View view = inflater
|
||||
@ -167,7 +171,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||
StatusViewHolder holder = (StatusViewHolder) viewHolder;
|
||||
StatusViewData.Concrete status = concreteNotificaton.getStatusViewData();
|
||||
holder.setupWithStatus(status,
|
||||
statusListener, mediaPreviewEnabled, payloadForHolder);
|
||||
statusListener, mediaPreviewEnabled, showBotOverlay, animateAvatar, payloadForHolder);
|
||||
if(concreteNotificaton.getType() == Notification.Type.POLL) {
|
||||
holder.setPollInfo(accountId.equals(concreteNotificaton.getAccount().getId()));
|
||||
} else {
|
||||
@ -266,6 +270,14 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||
this.useAbsoluteTime = useAbsoluteTime;
|
||||
}
|
||||
|
||||
public void setShowBotOverlay(boolean showBotOverlay) {
|
||||
this.showBotOverlay = showBotOverlay;
|
||||
}
|
||||
|
||||
public void setAnimateAvatar(boolean animateAvatar) {
|
||||
this.animateAvatar = animateAvatar;
|
||||
}
|
||||
|
||||
public interface NotificationActionListener {
|
||||
void onViewAccount(String id);
|
||||
|
||||
@ -288,13 +300,15 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||
private TextView usernameView;
|
||||
private TextView displayNameView;
|
||||
private ImageView avatar;
|
||||
private boolean animateAvatar;
|
||||
|
||||
FollowViewHolder(View itemView) {
|
||||
FollowViewHolder(View itemView, boolean animateAvatar) {
|
||||
super(itemView);
|
||||
message = itemView.findViewById(R.id.notification_text);
|
||||
usernameView = itemView.findViewById(R.id.notification_username);
|
||||
displayNameView = itemView.findViewById(R.id.notification_display_name);
|
||||
avatar = itemView.findViewById(R.id.notification_avatar);
|
||||
this.animateAvatar = animateAvatar;
|
||||
}
|
||||
|
||||
void setMessage(Account account, BidiFormatter bidiFormatter) {
|
||||
@ -313,15 +327,11 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||
|
||||
displayNameView.setText(emojifiedDisplayName);
|
||||
|
||||
if (TextUtils.isEmpty(account.getAvatar())) {
|
||||
avatar.setImageResource(R.drawable.avatar_default);
|
||||
} else {
|
||||
Glide.with(avatar)
|
||||
.asBitmap()
|
||||
.load(account.getAvatar())
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(avatar);
|
||||
}
|
||||
int avatarRadius = avatar.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.avatar_radius_24dp);
|
||||
|
||||
ImageLoadingHelper.loadAvatar(account.getAvatar(), avatar, avatarRadius, animateAvatar);
|
||||
|
||||
}
|
||||
|
||||
void setupButtons(final NotificationActionListener listener, final String accountId) {
|
||||
@ -349,10 +359,11 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||
private StatusViewData.Concrete statusViewData;
|
||||
|
||||
private boolean useAbsoluteTime;
|
||||
private boolean animateAvatar;
|
||||
private SimpleDateFormat shortSdf;
|
||||
private SimpleDateFormat longSdf;
|
||||
|
||||
StatusNotificationViewHolder(View itemView, boolean useAbsoluteTime) {
|
||||
StatusNotificationViewHolder(View itemView, boolean useAbsoluteTime, boolean animateAvatar) {
|
||||
super(itemView);
|
||||
message = itemView.findViewById(R.id.notification_top_text);
|
||||
statusNameBar = itemView.findViewById(R.id.status_name_bar);
|
||||
@ -376,6 +387,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||
contentWarningButton.setOnCheckedChangeListener(this);
|
||||
|
||||
this.useAbsoluteTime = useAbsoluteTime;
|
||||
this.animateAvatar = animateAvatar;
|
||||
shortSdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
|
||||
longSdf = new SimpleDateFormat("MM/dd HH:mm:ss", Locale.getDefault());
|
||||
}
|
||||
@ -495,23 +507,17 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||
|
||||
void setAvatars(@Nullable String statusAvatarUrl, @Nullable String notificationAvatarUrl) {
|
||||
|
||||
if (TextUtils.isEmpty(statusAvatarUrl)) {
|
||||
statusAvatar.setImageResource(R.drawable.avatar_default);
|
||||
} else {
|
||||
Glide.with(statusAvatar)
|
||||
.load(statusAvatarUrl)
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(statusAvatar);
|
||||
}
|
||||
int statusAvatarRadius = statusAvatar.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.avatar_radius_48dp);
|
||||
|
||||
if (TextUtils.isEmpty(notificationAvatarUrl)) {
|
||||
notificationAvatar.setImageResource(R.drawable.avatar_default);
|
||||
} else {
|
||||
Glide.with(notificationAvatar)
|
||||
.load(notificationAvatarUrl)
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(notificationAvatar);
|
||||
}
|
||||
ImageLoadingHelper.loadAvatar(statusAvatarUrl,
|
||||
statusAvatar, statusAvatarRadius, animateAvatar);
|
||||
|
||||
int notificationAvatarRadius = statusAvatar.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.avatar_radius_24dp);
|
||||
|
||||
ImageLoadingHelper.loadAvatar(notificationAvatarUrl,
|
||||
notificationAvatar, notificationAvatarRadius, animateAvatar);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -49,15 +49,19 @@ public class SearchResultsAdapter extends RecyclerView.Adapter {
|
||||
private boolean mediaPreviewsEnabled;
|
||||
private boolean alwaysShowSensitiveMedia;
|
||||
private boolean useAbsoluteTime;
|
||||
private boolean showBotOverlay;
|
||||
private boolean animateAvatar;
|
||||
|
||||
private LinkListener linkListener;
|
||||
private StatusActionListener statusListener;
|
||||
|
||||
public SearchResultsAdapter(boolean mediaPreviewsEnabled,
|
||||
boolean alwaysShowSensitiveMedia,
|
||||
LinkListener linkListener,
|
||||
public SearchResultsAdapter(LinkListener linkListener,
|
||||
StatusActionListener statusListener,
|
||||
boolean useAbsoluteTime) {
|
||||
boolean mediaPreviewsEnabled,
|
||||
boolean alwaysShowSensitiveMedia,
|
||||
boolean useAbsoluteTime,
|
||||
boolean showBotOverlay,
|
||||
boolean animateAvatar) {
|
||||
|
||||
this.accountList = Collections.emptyList();
|
||||
this.statusList = Collections.emptyList();
|
||||
@ -67,6 +71,8 @@ public class SearchResultsAdapter extends RecyclerView.Adapter {
|
||||
this.mediaPreviewsEnabled = mediaPreviewsEnabled;
|
||||
this.alwaysShowSensitiveMedia = alwaysShowSensitiveMedia;
|
||||
this.useAbsoluteTime = useAbsoluteTime;
|
||||
this.showBotOverlay = showBotOverlay;
|
||||
this.animateAvatar = animateAvatar;
|
||||
|
||||
this.linkListener = linkListener;
|
||||
this.statusListener = statusListener;
|
||||
@ -98,22 +104,23 @@ public class SearchResultsAdapter extends RecyclerView.Adapter {
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
|
||||
if (position >= accountList.size()) {
|
||||
if(position >= accountList.size() + concreteStatusList.size()) {
|
||||
HashtagViewHolder holder = (HashtagViewHolder) viewHolder;
|
||||
int index = position - accountList.size() - concreteStatusList.size();
|
||||
holder.setup(hashtagList.get(index), linkListener);
|
||||
} else {
|
||||
StatusViewHolder holder = (StatusViewHolder) viewHolder;
|
||||
int index = position - accountList.size();
|
||||
holder.setupWithStatus(concreteStatusList.get(index), statusListener, mediaPreviewsEnabled);
|
||||
}
|
||||
if (position >= accountList.size()) {
|
||||
if(position >= accountList.size() + concreteStatusList.size()) {
|
||||
HashtagViewHolder holder = (HashtagViewHolder) viewHolder;
|
||||
int index = position - accountList.size() - concreteStatusList.size();
|
||||
holder.setup(hashtagList.get(index), linkListener);
|
||||
} else {
|
||||
AccountViewHolder holder = (AccountViewHolder) viewHolder;
|
||||
holder.setupWithAccount(accountList.get(position));
|
||||
holder.setupLinkListener(linkListener);
|
||||
StatusViewHolder holder = (StatusViewHolder) viewHolder;
|
||||
int index = position - accountList.size();
|
||||
holder.setupWithStatus(concreteStatusList.get(index), statusListener,
|
||||
mediaPreviewsEnabled, showBotOverlay, animateAvatar);
|
||||
}
|
||||
} else {
|
||||
AccountViewHolder holder = (AccountViewHolder) viewHolder;
|
||||
holder.setupWithAccount(accountList.get(position));
|
||||
holder.setupLinkListener(linkListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
@ -133,11 +140,11 @@ public class SearchResultsAdapter extends RecyclerView.Adapter {
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable Status getStatusAtPosition(int position) {
|
||||
@Nullable public Status getStatusAtPosition(int position) {
|
||||
return statusList.get(position - accountList.size());
|
||||
}
|
||||
|
||||
public @Nullable StatusViewData.Concrete getConcreteStatusAtPosition(int position) {
|
||||
@Nullable public StatusViewData.Concrete getConcreteStatusAtPosition(int position) {
|
||||
return concreteStatusList.get(position - accountList.size());
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ package com.keylesspalace.tusky.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
@ -16,6 +15,13 @@ import android.widget.RadioGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.ToggleButton;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.emoji.text.EmojiCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.entity.Attachment;
|
||||
@ -29,6 +35,7 @@ import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||
import com.keylesspalace.tusky.util.DateUtils;
|
||||
import com.keylesspalace.tusky.util.HtmlUtils;
|
||||
import com.keylesspalace.tusky.util.ImageLoadingHelper;
|
||||
import com.keylesspalace.tusky.util.LinkHelper;
|
||||
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||
import com.keylesspalace.tusky.view.MediaPreviewImageView;
|
||||
@ -43,12 +50,6 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.emoji.text.EmojiCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import at.connyduck.sparkbutton.SparkButton;
|
||||
import at.connyduck.sparkbutton.SparkEventListener;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
@ -89,11 +90,15 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
private boolean useAbsoluteTime;
|
||||
private SimpleDateFormat shortSdf;
|
||||
private SimpleDateFormat longSdf;
|
||||
private boolean showBotOverlay;
|
||||
|
||||
private final NumberFormat numberFormat = NumberFormat.getNumberInstance();
|
||||
|
||||
protected StatusBaseViewHolder(View itemView, boolean useAbsoluteTime) {
|
||||
private int avatarRadius48dp;
|
||||
private int avatarRadius36dp;
|
||||
private int avatarRadius24dp;
|
||||
|
||||
protected StatusBaseViewHolder(View itemView,
|
||||
boolean useAbsoluteTime) {
|
||||
super(itemView);
|
||||
displayName = itemView.findViewById(R.id.status_display_name);
|
||||
username = itemView.findViewById(R.id.status_username);
|
||||
@ -104,8 +109,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
reblogButton = itemView.findViewById(R.id.status_inset);
|
||||
favouriteButton = itemView.findViewById(R.id.status_favourite);
|
||||
moreButton = itemView.findViewById(R.id.status_more);
|
||||
reblogged = false;
|
||||
favourited = false;
|
||||
|
||||
mediaPreviews = new MediaPreviewImageView[]{
|
||||
itemView.findViewById(R.id.status_media_preview_0),
|
||||
itemView.findViewById(R.id.status_media_preview_1),
|
||||
@ -153,7 +157,10 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
this.useAbsoluteTime = useAbsoluteTime;
|
||||
shortSdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
|
||||
longSdf = new SimpleDateFormat("MM/dd HH:mm:ss", Locale.getDefault());
|
||||
showBotOverlay = PreferenceManager.getDefaultSharedPreferences(itemView.getContext()).getBoolean("showBotOverlay", true);
|
||||
|
||||
this.avatarRadius48dp = itemView.getContext().getResources().getDimensionPixelSize(R.dimen.avatar_radius_48dp);
|
||||
this.avatarRadius36dp = itemView.getContext().getResources().getDimensionPixelSize(R.dimen.avatar_radius_36dp);
|
||||
this.avatarRadius24dp = itemView.getContext().getResources().getDimensionPixelSize(R.dimen.avatar_radius_24dp);
|
||||
}
|
||||
|
||||
protected abstract int getMediaPreviewHeight(Context context);
|
||||
@ -219,8 +226,13 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
}
|
||||
}
|
||||
|
||||
private void setAvatar(String url, @Nullable String rebloggedUrl, boolean isBot) {
|
||||
private void setAvatar(String url,
|
||||
@Nullable String rebloggedUrl,
|
||||
boolean isBot,
|
||||
boolean showBotOverlay,
|
||||
boolean animateAvatar) {
|
||||
|
||||
int avatarRadius;
|
||||
if(TextUtils.isEmpty(rebloggedUrl)) {
|
||||
avatar.setPaddingRelative(0, 0, 0, 0);
|
||||
|
||||
@ -235,28 +247,20 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
avatarInset.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
avatarRadius = avatarRadius48dp;
|
||||
|
||||
} else {
|
||||
int padding = Utils.convertDpToPx(avatar.getContext(), 12);
|
||||
avatar.setPaddingRelative(0, 0, padding, padding);
|
||||
|
||||
avatarInset.setVisibility(View.VISIBLE);
|
||||
avatarInset.setBackground(null);
|
||||
Glide.with(avatarInset)
|
||||
.asBitmap()
|
||||
.load(rebloggedUrl)
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(avatarInset);
|
||||
ImageLoadingHelper.loadAvatar(rebloggedUrl, avatarInset, avatarRadius24dp, animateAvatar);
|
||||
|
||||
avatarRadius = avatarRadius36dp;
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(url)) {
|
||||
avatar.setImageResource(R.drawable.avatar_default);
|
||||
} else {
|
||||
Glide.with(avatar)
|
||||
.asBitmap()
|
||||
.load(url)
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(avatar);
|
||||
}
|
||||
ImageLoadingHelper.loadAvatar(url, avatar, avatarRadius, animateAvatar);
|
||||
|
||||
}
|
||||
|
||||
@ -610,18 +614,22 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
}
|
||||
|
||||
protected void setupWithStatus(StatusViewData.Concrete status, final StatusActionListener listener,
|
||||
boolean mediaPreviewEnabled) {
|
||||
this.setupWithStatus(status, listener, mediaPreviewEnabled, null);
|
||||
boolean mediaPreviewEnabled, boolean showBotOverlay, boolean animateAvatar) {
|
||||
this.setupWithStatus(status, listener, mediaPreviewEnabled, showBotOverlay, animateAvatar, null);
|
||||
}
|
||||
|
||||
protected void setupWithStatus(StatusViewData.Concrete status, final StatusActionListener listener,
|
||||
boolean mediaPreviewEnabled, @Nullable Object payloads) {
|
||||
protected void setupWithStatus(StatusViewData.Concrete status,
|
||||
final StatusActionListener listener,
|
||||
boolean mediaPreviewEnabled,
|
||||
boolean showBotOverlay,
|
||||
boolean animateAvatar,
|
||||
@Nullable Object payloads) {
|
||||
if (payloads == null) {
|
||||
setDisplayName(status.getUserFullName(), status.getAccountEmojis());
|
||||
setUsername(status.getNickname());
|
||||
setCreatedAt(status.getCreatedAt());
|
||||
setIsReply(status.getInReplyToId() != null);
|
||||
setAvatar(status.getAvatar(), status.getRebloggedAvatar(), status.isBot());
|
||||
setAvatar(status.getAvatar(), status.getRebloggedAvatar(), status.isBot(), showBotOverlay, animateAvatar);
|
||||
setReblogged(status.isReblogged());
|
||||
setFavourited(status.isFavourited());
|
||||
List<Attachment> attachments = status.getAttachments();
|
||||
|
@ -125,8 +125,9 @@ class StatusDetailedViewHolder extends StatusBaseViewHolder {
|
||||
|
||||
@Override
|
||||
protected void setupWithStatus(final StatusViewData.Concrete status, final StatusActionListener listener,
|
||||
boolean mediaPreviewEnabled, @Nullable Object payloads) {
|
||||
super.setupWithStatus(status, listener, mediaPreviewEnabled, payloads);
|
||||
boolean mediaPreviewEnabled, boolean showBotOverlay, boolean animateAvatar,
|
||||
@Nullable Object payloads) {
|
||||
super.setupWithStatus(status, listener, mediaPreviewEnabled, showBotOverlay, animateAvatar, payloads);
|
||||
if (payloads == null) {
|
||||
setReblogAndFavCount(status.getReblogsCount(), status.getFavouritesCount(), listener);
|
||||
|
||||
|
@ -51,14 +51,15 @@ public class StatusViewHolder extends StatusBaseViewHolder {
|
||||
|
||||
@Override
|
||||
protected void setupWithStatus(StatusViewData.Concrete status, final StatusActionListener listener,
|
||||
boolean mediaPreviewEnabled, @Nullable Object payloads) {
|
||||
boolean mediaPreviewEnabled, boolean showBotOverlay, boolean animateAvatar,
|
||||
@Nullable Object payloads) {
|
||||
if (status == null || payloads == null) {
|
||||
if (status == null) {
|
||||
showContent(false);
|
||||
} else {
|
||||
showContent(true);
|
||||
setupCollapsedState(status, listener);
|
||||
super.setupWithStatus(status, listener, mediaPreviewEnabled, null);
|
||||
super.setupWithStatus(status, listener, mediaPreviewEnabled, showBotOverlay, animateAvatar, null);
|
||||
|
||||
String rebloggedByDisplayName = status.getRebloggedByUsername();
|
||||
if (rebloggedByDisplayName == null) {
|
||||
@ -70,7 +71,7 @@ public class StatusViewHolder extends StatusBaseViewHolder {
|
||||
|
||||
}
|
||||
} else {
|
||||
super.setupWithStatus(status, listener, mediaPreviewEnabled, payloads);
|
||||
super.setupWithStatus(status, listener, mediaPreviewEnabled, showBotOverlay, animateAvatar, payloads);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,8 @@ public class ThreadAdapter extends RecyclerView.Adapter {
|
||||
private StatusActionListener statusActionListener;
|
||||
private boolean mediaPreviewEnabled;
|
||||
private boolean useAbsoluteTime;
|
||||
private boolean showBotOverlay;
|
||||
private boolean animateAvatar;
|
||||
private int detailedStatusPosition;
|
||||
|
||||
public ThreadAdapter(StatusActionListener listener) {
|
||||
@ -44,6 +46,8 @@ public class ThreadAdapter extends RecyclerView.Adapter {
|
||||
this.statuses = new ArrayList<>();
|
||||
mediaPreviewEnabled = true;
|
||||
useAbsoluteTime = false;
|
||||
showBotOverlay = true;
|
||||
animateAvatar = false;
|
||||
detailedStatusPosition = RecyclerView.NO_POSITION;
|
||||
}
|
||||
|
||||
@ -70,10 +74,10 @@ public class ThreadAdapter extends RecyclerView.Adapter {
|
||||
StatusViewData.Concrete status = statuses.get(position);
|
||||
if (position == detailedStatusPosition) {
|
||||
StatusDetailedViewHolder holder = (StatusDetailedViewHolder) viewHolder;
|
||||
holder.setupWithStatus(status, statusActionListener, mediaPreviewEnabled);
|
||||
holder.setupWithStatus(status, statusActionListener, mediaPreviewEnabled, showBotOverlay, animateAvatar);
|
||||
} else {
|
||||
StatusViewHolder holder = (StatusViewHolder) viewHolder;
|
||||
holder.setupWithStatus(status, statusActionListener, mediaPreviewEnabled);
|
||||
holder.setupWithStatus(status, statusActionListener, mediaPreviewEnabled, showBotOverlay, animateAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,6 +159,14 @@ public class ThreadAdapter extends RecyclerView.Adapter {
|
||||
this.useAbsoluteTime = useAbsoluteTime;
|
||||
}
|
||||
|
||||
public void setShowBotOverlay(boolean showBotOverlay) {
|
||||
this.showBotOverlay = showBotOverlay;
|
||||
}
|
||||
|
||||
public void setAnimateAvatar(boolean animateAvatar) {
|
||||
this.animateAvatar = animateAvatar;
|
||||
}
|
||||
|
||||
public void setDetailedStatusPosition(int position) {
|
||||
if (position != detailedStatusPosition
|
||||
&& detailedStatusPosition != RecyclerView.NO_POSITION) {
|
||||
|
@ -43,14 +43,17 @@ public final class TimelineAdapter extends RecyclerView.Adapter {
|
||||
private final StatusActionListener statusListener;
|
||||
private boolean mediaPreviewEnabled;
|
||||
private boolean useAbsoluteTime;
|
||||
private boolean showBotOverlay;
|
||||
private boolean animateAvatar;
|
||||
|
||||
public TimelineAdapter(AdapterDataSource<StatusViewData> dataSource,
|
||||
StatusActionListener statusListener) {
|
||||
super();
|
||||
this.dataSource = dataSource;
|
||||
this.statusListener = statusListener;
|
||||
mediaPreviewEnabled = true;
|
||||
useAbsoluteTime = false;
|
||||
showBotOverlay = true;
|
||||
animateAvatar = false;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@ -89,7 +92,12 @@ public final class TimelineAdapter extends RecyclerView.Adapter {
|
||||
holder.setup(statusListener, ((StatusViewData.Placeholder) status).isLoading());
|
||||
} else if (status instanceof StatusViewData.Concrete) {
|
||||
StatusViewHolder holder = (StatusViewHolder) viewHolder;
|
||||
holder.setupWithStatus((StatusViewData.Concrete)status,statusListener, mediaPreviewEnabled,payloads!=null&&!payloads.isEmpty()?payloads.get(0):null);
|
||||
holder.setupWithStatus((StatusViewData.Concrete) status,
|
||||
statusListener,
|
||||
mediaPreviewEnabled,
|
||||
showBotOverlay,
|
||||
animateAvatar,
|
||||
payloads != null && !payloads.isEmpty() ? payloads.get(0) : null);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
@ -111,13 +119,21 @@ public final class TimelineAdapter extends RecyclerView.Adapter {
|
||||
}
|
||||
|
||||
public void setUseAbsoluteTime(boolean useAbsoluteTime){
|
||||
this.useAbsoluteTime=useAbsoluteTime;
|
||||
this.useAbsoluteTime = useAbsoluteTime;
|
||||
}
|
||||
|
||||
public boolean getMediaPreviewEnabled() {
|
||||
return mediaPreviewEnabled;
|
||||
}
|
||||
|
||||
public void setShowBotOverlay(boolean showBotOverlay) {
|
||||
this.showBotOverlay = showBotOverlay;
|
||||
}
|
||||
|
||||
public void setAnimateAvatar(boolean animateAvatar) {
|
||||
this.animateAvatar = animateAvatar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return dataSource.getItemAt(position).getViewDataId();
|
||||
|
@ -230,6 +230,10 @@ public class NotificationsFragment extends SFragment implements
|
||||
adapter.setMediaPreviewEnabled(mediaPreviewEnabled);
|
||||
boolean useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false);
|
||||
adapter.setUseAbsoluteTime(useAbsoluteTime);
|
||||
boolean showBotOverlay = preferences.getBoolean("showBotOverlay", true);
|
||||
adapter.setShowBotOverlay(showBotOverlay);
|
||||
boolean animateAvatar = preferences.getBoolean("animateGifAvatars", false);
|
||||
adapter.setAnimateAvatar(animateAvatar);
|
||||
recyclerView.setAdapter(adapter);
|
||||
|
||||
topLoading = false;
|
||||
@ -734,7 +738,7 @@ public class NotificationsFragment extends SFragment implements
|
||||
Log.w(TAG, "Didn't find a notification for ID: " + notificationId);
|
||||
}
|
||||
|
||||
public void onPreferenceChanged(String key) {
|
||||
private void onPreferenceChanged(String key) {
|
||||
switch (key) {
|
||||
case "fabHide": {
|
||||
hideFab = PreferenceManager.getDefaultSharedPreferences(getContext()).getBoolean("fabHide", false);
|
||||
@ -746,7 +750,6 @@ public class NotificationsFragment extends SFragment implements
|
||||
adapter.setMediaPreviewEnabled(enabled);
|
||||
fullyRefresh();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,8 +46,6 @@ class SearchFragment : SFragment(), StatusActionListener {
|
||||
private lateinit var searchAdapter: SearchResultsAdapter
|
||||
|
||||
private var alwaysShowSensitiveMedia = false
|
||||
private var mediaPreviewEnabled = true
|
||||
private var useAbsoluteTime = false
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_search, container, false)
|
||||
@ -55,20 +53,25 @@ class SearchFragment : SFragment(), StatusActionListener {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(view.context)
|
||||
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false)
|
||||
val useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false)
|
||||
val showBotOverlay = preferences.getBoolean("showBotOverlay", true)
|
||||
val animateAvatar = preferences.getBoolean("animateGifAvatars", false)
|
||||
|
||||
val account = accountManager.activeAccount
|
||||
alwaysShowSensitiveMedia = account?.alwaysShowSensitiveMedia ?: false
|
||||
mediaPreviewEnabled = account?.mediaPreviewEnabled ?: true
|
||||
val mediaPreviewEnabled = account?.mediaPreviewEnabled ?: true
|
||||
|
||||
searchRecyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL))
|
||||
searchRecyclerView.layoutManager = LinearLayoutManager(view.context)
|
||||
searchAdapter = SearchResultsAdapter(
|
||||
this,
|
||||
this,
|
||||
mediaPreviewEnabled,
|
||||
alwaysShowSensitiveMedia,
|
||||
this,
|
||||
this,
|
||||
useAbsoluteTime)
|
||||
useAbsoluteTime,
|
||||
showBotOverlay,
|
||||
animateAvatar
|
||||
)
|
||||
searchRecyclerView.adapter = searchAdapter
|
||||
|
||||
}
|
||||
|
@ -354,6 +354,10 @@ public class TimelineFragment extends SFragment implements
|
||||
adapter.setMediaPreviewEnabled(mediaPreviewEnabled);
|
||||
boolean useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false);
|
||||
adapter.setUseAbsoluteTime(useAbsoluteTime);
|
||||
boolean showBotOverlay = preferences.getBoolean("showBotOverlay", true);
|
||||
adapter.setShowBotOverlay(showBotOverlay);
|
||||
boolean animateAvatar = preferences.getBoolean("animateGifAvatars", false);
|
||||
adapter.setAnimateAvatar(animateAvatar);
|
||||
|
||||
boolean filter = preferences.getBoolean("tabFilterHomeReplies", true);
|
||||
filterRemoveReplies = kind == Kind.HOME && !filter;
|
||||
|
@ -0,0 +1,43 @@
|
||||
@file:JvmName("ImageLoadingHelper")
|
||||
|
||||
package com.keylesspalace.tusky.util
|
||||
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.Px
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.resource.bitmap.FitCenter
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
import com.keylesspalace.tusky.R
|
||||
|
||||
|
||||
private val fitCenterTransformation = FitCenter()
|
||||
|
||||
fun loadAvatar(url: String?, imageView: ImageView, @Px radius: Int, animate: Boolean) {
|
||||
|
||||
if(url.isNullOrBlank()) {
|
||||
Glide.with(imageView)
|
||||
.load(R.drawable.avatar_default)
|
||||
.into(imageView)
|
||||
} else {
|
||||
if (animate) {
|
||||
Glide.with(imageView)
|
||||
.load(url)
|
||||
.transform(
|
||||
fitCenterTransformation,
|
||||
RoundedCorners(radius)
|
||||
)
|
||||
.into(imageView)
|
||||
|
||||
} else {
|
||||
Glide.with(imageView)
|
||||
.asBitmap()
|
||||
.load(url)
|
||||
.transform(
|
||||
fitCenterTransformation,
|
||||
RoundedCorners(radius)
|
||||
)
|
||||
.into(imageView)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,325 +0,0 @@
|
||||
package com.keylesspalace.tusky.view;
|
||||
|
||||
/*
|
||||
* Original CircleImageView Copyright 2014 - 2018 Henning Dodenhof
|
||||
* Adapted to RoundedImageView by charlag in 2018
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewOutlineProvider;
|
||||
|
||||
public class RoundedImageView extends AppCompatImageView {
|
||||
|
||||
private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
|
||||
|
||||
private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
|
||||
private static final int COLORDRAWABLE_DIMENSION = 2;
|
||||
|
||||
private static final int DEFAULT_BORDER_WIDTH = 0;
|
||||
private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
|
||||
private static final int DEFAULT_CIRCLE_BACKGROUND_COLOR = Color.TRANSPARENT;
|
||||
private static float ROUNDED_PERCENT = 25;
|
||||
|
||||
private final RectF mDrawableRect = new RectF();
|
||||
private final RectF mBorderRect = new RectF();
|
||||
|
||||
private final Matrix mShaderMatrix = new Matrix();
|
||||
private final Paint mBitmapPaint = new Paint();
|
||||
private final Paint mBorderPaint = new Paint();
|
||||
private final Paint mCircleBackgroundPaint = new Paint();
|
||||
|
||||
private int mCircleBackgroundColor = DEFAULT_CIRCLE_BACKGROUND_COLOR;
|
||||
|
||||
private Bitmap mBitmap;
|
||||
private BitmapShader mBitmapShader;
|
||||
private int mBitmapWidth;
|
||||
private int mBitmapHeight;
|
||||
|
||||
private float mDrawableRadius;
|
||||
private float mBorderRadius;
|
||||
|
||||
private ColorFilter mColorFilter;
|
||||
|
||||
private boolean mReady;
|
||||
private boolean mSetupPending;
|
||||
|
||||
public RoundedImageView(Context context) {
|
||||
super(context);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
public RoundedImageView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public RoundedImageView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
super.setScaleType(SCALE_TYPE);
|
||||
mReady = true;
|
||||
|
||||
setOutlineProvider(new OutlineProvider());
|
||||
|
||||
if (mSetupPending) {
|
||||
setup();
|
||||
mSetupPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScaleType getScaleType() {
|
||||
return SCALE_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScaleType(ScaleType scaleType) {
|
||||
if (scaleType != SCALE_TYPE) {
|
||||
throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdjustViewBounds(boolean adjustViewBounds) {
|
||||
if (adjustViewBounds) {
|
||||
throw new IllegalArgumentException("adjustViewBounds not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
|
||||
if (mBitmap == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCircleBackgroundColor != Color.TRANSPARENT) {
|
||||
canvas.drawRoundRect(mDrawableRect, mDrawableRadius, mDrawableRadius,
|
||||
mCircleBackgroundPaint);
|
||||
}
|
||||
canvas.drawRoundRect(mDrawableRect, mDrawableRadius, mDrawableRadius, mBitmapPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
setup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPadding(int left, int top, int right, int bottom) {
|
||||
super.setPadding(left, top, right, bottom);
|
||||
setup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPaddingRelative(int start, int top, int end, int bottom) {
|
||||
super.setPaddingRelative(start, top, end, bottom);
|
||||
setup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImageBitmap(Bitmap bm) {
|
||||
super.setImageBitmap(bm);
|
||||
initializeBitmap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImageDrawable(Drawable drawable) {
|
||||
super.setImageDrawable(drawable);
|
||||
initializeBitmap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImageResource(@DrawableRes int resId) {
|
||||
super.setImageResource(resId);
|
||||
initializeBitmap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImageURI(Uri uri) {
|
||||
super.setImageURI(uri);
|
||||
initializeBitmap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
if (cf == mColorFilter) {
|
||||
return;
|
||||
}
|
||||
|
||||
mColorFilter = cf;
|
||||
applyColorFilter();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorFilter getColorFilter() {
|
||||
return mColorFilter;
|
||||
}
|
||||
|
||||
private void applyColorFilter() {
|
||||
mBitmapPaint.setColorFilter(mColorFilter);
|
||||
}
|
||||
|
||||
private static Bitmap getBitmapFromDrawable(Drawable drawable) {
|
||||
if (drawable == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
return ((BitmapDrawable) drawable).getBitmap();
|
||||
}
|
||||
|
||||
try {
|
||||
Bitmap bitmap;
|
||||
|
||||
if (drawable instanceof ColorDrawable) {
|
||||
bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
|
||||
} else {
|
||||
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
|
||||
}
|
||||
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
return bitmap;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeBitmap() {
|
||||
mBitmap = getBitmapFromDrawable(getDrawable());
|
||||
setup();
|
||||
}
|
||||
|
||||
private void setup() {
|
||||
if (!mReady) {
|
||||
mSetupPending = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (getWidth() == 0 && getHeight() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mBitmap == null) {
|
||||
invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
|
||||
|
||||
mBitmapPaint.setAntiAlias(true);
|
||||
mBitmapPaint.setShader(mBitmapShader);
|
||||
|
||||
mBorderPaint.setStyle(Paint.Style.STROKE);
|
||||
mBorderPaint.setAntiAlias(true);
|
||||
mBorderPaint.setColor(DEFAULT_BORDER_COLOR);
|
||||
mBorderPaint.setStrokeWidth(DEFAULT_BORDER_WIDTH);
|
||||
|
||||
mCircleBackgroundPaint.setStyle(Paint.Style.FILL);
|
||||
mCircleBackgroundPaint.setAntiAlias(true);
|
||||
mCircleBackgroundPaint.setColor(mCircleBackgroundColor);
|
||||
|
||||
mBitmapHeight = mBitmap.getHeight();
|
||||
mBitmapWidth = mBitmap.getWidth();
|
||||
|
||||
mBorderRect.set(calculateBounds());
|
||||
|
||||
float shorterSideBorder = Math.min(mBorderRect.width(), mBorderRect.height());
|
||||
mBorderRadius = shorterSideBorder / 2 * ROUNDED_PERCENT / 100;
|
||||
|
||||
mDrawableRect.set(mBorderRect);
|
||||
|
||||
float shorterSide = Math.min(mDrawableRect.width(), mDrawableRect.height());
|
||||
mDrawableRadius = shorterSide / 2 * ROUNDED_PERCENT / 100;
|
||||
|
||||
|
||||
applyColorFilter();
|
||||
updateShaderMatrix();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private RectF calculateBounds() {
|
||||
int availableWidth = getWidth() - getPaddingLeft() - getPaddingRight();
|
||||
int availableHeight = getHeight() - getPaddingTop() - getPaddingBottom();
|
||||
|
||||
int sideLength = Math.min(availableWidth, availableHeight);
|
||||
|
||||
float left = getPaddingLeft() + (availableWidth - sideLength) / 2f;
|
||||
float top = getPaddingTop() + (availableHeight - sideLength) / 2f;
|
||||
|
||||
return new RectF(left, top, left + sideLength, top + sideLength);
|
||||
}
|
||||
|
||||
private void updateShaderMatrix() {
|
||||
float scale;
|
||||
float dx = 0;
|
||||
float dy = 0;
|
||||
|
||||
mShaderMatrix.set(null);
|
||||
|
||||
if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
|
||||
scale = mDrawableRect.height() / (float) mBitmapHeight;
|
||||
dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
|
||||
} else {
|
||||
scale = mDrawableRect.width() / (float) mBitmapWidth;
|
||||
dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
|
||||
}
|
||||
|
||||
mShaderMatrix.setScale(scale, scale);
|
||||
mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
|
||||
|
||||
mBitmapShader.setLocalMatrix(mShaderMatrix);
|
||||
}
|
||||
|
||||
private class OutlineProvider extends ViewOutlineProvider {
|
||||
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
Rect bounds = new Rect();
|
||||
mBorderRect.roundOut(bounds);
|
||||
outline.setRoundRect(bounds, mBorderRadius);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -352,7 +352,7 @@
|
||||
|
||||
<include layout="@layout/item_status_bottom_sheet" />
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/accountAvatarImageView"
|
||||
android:layout_width="@dimen/account_activity_avatar_size"
|
||||
android:layout_height="@dimen/account_activity_avatar_size"
|
||||
@ -364,4 +364,4 @@
|
||||
app:layout_scrollFlags="scroll"
|
||||
app:srcCompat="@drawable/avatar_default" />
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
@ -13,7 +13,7 @@
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@android:color/transparent">
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/composeAvatar"
|
||||
android:layout_width="?attr/actionBarSize"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
|
@ -46,7 +46,7 @@
|
||||
app:layout_constraintStart_toStartOf="@id/headerPreview"
|
||||
app:layout_constraintTop_toTopOf="@id/headerPreview" />
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/avatarPreview"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
|
@ -8,7 +8,7 @@
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/account_avatar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
@ -19,7 +19,7 @@
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/avatar_default" />
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/account_avatar_inset"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
|
@ -6,7 +6,7 @@
|
||||
android:gravity="center_vertical"
|
||||
android:padding="8dp">
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
|
@ -8,7 +8,7 @@
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp">
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/blocked_user_avatar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
|
@ -29,7 +29,7 @@
|
||||
tools:text="ConnyDuck boosted"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/status_avatar_2"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="52dp"
|
||||
@ -42,7 +42,7 @@
|
||||
app:layout_constraintTop_toTopOf="@id/status_avatar_1"
|
||||
tools:src="@drawable/avatar_default" />
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/status_avatar_1"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="52dp"
|
||||
@ -55,7 +55,7 @@
|
||||
app:layout_constraintTop_toTopOf="@id/status_avatar"
|
||||
tools:src="@drawable/avatar_default" />
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/status_avatar"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="52dp"
|
||||
@ -68,7 +68,7 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/conversation_name"
|
||||
tools:src="@drawable/avatar_default" />
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/status_avatar_inset"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
* This is the for folnotificationsEnabledions, the layout for the follows/following listings on account
|
||||
* This is the for follow notifications, the layout for the follows/following listings on account
|
||||
* pages are instead in item_account.xml.
|
||||
-->
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
@ -27,7 +27,7 @@
|
||||
android:textSize="?attr/status_text_medium"
|
||||
tools:text="Someone followed you" />
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/notification_avatar"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
|
@ -8,7 +8,7 @@
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp">
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
|
@ -8,7 +8,7 @@
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp">
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/muted_user_avatar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
|
@ -31,7 +31,7 @@
|
||||
tools:text="ConnyDuck boosted"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/status_avatar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
@ -43,7 +43,7 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/status_info"
|
||||
tools:src="@drawable/avatar_default" />
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/status_avatar_inset"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
|
@ -11,7 +11,7 @@
|
||||
android:paddingLeft="14dp"
|
||||
android:paddingRight="14dp">
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/status_avatar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
@ -24,7 +24,7 @@
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/avatar_default" />
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/status_avatar_inset"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
|
@ -137,7 +137,7 @@
|
||||
android:textSize="?attr/status_text_medium"
|
||||
android:visibility="gone" />
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/notification_status_avatar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
@ -153,7 +153,7 @@
|
||||
tools:ignore="RtlHardcoded,RtlSymmetry"
|
||||
tools:src="@drawable/avatar_default" />
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/notification_notification_avatar"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
|
@ -17,7 +17,7 @@
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Account has moved" />
|
||||
|
||||
<com.keylesspalace.tusky.view.RoundedImageView
|
||||
<ImageView
|
||||
android:id="@+id/accountMovedAvatar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
|
@ -32,4 +32,12 @@
|
||||
<dimen name="preference_icon_size">20dp</dimen>
|
||||
|
||||
<dimen name="selected_drag_item_elevation">12dp</dimen>
|
||||
|
||||
<dimen name="avatar_radius_94dp">11.75dp</dimen> <!-- 1/8 of 100dp - 2 * 3dp padding -->
|
||||
<dimen name="avatar_radius_80dp">10dp</dimen> <!-- 1/8 of 80dp -->
|
||||
<dimen name="avatar_radius_48dp">6dp</dimen> <!-- 1/8 of 48dp -->
|
||||
<dimen name="avatar_radius_42dp">5.25dp</dimen> <!-- 1/8 of 42dp -->
|
||||
<dimen name="avatar_radius_40dp">5dp</dimen> <!-- 1/8 of 40dp -->
|
||||
<dimen name="avatar_radius_36dp">4.5dp</dimen> <!-- 1/8 of 36dp -->
|
||||
<dimen name="avatar_radius_24dp">3dp</dimen> <!-- 1/8 of 24dp -->
|
||||
</resources>
|
||||
|
@ -216,6 +216,9 @@
|
||||
<string name="pref_title_custom_tabs">Use Chrome Custom Tabs</string>
|
||||
<string name="pref_title_hide_follow_button">Hide compose button while scrolling</string>
|
||||
<string name="pref_title_language">Language</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_status_filter">Timeline filtering</string>
|
||||
<string name="pref_title_status_tabs">Tabs</string>
|
||||
<string name="pref_title_show_boosts">Show boosts</string>
|
||||
@ -463,7 +466,6 @@
|
||||
|
||||
<string name="compose_shortcut_long_label">Compose Toot</string>
|
||||
<string name="compose_shortcut_short_label">Compose</string>
|
||||
<string name="pref_title_bot_overlay">Show indicator for bots</string>
|
||||
|
||||
<string name="notification_clear_text">Are you sure you want to permanently clear all your notifications?</string>
|
||||
<string name="compose_preview_image_description">Actions for image %s</string>
|
||||
|
@ -49,6 +49,11 @@
|
||||
android:defaultValue="true"
|
||||
android:key="showBotOverlay"
|
||||
android:title="@string/pref_title_bot_overlay" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="animateGifAvatars"
|
||||
android:title="@string/pref_title_animate_gif_avatars" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/pref_title_browser_settings">
|
||||
|
Loading…
Reference in New Issue
Block a user