Fix message jump-to-position.

This commit is contained in:
Alex Hart 2020-06-10 17:06:40 -03:00 committed by GitHub
parent bf40a07bb9
commit e13f3254ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 23 additions and 49 deletions

View file

@ -30,16 +30,13 @@ class ConversationDataSource extends PositionalDataSource<MessageRecord> {
private final Context context; private final Context context;
private final long threadId; private final long threadId;
private final DataUpdatedCallback dataUpdateCallback;
private ConversationDataSource(@NonNull Context context, private ConversationDataSource(@NonNull Context context,
long threadId, long threadId,
@NonNull Invalidator invalidator, @NonNull Invalidator invalidator)
@NonNull DataUpdatedCallback dataUpdateCallback)
{ {
this.context = context; this.context = context;
this.threadId = threadId; this.threadId = threadId;
this.dataUpdateCallback = dataUpdateCallback;
ContentObserver contentObserver = new ContentObserver(null) { ContentObserver contentObserver = new ContentObserver(null) {
@Override @Override
@ -82,7 +79,6 @@ class ConversationDataSource extends PositionalDataSource<MessageRecord> {
SizeFixResult result = ensureMultipleOfPageSize(records, params.requestedStartPosition, params.pageSize, totalCount); SizeFixResult result = ensureMultipleOfPageSize(records, params.requestedStartPosition, params.pageSize, totalCount);
callback.onResult(result.messages, params.requestedStartPosition, result.total); callback.onResult(result.messages, params.requestedStartPosition, result.total);
Util.runOnMain(dataUpdateCallback::onDataUpdated);
} }
Log.d(TAG, "[Initial Load] " + (System.currentTimeMillis() - start) + " ms" + (isInvalid() ? " -- invalidated" : "")); Log.d(TAG, "[Initial Load] " + (System.currentTimeMillis() - start) + " ms" + (isInvalid() ? " -- invalidated" : ""));
@ -104,10 +100,6 @@ class ConversationDataSource extends PositionalDataSource<MessageRecord> {
callback.onResult(records); callback.onResult(records);
if (!isInvalid()) {
Util.runOnMain(dataUpdateCallback::onDataUpdated);
}
Log.d(TAG, "[Update] " + (System.currentTimeMillis() - start) + " ms" + (isInvalid() ? " -- invalidated" : "")); Log.d(TAG, "[Update] " + (System.currentTimeMillis() - start) + " ms" + (isInvalid() ? " -- invalidated" : ""));
} }
@ -164,18 +156,16 @@ class ConversationDataSource extends PositionalDataSource<MessageRecord> {
private final Context context; private final Context context;
private final long threadId; private final long threadId;
private final Invalidator invalidator; private final Invalidator invalidator;
private final DataUpdatedCallback callback;
Factory(Context context, long threadId, @NonNull Invalidator invalidator, @NonNull DataUpdatedCallback callback) { Factory(Context context, long threadId, @NonNull Invalidator invalidator) {
this.context = context; this.context = context;
this.threadId = threadId; this.threadId = threadId;
this.invalidator = invalidator; this.invalidator = invalidator;
this.callback = callback;
} }
@Override @Override
public @NonNull DataSource<Integer, MessageRecord> create() { public @NonNull DataSource<Integer, MessageRecord> create() {
return new ConversationDataSource(context, threadId, invalidator, callback); return new ConversationDataSource(context, threadId, invalidator);
} }
} }
} }

View file

