From f2fe81d9b55fc39e10d3c5d3aace88f0b593cd60 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 12 Jun 2020 17:23:40 -0300 Subject: [PATCH] Fix conversation jumping when loading at last scroll position. --- .../conversation/ConversationFragment.java | 8 +++-- .../conversation/ConversationViewModel.java | 33 +++++++++++++++++-- .../securesms/util/SnapToTopDataObserver.java | 2 -- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index 16e21c5f2f..d5a1ba1d59 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -424,7 +424,6 @@ public class ConversationFragment extends Fragment { this.threadId = this.getActivity().getIntent().getLongExtra(ConversationActivity.THREAD_ID_EXTRA, -1); this.unknownSenderView = new UnknownSenderView(getActivity(), recipient.get(), threadId, () -> clearHeaderIfNotTyping(getListAdapter())); - snapToTopDataObserver.requestScrollPosition(startingPosition); conversationViewModel.onConversationDataAvailable(threadId, startingPosition); OnScrollListener scrollListener = new ConversationScrollListener(getActivity()); @@ -966,7 +965,12 @@ public class ConversationFragment extends Fragment { private void moveToMessagePosition(int position, @Nullable Runnable onMessageNotFound) { conversationViewModel.onConversationDataAvailable(threadId, position); snapToTopDataObserver.buildScrollPosition(position) - .withOnScrollRequestComplete(() -> getListAdapter().pulseHighlightItem(position)) + .withOnPerformScroll(((layoutManager, p) -> + list.post(() -> { + layoutManager.scrollToPosition(p); + getListAdapter().pulseHighlightItem(position); + }) + )) .withOnInvalidPosition(() -> { if (onMessageNotFound != null) { onMessageNotFound.run(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationViewModel.java index 727f0c0140..5a6cc400c0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationViewModel.java @@ -17,10 +17,12 @@ 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 { @@ -81,9 +83,11 @@ class ConversationViewModel extends ViewModel { this.messages = Transformations.map(messagesForThreadId, Pair::second); - LiveData distinctThread = Transformations.distinctUntilChanged(Transformations.map(messagesForThreadId, Pair::first)); + LiveData distinctData = LiveDataUtil.combineLatest(messagesForThreadId, + metadata, + (m, data) -> new DistinctConversationDataByThreadId(data)); - conversationMetadata = Transformations.switchMap(distinctThread, thread -> metadata); + conversationMetadata = Transformations.map(Transformations.distinctUntilChanged(distinctData), DistinctConversationDataByThreadId::getConversationData); } void onAttachmentKeyboardOpen() { @@ -130,4 +134,29 @@ class ConversationViewModel extends ViewModel { 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()); + } + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SnapToTopDataObserver.java b/app/src/main/java/org/thoughtcrime/securesms/util/SnapToTopDataObserver.java index da3dd98b86..7b8039b959 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/SnapToTopDataObserver.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SnapToTopDataObserver.java @@ -77,10 +77,8 @@ public class SnapToTopDataObserver extends RecyclerView.AdapterDataObserver { if (!scrollRequestValidator.isPositionStillValid(position)) { onInvalidPosition.run(); } else if (scrollRequestValidator.isItemAtPositionLoaded(position)) { - recyclerView.post(() -> { onPerformScroll.onPerformScroll(layoutManager, position); onScrollRequestComplete.run(); - }); } else { deferred.setDeferred(true); deferred.defer(() -> requestScrollPositionInternal(position, onPerformScroll, onScrollRequestComplete, onInvalidPosition));