From cf98a22269a53402d343f84389ad568835a6da9c Mon Sep 17 00:00:00 2001 From: Greyson Parrelli <37311915+greyson-signal@users.noreply.github.com> Date: Fri, 12 Jun 2020 12:13:48 -0700 Subject: [PATCH] Add placeholder support for ConversationListAdapter. --- .../conversation/ConversationDataSource.java | 8 +-- ...pter.java => ConversationListAdapter.java} | 66 +++++++++++++------ .../ConversationListFragment.java | 6 +- .../ConversationListViewModel.java | 2 +- 4 files changed, 51 insertions(+), 31 deletions(-) rename app/src/main/java/org/thoughtcrime/securesms/conversationlist/{ConversationPagedListAdapter.java => ConversationListAdapter.java} (74%) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationDataSource.java index 6d8faef4799..40cc00f763b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationDataSource.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationDataSource.java @@ -65,10 +65,6 @@ public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialC int totalCount = db.getConversationCount(threadId); int effectiveCount = params.requestedStartPosition; - if (totalCount == 0 || params.requestedStartPosition > totalCount) { - - } - try (MmsSmsDatabase.Reader reader = db.readerFor(db.getConversation(threadId, params.requestedStartPosition, params.requestedLoadSize))) { MessageRecord record; while ((record = reader.getNext()) != null && effectiveCount < totalCount && !isInvalid()) { @@ -83,7 +79,7 @@ public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialC callback.onResult(result.getItems(), params.requestedStartPosition, result.getTotal()); } - Log.d(TAG, "[Initial Load] " + (System.currentTimeMillis() - start) + " ms" + (isInvalid() ? " -- invalidated" : "")); + Log.d(TAG, "[Initial Load] " + (System.currentTimeMillis() - start) + " ms | thread: " + threadId + ", start: " + params.requestedStartPosition + ", size: " + params.requestedLoadSize + (isInvalid() ? " -- invalidated" : "")); } @Override @@ -102,7 +98,7 @@ public void loadRange(@NonNull LoadRangeParams params, @NonNull LoadRangeCallbac callback.onResult(records); - Log.d(TAG, "[Update] " + (System.currentTimeMillis() - start) + " ms" + (isInvalid() ? " -- invalidated" : "")); + Log.d(TAG, "[Update] " + (System.currentTimeMillis() - start) + " ms | thread: " + threadId + ", start: " + params.startPosition + ", size: " + params.loadSize + (isInvalid() ? " -- invalidated" : "")); } static class Factory extends DataSource.Factory { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationPagedListAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListAdapter.java similarity index 74% rename from app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationPagedListAdapter.java rename to app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListAdapter.java index 2ae421bd5f2..ce0f5c6a171 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationPagedListAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListAdapter.java @@ -3,6 +3,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.paging.PagedListAdapter; @@ -16,7 +17,7 @@ import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.CachedInflater; -import org.thoughtcrime.securesms.util.Stopwatch; +import org.thoughtcrime.securesms.util.ViewUtil; import java.util.Collection; import java.util.Collections; @@ -28,7 +29,11 @@ import java.util.Objects; import java.util.Set; -class ConversationPagedListAdapter extends PagedListAdapter { +class ConversationListAdapter extends PagedListAdapter { + + private static final int TYPE_THREAD = 1; + private static final int TYPE_ACTION = 2; + private static final int TYPE_PLACEHOLDER = 3; private enum Payload { TYPING_INDICATOR, @@ -42,7 +47,7 @@ private enum Payload { private final Set typingSet = new HashSet<>(); private int archived; - protected ConversationPagedListAdapter(@NonNull GlideRequests glideRequests, @NonNull OnConversationClickListener onConversationClickListener) { + protected ConversationListAdapter(@NonNull GlideRequests glideRequests, @NonNull OnConversationClickListener onConversationClickListener) { super(new ConversationDiffCallback()); this.glideRequests = glideRequests; @@ -50,10 +55,10 @@ protected ConversationPagedListAdapter(@NonNull GlideRequests glideRequests, @No } @Override - public @NonNull ConversationViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - if (viewType == R.layout.conversation_list_item_action) { + public @NonNull RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + if (viewType == TYPE_ACTION) { ConversationViewHolder holder = new ConversationViewHolder(LayoutInflater.from(parent.getContext()) - .inflate(viewType, parent, false)); + .inflate(R.layout.conversation_list_item_action, parent, false)); holder.itemView.setOnClickListener(v -> { int position = holder.getAdapterPosition(); @@ -64,9 +69,9 @@ protected ConversationPagedListAdapter(@NonNull GlideRequests glideRequests, @No }); return holder; - } else { + } else if (viewType == TYPE_THREAD) { ConversationViewHolder holder = new ConversationViewHolder(CachedInflater.from(parent.getContext()) - .inflate(viewType, parent, false)); + .inflate(R.layout.conversation_list_item_view, parent, false)); holder.itemView.setOnClickListener(v -> { int position = holder.getAdapterPosition(); @@ -86,11 +91,17 @@ protected ConversationPagedListAdapter(@NonNull GlideRequests glideRequests, @No return false; }); return holder; + } else if (viewType == TYPE_PLACEHOLDER) { + View v = new FrameLayout(parent.getContext()); + v.setLayoutParams(new FrameLayout.LayoutParams(1, ViewUtil.dpToPx(100))); + return new PlaceholderViewHolder(v); + } else { + throw new IllegalStateException("Unknown type! " + viewType); } } @Override - public void onBindViewHolder(@NonNull ConversationViewHolder holder, int position, @NonNull List payloads) { + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position, @NonNull List payloads) { if (payloads.isEmpty()) { onBindViewHolder(holder, position); } else { @@ -99,9 +110,9 @@ public void onBindViewHolder(@NonNull ConversationViewHolder holder, int positio Payload payload = (Payload) payloadObject; if (payload == Payload.SELECTION) { - holder.getConversationListItem().setBatchMode(batchMode); + ((ConversationViewHolder) holder).getConversationListItem().setBatchMode(batchMode); } else { - holder.getConversationListItem().updateTypingIndicator(typingSet); + ((ConversationViewHolder) holder).getConversationListItem().updateTypingIndicator(typingSet); } } } @@ -109,9 +120,11 @@ public void onBindViewHolder(@NonNull ConversationViewHolder holder, int positio } @Override - public void onBindViewHolder(@NonNull ConversationViewHolder holder, int position) { - if (holder.getItemViewType() == R.layout.conversation_list_item_action) { - holder.getConversationListItem().bind(new ThreadRecord.Builder(100) + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + if (holder.getItemViewType() == TYPE_ACTION) { + ConversationViewHolder casted = (ConversationViewHolder) holder; + + casted.getConversationListItem().bind(new ThreadRecord.Builder(100) .setBody("") .setDate(100) .setRecipient(Recipient.UNKNOWN) @@ -122,10 +135,11 @@ public void onBindViewHolder(@NonNull ConversationViewHolder holder, int positio typingSet, getBatchSelectionIds(), batchMode); - } else { - Conversation conversation = Objects.requireNonNull(getItem(position)); + } else if (holder.getItemViewType() == TYPE_THREAD) { + ConversationViewHolder casted = (ConversationViewHolder) holder; + Conversation conversation = Objects.requireNonNull(getItem(position)); - holder.getConversationListItem().bind(conversation.getThreadRecord(), + casted.getConversationListItem().bind(conversation.getThreadRecord(), glideRequests, conversation.getLocale(), typingSet, @@ -135,8 +149,10 @@ public void onBindViewHolder(@NonNull ConversationViewHolder holder, int positio } @Override - public void onViewRecycled(@NonNull ConversationViewHolder holder) { - holder.getConversationListItem().unbind(); + public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) { + if (holder instanceof ConversationViewHolder) { + ((ConversationViewHolder) holder).getConversationListItem().unbind(); + } } void setTypingThreads(@NonNull Set typingThreadSet) { @@ -184,9 +200,11 @@ public int getItemCount() { @Override public int getItemViewType(int position) { if (archived > 0 && position == getItemCount() - 1) { - return R.layout.conversation_list_item_action; + return TYPE_ACTION; + } else if (getItem(position) == null) { + return TYPE_PLACEHOLDER; } else { - return R.layout.conversation_list_item_view; + return TYPE_THREAD; } } @@ -244,6 +262,12 @@ public boolean areContentsTheSame(@NonNull Conversation oldItem, @NonNull Conver } } + private static class PlaceholderViewHolder extends RecyclerView.ViewHolder { + PlaceholderViewHolder(@NonNull View itemView) { + super(itemView); + } + } + interface OnConversationClickListener { void onConversationClick(Conversation conversation); boolean onConversationLongClick(Conversation conversation); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index 0392e693d3a..90e23f07615 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -137,7 +137,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode.Callback, - ConversationPagedListAdapter.OnConversationClickListener, + ConversationListAdapter.OnConversationClickListener, ConversationListSearchAdapter.EventListener, MainNavigator.BackHandler, MegaphoneActionController @@ -167,7 +167,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode private View toolbarShadow; private ConversationListViewModel viewModel; private RecyclerView.Adapter activeAdapter; - private ConversationPagedListAdapter defaultAdapter; + private ConversationListAdapter defaultAdapter; private ConversationListSearchAdapter searchAdapter; private StickyHeaderDecoration searchAdapterDecoration; private ViewGroup megaphoneContainer; @@ -465,7 +465,7 @@ public void onSearchClosed() { } private void initializeListAdapters() { - defaultAdapter = new ConversationPagedListAdapter(GlideApp.with(this), this); + defaultAdapter = new ConversationListAdapter(GlideApp.with(this), this); searchAdapter = new ConversationListSearchAdapter(GlideApp.with(this), this, Locale.getDefault()); searchAdapterDecoration = new StickyHeaderDecoration(searchAdapter, false, false); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListViewModel.java index 6d758ced74e..b4666e23e94 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListViewModel.java @@ -69,7 +69,7 @@ public void onChange(boolean selfChange) { PagedList.Config config = new PagedList.Config.Builder() .setPageSize(15) .setInitialLoadSizeHint(30) - .setEnablePlaceholders(false) + .setEnablePlaceholders(true) .build(); this.conversationList = new LivePagedListBuilder<>(factory, config).setFetchExecutor(ConversationListDataSource.EXECUTOR)