@ -941,17 +941,15 @@ public class ConversationFragment extends Fragment {
} }
private void moveToMessagePosition(int position, @Nullable Runnable onMessageNotFound) { private void moveToMessagePosition(int position, @Nullable Runnable onMessageNotFound) {
if (position >= 0) { int itemCount = getListAdapter() != null ? getListAdapter().getItemCount() : 0;
list.scrollToPosition(position);
if (getListAdapter() == null || getListAdapter().getItem(position) == null) { if (position >= 0 && position < itemCount) {
Log.i(TAG, "[moveToMessagePosition] Position " + position + " not currently populated. Scheduling a jump."); if (getListAdapter().getItem(position) == null) {
conversationViewModel.scheduleForNextMessageUpdate(() -> { conversationViewModel.onConversationDataAvailable(threadId, position);
list.scrollToPosition(position); deferred.setDeferred(true);
getListAdapter().pulseHighlightItem(position); deferred.defer(() -> moveToMessagePosition(position, onMessageNotFound));
});
} else { } else {
getListAdapter().pulseHighlightItem(position); scrollToStartingPosition(position);
} }
} else { } else {
Log.w(TAG, "[moveToMessagePosition] Tried to navigate to message, but it wasn't found."); Log.w(TAG, "[moveToMessagePosition] Tried to navigate to message, but it wasn't found.");

View file

@ -40,7 +40,6 @@ class ConversationViewModel extends ViewModel {
private final MutableLiveData<Long> threadId; private final MutableLiveData<Long> threadId;
private final LiveData<PagedList<MessageRecord>> messages; private final LiveData<PagedList<MessageRecord>> messages;
private final LiveData<ConversationData> conversationMetadata; private final LiveData<ConversationData> conversationMetadata;
private final List<Runnable> onNextMessageLoad;
private final Invalidator invalidator; private final Invalidator invalidator;
private int jumpToPosition; private int jumpToPosition;
@ -51,28 +50,31 @@ class ConversationViewModel extends ViewModel {
this.conversationRepository = new ConversationRepository(); this.conversationRepository = new ConversationRepository();
this.recentMedia = new MutableLiveData<>(); this.recentMedia = new MutableLiveData<>();
this.threadId = new MutableLiveData<>(); this.threadId = new MutableLiveData<>();
this.onNextMessageLoad = new CopyOnWriteArrayList<>();
this.invalidator = new Invalidator(); this.invalidator = new Invalidator();
LiveData<ConversationData> conversationDataForRequestedThreadId = Transformations.switchMap(threadId, thread -> { LiveData<ConversationData> metadata = Transformations.switchMap(threadId, thread -> {
return conversationRepository.getConversationData(thread, jumpToPosition); LiveData<ConversationData> conversationData = conversationRepository.getConversationData(thread, jumpToPosition);
jumpToPosition = -1;
return conversationData;
}); });
LiveData<Pair<Long, PagedList<MessageRecord>>> messagesForThreadId = Transformations.switchMap(conversationDataForRequestedThreadId, data -> { LiveData<Pair<Long, PagedList<MessageRecord>>> messagesForThreadId = Transformations.switchMap(metadata, data -> {
DataSource.Factory<Integer, MessageRecord> factory = new ConversationDataSource.Factory(context, data.getThreadId(), invalidator, this::onMessagesUpdated); DataSource.Factory<Integer, MessageRecord> factory = new ConversationDataSource.Factory(context, data.getThreadId(), invalidator);
PagedList.Config config = new PagedList.Config.Builder() PagedList.Config config = new PagedList.Config.Builder()
.setPageSize(25) .setPageSize(25)
.setInitialLoadSizeHint(25) .setInitialLoadSizeHint(25)
.build(); .build();
final int startPosition; final int startPosition;
if (jumpToPosition > 0) { if (data.shouldJumpToMessage()) {
startPosition = jumpToPosition; startPosition = data.getJumpToPosition();
} else { } else {
startPosition = data.getLastSeenPosition(); startPosition = data.getLastSeenPosition();
} }
Log.d(TAG, "Starting at position " + startPosition + " :: " + jumpToPosition + " :: " + data.getLastSeenPosition()); Log.d(TAG, "Starting at position " + startPosition + " :: " + data.getJumpToPosition() + " :: " + data.getLastSeenPosition());
return Transformations.map(new LivePagedListBuilder<>(factory, config).setFetchExecutor(ConversationDataSource.EXECUTOR) return Transformations.map(new LivePagedListBuilder<>(factory, config).setFetchExecutor(ConversationDataSource.EXECUTOR)
.setInitialLoadKey(Math.max(startPosition, 0)) .setInitialLoadKey(Math.max(startPosition, 0))
@ -82,13 +84,9 @@ class ConversationViewModel extends ViewModel {
this.messages = Transformations.map(messagesForThreadId, Pair::second); this.messages = Transformations.map(messagesForThreadId, Pair::second);
LiveData<Long> threadIdForLoadedMessages = Transformations.distinctUntilChanged(Transformations.map(messagesForThreadId, Pair::first)); LiveData<Long> distinctThread = Transformations.distinctUntilChanged(threadId);
conversationMetadata = Transformations.switchMap(threadIdForLoadedMessages, m -> { conversationMetadata = Transformations.switchMap(distinctThread, thread -> metadata);
LiveData<ConversationData> data = conversationRepository.getConversationData(m, jumpToPosition);
jumpToPosition = -1;
return data;
});
} }
void onAttachmentKeyboardOpen() { void onAttachmentKeyboardOpen() {
@ -122,24 +120,12 @@ class ConversationViewModel extends ViewModel {
return conversationMetadata.getValue() != null ? conversationMetadata.getValue().getLastSeenPosition() : 0; return conversationMetadata.getValue() != null ? conversationMetadata.getValue().getLastSeenPosition() : 0;
} }
void scheduleForNextMessageUpdate(@NonNull Runnable runnable) {
onNextMessageLoad.add(runnable);
}
@Override @Override
protected void onCleared() { protected void onCleared() {
super.onCleared(); super.onCleared();
invalidator.invalidate(); invalidator.invalidate();
} }
private void onMessagesUpdated() {
for (Runnable runnable : onNextMessageLoad) {
runnable.run();
}
onNextMessageLoad.clear();
}
static class Factory extends ViewModelProvider.NewInstanceFactory { static class Factory extends ViewModelProvider.NewInstanceFactory {
@Override @Override
public @NonNull<T extends ViewModel> T create(@NonNull Class<T> modelClass) { public @NonNull<T extends ViewModel> T create(@NonNull Class<T> modelClass) {