Refactor use of MessageRecord to increase flexibility of ConversationAdapter.
parent
5c110ca359
commit
9c63b37bb4
|
@ -5,6 +5,7 @@ import androidx.annotation.Nullable;
|
|||
import android.view.View;
|
||||
|
||||
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationMessage;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.ReactionRecord;
|
||||
|
@ -21,17 +22,17 @@ import java.util.Locale;
|
|||
import java.util.Set;
|
||||
|
||||
public interface BindableConversationItem extends Unbindable {
|
||||
void bind(@NonNull MessageRecord messageRecord,
|
||||
void bind(@NonNull ConversationMessage messageRecord,
|
||||
@NonNull Optional<MessageRecord> previousMessageRecord,
|
||||
@NonNull Optional<MessageRecord> nextMessageRecord,
|
||||
@NonNull GlideRequests glideRequests,
|
||||
@NonNull Locale locale,
|
||||
@NonNull Set<MessageRecord> batchSelected,
|
||||
@NonNull Recipient recipients,
|
||||
@Nullable String searchQuery,
|
||||
boolean pulseHighlight);
|
||||
@NonNull GlideRequests glideRequests,
|
||||
@NonNull Locale locale,
|
||||
@NonNull Set<ConversationMessage> batchSelected,
|
||||
@NonNull Recipient recipients,
|
||||
@Nullable String searchQuery,
|
||||
boolean pulseHighlight);
|
||||
|
||||
MessageRecord getMessageRecord();
|
||||
ConversationMessage getConversationMessage();
|
||||
|
||||
void setEventListener(@Nullable EventListener listener);
|
||||
|
||||
|
|
|
@ -65,8 +65,8 @@ import java.util.Set;
|
|||
* manager, so position 0 is at the bottom of the screen. That's why the "header" is at the bottom,
|
||||
* the "footer" is at the top, and we refer to the "next" record as having a lower index.
|
||||
*/
|
||||
public class ConversationAdapter<V extends View & BindableConversationItem>
|
||||
extends PagedListAdapter<MessageRecord, RecyclerView.ViewHolder>
|
||||
public class ConversationAdapter
|
||||
extends PagedListAdapter<ConversationMessage, RecyclerView.ViewHolder>
|
||||
implements StickyHeaderDecoration.StickyHeaderAdapter<ConversationAdapter.StickyHeaderViewHolder>
|
||||
{
|
||||
|
||||
|
@ -89,16 +89,16 @@ public class ConversationAdapter<V extends View & BindableConversationItem>
|
|||
private final Locale locale;
|
||||
private final Recipient recipient;
|
||||
|
||||
private final Set<MessageRecord> selected;
|
||||
private final List<MessageRecord> fastRecords;
|
||||
private final Set<Long> releasedFastRecords;
|
||||
private final Calendar calendar;
|
||||
private final MessageDigest digest;
|
||||
private final Set<ConversationMessage> selected;
|
||||
private final List<ConversationMessage> fastRecords;
|
||||
private final Set<Long> releasedFastRecords;
|
||||
private final Calendar calendar;
|
||||
private final MessageDigest digest;
|
||||
|
||||
private String searchQuery;
|
||||
private MessageRecord recordToPulseHighlight;
|
||||
private View headerView;
|
||||
private View footerView;
|
||||
private String searchQuery;
|
||||
private ConversationMessage recordToPulseHighlight;
|
||||
private View headerView;
|
||||
private View footerView;
|
||||
|
||||
ConversationAdapter(@NonNull GlideRequests glideRequests,
|
||||
@NonNull Locale locale,
|
||||
|
@ -130,7 +130,8 @@ public class ConversationAdapter<V extends View & BindableConversationItem>
|
|||
return MESSAGE_TYPE_FOOTER;
|
||||
}
|
||||
|
||||
MessageRecord messageRecord = getItem(position);
|
||||
ConversationMessage conversationMessage = getItem(position);
|
||||
MessageRecord messageRecord = (conversationMessage != null) ? conversationMessage.getMessageRecord() : null;
|
||||
|
||||
if (messageRecord == null) {
|
||||
return MESSAGE_TYPE_PLACEHOLDER;
|
||||
|
@ -153,16 +154,13 @@ public class ConversationAdapter<V extends View & BindableConversationItem>
|
|||
return FOOTER_ID;
|
||||
}
|
||||
|
||||
MessageRecord record = getItem(position);
|
||||
ConversationMessage message = getItem(position);
|
||||
|
||||
if (record == null) {
|
||||
if (message == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
String unique = (record.isMms() ? "MMS::" : "SMS::") + record.getId();
|
||||
byte[] bytes = digest.digest(unique.getBytes());
|
||||
|
||||
return Conversions.byteArrayToLong(bytes);
|
||||
return message.getUniqueId(digest);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -175,22 +173,23 @@ public class ConversationAdapter<V extends View & BindableConversationItem>
|
|||
case MESSAGE_TYPE_UPDATE:
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
V itemView = CachedInflater.from(parent.getContext()).inflate(getLayoutForViewType(viewType), parent, false);
|
||||
View itemView = CachedInflater.from(parent.getContext()).inflate(getLayoutForViewType(viewType), parent, false);
|
||||
BindableConversationItem bindable = (BindableConversationItem) itemView;
|
||||
|
||||
itemView.setOnClickListener(view -> {
|
||||
if (clickListener != null) {
|
||||
clickListener.onItemClick(itemView.getMessageRecord());
|
||||
clickListener.onItemClick(bindable.getConversationMessage());
|
||||
}
|
||||
});
|
||||
|
||||
itemView.setOnLongClickListener(view -> {
|
||||
if (clickListener != null) {
|
||||
clickListener.onItemLongClick(itemView, itemView.getMessageRecord());
|
||||
clickListener.onItemLongClick(itemView, bindable.getConversationMessage());
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
itemView.setEventListener(clickListener);
|
||||
bindable.setEventListener(clickListener);
|
||||
|
||||
Log.d(TAG, String.format(Locale.US, "Inflate time: %d ms for View type: %d", System.currentTimeMillis() - start, viewType));
|
||||
return new ConversationViewHolder(itemView);
|
||||
|
@ -215,23 +214,23 @@ public class ConversationAdapter<V extends View & BindableConversationItem>
|
|||
case MESSAGE_TYPE_OUTGOING_MULTIMEDIA:
|
||||
case MESSAGE_TYPE_UPDATE:
|
||||
ConversationViewHolder conversationViewHolder = (ConversationViewHolder) holder;
|
||||
MessageRecord messageRecord = Objects.requireNonNull(getItem(position));
|
||||
ConversationMessage conversationMessage = Objects.requireNonNull(getItem(position));
|
||||
int adapterPosition = holder.getAdapterPosition();
|
||||
|
||||
MessageRecord previousRecord = adapterPosition < getItemCount() - 1 && !isFooterPosition(adapterPosition + 1) ? getItem(adapterPosition + 1) : null;
|
||||
MessageRecord nextRecord = adapterPosition > 0 && !isHeaderPosition(adapterPosition - 1) ? getItem(adapterPosition - 1) : null;
|
||||
ConversationMessage previousMessage = adapterPosition < getItemCount() - 1 && !isFooterPosition(adapterPosition + 1) ? getItem(adapterPosition + 1) : null;
|
||||
ConversationMessage nextMessage = adapterPosition > 0 && !isHeaderPosition(adapterPosition - 1) ? getItem(adapterPosition - 1) : null;
|
||||
|
||||
conversationViewHolder.getView().bind(messageRecord,
|
||||
Optional.fromNullable(previousRecord),
|
||||
Optional.fromNullable(nextRecord),
|
||||
glideRequests,
|
||||
locale,
|
||||
selected,
|
||||
recipient,
|
||||
searchQuery,
|
||||
messageRecord == recordToPulseHighlight);
|
||||
conversationViewHolder.getBindable().bind(conversationMessage,
|
||||
Optional.fromNullable(previousMessage != null ? previousMessage.getMessageRecord() : null),
|
||||
Optional.fromNullable(nextMessage != null ? nextMessage.getMessageRecord() : null),
|
||||
glideRequests,
|
||||
locale,
|
||||
selected,
|
||||
recipient,
|
||||
searchQuery,
|
||||
conversationMessage == recordToPulseHighlight);
|
||||
|
||||
if (messageRecord == recordToPulseHighlight) {
|
||||
if (conversationMessage == recordToPulseHighlight) {
|
||||
recordToPulseHighlight = null;
|
||||
}
|
||||
break;
|
||||
|
@ -245,13 +244,13 @@ public class ConversationAdapter<V extends View & BindableConversationItem>
|
|||
}
|
||||
|
||||
@Override
|
||||
public void submitList(@Nullable PagedList<MessageRecord> pagedList) {
|
||||
public void submitList(@Nullable PagedList<ConversationMessage> pagedList) {
|
||||
cleanFastRecords();
|
||||
super.submitList(pagedList);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable MessageRecord getItem(int position) {
|
||||
protected @Nullable ConversationMessage getItem(int position) {
|
||||
position = hasHeader() ? position - 1 : position;
|
||||
|
||||
if (position < fastRecords.size()) {
|
||||
|
@ -272,7 +271,7 @@ public class ConversationAdapter<V extends View & BindableConversationItem>
|
|||
@Override
|
||||
public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
|
||||
if (holder instanceof ConversationViewHolder) {
|
||||
((ConversationViewHolder) holder).getView().unbind();
|
||||
((ConversationViewHolder) holder).getBindable().unbind();
|
||||
} else if (holder instanceof HeaderFooterViewHolder) {
|
||||
((HeaderFooterViewHolder) holder).unbind();
|
||||
}
|
||||
|
@ -285,11 +284,11 @@ public class ConversationAdapter<V extends View & BindableConversationItem>
|
|||
if (position >= getItemCount()) return -1;
|
||||
if (position < 0) return -1;
|
||||
|
||||
MessageRecord record = getItem(position);
|
||||
ConversationMessage conversationMessage = getItem(position);
|
||||
|
||||
if (record == null) return -1;
|
||||
if (conversationMessage == null) return -1;
|
||||
|
||||
calendar.setTime(new Date(record.getDateSent()));
|
||||
calendar.setTime(new Date(conversationMessage.getMessageRecord().getDateSent()));
|
||||
return Util.hashCode(calendar.get(Calendar.YEAR), calendar.get(Calendar.DAY_OF_YEAR));
|
||||
}
|
||||
|
||||
|
@ -300,8 +299,8 @@ public class ConversationAdapter<V extends View & BindableConversationItem>
|
|||
|
||||
@Override
|
||||
public void onBindHeaderViewHolder(StickyHeaderViewHolder viewHolder, int position) {
|
||||
MessageRecord messageRecord = Objects.requireNonNull(getItem(position));
|
||||
viewHolder.setText(DateUtils.getRelativeDate(viewHolder.itemView.getContext(), locale, messageRecord.getDateReceived()));
|
||||
ConversationMessage conversationMessage = Objects.requireNonNull(getItem(position));
|
||||
viewHolder.setText(DateUtils.getRelativeDate(viewHolder.itemView.getContext(), locale, conversationMessage.getMessageRecord().getDateReceived()));
|
||||
}
|
||||
|
||||
void onBindLastSeenViewHolder(StickyHeaderViewHolder viewHolder, int position) {
|
||||
|
@ -328,12 +327,12 @@ public class ConversationAdapter<V extends View & BindableConversationItem>
|
|||
if (position >= getItemCount()) return 0;
|
||||
if (position < 0) return 0;
|
||||
|
||||
MessageRecord messageRecord = getItem(position);
|
||||
ConversationMessage conversationMessage = getItem(position);
|
||||
|
||||
if (messageRecord == null || messageRecord.isOutgoing()) {
|
||||
if (conversationMessage == null || conversationMessage.getMessageRecord().isOutgoing()) {
|
||||
return 0;
|
||||
} else {
|
||||
return messageRecord.getDateReceived();
|
||||
return conversationMessage.getMessageRecord().getDateReceived();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -403,8 +402,8 @@ public class ConversationAdapter<V extends View & BindableConversationItem>
|
|||
* for a database change.
|
||||
*/
|
||||
@MainThread
|
||||
void addFastRecord(MessageRecord record) {
|
||||
fastRecords.add(0, record);
|
||||
void addFastRecord(ConversationMessage conversationMessage) {
|
||||
fastRecords.add(0, conversationMessage);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
@ -422,7 +421,7 @@ public class ConversationAdapter<V extends View & BindableConversationItem>
|
|||
/**
|
||||
* Returns set of records that are selected in multi-select mode.
|
||||
*/
|
||||
Set<MessageRecord> getSelectedItems() {
|
||||
Set<ConversationMessage> getSelectedItems() {
|
||||
return new HashSet<>(selected);
|
||||
}
|
||||
|
||||
|
@ -436,11 +435,11 @@ public class ConversationAdapter<V extends View & BindableConversationItem>
|
|||
/**
|
||||
* Toggles the selected state of a record in multi-select mode.
|
||||
*/
|
||||
void toggleSelection(MessageRecord record) {
|
||||
if (selected.contains(record)) {
|
||||
selected.remove(record);
|
||||
void toggleSelection(ConversationMessage conversationMessage) {
|
||||
if (selected.contains(conversationMessage)) {
|
||||
selected.remove(conversationMessage);
|
||||
} else {
|
||||
selected.add(record);
|
||||
selected.add(conversationMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -464,11 +463,11 @@ public class ConversationAdapter<V extends View & BindableConversationItem>
|
|||
Util.assertMainThread();
|
||||
|
||||
synchronized (releasedFastRecords) {
|
||||
Iterator<MessageRecord> recordIterator = fastRecords.iterator();
|
||||
while (recordIterator.hasNext()) {
|
||||
long id = recordIterator.next().getId();
|
||||
Iterator<ConversationMessage> messageIterator = fastRecords.iterator();
|
||||
while (messageIterator.hasNext()) {
|
||||
long id = messageIterator.next().getMessageRecord().getId();
|
||||
if (releasedFastRecords.contains(id)) {
|
||||
recordIterator.remove();
|
||||
messageIterator.remove();
|
||||
releasedFastRecords.remove(id);
|
||||
}
|
||||
}
|
||||
|
@ -510,18 +509,17 @@ public class ConversationAdapter<V extends View & BindableConversationItem>
|
|||
}
|
||||
}
|
||||
|
||||
public @Nullable MessageRecord getLastVisibleMessageRecord(int position) {
|
||||
public @Nullable ConversationMessage getLastVisibleConversationMessage(int position) {
|
||||
return getItem(position - ((hasFooter() && position == getItemCount() - 1) ? 1 : 0));
|
||||
}
|
||||
|
||||
static class ConversationViewHolder extends RecyclerView.ViewHolder {
|
||||
public <V extends View & BindableConversationItem> ConversationViewHolder(final @NonNull V itemView) {
|
||||
public ConversationViewHolder(final @NonNull View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
|
||||
public <V extends View & BindableConversationItem> V getView() {
|
||||
//noinspection unchecked
|
||||
return (V)itemView;
|
||||
public BindableConversationItem getBindable() {
|
||||
return (BindableConversationItem) itemView;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -530,7 +528,7 @@ public class ConversationAdapter<V extends View & BindableConversationItem>
|
|||
|
||||
StickyHeaderViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
textView = ViewUtil.findById(itemView, R.id.text);
|
||||
textView = itemView.findViewById(R.id.text);
|
||||
}
|
||||
|
||||
StickyHeaderViewHolder(TextView textView) {
|
||||
|
@ -571,21 +569,21 @@ public class ConversationAdapter<V extends View & BindableConversationItem>
|
|||
}
|
||||
}
|
||||
|
||||
private static class DiffCallback extends DiffUtil.ItemCallback<MessageRecord> {
|
||||
private static class DiffCallback extends DiffUtil.ItemCallback<ConversationMessage> {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull MessageRecord oldItem, @NonNull MessageRecord newItem) {
|
||||
return oldItem.isMms() == newItem.isMms() && oldItem.getId() == newItem.getId();
|
||||
public boolean areItemsTheSame(@NonNull ConversationMessage oldItem, @NonNull ConversationMessage newItem) {
|
||||
return oldItem.getMessageRecord().isMms() == newItem.getMessageRecord().isMms() && oldItem.getMessageRecord().getId() == newItem.getMessageRecord().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull MessageRecord oldItem, @NonNull MessageRecord newItem) {
|
||||
public boolean areContentsTheSame(@NonNull ConversationMessage oldItem, @NonNull ConversationMessage newItem) {
|
||||
// Corner rounding is not part of the model, so we can't use this yet
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
interface ItemClickListener extends BindableConversationItem.EventListener {
|
||||
void onItemClick(MessageRecord item);
|
||||
void onItemLongClick(View maskTarget, MessageRecord item);
|
||||
void onItemClick(ConversationMessage item);
|
||||
void onItemLongClick(View maskTarget, ConversationMessage item);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import androidx.annotation.NonNull;
|
|||
import androidx.paging.DataSource;
|
||||
import androidx.paging.PositionalDataSource;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseContentProviders;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
|
@ -24,7 +26,7 @@ import java.util.concurrent.Executor;
|
|||
/**
|
||||
* Core data source for loading an individual conversation.
|
||||
*/
|
||||
class ConversationDataSource extends PositionalDataSource<MessageRecord> {
|
||||
class ConversationDataSource extends PositionalDataSource<ConversationMessage> {
|
||||
|
||||
private static final String TAG = Log.tag(ConversationDataSource.class);
|
||||
|
||||
|
@ -57,7 +59,7 @@ class ConversationDataSource extends PositionalDataSource<MessageRecord> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<MessageRecord> callback) {
|
||||
public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<ConversationMessage> callback) {
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
MmsSmsDatabase db = DatabaseFactory.getMmsSmsDatabase(context);
|
||||
|
@ -76,14 +78,18 @@ class ConversationDataSource extends PositionalDataSource<MessageRecord> {
|
|||
if (!isInvalid()) {
|
||||
SizeFixResult<MessageRecord> result = SizeFixResult.ensureMultipleOfPageSize(records, params.requestedStartPosition, params.pageSize, totalCount);
|
||||
|
||||
callback.onResult(result.getItems(), params.requestedStartPosition, result.getTotal());
|
||||
List<ConversationMessage> items = Stream.of(result.getItems())
|
||||
.map(ConversationMessage::new)
|
||||
.toList();
|
||||
|
||||
callback.onResult(items, params.requestedStartPosition, result.getTotal());
|
||||
}
|
||||
|
||||
Log.d(TAG, "[Initial Load] " + (System.currentTimeMillis() - start) + " ms | thread: " + threadId + ", start: " + params.requestedStartPosition + ", size: " + params.requestedLoadSize + (isInvalid() ? " -- invalidated" : ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadRange(@NonNull LoadRangeParams params, @NonNull LoadRangeCallback<MessageRecord> callback) {
|
||||
public void loadRange(@NonNull LoadRangeParams params, @NonNull LoadRangeCallback<ConversationMessage> callback) {
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
MmsSmsDatabase db = DatabaseFactory.getMmsSmsDatabase(context);
|
||||
|
@ -96,12 +102,15 @@ class ConversationDataSource extends PositionalDataSource<MessageRecord> {
|
|||
}
|
||||
}
|
||||
|
||||
callback.onResult(records);
|
||||
List<ConversationMessage> items = Stream.of(records)
|
||||
.map(ConversationMessage::new)
|
||||
.toList();
|
||||
callback.onResult(items);
|
||||
|
||||
Log.d(TAG, "[Update] " + (System.currentTimeMillis() - start) + " ms | thread: " + threadId + ", start: " + params.startPosition + ", size: " + params.loadSize + (isInvalid() ? " -- invalidated" : ""));
|
||||
}
|
||||
|
||||
static class Factory extends DataSource.Factory<Integer, MessageRecord> {
|
||||
static class Factory extends DataSource.Factory<Integer, ConversationMessage> {
|
||||
|
||||
private final Context context;
|
||||
private final long threadId;
|
||||
|
@ -114,7 +123,7 @@ class ConversationDataSource extends PositionalDataSource<MessageRecord> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public @NonNull DataSource<Integer, MessageRecord> create() {
|
||||
public @NonNull DataSource<Integer, ConversationMessage> create() {
|
||||
return new ConversationDataSource(context, threadId, invalidator);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
|||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
|
||||
|
||||
import com.annimon.stream.Collectors;
|
||||
import com.annimon.stream.Stream;
|
||||
import com.google.android.collect.Sets;
|
||||
|
||||
|
@ -210,8 +211,8 @@ public class ConversationFragment extends LoggingFragment {
|
|||
typingView = (ConversationTypingView) inflater.inflate(R.layout.conversation_typing_view, container, false);
|
||||
|
||||
new ConversationItemSwipeCallback(
|
||||
messageRecord -> actionMode == null &&
|
||||
MenuState.canReplyToMessage(MenuState.isActionMessage(messageRecord), messageRecord, messageRequestViewModel.shouldShowMessageRequest()),
|
||||
conversationMessage -> actionMode == null &&
|
||||
MenuState.canReplyToMessage(MenuState.isActionMessage(conversationMessage.getMessageRecord()), conversationMessage.getMessageRecord(), messageRequestViewModel.shouldShowMessageRequest()),
|
||||
this::handleReplyMessage
|
||||
).attachToRecyclerView(list);
|
||||
|
||||
|
@ -288,9 +289,9 @@ public class ConversationFragment extends LoggingFragment {
|
|||
|
||||
final long lastVisibleMessageTimestamp;
|
||||
if (firstVisiblePosition > 0 && lastVisiblePosition != RecyclerView.NO_POSITION) {
|
||||
MessageRecord message = getListAdapter().getLastVisibleMessageRecord(lastVisiblePosition);
|
||||
ConversationMessage message = getListAdapter().getLastVisibleConversationMessage(lastVisiblePosition);
|
||||
|
||||
lastVisibleMessageTimestamp = message != null ? message.getDateReceived() : 0;
|
||||
lastVisibleMessageTimestamp = message != null ? message.getMessageRecord().getDateReceived() : 0;
|
||||
} else {
|
||||
lastVisibleMessageTimestamp = 0;
|
||||
}
|
||||
|
@ -519,14 +520,14 @@ public class ConversationFragment extends LoggingFragment {
|
|||
}
|
||||
|
||||
private void setCorrectMenuVisibility(@NonNull Menu menu) {
|
||||
Set<MessageRecord> messageRecords = getListAdapter().getSelectedItems();
|
||||
Set<ConversationMessage> messages = getListAdapter().getSelectedItems();
|
||||
|
||||
if (actionMode != null && messageRecords.size() == 0) {
|
||||
if (actionMode != null && messages.size() == 0) {
|
||||
actionMode.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
MenuState menuState = MenuState.getMenuState(messageRecords, messageRequestViewModel.shouldShowMessageRequest());
|
||||
MenuState menuState = MenuState.getMenuState(Stream.of(messages).map(ConversationMessage::getMessageRecord).collect(Collectors.toSet()), messageRequestViewModel.shouldShowMessageRequest());
|
||||
|
||||
menu.findItem(R.id.menu_context_forward).setVisible(menuState.shouldShowForwardAction());
|
||||
menu.findItem(R.id.menu_context_reply).setVisible(menuState.shouldShowReplyAction());
|
||||
|
@ -544,8 +545,8 @@ public class ConversationFragment extends LoggingFragment {
|
|||
return (SmoothScrollingLinearLayoutManager) list.getLayoutManager();
|
||||
}
|
||||
|
||||
private MessageRecord getSelectedMessageRecord() {
|
||||
Set<MessageRecord> messageRecords = getListAdapter().getSelectedItems();
|
||||
private ConversationMessage getSelectedConversationMessage() {
|
||||
Set<ConversationMessage> messageRecords = getListAdapter().getSelectedItems();
|
||||
|
||||
if (messageRecords.size() == 1) return messageRecords.iterator().next();
|
||||
else throw new AssertionError();
|
||||
|
@ -581,8 +582,8 @@ public class ConversationFragment extends LoggingFragment {
|
|||
list.addItemDecoration(lastSeenDecoration);
|
||||
}
|
||||
|
||||
private void handleCopyMessage(final Set<MessageRecord> messageRecords) {
|
||||
List<MessageRecord> messageList = new LinkedList<>(messageRecords);
|
||||
private void handleCopyMessage(final Set<ConversationMessage> conversationMessages) {
|
||||
List<MessageRecord> messageList = Stream.of(conversationMessages).map(ConversationMessage::getMessageRecord).toList();
|
||||
Collections.sort(messageList, new Comparator<MessageRecord>() {
|
||||
@Override
|
||||
public int compare(MessageRecord lhs, MessageRecord rhs) {
|
||||
|
@ -611,7 +612,8 @@ public class ConversationFragment extends LoggingFragment {
|
|||
clipboard.setText(result);
|
||||
}
|
||||
|
||||
private void handleDeleteMessages(final Set<MessageRecord> messageRecords) {
|
||||
private void handleDeleteMessages(final Set<ConversationMessage> conversationMessages) {
|
||||
Set<MessageRecord> messageRecords = Stream.of(conversationMessages).map(ConversationMessage::getMessageRecord).collect(Collectors.toSet());
|
||||
if (FeatureFlags.remoteDelete()) {
|
||||
buildRemoteDeleteConfirmationDialog(messageRecords).show();
|
||||
} else {
|
||||
|
@ -725,11 +727,12 @@ public class ConversationFragment extends LoggingFragment {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleDisplayDetails(MessageRecord message) {
|
||||
startActivity(MessageDetailsActivity.getIntentForMessageDetails(requireContext(), message, recipient.getId(), threadId));
|
||||
private void handleDisplayDetails(ConversationMessage message) {
|
||||
startActivity(MessageDetailsActivity.getIntentForMessageDetails(requireContext(), message.getMessageRecord(), recipient.getId(), threadId));
|
||||
}
|
||||
|
||||
private void handleForwardMessage(MessageRecord message) {
|
||||
private void handleForwardMessage(ConversationMessage conversationMessage) {
|
||||
MessageRecord message = conversationMessage.getMessageRecord();
|
||||
if (message.isViewOnce()) {
|
||||
throw new AssertionError("Cannot forward a view-once message.");
|
||||
}
|
||||
|
@ -812,13 +815,13 @@ public class ConversationFragment extends LoggingFragment {
|
|||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, message);
|
||||
}
|
||||
|
||||
private void handleReplyMessage(final MessageRecord message) {
|
||||
private void handleReplyMessage(final ConversationMessage message) {
|
||||
if (getActivity() != null) {
|
||||
//noinspection ConstantConditions
|
||||
((AppCompatActivity) getActivity()).getSupportActionBar().collapseActionView();
|
||||
}
|
||||
|
||||
listener.handleReplyMessage(message);
|
||||
listener.handleReplyMessage(message.getMessageRecord());
|
||||
}
|
||||
|
||||
private void handleSaveAttachment(final MediaMmsMessageRecord message) {
|
||||
|
@ -858,7 +861,7 @@ public class ConversationFragment extends LoggingFragment {
|
|||
if (getListAdapter() != null) {
|
||||
clearHeaderIfNotTyping(getListAdapter());
|
||||
setLastSeen(0);
|
||||
getListAdapter().addFastRecord(messageRecord);
|
||||
getListAdapter().addFastRecord(new ConversationMessage(messageRecord));
|
||||
list.post(() -> list.scrollToPosition(0));
|
||||
}
|
||||
|
||||
|
@ -871,7 +874,7 @@ public class ConversationFragment extends LoggingFragment {
|
|||
if (getListAdapter() != null) {
|
||||
clearHeaderIfNotTyping(getListAdapter());
|
||||
setLastSeen(0);
|
||||
getListAdapter().addFastRecord(messageRecord);
|
||||
getListAdapter().addFastRecord(new ConversationMessage(messageRecord));
|
||||
list.post(() -> list.scrollToPosition(0));
|
||||
}
|
||||
|
||||
|
@ -1085,9 +1088,9 @@ public class ConversationFragment extends LoggingFragment {
|
|||
private class ConversationFragmentItemClickListener implements ItemClickListener {
|
||||
|
||||
@Override
|
||||
public void onItemClick(MessageRecord messageRecord) {
|
||||
public void onItemClick(ConversationMessage conversationMessage) {
|
||||
if (actionMode != null) {
|
||||
((ConversationAdapter) list.getAdapter()).toggleSelection(messageRecord);
|
||||
((ConversationAdapter) list.getAdapter()).toggleSelection(conversationMessage);
|
||||
list.getAdapter().notifyDataSetChanged();
|
||||
|
||||
if (getListAdapter().getSelectedItems().size() == 0) {
|
||||
|
@ -1100,10 +1103,12 @@ public class ConversationFragment extends LoggingFragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onItemLongClick(View maskTarget, MessageRecord messageRecord) {
|
||||
public void onItemLongClick(View maskTarget, ConversationMessage conversationMessage) {
|
||||
|
||||
if (actionMode != null) return;
|
||||
|
||||
MessageRecord messageRecord = conversationMessage.getMessageRecord();
|
||||
|
||||
if (messageRecord.isSecure() &&
|
||||
!messageRecord.isRemoteDelete() &&
|
||||
!messageRecord.isUpdate() &&
|
||||
|
@ -1113,12 +1118,12 @@ public class ConversationFragment extends LoggingFragment {
|
|||
{
|
||||
isReacting = true;
|
||||
list.setLayoutFrozen(true);
|
||||
listener.handleReaction(maskTarget, messageRecord, new ReactionsToolbarListener(messageRecord), () -> {
|
||||
listener.handleReaction(maskTarget, messageRecord, new ReactionsToolbarListener(conversationMessage), () -> {
|
||||
isReacting = false;
|
||||
list.setLayoutFrozen(false);
|
||||
});
|
||||
} else {
|
||||
((ConversationAdapter) list.getAdapter()).toggleSelection(messageRecord);
|
||||
((ConversationAdapter) list.getAdapter()).toggleSelection(conversationMessage);
|
||||
list.getAdapter().notifyDataSetChanged();
|
||||
|
||||
actionMode = ((AppCompatActivity)getActivity()).startSupportActionMode(actionModeCallback);
|
||||
|
@ -1287,8 +1292,8 @@ public class ConversationFragment extends LoggingFragment {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleEnterMultiSelect(@NonNull MessageRecord messageRecord) {
|
||||
((ConversationAdapter) list.getAdapter()).toggleSelection(messageRecord);
|
||||
private void handleEnterMultiSelect(@NonNull ConversationMessage conversationMessage) {
|
||||
((ConversationAdapter) list.getAdapter()).toggleSelection(conversationMessage);
|
||||
list.getAdapter().notifyDataSetChanged();
|
||||
|
||||
actionMode = ((AppCompatActivity)getActivity()).startSupportActionMode(actionModeCallback);
|
||||
|
@ -1342,23 +1347,23 @@ public class ConversationFragment extends LoggingFragment {
|
|||
|
||||
private class ReactionsToolbarListener implements Toolbar.OnMenuItemClickListener {
|
||||
|
||||
private final MessageRecord messageRecord;
|
||||
private final ConversationMessage conversationMessage;
|
||||
|
||||
private ReactionsToolbarListener(@NonNull MessageRecord messageRecord) {
|
||||
this.messageRecord = messageRecord;
|
||||
private ReactionsToolbarListener(@NonNull ConversationMessage conversationMessage) {
|
||||
this.conversationMessage = conversationMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_info: handleDisplayDetails(messageRecord); return true;
|
||||
case R.id.action_delete: handleDeleteMessages(Sets.newHashSet(messageRecord)); return true;
|
||||
case R.id.action_copy: handleCopyMessage(Sets.newHashSet(messageRecord)); return true;
|
||||
case R.id.action_reply: handleReplyMessage(messageRecord); return true;
|
||||
case R.id.action_multiselect: handleEnterMultiSelect(messageRecord); return true;
|
||||
case R.id.action_forward: handleForwardMessage(messageRecord); return true;
|
||||
case R.id.action_download: handleSaveAttachment((MediaMmsMessageRecord) messageRecord); return true;
|
||||
default: return false;
|
||||
case R.id.action_info: handleDisplayDetails(conversationMessage); return true;
|
||||
case R.id.action_delete: handleDeleteMessages(Sets.newHashSet(conversationMessage)); return true;
|
||||
case R.id.action_copy: handleCopyMessage(Sets.newHashSet(conversationMessage)); return true;
|
||||
case R.id.action_reply: handleReplyMessage(conversationMessage); return true;
|
||||
case R.id.action_multiselect: handleEnterMultiSelect(conversationMessage); return true;
|
||||
case R.id.action_forward: handleForwardMessage(conversationMessage); return true;
|
||||
case R.id.action_download: handleSaveAttachment((MediaMmsMessageRecord) conversationMessage.getMessageRecord()); return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1417,24 +1422,24 @@ public class ConversationFragment extends LoggingFragment {
|
|||
actionMode.finish();
|
||||
return true;
|
||||
case R.id.menu_context_details:
|
||||
handleDisplayDetails(getSelectedMessageRecord());
|
||||
handleDisplayDetails(getSelectedConversationMessage());
|
||||
actionMode.finish();
|
||||
return true;
|
||||
case R.id.menu_context_forward:
|
||||
handleForwardMessage(getSelectedMessageRecord());
|
||||
handleForwardMessage(getSelectedConversationMessage());
|
||||
actionMode.finish();
|
||||
return true;
|
||||
case R.id.menu_context_resend:
|
||||
handleResendMessage(getSelectedMessageRecord());
|
||||
handleResendMessage(getSelectedConversationMessage().getMessageRecord());
|
||||
actionMode.finish();
|
||||
return true;
|
||||
case R.id.menu_context_save_attachment:
|
||||
handleSaveAttachment((MediaMmsMessageRecord)getSelectedMessageRecord());
|
||||
handleSaveAttachment((MediaMmsMessageRecord) getSelectedConversationMessage().getMessageRecord());
|
||||
actionMode.finish();
|
||||
return true;
|
||||
case R.id.menu_context_reply:
|
||||
maybeShowSwipeToReplyTooltip();
|
||||
handleReplyMessage(getSelectedMessageRecord());
|
||||
handleReplyMessage(getSelectedConversationMessage());
|
||||
actionMode.finish();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -139,11 +139,12 @@ public class ConversationItem extends LinearLayout implements BindableConversati
|
|||
|
||||
private static final Rect SWIPE_RECT = new Rect();
|
||||
|
||||
private MessageRecord messageRecord;
|
||||
private Locale locale;
|
||||
private boolean groupThread;
|
||||
private LiveRecipient recipient;
|
||||
private GlideRequests glideRequests;
|
||||
private ConversationMessage conversationMessage;
|
||||
private MessageRecord messageRecord;
|
||||
private Locale locale;
|
||||
private boolean groupThread;
|
||||
private LiveRecipient recipient;
|
||||
private GlideRequests glideRequests;
|
||||
|
||||
protected ConversationItemBodyBubble bodyBubble;
|
||||
protected View reply;
|
||||
|
@ -160,7 +161,7 @@ public class ConversationItem extends LinearLayout implements BindableConversati
|
|||
private ViewGroup container;
|
||||
protected ReactionsConversationView reactionsView;
|
||||
|
||||
private @NonNull Set<MessageRecord> batchSelected = new HashSet<>();
|
||||
private @NonNull Set<ConversationMessage> batchSelected = new HashSet<>();
|
||||
private @NonNull Outliner outliner = new Outliner();
|
||||
private LiveRecipient conversationRecipient;
|
||||
private Stub<ConversationItemThumbnail> mediaThumbnailStub;
|
||||
|
@ -234,22 +235,23 @@ public class ConversationItem extends LinearLayout implements BindableConversati
|
|||
}
|
||||
|
||||
@Override
|
||||
public void bind(@NonNull MessageRecord messageRecord,
|
||||
public void bind(@NonNull ConversationMessage conversationMessage,
|
||||
@NonNull Optional<MessageRecord> previousMessageRecord,
|
||||
@NonNull Optional<MessageRecord> nextMessageRecord,
|
||||
@NonNull GlideRequests glideRequests,
|
||||
@NonNull Locale locale,
|
||||
@NonNull Set<MessageRecord> batchSelected,
|
||||
@NonNull Recipient conversationRecipient,
|
||||
@Nullable String searchQuery,
|
||||
boolean pulseHighlight)
|
||||
@NonNull GlideRequests glideRequests,
|
||||
@NonNull Locale locale,
|
||||
@NonNull Set<ConversationMessage> batchSelected,
|
||||
@NonNull Recipient conversationRecipient,
|
||||
@Nullable String searchQuery,
|
||||
boolean pulseHighlight)
|
||||
{
|
||||
if (this.recipient != null) this.recipient.removeForeverObserver(this);
|
||||
if (this.conversationRecipient != null) this.conversationRecipient.removeForeverObserver(this);
|
||||
|
||||
conversationRecipient = conversationRecipient.resolve();
|
||||
|
||||
this.messageRecord = messageRecord;
|
||||
this.conversationMessage = conversationMessage;
|
||||
this.messageRecord = conversationMessage.getMessageRecord();
|
||||
this.locale = locale;
|
||||
this.glideRequests = glideRequests;
|
||||
this.batchSelected = batchSelected;
|
||||
|
@ -263,7 +265,7 @@ public class ConversationItem extends LinearLayout implements BindableConversati
|
|||
setGutterSizes(messageRecord, groupThread);
|
||||
setMessageShape(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
|
||||
setMediaAttributes(messageRecord, previousMessageRecord, nextMessageRecord, conversationRecipient, groupThread);
|
||||
setInteractionState(messageRecord, pulseHighlight);
|
||||
setInteractionState(conversationMessage, pulseHighlight);
|
||||
setBodyText(messageRecord, searchQuery);
|
||||
setBubbleState(messageRecord);
|
||||
setStatusIcons(messageRecord);
|
||||
|
@ -381,8 +383,8 @@ public class ConversationItem extends LinearLayout implements BindableConversati
|
|||
}
|
||||
}
|
||||
|
||||
public MessageRecord getMessageRecord() {
|
||||
return messageRecord;
|
||||
public ConversationMessage getConversationMessage() {
|
||||
return conversationMessage;
|
||||
}
|
||||
|
||||
/// MessageRecord Attribute Parsers
|
||||
|
@ -424,8 +426,8 @@ public class ConversationItem extends LinearLayout implements BindableConversati
|
|||
}
|
||||
}
|
||||
|
||||
private void setInteractionState(MessageRecord messageRecord, boolean pulseHighlight) {
|
||||
if (batchSelected.contains(messageRecord)) {
|
||||
private void setInteractionState(ConversationMessage conversationMessage, boolean pulseHighlight) {
|
||||
if (batchSelected.contains(conversationMessage)) {
|
||||
setBackgroundResource(R.drawable.conversation_item_background);
|
||||
setSelected(true);
|
||||
} else if (pulseHighlight) {
|
||||
|
@ -437,19 +439,19 @@ public class ConversationItem extends LinearLayout implements BindableConversati
|
|||
}
|
||||
|
||||
if (mediaThumbnailStub.resolved()) {
|
||||
mediaThumbnailStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty());
|
||||
mediaThumbnailStub.get().setClickable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty());
|
||||
mediaThumbnailStub.get().setFocusable(!shouldInterceptClicks(conversationMessage.getMessageRecord()) && batchSelected.isEmpty());
|
||||
mediaThumbnailStub.get().setClickable(!shouldInterceptClicks(conversationMessage.getMessageRecord()) && batchSelected.isEmpty());
|
||||
mediaThumbnailStub.get().setLongClickable(batchSelected.isEmpty());
|
||||
}
|
||||
|
||||
if (audioViewStub.resolved()) {
|
||||
audioViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty());
|
||||
audioViewStub.get().setFocusable(!shouldInterceptClicks(conversationMessage.getMessageRecord()) && batchSelected.isEmpty());
|
||||
audioViewStub.get().setClickable(batchSelected.isEmpty());
|
||||
audioViewStub.get().setEnabled(batchSelected.isEmpty());
|
||||
}
|
||||
|
||||
if (documentViewStub.resolved()) {
|
||||
documentViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty());
|
||||
documentViewStub.get().setFocusable(!shouldInterceptClicks(conversationMessage.getMessageRecord()) && batchSelected.isEmpty());
|
||||
documentViewStub.get().setClickable(batchSelected.isEmpty());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,8 +114,8 @@ class ConversationItemSwipeCallback extends ItemTouchHelper.SimpleCallback {
|
|||
private void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder) {
|
||||
if (cannotSwipeViewHolder(viewHolder)) return;
|
||||
|
||||
ConversationItem item = ((ConversationItem) viewHolder.itemView);
|
||||
MessageRecord messageRecord = item.getMessageRecord();
|
||||
ConversationItem item = ((ConversationItem) viewHolder.itemView);
|
||||
ConversationMessage messageRecord = item.getConversationMessage();
|
||||
|
||||
onSwipeListener.onSwipe(messageRecord);
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ class ConversationItemSwipeCallback extends ItemTouchHelper.SimpleCallback {
|
|||
if (!(viewHolder.itemView instanceof ConversationItem)) return true;
|
||||
|
||||
ConversationItem item = ((ConversationItem) viewHolder.itemView);
|
||||
return !swipeAvailabilityProvider.isSwipeAvailable(item.getMessageRecord()) ||
|
||||
return !swipeAvailabilityProvider.isSwipeAvailable(item.getConversationMessage()) ||
|
||||
item.disallowSwipe(latestDownX, latestDownY);
|
||||
}
|
||||
|
||||
|
@ -192,10 +192,10 @@ class ConversationItemSwipeCallback extends ItemTouchHelper.SimpleCallback {
|
|||
}
|
||||
|
||||
interface SwipeAvailabilityProvider {
|
||||
boolean isSwipeAvailable(MessageRecord messageRecord);
|
||||
boolean isSwipeAvailable(ConversationMessage conversationMessage);
|
||||
}
|
||||
|
||||
interface OnSwipeListener {
|
||||
void onSwipe(MessageRecord messageRecord);
|
||||
void onSwipe(ConversationMessage conversationMessage);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package org.thoughtcrime.securesms.conversation;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.util.Conversions;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/**
|
||||
* A view level model used to pass arbitrary message related information needed
|
||||
* for various presentations.
|
||||
*/
|
||||
public class ConversationMessage {
|
||||
private final MessageRecord messageRecord;
|
||||
|
||||
public ConversationMessage(@NonNull MessageRecord messageRecord) {
|
||||
this.messageRecord = messageRecord;
|
||||
}
|
||||
|
||||
public @NonNull MessageRecord getMessageRecord() {
|
||||
return messageRecord;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final ConversationMessage that = (ConversationMessage) o;
|
||||
return messageRecord.equals(that.messageRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return messageRecord.hashCode();
|
||||
}
|
||||
|
||||
public long getUniqueId(@NonNull MessageDigest digest) {
|
||||
String unique = (messageRecord.isMms() ? "MMS::" : "SMS::") + messageRecord.getId();
|
||||
byte[] bytes = digest.digest(unique.getBytes());
|
||||
|
||||
return Conversions.byteArrayToLong(bytes);
|
||||
}
|
||||
}
|
|
@ -50,13 +50,14 @@ public final class ConversationUpdateItem extends LinearLayout
|
|||
{
|
||||
private static final String TAG = ConversationUpdateItem.class.getSimpleName();
|
||||
|
||||
private Set<MessageRecord> batchSelected;
|
||||
private Set<ConversationMessage> batchSelected;
|
||||
|
||||
private ImageView icon;
|
||||
private TextView title;
|
||||
private TextView body;
|
||||
private TextView date;
|
||||
private LiveRecipient sender;
|
||||
private ConversationMessage conversationMessage;
|
||||
private MessageRecord messageRecord;
|
||||
private Locale locale;
|
||||
private LiveData<SpannableString> displayBody;
|
||||
|
@ -84,19 +85,19 @@ public final class ConversationUpdateItem extends LinearLayout
|
|||
}
|
||||
|
||||
@Override
|
||||
public void bind(@NonNull MessageRecord messageRecord,
|
||||
public void bind(@NonNull ConversationMessage conversationMessage,
|
||||
@NonNull Optional<MessageRecord> previousMessageRecord,
|
||||
@NonNull Optional<MessageRecord> nextMessageRecord,
|
||||
@NonNull GlideRequests glideRequests,
|
||||
@NonNull Locale locale,
|
||||
@NonNull Set<MessageRecord> batchSelected,
|
||||
@NonNull Recipient conversationRecipient,
|
||||
@Nullable String searchQuery,
|
||||
boolean pulseUpdate)
|
||||
@NonNull GlideRequests glideRequests,
|
||||
@NonNull Locale locale,
|
||||
@NonNull Set<ConversationMessage> batchSelected,
|
||||
@NonNull Recipient conversationRecipient,
|
||||
@Nullable String searchQuery,
|
||||
boolean pulseUpdate)
|
||||
{
|
||||
this.batchSelected = batchSelected;
|
||||
|
||||
bind(messageRecord, locale);
|
||||
bind(conversationMessage, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -111,11 +112,11 @@ public final class ConversationUpdateItem extends LinearLayout
|
|||
}
|
||||
|
||||
@Override
|
||||
public MessageRecord getMessageRecord() {
|
||||
return messageRecord;
|
||||
public ConversationMessage getConversationMessage() {
|
||||
return conversationMessage;
|
||||
}
|
||||
|
||||
private void bind(@NonNull MessageRecord messageRecord, @NonNull Locale locale) {
|
||||
private void bind(@NonNull ConversationMessage conversationMessage, @NonNull Locale locale) {
|
||||
if (this.sender != null) {
|
||||
this.sender.removeForeverObserver(this);
|
||||
}
|
||||
|
@ -123,9 +124,10 @@ public final class ConversationUpdateItem extends LinearLayout
|
|||
observeDisplayBody(null);
|
||||
setBodyText(null);
|
||||
|
||||
this.messageRecord = messageRecord;
|
||||
this.sender = messageRecord.getIndividualRecipient().live();
|
||||
this.locale = locale;
|
||||
this.conversationMessage = conversationMessage;
|
||||
this.messageRecord = conversationMessage.getMessageRecord();
|
||||
this.sender = messageRecord.getIndividualRecipient().live();
|
||||
this.locale = locale;
|
||||
|
||||
this.sender.observeForever(this);
|
||||
|
||||
|
@ -133,7 +135,7 @@ public final class ConversationUpdateItem extends LinearLayout
|
|||
LiveData<String> liveUpdateMessage = LiveUpdateMessage.fromMessageDescription(updateDescription);
|
||||
LiveData<SpannableString> spannableStringMessage = Transformations.map(liveUpdateMessage, SpannableString::new);
|
||||
|
||||
present(messageRecord);
|
||||
present(conversationMessage);
|
||||
|
||||
observeDisplayBody(spannableStringMessage);
|
||||
}
|
||||
|
@ -162,7 +164,8 @@ public final class ConversationUpdateItem extends LinearLayout
|
|||
}
|
||||
}
|
||||
|
||||
private void present(MessageRecord messageRecord) {
|
||||
private void present(ConversationMessage conversationMessage) {
|
||||
MessageRecord messageRecord = conversationMessage.getMessageRecord();
|
||||
if (messageRecord.isGroupAction()) setGroupRecord();
|
||||
else if (messageRecord.isCallLog()) setCallRecord(messageRecord);
|
||||
else if (messageRecord.isJoined()) setJoinedRecord();
|
||||
|
@ -174,8 +177,8 @@ public final class ConversationUpdateItem extends LinearLayout
|
|||
else if (messageRecord.isProfileChange()) setProfileNameChangeRecord();
|
||||
else throw new AssertionError("Neither group nor log nor joined.");
|
||||
|
||||
if (batchSelected.contains(messageRecord)) setSelected(true);
|
||||
else setSelected(false);
|
||||
if (batchSelected.contains(conversationMessage)) setSelected(true);
|
||||
else setSelected(false);
|
||||
}
|
||||
|
||||
private void setCallRecord(MessageRecord messageRecord) {
|
||||
|
@ -256,7 +259,7 @@ public final class ConversationUpdateItem extends LinearLayout
|
|||
|
||||
@Override
|
||||
public void onRecipientChanged(@NonNull Recipient recipient) {
|
||||
present(messageRecord);
|
||||
present(conversationMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -28,14 +28,14 @@ class ConversationViewModel extends ViewModel {
|
|||
|
||||
private static final String TAG = Log.tag(ConversationViewModel.class);
|
||||
|
||||
private final Application context;
|
||||
private final MediaRepository mediaRepository;
|
||||
private final ConversationRepository conversationRepository;
|
||||
private final MutableLiveData<List<Media>> recentMedia;
|
||||
private final MutableLiveData<Long> threadId;
|
||||
private final LiveData<PagedList<MessageRecord>> messages;
|
||||
private final LiveData<ConversationData> conversationMetadata;
|
||||
private final Invalidator invalidator;
|
||||
private final Application context;
|
||||
private final MediaRepository mediaRepository;
|
||||
private final ConversationRepository conversationRepository;
|
||||
private final MutableLiveData<List<Media>> recentMedia;
|
||||
private final MutableLiveData<Long> threadId;
|
||||
private final LiveData<PagedList<ConversationMessage>> messages;
|
||||
private final LiveData<ConversationData> conversationMetadata;
|
||||
private final Invalidator invalidator;
|
||||
|
||||
private int jumpToPosition;
|
||||
|
||||
|
@ -55,12 +55,12 @@ class ConversationViewModel extends ViewModel {
|
|||
return conversationData;
|
||||
});
|
||||
|
||||
LiveData<Pair<Long, PagedList<MessageRecord>>> messagesForThreadId = Transformations.switchMap(metadata, data -> {
|
||||
DataSource.Factory<Integer, MessageRecord> factory = new ConversationDataSource.Factory(context, data.getThreadId(), invalidator);
|
||||
PagedList.Config config = new PagedList.Config.Builder()
|
||||
.setPageSize(25)
|
||||
.setInitialLoadSizeHint(25)
|
||||
.build();
|
||||
LiveData<Pair<Long, PagedList<ConversationMessage>>> messagesForThreadId = Transformations.switchMap(metadata, data -> {
|
||||
DataSource.Factory<Integer, ConversationMessage> factory = new ConversationDataSource.Factory(context, data.getThreadId(), invalidator);
|
||||
PagedList.Config config = new PagedList.Config.Builder()
|
||||
.setPageSize(25)
|
||||
.setInitialLoadSizeHint(25)
|
||||
.build();
|
||||
|
||||
final int startPosition;
|
||||
if (data.shouldJumpToMessage()) {
|
||||
|
@ -109,7 +109,7 @@ class ConversationViewModel extends ViewModel {
|
|||
return conversationMetadata;
|
||||
}
|
||||
|
||||
@NonNull LiveData<PagedList<MessageRecord>> getMessages() {
|
||||
@NonNull LiveData<PagedList<ConversationMessage>> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationItem;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationMessage;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
|
@ -80,7 +81,7 @@ final class MessageHeaderViewHolder extends RecyclerView.ViewHolder {
|
|||
else if (messageRecord.isOutgoing()) conversationItem = (ConversationItem) sentStub.inflate();
|
||||
else conversationItem = (ConversationItem) receivedStub.inflate();
|
||||
}
|
||||
conversationItem.bind(messageRecord, Optional.absent(), Optional.absent(), glideRequests, Locale.getDefault(), new HashSet<>(), messageRecord.getRecipient(), null, false);
|
||||
conversationItem.bind(new ConversationMessage(messageRecord), Optional.absent(), Optional.absent(), glideRequests, Locale.getDefault(), new HashSet<>(), messageRecord.getRecipient(), null, false);
|
||||
}
|
||||
|
||||
private void bindErrorState(MessageRecord messageRecord) {
|
||||
|
|
Loading…
Reference in New Issue