Fix conversation list jank after returning from calls tab.
This commit is contained in:
parent
9ed80d46b6
commit
49d6743cbb
7 changed files with 95 additions and 49 deletions
|
@ -20,4 +20,5 @@ public interface BindableConversationListItem extends Unbindable {
|
|||
|
||||
void setSelectedConversations(@NonNull ConversationSet conversations);
|
||||
void updateTypingIndicator(@NonNull Set<Long> typingThreads);
|
||||
void updateTimestamp();
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import java.util.Locale;
|
|||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
class ConversationListAdapter extends ListAdapter<Conversation, RecyclerView.ViewHolder> {
|
||||
class ConversationListAdapter extends ListAdapter<Conversation, RecyclerView.ViewHolder> implements TimestampPayloadSupport {
|
||||
|
||||
private static final int TYPE_THREAD = 1;
|
||||
private static final int TYPE_ACTION = 2;
|
||||
|
@ -41,7 +41,8 @@ class ConversationListAdapter extends ListAdapter<Conversation, RecyclerView.Vie
|
|||
|
||||
private enum Payload {
|
||||
TYPING_INDICATOR,
|
||||
SELECTION
|
||||
SELECTION,
|
||||
TIMESTAMP
|
||||
}
|
||||
|
||||
private final LifecycleOwner lifecycleOwner;
|
||||
|
@ -129,12 +130,13 @@ class ConversationListAdapter extends ListAdapter<Conversation, RecyclerView.Vie
|
|||
} else if (holder instanceof ConversationViewHolder) {
|
||||
for (Object payloadObject : payloads) {
|
||||
if (payloadObject instanceof Payload) {
|
||||
Payload payload = (Payload) payloadObject;
|
||||
Payload payload = (Payload) payloadObject;
|
||||
ConversationViewHolder vh = (ConversationViewHolder) holder;
|
||||
|
||||
if (payload == Payload.SELECTION) {
|
||||
((ConversationViewHolder) holder).getConversationListItem().setSelectedConversations(selectedConversations);
|
||||
} else {
|
||||
((ConversationViewHolder) holder).getConversationListItem().updateTypingIndicator(typingSet);
|
||||
switch (payload) {
|
||||
case TYPING_INDICATOR -> vh.getConversationListItem().updateTypingIndicator(typingSet);
|
||||
case SELECTION -> vh.getConversationListItem().setSelectedConversations(selectedConversations);
|
||||
case TIMESTAMP -> vh.getConversationListItem().updateTimestamp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,6 +192,11 @@ class ConversationListAdapter extends ListAdapter<Conversation, RecyclerView.Vie
|
|||
return super.getItem(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyTimestampPayloadUpdate() {
|
||||
notifyItemRangeChanged(0, getItemCount(), Payload.TIMESTAMP);
|
||||
}
|
||||
|
||||
public void setPagingController(@Nullable PagingController pagingController) {
|
||||
this.pagingController = pagingController;
|
||||
}
|
||||
|
|
|
@ -476,8 +476,8 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||
setAdapter(defaultAdapter);
|
||||
}
|
||||
|
||||
if (activeAdapter != null) {
|
||||
activeAdapter.notifyItemRangeChanged(0, activeAdapter.getItemCount());
|
||||
if (activeAdapter instanceof TimestampPayloadSupport) {
|
||||
((TimestampPayloadSupport) activeAdapter).notifyTimestampPayloadUpdate();
|
||||
}
|
||||
|
||||
SignalProxyUtil.startListeningToWebsocket();
|
||||
|
|
|
@ -141,6 +141,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
|
|||
|
||||
private LiveData<SpannableString> displayBody;
|
||||
private Disposable joinMembersDisposable = Disposable.empty();
|
||||
private Runnable updateDateView = null;
|
||||
|
||||
public ConversationListItem(Context context) {
|
||||
this(context, null);
|
||||
|
@ -249,11 +250,16 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
|
|||
observeDisplayBody(lifecycleOwner, displayBody);
|
||||
|
||||
if (thread.getDate() > 0) {
|
||||
CharSequence date = DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, thread.getDate());
|
||||
dateView.setText(date);
|
||||
dateView.setTypeface(thread.isRead() ? LIGHT_TYPEFACE : BOLD_TYPEFACE);
|
||||
dateView.setTextColor(thread.isRead() ? ContextCompat.getColor(getContext(), R.color.signal_text_secondary)
|
||||
: ContextCompat.getColor(getContext(), R.color.signal_text_primary));
|
||||
|
||||
updateDateView = () -> {
|
||||
CharSequence date = DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, thread.getDate());
|
||||
dateView.setText(date);
|
||||
};
|
||||
|
||||
updateDateView.run();
|
||||
}
|
||||
|
||||
if (thread.isArchived()) {
|
||||
|
@ -278,35 +284,6 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
|
|||
}
|
||||
}
|
||||
|
||||
public void bindContact(@NonNull LifecycleOwner lifecycleOwner,
|
||||
@NonNull Recipient contact,
|
||||
@NonNull GlideRequests glideRequests,
|
||||
@NonNull Locale locale,
|
||||
@Nullable String highlightSubstring)
|
||||
{
|
||||
this.glideRequests = glideRequests;
|
||||
this.locale = locale;
|
||||
this.highlightSubstring = highlightSubstring;
|
||||
|
||||
observeRecipient(lifecycleOwner, contact.live());
|
||||
observeDisplayBody(null, null);
|
||||
joinMembersDisposable.dispose();
|
||||
setSubjectViewText(null);
|
||||
|
||||
fromView.setText(contact, SearchUtil.getHighlightedSpan(locale, searchStyleFactory, new SpannableString(contact.getDisplayName(getContext())), highlightSubstring, SearchUtil.MATCH_ALL), true, null);
|
||||
setSubjectViewText(SearchUtil.getHighlightedSpan(locale, searchStyleFactory, contact.getE164().orElse(""), highlightSubstring, SearchUtil.MATCH_ALL));
|
||||
dateView.setText("");
|
||||
archivedView.setVisibility(GONE);
|
||||
unreadIndicator.setVisibility(GONE);
|
||||
unreadMentions.setVisibility(GONE);
|
||||
deliveryStatusIndicator.setNone();
|
||||
alertView.setNone();
|
||||
|
||||
setSelectedConversations(new ConversationSet());
|
||||
setBadgeFromRecipient(recipient.get());
|
||||
contactPhotoImage.setAvatar(glideRequests, recipient.get(), !batchMode);
|
||||
}
|
||||
|
||||
public void bindMessage(@NonNull LifecycleOwner lifecycleOwner,
|
||||
@NonNull MessageResult messageResult,
|
||||
@NonNull GlideRequests glideRequests,
|
||||
|
@ -324,7 +301,10 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
|
|||
|
||||
fromView.setText(recipient.get(), recipient.get().getDisplayNameOrUsername(getContext()), false, null, false);
|
||||
setSubjectViewText(SearchUtil.getHighlightedSpan(locale, searchStyleFactory, messageResult.getBodySnippet(), highlightSubstring, SearchUtil.MATCH_ALL));
|
||||
dateView.setText(DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, messageResult.getReceivedTimestampMs()));
|
||||
|
||||
updateDateView = () -> dateView.setText(DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, messageResult.getReceivedTimestampMs()));
|
||||
|
||||
updateDateView.run();
|
||||
archivedView.setVisibility(GONE);
|
||||
unreadIndicator.setVisibility(GONE);
|
||||
unreadMentions.setVisibility(GONE);
|
||||
|
@ -353,7 +333,9 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
|
|||
});
|
||||
|
||||
fromView.setText(recipient.get(), false);
|
||||
dateView.setText(DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, groupWithMembers.getDate()));
|
||||
|
||||
updateDateView = () -> dateView.setText(DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, groupWithMembers.getDate()));
|
||||
updateDateView.run();
|
||||
archivedView.setVisibility(GONE);
|
||||
unreadIndicator.setVisibility(GONE);
|
||||
unreadMentions.setVisibility(GONE);
|
||||
|
@ -386,6 +368,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
|
|||
|
||||
observeDisplayBody(null, null);
|
||||
joinMembersDisposable.dispose();
|
||||
updateDateView = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -429,6 +412,13 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTimestamp() {
|
||||
if (updateDateView != null) {
|
||||
updateDateView.run();
|
||||
}
|
||||
}
|
||||
|
||||
public Recipient getRecipient() {
|
||||
return recipient.get();
|
||||
}
|
||||
|
|
|
@ -64,4 +64,9 @@ public class ConversationListItemAction extends FrameLayout implements BindableC
|
|||
public void updateTypingIndicator(@NonNull Set<Long> typingThreads) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTimestamp() {
|
||||
// Intentionally left blank.
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,11 @@ class ConversationListSearchAdapter(
|
|||
callButtonClickCallbacks: CallButtonClickCallbacks,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
glideRequests: GlideRequests
|
||||
) : ContactSearchAdapter(context, fixedContacts, displayOptions, onClickedCallbacks, longClickCallbacks, storyContextMenuCallbacks, callButtonClickCallbacks) {
|
||||
) : ContactSearchAdapter(context, fixedContacts, displayOptions, onClickedCallbacks, longClickCallbacks, storyContextMenuCallbacks, callButtonClickCallbacks), TimestampPayloadSupport {
|
||||
|
||||
companion object {
|
||||
private const val PAYLOAD_TIMESTAMP = 0
|
||||
}
|
||||
|
||||
init {
|
||||
registerFactory(
|
||||
|
@ -62,6 +66,27 @@ class ConversationListSearchAdapter(
|
|||
)
|
||||
}
|
||||
|
||||
override fun notifyTimestampPayloadUpdate() {
|
||||
notifyItemRangeChanged(0, itemCount, PAYLOAD_TIMESTAMP)
|
||||
}
|
||||
|
||||
private abstract class ConversationListItemViewHolder<M : MappingModel<M>>(
|
||||
itemView: View
|
||||
) : MappingViewHolder<M>(itemView) {
|
||||
private val conversationListItem: ConversationListItem = itemView as ConversationListItem
|
||||
|
||||
override fun bind(model: M) {
|
||||
if (payload.contains(PAYLOAD_TIMESTAMP)) {
|
||||
conversationListItem.updateTimestamp()
|
||||
return
|
||||
}
|
||||
|
||||
fullBind(model)
|
||||
}
|
||||
|
||||
abstract fun fullBind(model: M)
|
||||
}
|
||||
|
||||
private class EmptyViewHolder(
|
||||
itemView: View
|
||||
) : MappingViewHolder<EmptyModel>(itemView) {
|
||||
|
@ -69,6 +94,10 @@ class ConversationListSearchAdapter(
|
|||
private val noResults = itemView.findViewById<TextView>(R.id.search_no_results)
|
||||
|
||||
override fun bind(model: EmptyModel) {
|
||||
if (payload.isNotEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
noResults.text = context.getString(R.string.SearchFragment_no_results, model.empty.query ?: "")
|
||||
}
|
||||
}
|
||||
|
@ -78,8 +107,8 @@ class ConversationListSearchAdapter(
|
|||
private val lifecycleOwner: LifecycleOwner,
|
||||
private val glideRequests: GlideRequests,
|
||||
itemView: View
|
||||
) : MappingViewHolder<ThreadModel>(itemView) {
|
||||
override fun bind(model: ThreadModel) {
|
||||
) : ConversationListItemViewHolder<ThreadModel>(itemView) {
|
||||
override fun fullBind(model: ThreadModel) {
|
||||
itemView.setOnClickListener {
|
||||
threadListener.onClicked(itemView, model.thread, false)
|
||||
}
|
||||
|
@ -101,8 +130,8 @@ class ConversationListSearchAdapter(
|
|||
private val lifecycleOwner: LifecycleOwner,
|
||||
private val glideRequests: GlideRequests,
|
||||
itemView: View
|
||||
) : MappingViewHolder<MessageModel>(itemView) {
|
||||
override fun bind(model: MessageModel) {
|
||||
) : ConversationListItemViewHolder<MessageModel>(itemView) {
|
||||
override fun fullBind(model: MessageModel) {
|
||||
itemView.setOnClickListener {
|
||||
messageListener.onClicked(itemView, model.message, false)
|
||||
}
|
||||
|
@ -122,8 +151,8 @@ class ConversationListSearchAdapter(
|
|||
private val lifecycleOwner: LifecycleOwner,
|
||||
private val glideRequests: GlideRequests,
|
||||
itemView: View
|
||||
) : MappingViewHolder<GroupWithMembersModel>(itemView) {
|
||||
override fun bind(model: GroupWithMembersModel) {
|
||||
) : ConversationListItemViewHolder<GroupWithMembersModel>(itemView) {
|
||||
override fun fullBind(model: GroupWithMembersModel) {
|
||||
itemView.setOnClickListener {
|
||||
groupWithMembersListener.onClicked(itemView, model.groupWithMembers, false)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.conversationlist
|
||||
|
||||
/**
|
||||
* Generic interface for the adapters to support updating the
|
||||
* timestamp in a given row as opposed to rebinding every item.
|
||||
*/
|
||||
interface TimestampPayloadSupport {
|
||||
fun notifyTimestampPayloadUpdate()
|
||||
}
|
Loading…
Add table
Reference in a new issue