171 lines
6.9 KiB
Java
171 lines
6.9 KiB
Java
package org.thoughtcrime.securesms.conversation;
|
|
|
|
import android.app.Application;
|
|
|
|
import androidx.annotation.MainThread;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.WorkerThread;
|
|
import androidx.lifecycle.LiveData;
|
|
import androidx.lifecycle.MutableLiveData;
|
|
import androidx.lifecycle.Transformations;
|
|
import androidx.lifecycle.ViewModel;
|
|
import androidx.lifecycle.ViewModelProvider;
|
|
import androidx.paging.DataSource;
|
|
import androidx.paging.LivePagedListBuilder;
|
|
import androidx.paging.PagedList;
|
|
|
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
|
import org.thoughtcrime.securesms.logging.Log;
|
|
import org.thoughtcrime.securesms.mediasend.Media;
|
|
import org.thoughtcrime.securesms.mediasend.MediaRepository;
|
|
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
|
import org.thoughtcrime.securesms.util.paging.Invalidator;
|
|
import org.whispersystems.libsignal.util.Pair;
|
|
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
|
|
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<ConversationMessage>> messages;
|
|
private final LiveData<ConversationData> conversationMetadata;
|
|
private final Invalidator invalidator;
|
|
|
|
private int jumpToPosition;
|
|
|
|
private ConversationViewModel() {
|
|
this.context = ApplicationDependencies.getApplication();
|
|
this.mediaRepository = new MediaRepository();
|
|
this.conversationRepository = new ConversationRepository();
|
|
this.recentMedia = new MutableLiveData<>();
|
|
this.threadId = new MutableLiveData<>();
|
|
this.invalidator = new Invalidator();
|
|
|
|
LiveData<ConversationData> metadata = Transformations.switchMap(threadId, thread -> {
|
|
LiveData<ConversationData> conversationData = conversationRepository.getConversationData(thread, jumpToPosition);
|
|
|
|
jumpToPosition = -1;
|
|
|
|
return conversationData;
|
|
});
|
|
|
|
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()) {
|
|
startPosition = data.getJumpToPosition();
|
|
} else if (data.isMessageRequestAccepted() && data.shouldScrollToLastSeen()) {
|
|
startPosition = data.getLastSeenPosition();
|
|
} else if (data.isMessageRequestAccepted()) {
|
|
startPosition = data.getLastScrolledPosition();
|
|
} else {
|
|
startPosition = data.getThreadSize();
|
|
}
|
|
|
|
Log.d(TAG, "Starting at position startPosition: " + startPosition + " jumpToPosition: " + jumpToPosition + " lastSeenPosition: " + data.getLastSeenPosition() + " lastScrolledPosition: " + data.getLastScrolledPosition());
|
|
|
|
return Transformations.map(new LivePagedListBuilder<>(factory, config).setFetchExecutor(ConversationDataSource.EXECUTOR)
|
|
.setInitialLoadKey(Math.max(startPosition, 0))
|
|
.build(),
|
|
input -> new Pair<>(data.getThreadId(), input));
|
|
});
|
|
|
|
this.messages = Transformations.map(messagesForThreadId, Pair::second);
|
|
|
|
LiveData<DistinctConversationDataByThreadId> distinctData = LiveDataUtil.combineLatest(messagesForThreadId,
|
|
metadata,
|
|
(m, data) -> new DistinctConversationDataByThreadId(data));
|
|
|
|
conversationMetadata = Transformations.map(Transformations.distinctUntilChanged(distinctData), DistinctConversationDataByThreadId::getConversationData);
|
|
}
|
|
|
|
void onAttachmentKeyboardOpen() {
|
|
mediaRepository.getMediaInBucket(context, Media.ALL_MEDIA_BUCKET_ID, recentMedia::postValue);
|
|
}
|
|
|
|
@MainThread
|
|
void onConversationDataAvailable(long threadId, int startingPosition) {
|
|
Log.d(TAG, "[onConversationDataAvailable] threadId: " + threadId + ", startingPosition: " + startingPosition);
|
|
this.jumpToPosition = startingPosition;
|
|
|
|
this.threadId.setValue(threadId);
|
|
}
|
|
|
|
void clearThreadId() {
|
|
this.jumpToPosition = -1;
|
|
this.threadId.postValue(-1L);
|
|
}
|
|
|
|
@NonNull LiveData<List<Media>> getRecentMedia() {
|
|
return recentMedia;
|
|
}
|
|
|
|
@NonNull LiveData<ConversationData> getConversationMetadata() {
|
|
return conversationMetadata;
|
|
}
|
|
|
|
@NonNull LiveData<PagedList<ConversationMessage>> getMessages() {
|
|
return messages;
|
|
}
|
|
|
|
long getLastSeen() {
|
|
return conversationMetadata.getValue() != null ? conversationMetadata.getValue().getLastSeen() : 0;
|
|
}
|
|
|
|
int getLastSeenPosition() {
|
|
return conversationMetadata.getValue() != null ? conversationMetadata.getValue().getLastSeenPosition() : 0;
|
|
}
|
|
|
|
@Override
|
|
protected void onCleared() {
|
|
super.onCleared();
|
|
invalidator.invalidate();
|
|
}
|
|
|
|
static class Factory extends ViewModelProvider.NewInstanceFactory {
|
|
@Override
|
|
public @NonNull<T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
|
//noinspection ConstantConditions
|
|
return modelClass.cast(new ConversationViewModel());
|
|
}
|
|
}
|
|
|
|
private static class DistinctConversationDataByThreadId {
|
|
private final ConversationData conversationData;
|
|
|
|
private DistinctConversationDataByThreadId(@NonNull ConversationData conversationData) {
|
|
this.conversationData = conversationData;
|
|
}
|
|
|
|
public @NonNull ConversationData getConversationData() {
|
|
return conversationData;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
if (this == o) return true;
|
|
if (o == null || getClass() != o.getClass()) return false;
|
|
DistinctConversationDataByThreadId that = (DistinctConversationDataByThreadId) o;
|
|
return Objects.equals(conversationData.getThreadId(), that.conversationData.getThreadId());
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hash(conversationData.getThreadId());
|
|
}
|
|
}
|
|
}
|