Use blurhash as image preview and as sensitive media cover, close #1571 (#1581)

* Use blurhash as image preview and as sensitive media cover, close #1571

* Fix focal point for blurhashes

* Fix video indicator overlapping sensitive media indicator

* Add a preference for blurhash

* Add blurhash to report UI.

* Introduce StatusDisplayOptions
This commit is contained in:
Ivan Kupalov 2019-12-30 21:37:20 +01:00 committed by Konrad Pozniak
commit 7623962a0d
32 changed files with 560 additions and 368 deletions

View file

@ -12,20 +12,21 @@ import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.adapter.NetworkStateViewHolder
import com.keylesspalace.tusky.interfaces.StatusActionListener
import com.keylesspalace.tusky.util.NetworkState
import com.keylesspalace.tusky.util.StatusDisplayOptions
class ConversationAdapter(private val useAbsoluteTime: Boolean,
private val mediaPreviewEnabled: Boolean,
private val listener: StatusActionListener,
private val topLoadedCallback: () -> Unit,
private val retryCallback: () -> Unit)
: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
class ConversationAdapter(
private val statusDisplayOptions: StatusDisplayOptions,
private val listener: StatusActionListener,
private val topLoadedCallback: () -> Unit,
private val retryCallback: () -> Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var networkState: NetworkState? = null
private val differ: AsyncPagedListDiffer<ConversationEntity> = AsyncPagedListDiffer(object: ListUpdateCallback {
private val differ: AsyncPagedListDiffer<ConversationEntity> = AsyncPagedListDiffer(object : ListUpdateCallback {
override fun onInserted(position: Int, count: Int) {
notifyItemRangeInserted(position, count)
if(position == 0) {
if (position == 0) {
topLoadedCallback()
}
}
@ -51,7 +52,8 @@ class ConversationAdapter(private val useAbsoluteTime: Boolean,
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
return when (viewType) {
R.layout.item_network_state -> NetworkStateViewHolder(view, retryCallback)
R.layout.item_conversation -> ConversationViewHolder(view, listener, useAbsoluteTime, mediaPreviewEnabled)
R.layout.item_conversation -> ConversationViewHolder(view, statusDisplayOptions,
listener)
else -> throw IllegalArgumentException("unknown view type $viewType")
}
}

View file

@ -23,7 +23,6 @@ import android.widget.ImageView;
import android.widget.TextView;
import android.widget.ToggleButton;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.RecyclerView;
import com.keylesspalace.tusky.R;
@ -32,6 +31,7 @@ import com.keylesspalace.tusky.entity.Attachment;
import com.keylesspalace.tusky.interfaces.StatusActionListener;
import com.keylesspalace.tusky.util.ImageLoadingHelper;
import com.keylesspalace.tusky.util.SmartLengthInputFilter;
import com.keylesspalace.tusky.util.StatusDisplayOptions;
import com.keylesspalace.tusky.viewdata.PollViewDataKt;
import java.util.List;
@ -44,15 +44,13 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
private ToggleButton contentCollapseButton;
private ImageView[] avatars;
private StatusDisplayOptions statusDisplayOptions;
private StatusActionListener listener;
private boolean mediaPreviewEnabled;
private boolean animateAvatars;
ConversationViewHolder(View itemView,
StatusActionListener listener,
boolean useAbsoluteTime,
boolean mediaPreviewEnabled) {
super(itemView, useAbsoluteTime);
StatusDisplayOptions statusDisplayOptions,
StatusActionListener listener) {
super(itemView);
conversationNameTextView = itemView.findViewById(R.id.conversation_name);
contentCollapseButton = itemView.findViewById(R.id.button_toggle_content);
avatars = new ImageView[]{
@ -60,11 +58,10 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
itemView.findViewById(R.id.status_avatar_1),
itemView.findViewById(R.id.status_avatar_2)
};
this.statusDisplayOptions = statusDisplayOptions;
this.listener = listener;
this.mediaPreviewEnabled = mediaPreviewEnabled;
this.animateAvatars = PreferenceManager.getDefaultSharedPreferences(itemView.getContext()).getBoolean("animateGifAvatars", false);
}
@Override
@ -86,8 +83,9 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
setBookmarked(status.getBookmarked());
List<Attachment> attachments = status.getAttachments();
boolean sensitive = status.getSensitive();
if(mediaPreviewEnabled && !hasAudioAttachment(attachments)) {
setMediaPreviews(attachments, sensitive, listener, status.getShowingHiddenContent());
if (statusDisplayOptions.mediaPreviewEnabled() && !hasAudioAttachment(attachments)) {
setMediaPreviews(attachments, sensitive, listener, status.getShowingHiddenContent(),
statusDisplayOptions.useBlurhash());
if (attachments.size() == 0) {
hideSensitiveMediaWarning();
@ -118,11 +116,11 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
private void setConversationName(List<ConversationAccountEntity> accounts) {
Context context = conversationNameTextView.getContext();
String conversationName = "";
if(accounts.size() == 1) {
if (accounts.size() == 1) {
conversationName = context.getString(R.string.conversation_1_recipients, accounts.get(0).getUsername());
} else if(accounts.size() == 2) {
} else if (accounts.size() == 2) {
conversationName = context.getString(R.string.conversation_2_recipients, accounts.get(0).getUsername(), accounts.get(1).getUsername());
} else if (accounts.size() > 2){
} else if (accounts.size() > 2) {
conversationName = context.getString(R.string.conversation_more_recipients, accounts.get(0).getUsername(), accounts.get(1).getUsername(), accounts.size() - 2);
}
@ -130,10 +128,11 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
}
private void setAvatars(List<ConversationAccountEntity> accounts) {
for(int i=0; i < avatars.length; i++) {
for (int i = 0; i < avatars.length; i++) {
ImageView avatarView = avatars[i];
if(i < accounts.size()) {
ImageLoadingHelper.loadAvatar(accounts.get(i).getAvatar(), avatarView, avatarRadius48dp, animateAvatars);
if (i < accounts.size()) {
ImageLoadingHelper.loadAvatar(accounts.get(i).getAvatar(), avatarView,
avatarRadius48dp, statusDisplayOptions.animateAvatars());
avatarView.setVisibility(View.VISIBLE);
} else {
avatarView.setVisibility(View.GONE);

View file

@ -37,6 +37,7 @@ import com.keylesspalace.tusky.fragment.SFragment
import com.keylesspalace.tusky.interfaces.ReselectableFragment
import com.keylesspalace.tusky.interfaces.StatusActionListener
import com.keylesspalace.tusky.util.NetworkState
import com.keylesspalace.tusky.util.StatusDisplayOptions
import com.keylesspalace.tusky.util.ThemeUtils
import com.keylesspalace.tusky.util.hide
import kotlinx.android.synthetic.main.fragment_timeline.*
@ -62,15 +63,18 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val preferences = PreferenceManager.getDefaultSharedPreferences(view.context)
val useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false)
val account = accountManager.activeAccount
val mediaPreviewEnabled = account?.mediaPreviewEnabled ?: true
val statusDisplayOptions = StatusDisplayOptions(
animateAvatars = preferences.getBoolean("animateGifAvatars", false),
mediaPreviewEnabled = accountManager.activeAccount?.mediaPreviewEnabled ?: true,
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false),
showBotOverlay = preferences.getBoolean("showBotOverlay", true),
useBlurhash = preferences.getBoolean("useBlurhash", true)
)
adapter = ConversationAdapter(useAbsoluteTime, mediaPreviewEnabled, this, ::onTopLoaded, viewModel::retry)
adapter = ConversationAdapter(statusDisplayOptions, this, ::onTopLoaded, viewModel::retry)
recyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL))
layoutManager = LinearLayoutManager(view.context)