Add support for granular conversation data changes.

This commit is contained in:
Greyson Parrelli
2021-08-30 15:08:38 -04:00
parent bca2205945
commit f5a6d61362
28 changed files with 502 additions and 162 deletions

View File

@@ -2927,7 +2927,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
long id = fragment.stageOutgoingMessage(secureMessage);
SimpleTask.run(() -> {
long resultId = MessageSender.sendPushWithPreUploadedMedia(this, secureMessage, result.getPreUploadResults(), thread, () -> fragment.releaseOutgoingMessage(id));
long resultId = MessageSender.sendPushWithPreUploadedMedia(this, secureMessage, result.getPreUploadResults(), thread, null);
int deleted = DatabaseFactory.getAttachmentDatabase(this).deleteAbandonedPreuploadedAttachments();
Log.i(TAG, "Deleted " + deleted + " abandoned attachments.");
@@ -3017,7 +3017,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
final long id = fragment.stageOutgoingMessage(outgoingMessage);
SimpleTask.run(() -> {
return MessageSender.send(context, outgoingMessage, thread, forceSms, metricId, () -> fragment.releaseOutgoingMessage(id));
return MessageSender.send(context, outgoingMessage, thread, forceSms, metricId, null);
}, result -> {
sendComplete(result);
future.set(null);
@@ -3058,7 +3058,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
.onAllGranted(() -> {
final long id = new SecureRandom().nextLong();
SimpleTask.run(() -> {
return MessageSender.send(context, message, thread, forceSms, metricId, () -> fragment.releaseOutgoingMessage(id));
return MessageSender.send(context, message, thread, forceSms, metricId, null);
}, this::sendComplete);
silentlySetComposeText("");

View File

@@ -13,16 +13,21 @@ import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.conversation.ConversationData.MessageRequestData;
import org.thoughtcrime.securesms.conversation.ConversationMessage.ConversationMessageFactory;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MentionDatabase;
import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.model.InMemoryMessageRecord;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.Mention;
import org.thoughtcrime.securesms.database.model.MessageId;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.util.Stopwatch;
import org.thoughtcrime.securesms.util.Util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -32,7 +37,7 @@ import java.util.stream.Collectors;
/**
* Core data source for loading an individual conversation.
*/
class ConversationDataSource implements PagedDataSource<ConversationMessage> {
class ConversationDataSource implements PagedDataSource<MessageId, ConversationMessage> {
private static final String TAG = Log.tag(ConversationDataSource.class);
@@ -109,6 +114,48 @@ class ConversationDataSource implements PagedDataSource<ConversationMessage> {
return messages;
}
@Override
public @Nullable ConversationMessage load(@NonNull MessageId messageId) {
Stopwatch stopwatch = new Stopwatch("load(" + messageId + "), thread " + threadId);
MessageDatabase database = messageId.isMms() ? DatabaseFactory.getMmsDatabase(context) : DatabaseFactory.getSmsDatabase(context);
MessageRecord record = database.getMessageRecordOrNull(messageId.getId());
stopwatch.split("message");
try {
if (record != null) {
List<Mention> mentions;
if (messageId.isMms()) {
mentions = DatabaseFactory.getMentionDatabase(context).getMentionsForMessage(messageId.getId());
} else {
mentions = Collections.emptyList();
}
stopwatch.split("mentions");
if (messageId.isMms()) {
List<DatabaseAttachment> attachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(messageId.getId());
if (attachments.size() > 0) {
record = ((MediaMmsMessageRecord) record).withAttachments(context, attachments);
}
}
stopwatch.split("attachments");
return ConversationMessage.ConversationMessageFactory.createWithUnresolvedData(ApplicationDependencies.getApplication(), record, mentions);
} else {
return null;
}
} finally {
stopwatch.stop(TAG);
}
}
@Override
public @NonNull MessageId getKey(@NonNull ConversationMessage conversationMessage) {
return new MessageId(conversationMessage.getMessageRecord().getId(), conversationMessage.getMessageRecord().isMms());
}
private static class MentionHelper {
private Collection<Long> messageIds = new LinkedList<>();

View File

@@ -1055,7 +1055,6 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
if (getListAdapter() != null) {
clearHeaderIfNotTyping(getListAdapter());
setLastSeen(0);
getListAdapter().addFastRecord(ConversationMessageFactory.createWithResolvedData(messageRecord, messageRecord.getDisplayBody(requireContext()), message.getMentions()));
list.post(() -> list.scrollToPosition(0));
}
@@ -1068,19 +1067,12 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
if (getListAdapter() != null) {
clearHeaderIfNotTyping(getListAdapter());
setLastSeen(0);
getListAdapter().addFastRecord(ConversationMessageFactory.createWithResolvedData(messageRecord));
list.post(() -> list.scrollToPosition(0));
}
return messageRecord.getId();
}
public void releaseOutgoingMessage(long id) {
if (getListAdapter() != null) {
getListAdapter().releaseFastRecord(id);
}
}
private void presentConversationMetadata(@NonNull ConversationData conversation) {
ConversationAdapter adapter = getListAdapter();
if (adapter == null) {

View File

@@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.conversation.colors.ChatColors;
import org.thoughtcrime.securesms.conversation.colors.ChatColorsPalette;
import org.thoughtcrime.securesms.conversation.colors.NameColor;
import org.thoughtcrime.securesms.database.DatabaseObserver;
import org.thoughtcrime.securesms.database.model.MessageId;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.LiveGroup;
@@ -64,8 +65,10 @@ public class ConversationViewModel extends ViewModel {
private final MutableLiveData<Boolean> showScrollButtons;
private final MutableLiveData<Boolean> hasUnreadMentions;
private final LiveData<Boolean> canShowAsBubble;
private final ProxyPagingController pagingController;
private final DatabaseObserver.Observer messageObserver;
private final ProxyPagingController<MessageId> pagingController;
private final DatabaseObserver.Observer conversationObserver;
private final DatabaseObserver.MessageObserver messageUpdateObserver;
private final DatabaseObserver.MessageObserver messageInsertObserver;
private final MutableLiveData<RecipientId> recipientId;
private final LiveData<ChatWallpaper> wallpaper;
private final SingleLiveEvent<Event> events;
@@ -89,8 +92,10 @@ public class ConversationViewModel extends ViewModel {
this.hasUnreadMentions = new MutableLiveData<>(false);
this.recipientId = new MutableLiveData<>();
this.events = new SingleLiveEvent<>();
this.pagingController = new ProxyPagingController();
this.messageObserver = pagingController::onDataInvalidated;
this.pagingController = new ProxyPagingController<>();
this.conversationObserver = pagingController::onDataInvalidated;
this.messageUpdateObserver = pagingController::onDataItemChanged;
this.messageInsertObserver = messageId -> pagingController.onDataItemInserted(messageId, 0);
this.toolbarBottom = new MutableLiveData<>();
this.inlinePlayerHeight = new MutableLiveData<>();
this.scrollDateTopMargin = Transformations.distinctUntilChanged(LiveDataUtil.combineLatest(toolbarBottom, inlinePlayerHeight, Integer::sum));
@@ -106,7 +111,9 @@ public class ConversationViewModel extends ViewModel {
return conversationData;
});
LiveData<Pair<Long, PagedData<ConversationMessage>>> pagedDataForThreadId = Transformations.map(metadata, data -> {
ApplicationDependencies.getDatabaseObserver().registerMessageUpdateObserver(messageUpdateObserver);
LiveData<Pair<Long, PagedData<MessageId, ConversationMessage>>> pagedDataForThreadId = Transformations.map(metadata, data -> {
int startPosition;
ConversationData.MessageRequestData messageRequestData = data.getMessageRequestData();
@@ -120,8 +127,10 @@ public class ConversationViewModel extends ViewModel {
startPosition = data.getThreadSize();
}
ApplicationDependencies.getDatabaseObserver().unregisterObserver(messageObserver);
ApplicationDependencies.getDatabaseObserver().registerConversationObserver(data.getThreadId(), messageObserver);
ApplicationDependencies.getDatabaseObserver().unregisterObserver(conversationObserver);
ApplicationDependencies.getDatabaseObserver().unregisterObserver(messageInsertObserver);
ApplicationDependencies.getDatabaseObserver().registerConversationObserver(data.getThreadId(), conversationObserver);
ApplicationDependencies.getDatabaseObserver().registerMessageInsertObserver(data.getThreadId(), messageInsertObserver);
ConversationDataSource dataSource = new ConversationDataSource(context, data.getThreadId(), messageRequestData, data.showUniversalExpireTimerMessage());
PagingConfig config = new PagingConfig.Builder().setPageSize(25)
@@ -292,7 +301,9 @@ public class ConversationViewModel extends ViewModel {
@Override
protected void onCleared() {
super.onCleared();
ApplicationDependencies.getDatabaseObserver().unregisterObserver(messageObserver);
ApplicationDependencies.getDatabaseObserver().unregisterObserver(conversationObserver);
ApplicationDependencies.getDatabaseObserver().unregisterObserver(messageUpdateObserver);
ApplicationDependencies.getDatabaseObserver().unregisterObserver(messageInsertObserver);
EventBus.getDefault().unregister(this);
}