* 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:
parent
2994af7091
commit
7623962a0d
32 changed files with 560 additions and 368 deletions
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -31,12 +31,13 @@ import com.keylesspalace.tusky.viewdata.toViewData
|
|||
import kotlinx.android.synthetic.main.item_report_status.view.*
|
||||
import java.util.*
|
||||
|
||||
class StatusViewHolder(itemView: View,
|
||||
private val useAbsoluteTime: Boolean,
|
||||
private val mediaPreviewEnabled: Boolean,
|
||||
private val viewState: StatusViewState,
|
||||
private val adapterHandler: AdapterHandler,
|
||||
private val getStatusForPosition: (Int) -> Status?) : RecyclerView.ViewHolder(itemView) {
|
||||
class StatusViewHolder(
|
||||
itemView: View,
|
||||
private val statusDisplayOptions: StatusDisplayOptions,
|
||||
private val viewState: StatusViewState,
|
||||
private val adapterHandler: AdapterHandler,
|
||||
private val getStatusForPosition: (Int) -> Status?
|
||||
) : RecyclerView.ViewHolder(itemView) {
|
||||
private val mediaViewHeight = itemView.context.resources.getDimensionPixelSize(R.dimen.status_media_preview_height)
|
||||
private val statusViewHelper = StatusViewHelper(itemView)
|
||||
|
||||
|
@ -69,11 +70,11 @@ class StatusViewHolder(itemView: View,
|
|||
|
||||
val sensitive = status.sensitive
|
||||
|
||||
statusViewHelper.setMediasPreview(mediaPreviewEnabled, status.attachments, sensitive, previewListener,
|
||||
viewState.isMediaShow(status.id, status.sensitive),
|
||||
statusViewHelper.setMediasPreview(statusDisplayOptions, status.attachments,
|
||||
sensitive, previewListener, viewState.isMediaShow(status.id, status.sensitive),
|
||||
mediaViewHeight)
|
||||
|
||||
statusViewHelper.setupPollReadonly(status.poll.toViewData(), status.emojis, useAbsoluteTime)
|
||||
statusViewHelper.setupPollReadonly(status.poll.toViewData(), status.emojis, statusDisplayOptions.useAbsoluteTime)
|
||||
setCreatedAt(status.createdAt)
|
||||
}
|
||||
|
||||
|
@ -124,7 +125,7 @@ class StatusViewHolder(itemView: View,
|
|||
}
|
||||
|
||||
private fun setCreatedAt(createdAt: Date?) {
|
||||
if (useAbsoluteTime) {
|
||||
if (statusDisplayOptions.useAbsoluteTime) {
|
||||
itemView.timestampInfo.text = statusViewHelper.getAbsoluteTime(createdAt)
|
||||
} else {
|
||||
itemView.timestampInfo.text = if (createdAt != null) {
|
||||
|
|
|
@ -23,12 +23,13 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.components.report.model.StatusViewState
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||
|
||||
class StatusesAdapter(private val useAbsoluteTime: Boolean,
|
||||
private val mediaPreviewEnabled: Boolean,
|
||||
private val statusViewState: StatusViewState,
|
||||
private val adapterHandler: AdapterHandler)
|
||||
: PagedListAdapter<Status, RecyclerView.ViewHolder>(STATUS_COMPARATOR) {
|
||||
class StatusesAdapter(
|
||||
private val statusDisplayOptions: StatusDisplayOptions,
|
||||
private val statusViewState: StatusViewState,
|
||||
private val adapterHandler: AdapterHandler
|
||||
) : PagedListAdapter<Status, RecyclerView.ViewHolder>(STATUS_COMPARATOR) {
|
||||
|
||||
private val statusForPosition: (Int) -> Status? = { position: Int ->
|
||||
if (position != RecyclerView.NO_POSITION) getItem(position) else null
|
||||
|
@ -36,8 +37,10 @@ class StatusesAdapter(private val useAbsoluteTime: Boolean,
|
|||
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return StatusViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_report_status, parent, false),
|
||||
useAbsoluteTime, mediaPreviewEnabled, statusViewState, adapterHandler, statusForPosition)
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.item_report_status, parent, false)
|
||||
return StatusViewHolder(view, statusDisplayOptions, statusViewState, adapterHandler,
|
||||
statusForPosition)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
|
|
|
@ -43,6 +43,7 @@ import com.keylesspalace.tusky.di.Injectable
|
|||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||
import com.keylesspalace.tusky.util.ThemeUtils
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
|
@ -119,14 +120,16 @@ class ReportStatusesFragment : Fragment(), Injectable, AdapterHandler {
|
|||
|
||||
private fun initStatusesView() {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
val statusDisplayOptions = StatusDisplayOptions(
|
||||
animateAvatars = false,
|
||||
mediaPreviewEnabled = accountManager.activeAccount?.mediaPreviewEnabled ?: true,
|
||||
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false),
|
||||
showBotOverlay = false,
|
||||
useBlurhash = preferences.getBoolean("useBlurhash", true)
|
||||
)
|
||||
|
||||
val useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false)
|
||||
|
||||
val account = accountManager.activeAccount
|
||||
val mediaPreviewEnabled = account?.mediaPreviewEnabled ?: true
|
||||
|
||||
|
||||
adapter = StatusesAdapter(useAbsoluteTime, mediaPreviewEnabled, viewModel.statusViewState, this)
|
||||
adapter = StatusesAdapter(statusDisplayOptions,
|
||||
viewModel.statusViewState, this)
|
||||
|
||||
recyclerView.addItemDecoration(DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL))
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
|
|
|
@ -24,28 +24,26 @@ import com.keylesspalace.tusky.R
|
|||
import com.keylesspalace.tusky.adapter.StatusViewHolder
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||
|
||||
class SearchStatusesAdapter(private val useAbsoluteTime: Boolean,
|
||||
private val mediaPreviewEnabled: Boolean,
|
||||
private val showBotOverlay: Boolean,
|
||||
private val animateAvatar: Boolean,
|
||||
private val statusListener: StatusActionListener)
|
||||
: PagedListAdapter<Pair<Status, StatusViewData.Concrete>, RecyclerView.ViewHolder>(STATUS_COMPARATOR) {
|
||||
class SearchStatusesAdapter(
|
||||
private val statusDisplayOptions: StatusDisplayOptions,
|
||||
private val statusListener: StatusActionListener
|
||||
) : PagedListAdapter<Pair<Status, StatusViewData.Concrete>, RecyclerView.ViewHolder>(STATUS_COMPARATOR) {
|
||||
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.item_status, parent, false)
|
||||
return StatusViewHolder(view, useAbsoluteTime)
|
||||
return StatusViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
getItem(position)?.let { item ->
|
||||
(holder as? StatusViewHolder)?.setupWithStatus(item.second, statusListener,
|
||||
mediaPreviewEnabled, showBotOverlay, animateAvatar)
|
||||
statusDisplayOptions)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override fun getItem(position: Int): Pair<Status, StatusViewData.Concrete>? {
|
||||
|
|
|
@ -52,6 +52,7 @@ import com.keylesspalace.tusky.entity.Status
|
|||
import com.keylesspalace.tusky.interfaces.AccountSelectionListener
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener
|
||||
import com.keylesspalace.tusky.util.NetworkState
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||
import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from
|
||||
|
@ -71,13 +72,17 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
|||
|
||||
override fun createAdapter(): PagedListAdapter<Pair<Status, StatusViewData.Concrete>, *> {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(searchRecyclerView.context)
|
||||
val useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false)
|
||||
val showBotOverlay = preferences.getBoolean("showBotOverlay", true)
|
||||
val animateAvatar = preferences.getBoolean("animateGifAvatars", false)
|
||||
val statusDisplayOptions = StatusDisplayOptions(
|
||||
animateAvatars = preferences.getBoolean("animateGifAvatars", false),
|
||||
mediaPreviewEnabled = viewModel.mediaPreviewEnabled,
|
||||
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false),
|
||||
showBotOverlay = preferences.getBoolean("showBotOverlay", true),
|
||||
useBlurhash = preferences.getBoolean("useBlurhash", true)
|
||||
)
|
||||
|
||||
searchRecyclerView.addItemDecoration(DividerItemDecoration(searchRecyclerView.context, DividerItemDecoration.VERTICAL))
|
||||
searchRecyclerView.layoutManager = LinearLayoutManager(searchRecyclerView.context)
|
||||
return SearchStatusesAdapter(useAbsoluteTime, viewModel.mediaPreviewEnabled, showBotOverlay, animateAvatar, this)
|
||||
return SearchStatusesAdapter(statusDisplayOptions, this)
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue