From 3091f299263d2db2956698e3e520d7ef5609acde Mon Sep 17 00:00:00 2001 From: "B. Petersen" Date: Thu, 20 Sep 2018 00:31:18 +0200 Subject: [PATCH] make ConversationAdapter basically work --- .../securesms/BindableConversationItem.java | 2 +- .../securesms/ConversationAdapter.java | 76 +++++++++++-------- .../securesms/ConversationFragment.java | 46 +++-------- .../securesms/ConversationItem.java | 48 +++++------- .../securesms/ConversationListAdapter.java | 4 +- .../securesms/ConversationUpdateItem.java | 2 +- .../securesms/connect/DcMsgListLoader.java | 39 ++++++++++ .../securesms/ConversationAdapterTest.java | 37 --------- 8 files changed, 115 insertions(+), 139 deletions(-) create mode 100644 src/org/thoughtcrime/securesms/connect/DcMsgListLoader.java delete mode 100644 test/unitTest/java/org/thoughtcrime/securesms/ConversationAdapterTest.java diff --git a/src/org/thoughtcrime/securesms/BindableConversationItem.java b/src/org/thoughtcrime/securesms/BindableConversationItem.java index 9d9b75975..864d60cc3 100644 --- a/src/org/thoughtcrime/securesms/BindableConversationItem.java +++ b/src/org/thoughtcrime/securesms/BindableConversationItem.java @@ -23,7 +23,7 @@ public interface BindableConversationItem extends Unbindable { @NonNull Optional nextMessageRecord, @NonNull GlideRequests glideRequests, @NonNull Locale locale, - @NonNull int[] messageIdsSelected, + @NonNull Set batchSelected, @NonNull Recipient recipients, boolean pulseHighlight); diff --git a/src/org/thoughtcrime/securesms/ConversationAdapter.java b/src/org/thoughtcrime/securesms/ConversationAdapter.java index a425d7ebb..7b2d5b2b2 100644 --- a/src/org/thoughtcrime/securesms/ConversationAdapter.java +++ b/src/org/thoughtcrime/securesms/ConversationAdapter.java @@ -75,14 +75,14 @@ import java.util.Set; * */ public class ConversationAdapter - extends RecyclerView.Adapter + extends RecyclerView.Adapter implements StickyHeaderDecoration.StickyHeaderAdapter { private static final int MAX_CACHE_SIZE = 40; private static final String TAG = ConversationAdapter.class.getSimpleName(); - private final Map> messageRecordCache = - Collections.synchronizedMap(new LRUCache>(MAX_CACHE_SIZE)); + private final Map> messageRecordCache = + Collections.synchronizedMap(new LRUCache>(MAX_CACHE_SIZE)); // TODO: use the cache private static final int MESSAGE_TYPE_OUTGOING = 0; private static final int MESSAGE_TYPE_INCOMING = 1; @@ -106,8 +106,7 @@ public class ConversationAdapter private final @NonNull MessageDigest digest; private ApplicationDcContext dcContext; - private int[] dcMsgList = new int[0]; - private DcChat dcChat; + private @NonNull int[] dcMsgList = new int[0]; private int recordToPulseHighlight; protected static class ViewHolder extends RecyclerView.ViewHolder { @@ -130,6 +129,10 @@ public class ConversationAdapter return dcMsgList.length; } + private DcMsg getMsg(int position) { + return dcContext.getMsg(dcMsgList[position]); + } + static class HeaderViewHolder extends RecyclerView.ViewHolder { TextView textView; @@ -197,10 +200,10 @@ public class ConversationAdapter @Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) { ConversationAdapter.ViewHolder holder = (ConversationAdapter.ViewHolder)viewHolder; - Optional previous = position <= 0? Optional.absent() : Optional.of(dcContext.getMsg(dcMsgList[position -1])); - Optional next = position >= dcMsgList.length ? Optional.absent() : Optional.of(dcContext.getMsg(dcMsgList[position +1])); + Optional previous = position <= 0? Optional.absent() : Optional.of(getMsg(position-1)); + Optional next = position >= dcMsgList.length-1? Optional.absent() : Optional.of(getMsg(position+1)); boolean pulseHighlight = dcMsgList[position] == recordToPulseHighlight; - holder.getItem().bind(dcContext.getMsg(dcMsgList[position]), previous, next, glideRequests, locale, dcMsgList, recipient, pulseHighlight); + holder.getItem().bind(getMsg(position), previous, next, glideRequests, locale, batchSelected, recipient, pulseHighlight); } @Override @@ -238,18 +241,42 @@ public class ConversationAdapter } } + @Override + public int getItemViewType(int i) { + DcMsg messageRecord = getMsg(i); + int type = messageRecord.getType(); + if (messageRecord.isUpdate()) { + return MESSAGE_TYPE_UPDATE; + } else if (type==DcMsg.DC_MSG_AUDIO || type==DcMsg.DC_MSG_VOICE) { + if (messageRecord.isOutgoing()) return MESSAGE_TYPE_AUDIO_OUTGOING; + else return MESSAGE_TYPE_AUDIO_INCOMING; + } else if (type==DcMsg.DC_MSG_FILE) { + if (messageRecord.isOutgoing()) return MESSAGE_TYPE_DOCUMENT_OUTGOING; + else return MESSAGE_TYPE_DOCUMENT_INCOMING; + } else if (type==DcMsg.DC_MSG_IMAGE || type==DcMsg.DC_MSG_GIF || type==DcMsg.DC_MSG_VIDEO) { + if (messageRecord.isOutgoing()) return MESSAGE_TYPE_THUMBNAIL_OUTGOING; + else return MESSAGE_TYPE_THUMBNAIL_INCOMING; + } else if (messageRecord.isOutgoing()) { + return MESSAGE_TYPE_OUTGOING; + } else { + return MESSAGE_TYPE_INCOMING; + } + } + public int findLastSeenPosition(long lastSeen) { + /* TODO -- we shoud do this without loading all messages in the chat if (lastSeen <= 0) return -1; if (isActive()) return -1; int count = getItemCount(); for (int i = 0;i } } - private boolean hasAudio(MessageRecord messageRecord) { - return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getAudioSlide() != null; - } - - private boolean hasDocument(MessageRecord messageRecord) { - return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getDocumentSlide() != null; - } - - private boolean hasThumbnail(MessageRecord messageRecord) { - return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide() != null; - } - - private @Nullable View header; - private @Nullable View footer; - protected boolean isFooterPosition(int position) { // return hasFooterView() && position == getItemCount() - 1; return false; @@ -326,7 +338,7 @@ public class ConversationAdapter if (position >= getItemCount()) return 0; if (position < 0) return 0; - DcMsg msg = dcContext.getMsg(dcMsgList[position]); + DcMsg msg = getMsg(position); return msg.getTimestamp(); } @@ -346,7 +358,7 @@ public class ConversationAdapter @Override public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) { - DcMsg msg = dcContext.getMsg(dcMsgList[position]); + DcMsg msg = getMsg(position); viewHolder.setText(DateUtils.getRelativeDate(getContext(), locale, msg.getTimestamp())); } @@ -404,18 +416,16 @@ public class ConversationAdapter } } - public void setFooterView(@Nullable View footer) { - // TODO: must not be implemented, remove calls to this function + public void changeData(@Nullable int[] dcMsgList) { + this.dcMsgList = dcMsgList==null? new int[0] : dcMsgList; + messageRecordCache.clear(); + notifyDataSetChanged(); } - public void setHeaderView(@Nullable View footer) { + public void setHeaderView(@Nullable View headerView) { // TODO: must not be implemented, remove calls to this function } - public void changeCursor(Cursor cursor) { - // TODO: implement this function - } - public void addFastRecord(@NonNull MessageRecord record) { // TODO: i think this is not need, we simply should reload the view } diff --git a/src/org/thoughtcrime/securesms/ConversationFragment.java b/src/org/thoughtcrime/securesms/ConversationFragment.java index 8a5211c78..575a8e503 100644 --- a/src/org/thoughtcrime/securesms/ConversationFragment.java +++ b/src/org/thoughtcrime/securesms/ConversationFragment.java @@ -60,6 +60,7 @@ import org.thoughtcrime.securesms.ConversationAdapter.HeaderViewHolder; import org.thoughtcrime.securesms.ConversationAdapter.ItemClickListener; import org.thoughtcrime.securesms.connect.ApplicationDcContext; import org.thoughtcrime.securesms.connect.DcHelper; +import org.thoughtcrime.securesms.connect.DcMsgListLoader; import org.thoughtcrime.securesms.contactshare.ContactUtil; import org.thoughtcrime.securesms.contactshare.SharedContactDetailsActivity; import org.thoughtcrime.securesms.contactshare.Contact; @@ -94,7 +95,7 @@ import java.util.Set; @SuppressLint("StaticFieldLeak") public class ConversationFragment extends Fragment - implements LoaderManager.LoaderCallbacks + implements LoaderManager.LoaderCallbacks { private static final String TAG = ConversationFragment.class.getSimpleName(); private static final String KEY_LIMIT = "limit"; @@ -461,54 +462,29 @@ public class ConversationFragment extends Fragment } @Override - public Loader onCreateLoader(int id, Bundle args) { + public Loader onCreateLoader(int id, Bundle args) { Log.w(TAG, "onCreateLoader"); loaderStartTime = System.currentTimeMillis(); - int limit = args.getInt(KEY_LIMIT, PARTIAL_CONVERSATION_LIMIT); - int offset = 0; - if (limit != 0 && startingPosition > limit) { - offset = Math.max(startingPosition - (limit / 2) + 1, 0); - startingPosition -= offset - 1; - } - - return new ConversationLoader(getActivity(), threadId, offset, limit, lastSeen); + return new DcMsgListLoader(getActivity(), (int)threadId, 0, 0); } @Override - public void onLoadFinished(Loader cursorLoader, Cursor cursor) { + public void onLoadFinished(Loader arg0, int[] dcMsgList) { long loadTime = System.currentTimeMillis() - loaderStartTime; - int count = cursor.getCount(); - Log.w(TAG, "onLoadFinished - took " + loadTime + " ms to load a cursor of size " + count); - ConversationLoader loader = (ConversationLoader)cursorLoader; + int count = dcMsgList.length; + Log.w(TAG, "onLoadFinished - took " + loadTime + " ms to load a message list of size " + count); ConversationAdapter adapter = getListAdapter(); if (adapter == null) { return; } - if (cursor.getCount() >= PARTIAL_CONVERSATION_LIMIT && loader.hasLimit()) { - adapter.setFooterView(topLoadMoreView); - } else { - adapter.setFooterView(null); - } - if (lastSeen == -1) { - setLastSeen(loader.getLastSeen()); + //setLastSeen(loader.getLastSeen()); -- TODO } - if (!loader.hasSent() && !recipient.isSystemContact() && !recipient.isGroupRecipient() && recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) { - adapter.setHeaderView(unknownSenderView); - } else { - adapter.setHeaderView(null); - } - - if (loader.hasOffset()) { - adapter.setHeaderView(bottomLoadMoreView); - previousOffset = loader.getOffset(); - } - - adapter.changeCursor(cursor); + adapter.changeData(dcMsgList); int lastSeenPosition = adapter.findLastSeenPosition(lastSeen); @@ -536,9 +512,9 @@ public class ConversationFragment extends Fragment } @Override - public void onLoaderReset(Loader arg0) { + public void onLoaderReset(Loader arg0) { if (list.getAdapter() != null) { - getListAdapter().changeCursor(null); + getListAdapter().changeData(null); } } diff --git a/src/org/thoughtcrime/securesms/ConversationItem.java b/src/org/thoughtcrime/securesms/ConversationItem.java index 4901dad1a..41c6670da 100644 --- a/src/org/thoughtcrime/securesms/ConversationItem.java +++ b/src/org/thoughtcrime/securesms/ConversationItem.java @@ -123,7 +123,7 @@ public class ConversationItem extends LinearLayout private AlertView alertView; private ViewGroup container; - private @NonNull int[] batchSelected = new int[0]; + private @NonNull Set batchSelected = new HashSet<>(); private @NonNull Recipient conversationRecipient; private @NonNull Stub mediaThumbnailStub; private @NonNull Stub audioViewStub; @@ -193,14 +193,14 @@ public class ConversationItem extends LinearLayout @NonNull Optional nextDcMsg, @NonNull GlideRequests glideRequests, @NonNull Locale locale, - @NonNull int[] messageIdsSelected, + @NonNull Set batchSelected, @NonNull Recipient recipients, boolean pulseHighlight) { this.messageRecord = messageRecord; this.locale = locale; this.glideRequests = glideRequests; - this.batchSelected = messageIdsSelected; + this.batchSelected = batchSelected; this.conversationRecipient = recipients; this.groupThread = conversationRecipient.isGroupRecipient(); this.recipient = dcContext.getRecipient(dcContext.getChat(messageRecord.getChatId())); @@ -329,20 +329,8 @@ public class ConversationItem extends LinearLayout } } - private boolean batchIsEmpty() { - return batchSelected.length == 0; - } - - private boolean batchContains(DcMsg msg) { - for(int ii = 0; ii < batchSelected.length; ii++) { - if(batchSelected[ii] == msg.getId()) - return true; - } - return false; - } - private void setInteractionState(DcMsg messageRecord, boolean pulseHighlight) { - if (batchContains(messageRecord)) { + if (batchSelected.contains(messageRecord)) { setBackgroundResource(R.drawable.conversation_item_background); setSelected(true); } else if (pulseHighlight) { @@ -354,20 +342,20 @@ public class ConversationItem extends LinearLayout } if (mediaThumbnailStub.resolved()) { - mediaThumbnailStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchIsEmpty()); - mediaThumbnailStub.get().setClickable(!shouldInterceptClicks(messageRecord) && batchIsEmpty()); - mediaThumbnailStub.get().setLongClickable(batchIsEmpty()); + mediaThumbnailStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty()); + mediaThumbnailStub.get().setClickable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty()); + mediaThumbnailStub.get().setLongClickable(batchSelected.isEmpty()); } if (audioViewStub.resolved()) { - audioViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchIsEmpty()); - audioViewStub.get().setClickable(batchIsEmpty()); - audioViewStub.get().setEnabled(batchIsEmpty()); + audioViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty()); + audioViewStub.get().setClickable(batchSelected.isEmpty()); + audioViewStub.get().setEnabled(batchSelected.isEmpty()); } if (documentViewStub.resolved()) { - documentViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchIsEmpty()); - documentViewStub.get().setClickable(batchIsEmpty()); + documentViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty()); + documentViewStub.get().setClickable(batchSelected.isEmpty()); } } @@ -413,7 +401,7 @@ public class ConversationItem extends LinearLayout if (isCaptionlessMms(messageRecord)) { bodyText.setVisibility(View.GONE); } else { - bodyText.setText(linkifyMessageBody(new SpannableString(messageRecord.getText()), batchIsEmpty())); + bodyText.setText(linkifyMessageBody(new SpannableString(messageRecord.getText()), batchSelected.isEmpty())); bodyText.setVisibility(View.VISIBLE); } } @@ -701,7 +689,7 @@ public class ConversationItem extends LinearLayout } private boolean shouldInterceptClicks(DcMsg messageRecord) { - return batchIsEmpty() && (messageRecord.isFailed() || + return batchSelected.isEmpty() && (messageRecord.isFailed() || messageRecord.isSetupMessage()); } @@ -824,7 +812,7 @@ public class ConversationItem extends LinearLayout private class SharedContactEventListener implements SharedContactView.EventListener { @Override public void onAddToContactsClicked(@NonNull Contact contact) { - if (eventListener != null && batchIsEmpty()) { + if (eventListener != null && batchSelected.isEmpty()) { eventListener.onAddToContactsClicked(contact); } else { passthroughClickListener.onClick(sharedContactStub.get()); @@ -833,7 +821,7 @@ public class ConversationItem extends LinearLayout @Override public void onInviteClicked(@NonNull List choices) { - if (eventListener != null && batchIsEmpty()) { + if (eventListener != null && batchSelected.isEmpty()) { eventListener.onInviteSharedContactClicked(choices); } else { passthroughClickListener.onClick(sharedContactStub.get()); @@ -842,7 +830,7 @@ public class ConversationItem extends LinearLayout @Override public void onMessageClicked(@NonNull List choices) { - if (eventListener != null && batchIsEmpty()) { + if (eventListener != null && batchSelected.isEmpty()) { eventListener.onMessageSharedContactClicked(choices); } else { passthroughClickListener.onClick(sharedContactStub.get()); @@ -884,7 +872,7 @@ public class ConversationItem extends LinearLayout private class ThumbnailClickListener implements SlideClickListener { public void onClick(final View v, final Slide slide) { - if (shouldInterceptClicks(messageRecord) || !batchIsEmpty()) { + if (shouldInterceptClicks(messageRecord) || !batchSelected.isEmpty()) { performClick(); } else if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType()) && slide.getUri() != null) { Intent intent = new Intent(context, MediaPreviewActivity.class); diff --git a/src/org/thoughtcrime/securesms/ConversationListAdapter.java b/src/org/thoughtcrime/securesms/ConversationListAdapter.java index e54a18f16..846430b97 100644 --- a/src/org/thoughtcrime/securesms/ConversationListAdapter.java +++ b/src/org/thoughtcrime/securesms/ConversationListAdapter.java @@ -182,8 +182,8 @@ class ConversationListAdapter extends RecyclerView.Adapter { void onSwitchToArchive(); } - void changeData(DcChatlist chatlist) { - dcChatlist = chatlist; + void changeData(@Nullable DcChatlist chatlist) { + dcChatlist = chatlist==null? new DcChatlist(0) : chatlist; notifyDataSetChanged(); } } \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/ConversationUpdateItem.java b/src/org/thoughtcrime/securesms/ConversationUpdateItem.java index 029530c6b..a64d7e302 100644 --- a/src/org/thoughtcrime/securesms/ConversationUpdateItem.java +++ b/src/org/thoughtcrime/securesms/ConversationUpdateItem.java @@ -76,7 +76,7 @@ public class ConversationUpdateItem extends LinearLayout @NonNull Optional nextMessageRecord, @NonNull GlideRequests glideRequests, @NonNull Locale locale, - @NonNull int[] messageIdsSelected, + @NonNull Set batchSelected, @NonNull Recipient conversationRecipient, boolean pulseUpdate) { diff --git a/src/org/thoughtcrime/securesms/connect/DcMsgListLoader.java b/src/org/thoughtcrime/securesms/connect/DcMsgListLoader.java new file mode 100644 index 000000000..9c24f4400 --- /dev/null +++ b/src/org/thoughtcrime/securesms/connect/DcMsgListLoader.java @@ -0,0 +1,39 @@ +package org.thoughtcrime.securesms.connect; + + +import android.content.Context; +import android.support.annotation.NonNull; +import android.util.Log; + +import com.b44t.messenger.DcChatlist; + +import org.thoughtcrime.securesms.util.AsyncLoader; + +public class DcMsgListLoader extends AsyncLoader { + + private static final String TAG = DcMsgListLoader.class.getName(); + + private final int chatId; + private final int listflags; + private final int marker1before; + + public DcMsgListLoader(Context context, int chatId, int listflags, int marker1before) { + super(context); + this.chatId = chatId; + this.listflags = listflags; + this.marker1before = marker1before; + } + + @Override + public @NonNull + int[] loadInBackground() { + try { + return DcHelper.getContext(getContext()).getChatMsgs(chatId, listflags, marker1before); + + } catch (Exception e) { + Log.w(TAG, e); + } + + return new int[0]; + } +} diff --git a/test/unitTest/java/org/thoughtcrime/securesms/ConversationAdapterTest.java b/test/unitTest/java/org/thoughtcrime/securesms/ConversationAdapterTest.java deleted file mode 100644 index 62ad3497a..000000000 --- a/test/unitTest/java/org/thoughtcrime/securesms/ConversationAdapterTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.thoughtcrime.securesms; - -import android.database.Cursor; - -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.*; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.when; - -public class ConversationAdapterTest extends BaseUnitTest { - private Cursor cursor = mock(Cursor.class); - private ConversationAdapter adapter; - - @Override - @Before - public void setUp() throws Exception { - super.setUp(); - adapter = new ConversationAdapter(context, cursor); - when(cursor.getColumnIndexOrThrow(anyString())).thenReturn(0); - } - - @Test - public void testGetItemIdEquals() throws Exception { - when(cursor.getString(anyInt())).thenReturn(null).thenReturn("SMS::1::1"); - long firstId = adapter.getItemId(cursor); - when(cursor.getString(anyInt())).thenReturn(null).thenReturn("MMS::1::1"); - long secondId = adapter.getItemId(cursor); - assertNotEquals(firstId, secondId); - when(cursor.getString(anyInt())).thenReturn(null).thenReturn("MMS::2::1"); - long thirdId = adapter.getItemId(cursor); - assertNotEquals(secondId, thirdId); - } -} \ No newline at end of file